diff --git a/__tests__/unit/lib/filters.test.js b/__tests__/unit/lib/filters.test.js index cc9374e..cae4dce 100644 --- a/__tests__/unit/lib/filters.test.js +++ b/__tests__/unit/lib/filters.test.js @@ -94,6 +94,65 @@ describe("filterOnly", () => { const expectedQuery = [{ free: true }] expect(filters.filterOnly(only)).toEqual(expectedQuery) }) + + it('should return a query object with local_offer: true if only includes "local-offer"', () => { + const localOffer = ["local-offer"] + const expectedQuery = [{ local_offer: { $exists: true, $ne: null } }] + expect(filters.filterOnly(localOffer)).toEqual(expectedQuery) + }) + + it('should return a query object with needs_referral: true if only includes "needs-referral"', () => { + const needsReferral = ["needs-referral"] + const expectedQuery = [{ needs_referral: true }] + expect(filters.filterOnly(needsReferral)).toEqual(expectedQuery) + }) +}) + +describe("filterMeta", () => { + it("should return an empty array if meta is not provided", () => { + expect(filters.filterMeta()).toEqual([]) + }) + it("should return a query object with key and value if meta includes information", () => { + expect( + filters.filterMeta([ + { key: "service-meta-key", value: "service-meta-value" }, + ]) + ).toEqual([ + { + meta: { + $elemMatch: { + key: "service-meta-key", + value: "service-meta-value", + }, + }, + }, + ]) + }) + it("should return a query object with each key and value if meta includes information", () => { + expect( + filters.filterMeta([ + { key: "service-meta-key", value: "service-meta-value" }, + { key: "service-meta-key-2", value: "service-meta-value-2" }, + ]) + ).toEqual([ + { + meta: { + $elemMatch: { + key: "service-meta-key", + value: "service-meta-value", + }, + }, + }, + { + meta: { + $elemMatch: { + key: "service-meta-key-2", + value: "service-meta-value-2", + }, + }, + }, + ]) + }) }) describe("filterTaxonomies", () => { diff --git a/__tests__/unit/v1/services/routes/get-services.test.js b/__tests__/unit/v1/services/routes/get-services.test.js index 6f33cfc..d71ef70 100644 --- a/__tests__/unit/v1/services/routes/get-services.test.js +++ b/__tests__/unit/v1/services/routes/get-services.test.js @@ -36,6 +36,7 @@ describe("get-services", () => { endDate: undefined, accessibilities: [], only: [], + meta: [], minAge: undefined, maxAge: undefined, interpreted_location: undefined, @@ -424,6 +425,44 @@ describe("get-services", () => { }) }) + describe("meta", () => { + it("should return undefined if only are not provided", async () => { + const { meta } = await parseRequestParameters({}) + expect(meta).toEqual([]) + }) + it("should cope with multiple dividers", async () => { + const { meta } = await parseRequestParameters({ + meta: "service-meta-key:service-meta:-value", + }) + expect(new Set(meta)).toEqual( + new Set([{ key: "service-meta-key", value: "service-meta:-value" }]) + ) + }) + it("should return a unique array if one target is passed through", async () => { + const { meta } = await parseRequestParameters({ + meta: "service-meta-key:service-meta-value", + }) + expect(new Set(meta)).toEqual( + new Set([{ key: "service-meta-key", value: "service-meta-value" }]) + ) + }) + it("should return a unique array one target is passed through", async () => { + const { meta } = await parseRequestParameters({ + meta: [ + "service-meta-key:service-meta-value", + "service-meta-key-2:service-meta-value-2", + ], + }) + console.log(meta) + expect(new Set(meta)).toEqual( + new Set([ + { key: "service-meta-key", value: "service-meta-value" }, + { key: "service-meta-key-2", value: "service-meta-value-2" }, + ]) + ) + }) + }) + describe("minAge", () => { it("should return undefined if minAge is not provided", async () => { const { minAge } = await parseRequestParameters({}) diff --git a/src/controllers/v1/services/routes/get-services.js b/src/controllers/v1/services/routes/get-services.js index 7e52be7..9dd327f 100644 --- a/src/controllers/v1/services/routes/get-services.js +++ b/src/controllers/v1/services/routes/get-services.js @@ -44,6 +44,8 @@ module.exports = { // days = days=Monday&days=Tuesday - deprecated let daysDeprecated = queryParams?.days ? [].concat(queryParams.days) : [] let only = queryParams?.only ? [].concat(queryParams.only) : [] + let meta = queryParams?.meta ? [].concat(queryParams.meta) : [] + const minAge = parseInt(queryParams.min_age) || undefined const maxAge = parseInt(queryParams.max_age) || undefined @@ -72,6 +74,14 @@ module.exports = { ] daysDeprecated = [...new Set(daysDeprecated.flatMap(str => str.split(",")))] only = [...new Set(only.flatMap(str => str.split(",")))] + meta = [...new Set(meta.flatMap(str => str.split(",")))] + + // split meta into key values + meta = meta.map(str => { + const [key, ...valueParts] = str.split(":") + const value = valueParts.join(":") // Join the rest of the parts to handle cases where the value contains colons + return { key, value } + }) // we dont de-dupe these as they are used in pairs startTime = [...startTime.flatMap(str => str.split(","))] @@ -123,6 +133,7 @@ module.exports = { endDate, accessibilities, only, + meta, minAge, maxAge, interpreted_location, diff --git a/src/lib/filters.js b/src/lib/filters.js index d538a44..5862bb4 100644 --- a/src/lib/filters.js +++ b/src/lib/filters.js @@ -56,16 +56,42 @@ const filters = { // filters by only // only=free + // only=needs-referral + // only=local-offer filterOnly: only => { let query = [] if (only) { if (only.includes("free")) query.push({ free: true }) + if (only.includes("needs-referral")) query.push({ needs_referral: true }) + if (only.includes("local-offer")) + query.push({ local_offer: { $exists: true, $ne: null } }) // if(only.includes("open-weekends")) query["regular_schedules.weekday"] = { $in: [ "Saturday", "Sunday"] } // if(only.includes("open-after-six")) query["regular_schedules.closes_at"] = { $gte: "18:00"} } return query }, + // filters by meta + // meta-service-meta-key=service-meta-value + filterMeta: meta => { + let query = [] + if (meta) { + meta.forEach(m => { + if (m.key && m.value) { + query.push({ + meta: { + $elemMatch: { + key: m.key, + value: m.value, + }, + }, + }) + } + }) + } + return query + }, + /** * Specify the taxonomies to search for services * We are using $in to match any of the taxonomies diff --git a/src/lib/queries.js b/src/lib/queries.js index 1b6cc34..63ef299 100644 --- a/src/lib/queries.js +++ b/src/lib/queries.js @@ -65,6 +65,10 @@ module.exports = { const only = filters.filterOnly(parameters.only) query.$and.push(...only) + // apply meta filters + const meta = filters.filterMeta(parameters.meta) + query.$and.push(...meta) + // apply visibility filtering const visibleNow = filters.visibleNow() query.$and.push(...visibleNow) diff --git a/src/routes/parameters.yml b/src/routes/parameters.yml index 9f1dd65..6f8c662 100644 --- a/src/routes/parameters.yml +++ b/src/routes/parameters.yml @@ -133,12 +133,22 @@ parameters: only: name: only in: query - description: Find services that are only x. Currently only free is supported. eg/ "free" + description: Find services that are only x. Currently only free, needs-referral and local-offer are supported. eg/ "free" required: false schema: type: enum enum: - "free" + - "needs-referral" + - "local-offer" + + meta: + name: meta + in: query + description: Filter by meta data. eg/ "meta=foo:bar" + required: false + schema: + type: string min_age: name: min_age diff --git a/src/routes/routes.js b/src/routes/routes.js index 4873ddb..87ebe8c 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -25,6 +25,7 @@ module.exports.setup = app => { * - $ref: '#/parameters/accessibilities' * - $ref: '#/parameters/days' * - $ref: '#/parameters/only' + * - $ref: '#/parameters/meta' * - $ref: '#/parameters/min_age' * - $ref: '#/parameters/max_age' * - $ref: '#/parameters/start_time'