Skip to content

Commit

Permalink
feat: support more geometry types
Browse files Browse the repository at this point in the history
In addition to `Point`, we now support additional geometry objects.

Closes [#35].

[#35]: #35
  • Loading branch information
EvanHahn committed Dec 19, 2024
1 parent 2b669b9 commit 1847f93
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 47 deletions.
2 changes: 1 addition & 1 deletion src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ async function ensureProjectExists(fastify, req) {
try {
await fastify.comapeo.getProject(req.params.projectPublicId)
} catch (e) {
if (e instanceof Error && e.message.startsWith('NotFound')) {
if (e instanceof Error && e.constructor.name === 'NotFoundError') {
throw errors.projectNotFoundError()
}
throw e
Expand Down
34 changes: 30 additions & 4 deletions src/schemas.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export const observationResult = Type.Object({
),
})

const position = Type.Tuple([longitude, latitude])

export const remoteDetectionAlertToAdd = Type.Object({
detectionDateStart: dateTimeString,
detectionDateEnd: dateTimeString,
Expand All @@ -68,8 +70,32 @@ export const remoteDetectionAlertToAdd = Type.Object({
),
]),
),
geometry: Type.Object({
type: Type.Literal('Point'),
coordinates: Type.Tuple([longitude, latitude]),
}),
geometry: Type.Union([
Type.Object({
type: Type.Literal('Point'),
coordinates: position,
}),
Type.Object({
type: Type.Literal('LineString'),
coordinates: Type.Array(position, { minItems: 2 }),
}),
Type.Object({
type: Type.Literal('MultiLineString'),
coordinates: Type.Array(Type.Array(position, { minItems: 2 })),
}),
Type.Object({
type: Type.Literal('Polygon'),
coordinates: Type.Array(Type.Array(position, { minItems: 4 })),
}),
Type.Object({
type: Type.Literal('MultiPoint'),
coordinates: Type.Array(position),
}),
Type.Object({
type: Type.Literal('MultiPolygon'),
coordinates: Type.Array(
Type.Array(Type.Array(position, { minItems: 4 })),
),
}),
]),
})
77 changes: 35 additions & 42 deletions test/add-alerts-endpoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
runWithRetries,
} from './test-helpers.js'

/** @import { RemoteDetectionAlertValue } from '@comapeo/schema'*/
/** @import { FastifyInstance } from 'fastify' */

test('returns a 401 if no auth is provided', async (t) => {
Expand Down Expand Up @@ -119,16 +118,6 @@ test('returns a 400 if trying to add invalid alerts', async (t) => {
coordinates: [0, 90.01],
},
},
{
...generateAlert(),
geometry: {
type: 'MultiPoint',
coordinates: [
[1, 2],
[3, 4],
],
},
},
...alertKeys.flatMap((keyToMessUp) => [
omit(generateAlert(), keyToMessUp),
{ ...generateAlert(), [keyToMessUp]: null },
Expand Down Expand Up @@ -177,32 +166,42 @@ test('adding alerts', async (t) => {
dangerouslyAllowInsecureConnections: true,
})

const alert = generateAlert()
const alerts = generateAlerts(100)

const response = await server.inject({
authority: serverUrl.host,
method: 'POST',
url: `/projects/${projectId}/remoteDetectionAlerts`,
headers: {
Authorization: 'Bearer ' + BEARER_TOKEN,
'Content-Type': 'application/json',
},
body: JSON.stringify(alert),
})
assert.equal(response.statusCode, 201)
assert.equal(response.body, '')
await Promise.all(
alerts.map(async (alert) => {
const response = await server.inject({
authority: serverUrl.host,
method: 'POST',
url: `/projects/${projectId}/remoteDetectionAlerts`,
headers: {
Authorization: 'Bearer ' + BEARER_TOKEN,
'Content-Type': 'application/json',
},
body: JSON.stringify(alert),
})
assert.equal(response.statusCode, 201)
assert.equal(response.body, '')
}),
)

project.$sync.start()
project.$sync.connectServers()

await project.$sync.waitForSync('full')

const expectedSourceIds = new Set(alerts.map((a) => a.sourceId))

// It's possible that the client thinks it's synced but doesn't know about
// the server's alert yet, so we try a few times.
await runWithRetries(3, async () => {
const alerts = await project.remoteDetectionAlert.getMany()
const hasOurAlert = alerts.some((a) => a.sourceId === alert.sourceId)
assert(hasOurAlert, 'alert was added and synced')
const actualSourceIds = new Set(alerts.map((a) => a.sourceId))
assert.deepEqual(
actualSourceIds,
expectedSourceIds,
'alerts were added and synced',
)
})
})

Expand All @@ -223,23 +222,17 @@ async function addProject(server) {
return projectKeyToPublicId(Buffer.from(projectKey, 'hex'))
}

function generateAlert() {
const [result] = generateAlerts(1)
assert(result)
return result
}

/**
* @param {number} min
* @param {number} max
* @returns {number}
* @param {number} count
*/
const randomNumber = (min, max) => min + Math.random() * (max - min)
const randomLatitude = randomNumber.bind(null, -90, 90)
const randomLongitude = randomNumber.bind(null, -180, 180)

function generateAlert() {
const remoteDetectionAlertDoc = generate('remoteDetectionAlert')[0]
assert(remoteDetectionAlertDoc)
return valueOf({
...remoteDetectionAlertDoc,
geometry: {
type: 'Point',
coordinates: [randomLongitude(), randomLatitude()],
},
function generateAlerts(count) {
return generate('remoteDetectionAlert', { count }).map((alert) => {
return valueOf(alert)
})
}

0 comments on commit 1847f93

Please sign in to comment.