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

feat: support more geometry types #36

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
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)
})
}
Loading