fixed missing endpoint GET /routes/:id/pois/:id and added json style fileupload
This commit is contained in:
+16
-7
@@ -20,12 +20,12 @@ test('complete REST API documentation is kept outside the README', async () => {
|
||||
'GET /api/routes/:id',
|
||||
'GET /api/routes/:id/gpx',
|
||||
'GET /api/routes/:id/pois',
|
||||
'GET /api/pois/:id',
|
||||
'GET /api/routes/:routeId/pois/:poiId',
|
||||
'POST /api/routes',
|
||||
'PUT /api/routes/:id',
|
||||
'POST /api/routes/:id/append',
|
||||
'POST /api/routes/:id/pois',
|
||||
'PUT /api/pois/:id',
|
||||
'PUT /api/routes/:routeId/pois/:poiId',
|
||||
'GET /api/routes/:routeId/pois/:poiId/pictures',
|
||||
'GET /api/routes/:routeId/pois/:poiId/pictures/:pictureId',
|
||||
'POST /api/routes/:routeId/pois/:poiId/pictures',
|
||||
@@ -44,7 +44,8 @@ test('complete REST API documentation is kept outside the README', async () => {
|
||||
for (const parameter of [
|
||||
'lat', 'lon', 'radiusKm', 'includeDeleted', 'gpx', 'name', 'slug',
|
||||
'description', 'schoolName', 'title', 'triggerRadiusM', 'sequence',
|
||||
'picture', 'pictureId', 'caption', 'audio', 'poiId', 'metadata'
|
||||
'picture', 'pictureId', 'caption', 'audio', 'poiId', 'metadata',
|
||||
'filename', 'contentType', 'base64', 'dataUrl'
|
||||
]) {
|
||||
assert.match(api, new RegExp(`\\b${parameter}\\b`), `missing parameter documentation: ${parameter}`);
|
||||
}
|
||||
@@ -54,16 +55,20 @@ test('POI media uploads use POI-scoped single-resource endpoints', async () => {
|
||||
const router = await read('src/routes/api.js');
|
||||
const routesService = await read('src/services/routes-service.js');
|
||||
const mediaService = await read('src/services/media-service.js');
|
||||
const api = await read('docs/REST-API.md');
|
||||
|
||||
assert.match(router, /post\('\/routes\/:routeId\/pois\/:poiId\/pictures', upload\.single\('picture'\)/);
|
||||
assert.match(router, /put\('\/routes\/:routeId\/pois\/:poiId\/pictures\/:pictureId', upload\.single\('picture'\)/);
|
||||
assert.match(router, /post\('\/routes\/:routeId\/pois\/:poiId\/pictures', upload\.single\('picture'\), jsonUpload\('picture'\)/);
|
||||
assert.match(router, /put\('\/routes\/:routeId\/pois\/:poiId\/pictures\/:pictureId', upload\.single\('picture'\), jsonUpload\('picture'\)/);
|
||||
assert.match(router, /delete\('\/routes\/:routeId\/pois\/:poiId\/pictures\/:pictureId'/);
|
||||
assert.match(router, /get\('\/routes\/:routeId\/pois\/:poiId\/audio'/);
|
||||
assert.match(router, /post\('\/routes\/:routeId\/pois\/:poiId\/audio', upload\.single\('audio'\)/);
|
||||
assert.match(router, /put\('\/routes\/:routeId\/pois\/:poiId\/audio', upload\.single\('audio'\)/);
|
||||
assert.match(router, /post\('\/routes\/:routeId\/pois\/:poiId\/audio', upload\.single\('audio'\), jsonUpload\('audio'\)/);
|
||||
assert.match(router, /put\('\/routes\/:routeId\/pois\/:poiId\/audio', upload\.single\('audio'\), jsonUpload\('audio'\)/);
|
||||
assert.match(router, /delete\('\/routes\/:routeId\/pois\/:poiId\/audio'/);
|
||||
assert.doesNotMatch(router, /\/routes\/:id\/(?:pictures|audio)/);
|
||||
assert.match(router, /post\('\/routes\/:id\/pois', upload\.none\(\),/);
|
||||
assert.match(router, /get\('\/routes\/:routeId\/pois\/:poiId'/);
|
||||
assert.match(router, /put\('\/routes\/:routeId\/pois\/:poiId', upload\.none\(\)/);
|
||||
assert.doesNotMatch(router, /['"]\/pois\/:id['"]/);
|
||||
assert.doesNotMatch(routesService, /files\.images|files\.audio/);
|
||||
assert.match(mediaService, /caption/);
|
||||
assert.match(mediaService, /removeStoredFile/);
|
||||
@@ -73,4 +78,8 @@ test('POI media uploads use POI-scoped single-resource endpoints', async () => {
|
||||
assert.doesNotMatch(router, /['"`]\/media\//);
|
||||
assert.doesNotMatch(routesService, /safeMediaUrl|\/media\//);
|
||||
assert.doesNotMatch(mediaService, /safeMediaUrl|\/media\//);
|
||||
assert.match(router, /jsonUpload/);
|
||||
assert.match(api, /Content-Type: `application\/json`/);
|
||||
assert.match(api, /`text\/json`/);
|
||||
assert.match(api, /Base64/);
|
||||
});
|
||||
|
||||
+73
-3
@@ -114,6 +114,27 @@ test('pictures and audio are managed as individual REST resources', { timeout: 3
|
||||
})
|
||||
}, 201);
|
||||
|
||||
const { body: fetchedPoi } = await requestJson(
|
||||
`${baseUrl}/api/routes/${route.id}/pois/${poi.id}`
|
||||
);
|
||||
assert.equal(fetchedPoi.id, poi.id);
|
||||
assert.equal(fetchedPoi.routeId, route.id);
|
||||
assert.equal(fetchedPoi.title, 'Teststation');
|
||||
|
||||
const { body: updatedPoi } = await requestJson(
|
||||
`${baseUrl}/api/routes/${route.id}/pois/${poi.id}`,
|
||||
{
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ title: 'Aktualisierte Teststation' })
|
||||
}
|
||||
);
|
||||
assert.equal(updatedPoi.title, 'Aktualisierte Teststation');
|
||||
|
||||
await requestJson(`${baseUrl}/api/routes/999999/pois/${poi.id}`, {}, 404);
|
||||
await requestJson(`${baseUrl}/api/routes/${route.id}/pois/999999`, {}, 404);
|
||||
await requestJson(`${baseUrl}/api/pois/${poi.id}`, {}, 404);
|
||||
|
||||
const pictureForm = new FormData();
|
||||
pictureForm.append('poiId', String(otherPoi.id));
|
||||
pictureForm.append('caption', 'Erste Bildbeschreibung');
|
||||
@@ -144,6 +165,40 @@ test('pictures and audio are managed as individual REST resources', { timeout: 3
|
||||
assert.equal(pictureList.pictures.length, 1);
|
||||
assert.equal(pictureList.pictures[0].id, picture.id);
|
||||
|
||||
const jsonPicturePayload = {
|
||||
caption: 'Bild aus JSON',
|
||||
sequence: 1,
|
||||
picture: {
|
||||
filename: 'bild-json.webp',
|
||||
contentType: 'image/webp',
|
||||
base64: Buffer.from('picture-json').toString('base64')
|
||||
}
|
||||
};
|
||||
const { body: jsonPicture } = await requestJson(
|
||||
`${baseUrl}/api/routes/${route.id}/pois/${otherPoi.id}/pictures`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'text/json' },
|
||||
body: JSON.stringify(jsonPicturePayload)
|
||||
},
|
||||
201
|
||||
);
|
||||
assert.equal(jsonPicture.poiId, otherPoi.id);
|
||||
assert.equal(jsonPicture.caption, 'Bild aus JSON');
|
||||
const { response: jsonPictureResponse, body: jsonPictureFile } = await requestBuffer(`${baseUrl}${jsonPicture.url}`);
|
||||
assert.match(jsonPictureResponse.headers.get('content-type') || '', /image\/webp/);
|
||||
assert.equal(jsonPictureFile.toString(), 'picture-json');
|
||||
|
||||
await requestJson(
|
||||
`${baseUrl}/api/routes/${route.id}/pois/${otherPoi.id}/pictures`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'text/json' },
|
||||
body: '{}'
|
||||
},
|
||||
400
|
||||
);
|
||||
|
||||
const { body: updatedPicture } = await requestJson(
|
||||
`${baseUrl}/api/routes/${route.id}/pois/${poi.id}/pictures/${picture.id}`,
|
||||
{
|
||||
@@ -178,11 +233,20 @@ test('pictures and audio are managed as individual REST resources', { timeout: 3
|
||||
body: duplicateAudio
|
||||
}, 409);
|
||||
|
||||
const replacementAudio = new FormData();
|
||||
replacementAudio.append('audio', new Blob(['audio-two'], { type: 'audio/ogg' }), 'ansage-neu.ogg');
|
||||
const replacementAudio = {
|
||||
audio: {
|
||||
filename: 'ansage-neu.ogg',
|
||||
contentType: 'audio/ogg',
|
||||
base64: Buffer.from('audio-two').toString('base64')
|
||||
}
|
||||
};
|
||||
const { body: replacedAudio } = await requestJson(
|
||||
`${baseUrl}/api/routes/${route.id}/pois/${poi.id}/audio`,
|
||||
{ method: 'PUT', body: replacementAudio }
|
||||
{
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(replacementAudio)
|
||||
}
|
||||
);
|
||||
assert.equal(replacedAudio.url, `/api/routes/${route.id}/pois/${poi.id}/audio`);
|
||||
const { response: replacedAudioResponse, body: replacedAudioFile } = await requestBuffer(`${baseUrl}${replacedAudio.url}`);
|
||||
@@ -214,6 +278,12 @@ test('pictures and audio are managed as individual REST resources', { timeout: 3
|
||||
);
|
||||
assert.equal(deletedPicture.deleted, true);
|
||||
|
||||
const { body: deletedJsonPicture } = await requestJson(
|
||||
`${baseUrl}/api/routes/${route.id}/pois/${otherPoi.id}/pictures/${jsonPicture.id}`,
|
||||
{ method: 'DELETE' }
|
||||
);
|
||||
assert.equal(deletedJsonPicture.deleted, true);
|
||||
|
||||
await requestJson(`${baseUrl}/api/routes/${route.id}/pois/${poi.id}/pictures/${picture.id}`, {}, 404);
|
||||
await requestJson(`${baseUrl}/api/routes/${route.id}/pois/${poi.id}/audio`, {}, 404);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user