Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TOP-203 add in support for start_time, end_time and day filters in ORUK #59

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
91 changes: 91 additions & 0 deletions __tests__/unit/lib/filters.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,94 @@ describe("filterDays", () => {
expect(filters.filterDays(days)).toEqual(expectedQuery)
})
})

describe("filterStartTimeEndTimeDay", () => {
it("should return a query object if start_time, end_time, and day are provided", () => {
const startTime = ["22:00"]
const endTime = ["22:30"]
const day = ["Monday"]
const expectedQuery = {
$or: [
{
"regular_schedules.opens_at": { $gte: "22:00" },
"regular_schedules.closes_at": { $lte: "22:30" },
"regular_schedules.weekday": "Monday",
},
],
}
expect(filters.filterStartTimeEndTimeDay(startTime, endTime, day)).toEqual(
expectedQuery
)
})

it("should return a query object if only start_time is provided", () => {
const startTime = ["22:00"]
const endTime = []
const day = []
const expectedQuery = {
$or: [
{
"regular_schedules.opens_at": { $gte: "22:00" },
},
],
}
expect(filters.filterStartTimeEndTimeDay(startTime, endTime, day)).toEqual(
expectedQuery
)
})

it("should return a query object if only end_time is provided", () => {
const startTime = []
const endTime = ["22:30"]
const day = []
const expectedQuery = {
$or: [
{
"regular_schedules.closes_at": { $lte: "22:30" },
},
],
}
expect(filters.filterStartTimeEndTimeDay(startTime, endTime, day)).toEqual(
expectedQuery
)
})

it("should return a query object if only day is provided", () => {
const startTime = []
const endTime = []
const day = ["Monday"]
const expectedQuery = {
$or: [
{
"regular_schedules.weekday": "Monday",
},
],
}
expect(filters.filterStartTimeEndTimeDay(startTime, endTime, day)).toEqual(
expectedQuery
)
})

it("should return a query object if multiple sets of start_time, end_time, and day are provided", () => {
const startTime = ["22:00", "22:00"]
const endTime = ["22:30", "22:30"]
const day = ["Monday", "Monday"]
const expectedQuery = {
$or: [
{
"regular_schedules.opens_at": { $gte: "22:00" },
"regular_schedules.closes_at": { $lte: "22:30" },
"regular_schedules.weekday": "Monday",
},
{
"regular_schedules.opens_at": { $gte: "22:00" },
"regular_schedules.closes_at": { $lte: "22:30" },
"regular_schedules.weekday": "Monday",
},
],
}
expect(filters.filterStartTimeEndTimeDay(startTime, endTime, day)).toEqual(
expectedQuery
)
})
})
106 changes: 98 additions & 8 deletions __tests__/unit/v1/services/routes/get-services.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ describe("get-services", () => {
taxonomies: [],
needs: [],
suitabilities: [],
days: [],
daysDeprecated: [],
startTime: [],
endTime: [],
day: [],
accessibilities: [],
only: [],
minAge: undefined,
Expand Down Expand Up @@ -268,22 +271,109 @@ describe("get-services", () => {
})
})

