Skip to content

Commit

Permalink
Filter location changes by datetime interval
Browse files Browse the repository at this point in the history
Replaces limit and offset with explicit timestamp interval earliest–latest.

Closes API for activity page #44.
  • Loading branch information
ezwelty committed Nov 9, 2024
1 parent fc8ddfc commit b01a1b6
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 24 deletions.
22 changes: 14 additions & 8 deletions db/repos/changes.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const sql = require('../sql').changes
const e = require('express')
const _ = require('../../helpers')

class Changes {
Expand All @@ -7,21 +8,26 @@ class Changes {
this.pgp = pgp
}

list({limit = '100', offset = '0', user_id = null, range = 'false'}) {
let user_filter
list({earliest = null, latest = null, user_id = null, range = 'false'}) {
earliest = _.parse_datetime(earliest)
latest = _.parse_datetime(latest)
const filters = ['NOT l.hidden']
if (earliest) {
filters.push(`c.created_at >= '${earliest}'::timestamptz`)
}
if (latest) {
filters.push(`c.created_at < '${latest}'::timestamptz`)
}
if (user_id) {
let user_filter
if (range === 'true') {
user_filter = `ST_INTERSECTS(l.location, (SELECT range FROM users u2 WHERE u2.id = ${parseInt(user_id)}))`
} else {
user_filter = `c.user_id = ${parseInt(user_id)}`
}
filters.push(user_filter)
}
const filters = ['NOT l.hidden', user_filter]
const values = {
limit: parseInt(limit),
offset: parseInt(offset),
where: filters.filter(Boolean).join(' AND ')
}
const values = { where: filters.join(' AND ') }
return this.db.any(sql.list, values)
}

Expand Down
2 changes: 0 additions & 2 deletions db/sql/changes/list.sql
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,3 @@ LEFT JOIN users u
ON c.user_id = u.id
WHERE ${where:raw}
ORDER BY c.created_at DESC
LIMIT ${limit}
OFFSET ${offset}
21 changes: 14 additions & 7 deletions docs/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -256,15 +256,22 @@ paths:
- key: []
token: []
parameters:
- name: limit
- name: earliest
in: query
description: Maximum number of changes to return.
description: Earliest UTC datetime of change (inclusive).
schema:
type: integer
minimum: 1
default: 100
nullable: true
- $ref: '#/components/parameters/offset'
type: string
format: date-time
default: null
example: '2024-11-09T00:00:00Z'
- name: latest
in: query
description: Latest UTC datetime of change (exclusive).
schema:
type: string
format: date-time
default: null
example: null
- name: user_id
in: query
description: User ID of changes to return.
Expand Down
21 changes: 14 additions & 7 deletions docs/paths/locations~changes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,22 @@ get:
- key: []
token: []
parameters:
- name: limit
- name: earliest
in: query
description: Maximum number of changes to return.
description: Earliest UTC datetime of change (inclusive).
schema:
type: integer
minimum: 1
default: 100
nullable: true
- $ref: ../components/parameters.yml#/offset
type: string
format: date-time
default: null
example: '2024-11-09T00:00:00Z'
- name: latest
in: query
description: Latest UTC datetime of change (exclusive).
schema:
type: string
format: date-time
default: null
example: null
- name: user_id
in: query
description: User ID of changes to return.
Expand Down
43 changes: 43 additions & 0 deletions helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,49 @@ _.parse_point = function(value) {
return points
}

/**
* Parse date.
*
* @param {string} value – Date in the format 'yyyy-mm-dd'.
* @returns {string} Date in the format 'yyyy-mm-dd' or null.
*/
_.parse_date = function(value) {
if (!value) {
return null
}
// Check that date is yyyy-mm-dd
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
throw Error(`Date not formatted as yyyy-mm-dd: ${value}`)
}
const date = new Date(value)
if (isNaN(date.getTime())) {
throw Error(`Invalid date: ${value}`)
}
return date.toISOString().substring(0, 10)
}

/**
* Parse datetime.
*
* @param {string} value – Datetime in the format 'yyyy-mm-ddTHH:MM:SSZ'
* (decimal seconds are permitted).
* @returns {string} Datetime in the same format, or null.
*/
_.parse_datetime = function(value) {
if (!value) {
return null
}
// Check that datetime is correctly formatted
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/.test(value)) {
throw Error(`Datetime not formatted as yyyy-mm-ddTHH:MM:SSZ: ${value}`)
}
const date = new Date(value)
if (isNaN(date.getTime())) {
throw Error(`Invalid datetime: ${value}`)
}
return value
}

/**
* Convert bounding box to SQL condition.
*
Expand Down

0 comments on commit b01a1b6

Please sign in to comment.