Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ Returns service health.

Returns SQLite ingredient rows and their synonyms.

Use `faceReality=true` to return only ingredients flagged by Face Reality:

```bash
curl -s "http://127.0.0.1:3333/ingredients?faceReality=true&limit=100"
```

### `POST /api/check`

Checks an ingredient list. `/check` is also registered locally, but `/api/check` is the recommended hosted endpoint.
Expand Down
6 changes: 5 additions & 1 deletion examples/chekit-core-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,16 @@ export function createChekItCoreClient({ baseUrl = DEFAULT_BASE_URL, fetchImpl =
return request('/health');
},

listIngredients({ limit = 100, offset = 0 } = {}) {
listIngredients({ limit = 100, offset = 0, faceReality = false } = {}) {
const params = new URLSearchParams({
limit: String(limit),
offset: String(offset)
});

if (faceReality) {
params.set('faceReality', 'true');
}

return request(`/ingredients?${params.toString()}`);
},

Expand Down
27 changes: 27 additions & 0 deletions scripts/smoke.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ upsertIngredients([
description: 'Seed row for local smoke testing.',
comedogenic_score: 4,
face_reality: true
},
{
id: 2,
name: 'myristate',
causes_acne: true,
description: 'Non-Face Reality smoke test row.',
comedogenic_score: 4,
face_reality: false
}
]);

Expand Down Expand Up @@ -47,6 +55,25 @@ try {
}
}

const faceRealityResponse = await server.inject({
method: 'GET',
url: '/ingredients?faceReality=true'
});

if (faceRealityResponse.statusCode !== 200) {
throw new Error(faceRealityResponse.body);
}

const faceRealityBody = JSON.parse(faceRealityResponse.body);
if (
faceRealityBody.onlyFaceReality !== true
|| faceRealityBody.data.length !== 1
|| faceRealityBody.data[0].name !== 'coconut oil'
|| faceRealityBody.data.some((ingredient) => ingredient.faceReality !== true)
) {
throw new Error(JSON.stringify(faceRealityBody, null, 2));
}

console.log('Smoke test passed.');
} finally {
await server.close();
Expand Down
5 changes: 3 additions & 2 deletions src/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,16 +145,17 @@ export function seedKnownSynonyms() {
transaction();
}

export function listIngredients({ limit = 100, offset = 0 } = {}) {
export function listIngredients({ limit = 100, offset = 0, onlyFaceReality = false } = {}) {
const database = initDb();
const rows = database
.prepare(`
SELECT i.*
FROM ingredients i
WHERE (? = 0 OR i.face_reality = 1)
ORDER BY i.name ASC
LIMIT ? OFFSET ?
`)
.all(limit, offset);
.all(onlyFaceReality ? 1 : 0, limit, offset);
const synonymsForIngredient = database.prepare(`
SELECT synonym FROM ingredient_synonyms WHERE ingredient_id = ? ORDER BY synonym ASC
`);
Expand Down
24 changes: 22 additions & 2 deletions src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@ export function buildServer() {
fastify.get('/ingredients', async (request) => {
const limit = Math.min(Number(request.query.limit || 100), 500);
const offset = Math.max(Number(request.query.offset || 0), 0);
const onlyFaceReality = parseBooleanQuery(
request.query.faceReality ?? request.query.onlyFaceReality
);

return {
data: listIngredients({ limit, offset }),
data: listIngredients({ limit, offset, onlyFaceReality }),
limit,
offset
offset,
onlyFaceReality
};
});

Expand Down Expand Up @@ -59,6 +63,22 @@ export function buildServer() {
return fastify;
}

function parseBooleanQuery(value) {
if (Array.isArray(value)) {
return parseBooleanQuery(value[0]);
}

if (typeof value === 'boolean') {
return value;
}

if (typeof value !== 'string') {
return false;
}

return ['1', 'true', 'yes'].includes(value.toLowerCase());
}

if (import.meta.url === `file://${process.argv[1]}`) {
const server = buildServer();
await server.listen({
Expand Down
Loading