describe("days", () => {
describe("daysDeprecated", () => {
it("should return undefined if days are not provided", async () => {
const { days } = await parseRequestParameters({})
expect(days).toEqual([])
const { daysDeprecated } = await parseRequestParameters({})
expect(daysDeprecated).toEqual([])
})
it("should return a unique array multiple targets are passed through", async () => {
const { days } = await parseRequestParameters({
const { daysDeprecated } = await parseRequestParameters({
days: ["a", "b,a"],
})
expect(new Set(days)).toEqual(new Set(["a", "b"]))
expect(new Set(daysDeprecated)).toEqual(new Set(["a", "b"]))
})
it("should return a unique array one target is passed through", async () => {
const { days } = await parseRequestParameters({
const { daysDeprecated } = await parseRequestParameters({
days: ["a,b"],
})
expect(new Set(days)).toEqual(new Set(["a", "b"]))
expect(new Set(daysDeprecated)).toEqual(new Set(["a", "b"]))
})
})

describe("startTime", () => {
it("should return undefined if days are not provided", async () => {
const { startTime } = await parseRequestParameters({})
expect(startTime).toEqual([])
})
it("should return a non unique array multiple targets are passed through", async () => {
const { startTime } = await parseRequestParameters({
start_time: ["10:00", "10:00,11:00"],
})
expect(startTime).toEqual(["10:00", "10:00", "11:00"])
})
it("should return a non unique array one target is passed through", async () => {
const { startTime } = await parseRequestParameters({
start_time: ["10:00,10:00,11:00"],
})
expect(startTime).toEqual(["10:00", "10:00", "11:00"])
})
})

describe("endTime", () => {
it("should return undefined if days are not provided", async () => {
const { endTime } = await parseRequestParameters({})
expect(endTime).toEqual([])
})
it("should return a non unique array multiple targets are passed through", async () => {
const { endTime } = await parseRequestParameters({
end_time: ["10:00", "10:00,11:00"],
})
expect(endTime).toEqual(["10:00", "10:00", "11:00"])
})
it("should return a non unique array one target is passed through", async () => {
const { endTime } = await parseRequestParameters({
end_time: ["10:00,10:00,11:00"],
})
expect(endTime).toEqual(["10:00", "10:00", "11:00"])
})
})

describe("day", () => {
it("should return undefined if day are not provided", async () => {
const { day } = await parseRequestParameters({})
expect(day).toEqual([])
})
it("should return a non unique array multiple targets are passed through", async () => {
const { day } = await parseRequestParameters({
day: ["MO", "MO,TU"],
})
expect(day).toEqual(["Monday", "Monday", "Tuesday"])
})
it("should return a non unique array one target is passed through", async () => {
const { day } = await parseRequestParameters({
day: ["MO,MO,TU"],
})
expect(day).toEqual(["Monday", "Monday", "Tuesday"])
})
})

describe("startTime, endTime, and day validation", () => {
it("should not throw an error if start_time and end_time are of equal lengths", async () => {
const params = {
start_time: ["10:00,11:00"],
end_time: ["12:00,13:00"],
}
await expect(parseRequestParameters(params)).resolves.not.toThrow()
})

it("should not throw an error if start_time, end_time, and day are of equal lengths", async () => {
const params = {
start_time: ["10:00,11:00"],
end_time: ["12:00,13:00"],
day: ["MO,TU"],
}
await expect(parseRequestParameters(params)).resolves.not.toThrow()
})

it("should throw an error if start_time, end_time, and day are of unequal lengths", async () => {
const params = {
start_time: ["10:00,11:00"],
end_time: ["12:00"],
day: ["MO,TU"],
}
await expect(parseRequestParameters(params)).rejects.toThrow(
"The number of start_time, end_time, and day parameters must be equal if more than one is provided"
)
})
})

Expand Down
39 changes: 35 additions & 4 deletions src/controllers/v1/services/routes/get-services.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
const filters = require("../../../../lib/filters")
const queries = require("../../../../lib/queries")
const { calculateDistance, geocode, projection } = require("../../../../lib")
const {
calculateDistance,
geocode,
projection,
dayMapping,
} = require("../../../../lib")
const { db } = require("../../../../db")
const logger = require("../../../../../utils/logger")
const locations = require("../../../../lib/locations")
Expand Down Expand Up @@ -32,11 +37,18 @@ module.exports = {
let accessibilities = queryParams?.accessibilities
? [].concat(queryParams.accessibilities)
: []
let days = queryParams?.days ? [].concat(queryParams.days) : []
// days = days=Monday&days=Tuesday - deprecated
let daysDeprecated = queryParams?.days ? [].concat(queryParams.days) : []
let only = queryParams?.only ? [].concat(queryParams.only) : []
const minAge = parseInt(queryParams.min_age) || undefined
const maxAge = parseInt(queryParams.max_age) || undefined

let startTime = queryParams?.start_time
? [].concat(queryParams.start_time)
: []
let endTime = queryParams?.end_time ? [].concat(queryParams.end_time) : []
let day = queryParams?.day ? [].concat(queryParams.day) : []

// not a param but we want to save on requests
let interpreted_location

Expand All @@ -51,9 +63,25 @@ module.exports = {
accessibilities = [
...new Set(accessibilities.flatMap(str => str.split(","))),
]
days = [...new Set(days.flatMap(str => str.split(",")))]
daysDeprecated = [...new Set(daysDeprecated.flatMap(str => str.split(",")))]
only = [...new Set(only.flatMap(str => str.split(",")))]

// we dont de-dupe these as they are used in pairs
startTime = [...startTime.flatMap(str => str.split(","))]
endTime = [...endTime.flatMap(str => str.split(","))]
day = [...day.flatMap(str => str.split(","))]
// Convert day abbreviations to full names
day = day.map(d => dayMapping[d] || d)

const lengths = [startTime.length, endTime.length, day.length].filter(
len => len > 0
)
if (lengths.length > 1 && !lengths.every(len => len === lengths[0])) {
throw new Error(
"The number of start_time, end_time, and day parameters must be equal if more than one is provided"
)
}

// if we have a location then we can find lat lng
if (location && !(lat && lng)) {
try {
Expand All @@ -80,7 +108,10 @@ module.exports = {
taxonomies,
needs,
suitabilities,
days,
daysDeprecated,
startTime,
endTime,
day,
accessibilities,
only,
minAge,
Expand Down
44 changes: 41 additions & 3 deletions src/lib/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,9 @@ module.exports = {

/**
* Days
* this has changed from previous iterations since the results returned wouldn't be accurate
* @TODO test http://localhost:3001/api/v1/services?accessibilities=accessible-toilet-facilities
* @TODO test http://localhost:3001/api/v1/services?accessibilities=accessible-toilet-facilities&accessibilities=wheelchair-accessible-entrance
* Returns the regular_schedules.weekday for the days
* @TODO test http://localhost:3001/api/v1/services?days=Monday
* @TODO test http://localhost:3001/api/v1/services?days=Monday&days=Tuesday
* @param {*} needs
* @returns
*/
Expand All @@ -195,4 +195,42 @@ module.exports = {
}
return {}
},

/**
* Filters by opens_at, closes_at and day
* @TODO test http://localhost:3002/api/v1/services?start_time=22:00&end_time=22:30&day=MO
* @TODO test http://localhost:3002/api/v1/services?start_time=22:00
* @TODO test http://localhost:3002/api/v1/services?end_time=22:30
* @TODO test http://localhost:3002/api/v1/services?day=MO
* @TODO test http://localhost:3002/api/v1/services?start_time=22:00&end_time=22:30&day=MO&start_time=22:00&end_time=22:30&day=MO
* @param {*} startTime
* @param {*} endTime
* @param {*} day
* @returns
*/
filterStartTimeEndTimeDay: (startTime, endTime, day) => {
let orConditions = []
const maxLength = Math.max(startTime.length, endTime.length, day.length)

for (let i = 0; i < maxLength; i++) {
let condition = {}
if (startTime[i]) {
condition["regular_schedules.opens_at"] = { $gte: startTime[i] }
}
if (endTime[i]) {
condition["regular_schedules.closes_at"] = { $lte: endTime[i] }
}
if (day[i]) {
condition["regular_schedules.weekday"] = day[i]
}
orConditions.push(condition)
}

let query = {}
if (orConditions.length > 0) {
query.$or = orConditions
}

return query
},
}
13 changes: 13 additions & 0 deletions src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,17 @@ module.exports = {
visible_from: 0,
visible_to: 0,
},

/**
* Used to map the day abbreviation to the full day names
*/
dayMapping: {
SU: "Sunday",
MO: "Monday",
TU: "Tuesday",
WE: "Wednesday",
TH: "Thursday",
FR: "Friday",
SA: "Saturday",
},
}
7 changes: 6 additions & 1 deletion src/lib/queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,12 @@ module.exports = {
filters.filterNeeds(parameters.needs),
filters.filterSuitabilities(parameters.suitabilities),
filters.filterAccessibilities(parameters.accessibilities),
filters.filterDays(parameters.days)
filters.filterDays(parameters.daysDeprecated),
filters.filterStartTimeEndTimeDay(
parameters.startTime,
parameters.endTime,
parameters.day
)
)

// clear empty values
Expand Down
Loading