From ed23b310c90f388af180228c8a8dd9cfe79b71d0 Mon Sep 17 00:00:00 2001 From: Evan Hahn Date: Tue, 26 Nov 2024 20:11:19 +0000 Subject: [PATCH] feat: support more geometry types In addition to `Point`, we now support `Polygon`, `MultiPoint`, and `MultiPolygon` geometries. Closes [#35]. [#35]: https://github.com/digidem/comapeo-cloud/issues/35 --- src/schemas.js | 26 ++++++++++-- test/add-alerts-endpoint.js | 83 ++++++++++++++++++------------------- 2 files changed, 63 insertions(+), 46 deletions(-) diff --git a/src/schemas.js b/src/schemas.js index 11a7917..7ab8681 100644 --- a/src/schemas.js +++ b/src/schemas.js @@ -52,6 +52,8 @@ export const observationResult = Type.Object({ ), }) +const position = Type.Tuple([longitude, latitude]) + export const remoteDetectionAlertToAdd = Type.Object({ detectionDateStart: dateTimeString, detectionDateEnd: dateTimeString, @@ -68,8 +70,24 @@ 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('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 })), + ), + }), + ]), }) diff --git a/test/add-alerts-endpoint.js b/test/add-alerts-endpoint.js index 66bf45b..fe96799 100644 --- a/test/add-alerts-endpoint.js +++ b/test/add-alerts-endpoint.js @@ -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) => { @@ -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 }, @@ -177,32 +166,48 @@ 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 sourceIds = alerts.map((a) => a.sourceId) + const expectedSourceIds = new Set(sourceIds) + assert.equal( + sourceIds.length, + expectedSourceIds.size, + 'test setup: all alerts should have unique source IDs', + ) + // 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', + ) }) }) @@ -223,23 +228,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) }) }