remove /media endpoint
This commit is contained in:
+12
-8
@@ -18,6 +18,7 @@ test('complete REST API documentation is kept outside the README', async () => {
|
||||
'GET /api/health',
|
||||
'GET /api/routes',
|
||||
'GET /api/routes/:id',
|
||||
'GET /api/routes/:id/gpx',
|
||||
'GET /api/routes/:id/pois',
|
||||
'GET /api/pois/:id',
|
||||
'POST /api/routes',
|
||||
@@ -31,9 +32,10 @@ test('complete REST API documentation is kept outside the README', async () => {
|
||||
'PUT /api/routes/:id/pictures/:pictureId',
|
||||
'DELETE /api/routes/:id/pictures/:pictureId',
|
||||
'GET /api/routes/:id/audio',
|
||||
'GET /api/routes/:id/audio/:poiId',
|
||||
'POST /api/routes/:id/audio',
|
||||
'PUT /api/routes/:id/audio',
|
||||
'DELETE /api/routes/:id/audio',
|
||||
'PUT /api/routes/:id/audio/:poiId',
|
||||
'DELETE /api/routes/:id/audio/:poiId',
|
||||
'DELETE /api/routes/:id',
|
||||
'POST /api/routes/:id/restore'
|
||||
]) {
|
||||
@@ -43,7 +45,7 @@ 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'
|
||||
'picture', 'pictureId', 'caption', 'audio', 'poiId', 'metadata'
|
||||
]) {
|
||||
assert.match(api, new RegExp(`\\b${parameter}\\b`), `missing parameter documentation: ${parameter}`);
|
||||
}
|
||||
@@ -58,13 +60,15 @@ test('POI media uploads use dedicated single-resource endpoints', async () => {
|
||||
assert.match(router, /put\('\/routes\/:id\/pictures\/:pictureId', upload\.single\('picture'\)/);
|
||||
assert.match(router, /delete\('\/routes\/:id\/pictures\/:pictureId'/);
|
||||
assert.match(router, /post\('\/routes\/:id\/audio', upload\.single\('audio'\)/);
|
||||
assert.match(router, /put\('\/routes\/:id\/audio', upload\.single\('audio'\)/);
|
||||
assert.match(router, /delete\('\/routes\/:id\/audio'/);
|
||||
assert.match(router, /put\('\/routes\/:id\/audio\/:poiId', upload\.single\('audio'\)/);
|
||||
assert.match(router, /delete\('\/routes\/:id\/audio\/:poiId'/);
|
||||
assert.match(router, /post\('\/routes\/:id\/pois', upload\.none\(\)/);
|
||||
assert.doesNotMatch(routesService, /files\.images|files\.audio/);
|
||||
assert.match(mediaService, /caption/);
|
||||
assert.match(mediaService, /removeStoredFile/);
|
||||
assert.match(mediaService, /pictureApiUrl/);
|
||||
assert.match(mediaService, /audioApiUrl/);
|
||||
assert.doesNotMatch(router, /audio\/:poiId/);
|
||||
assert.match(router, /get\('\/routes\/:id\/gpx'/);
|
||||
assert.match(router, /req\.query\.metadata === 'true'/);
|
||||
assert.doesNotMatch(router, /['"`]\/media\//);
|
||||
assert.doesNotMatch(routesService, /safeMediaUrl|\/media\//);
|
||||
assert.doesNotMatch(mediaService, /safeMediaUrl|\/media\//);
|
||||
});
|
||||
|
||||
+34
-24
@@ -26,11 +26,11 @@ async function requestJson(url, options = {}, expectedStatus = 200) {
|
||||
return { response, body };
|
||||
}
|
||||
|
||||
async function requestBytes(url, expectedStatus = 200) {
|
||||
async function requestBuffer(url, expectedStatus = 200) {
|
||||
const response = await fetch(url);
|
||||
const bytes = Buffer.from(await response.arrayBuffer());
|
||||
assert.equal(response.status, expectedStatus, bytes.toString());
|
||||
return { response, bytes };
|
||||
const body = Buffer.from(await response.arrayBuffer());
|
||||
assert.equal(response.status, expectedStatus);
|
||||
return { response, body };
|
||||
}
|
||||
|
||||
async function waitForServer(baseUrl, child, output) {
|
||||
@@ -47,7 +47,7 @@ async function waitForServer(baseUrl, child, output) {
|
||||
throw new Error(`Serverstart hat das Zeitlimit überschritten.\n${output.join('')}`);
|
||||
}
|
||||
|
||||
test('pictures and per-POI audio are delivered and managed through REST API resources', { timeout: 30000 }, async t => {
|
||||
test('pictures and audio are managed as individual REST resources', { timeout: 30000 }, async t => {
|
||||
const runtime = await fs.mkdtemp(path.join(os.tmpdir(), 'wegwichtel-media-api-'));
|
||||
const port = await freePort();
|
||||
const baseUrl = `http://127.0.0.1:${port}`;
|
||||
@@ -84,6 +84,10 @@ test('pictures and per-POI audio are delivered and managed through REST API reso
|
||||
method: 'POST',
|
||||
body: routeForm
|
||||
}, 201);
|
||||
assert.equal(route.gpxUrl, `/api/routes/${route.id}/gpx`);
|
||||
const { response: gpxResponse, body: downloadedGpx } = await requestBuffer(`${baseUrl}${route.gpxUrl}`);
|
||||
assert.match(gpxResponse.headers.get('content-type') || '', /application\/gpx\+xml/);
|
||||
assert.deepEqual(downloadedGpx, gpx);
|
||||
|
||||
const { body: poi } = await requestJson(`${baseUrl}/api/routes/${route.id}/pois`, {
|
||||
method: 'POST',
|
||||
@@ -111,17 +115,16 @@ test('pictures and per-POI audio are delivered and managed through REST API reso
|
||||
assert.equal(pictureResponse.headers.get('location'), `/api/routes/${route.id}/pictures/${picture.id}`);
|
||||
assert.equal(picture.poiId, poi.id);
|
||||
assert.equal(picture.caption, 'Erste Bildbeschreibung');
|
||||
assert.equal(picture.url, `/api/routes/${route.id}/pictures/${picture.id}`);
|
||||
const { response: pictureFileResponse, body: pictureFile } = await requestBuffer(`${baseUrl}${picture.url}`);
|
||||
assert.match(pictureFileResponse.headers.get('content-type') || '', /image\/png/);
|
||||
assert.equal(pictureFile.toString(), 'picture-one');
|
||||
const { body: pictureMetadata } = await requestJson(`${baseUrl}${picture.url}?metadata=true`);
|
||||
assert.equal(pictureMetadata.caption, 'Erste Bildbeschreibung');
|
||||
|
||||
const { body: pictureList } = await requestJson(`${baseUrl}/api/routes/${route.id}/pictures?poiId=${poi.id}`);
|
||||
assert.equal(pictureList.pictures.length, 1);
|
||||
assert.equal(pictureList.pictures[0].id, picture.id);
|
||||
assert.equal(pictureList.pictures[0].url, `/api/routes/${route.id}/pictures/${picture.id}`);
|
||||
|
||||
const { response: pictureContentResponse, bytes: pictureBytes } = await requestBytes(
|
||||
`${baseUrl}/api/routes/${route.id}/pictures/${picture.id}`
|
||||
);
|
||||
assert.equal(pictureContentResponse.headers.get('content-type'), 'image/png');
|
||||
assert.equal(pictureBytes.toString(), 'picture-one');
|
||||
|
||||
const { body: updatedPicture } = await requestJson(
|
||||
`${baseUrl}/api/routes/${route.id}/pictures/${picture.id}`,
|
||||
@@ -142,8 +145,14 @@ test('pictures and per-POI audio are delivered and managed through REST API reso
|
||||
{ method: 'POST', body: audioForm },
|
||||
201
|
||||
);
|
||||
assert.equal(audioResponse.headers.get('location'), `/api/routes/${route.id}/audio?poiId=${poi.id}`);
|
||||
assert.equal(audioResponse.headers.get('location'), `/api/routes/${route.id}/audio/${poi.id}`);
|
||||
assert.equal(audio.poiId, poi.id);
|
||||
assert.equal(audio.url, `/api/routes/${route.id}/audio/${poi.id}`);
|
||||
const { response: audioFileResponse, body: audioFile } = await requestBuffer(`${baseUrl}${audio.url}`);
|
||||
assert.match(audioFileResponse.headers.get('content-type') || '', /audio\/mpeg/);
|
||||
assert.equal(audioFile.toString(), 'audio-one');
|
||||
const { body: audioMetadata } = await requestJson(`${baseUrl}${audio.url}?metadata=true`);
|
||||
assert.equal(audioMetadata.poiId, poi.id);
|
||||
|
||||
const duplicateAudio = new FormData();
|
||||
duplicateAudio.append('poiId', String(poi.id));
|
||||
@@ -156,24 +165,25 @@ test('pictures and per-POI audio are delivered and managed through REST API reso
|
||||
const replacementAudio = new FormData();
|
||||
replacementAudio.append('audio', new Blob(['audio-two'], { type: 'audio/ogg' }), 'ansage-neu.ogg');
|
||||
const { body: replacedAudio } = await requestJson(
|
||||
`${baseUrl}/api/routes/${route.id}/audio?poiId=${poi.id}`,
|
||||
`${baseUrl}/api/routes/${route.id}/audio/${poi.id}`,
|
||||
{ method: 'PUT', body: replacementAudio }
|
||||
);
|
||||
assert.equal(replacedAudio.url, `/api/routes/${route.id}/audio?poiId=${poi.id}`);
|
||||
assert.equal(replacedAudio.url, `/api/routes/${route.id}/audio/${poi.id}`);
|
||||
const { response: replacedAudioResponse, body: replacedAudioFile } = await requestBuffer(`${baseUrl}${replacedAudio.url}`);
|
||||
assert.match(replacedAudioResponse.headers.get('content-type') || '', /audio\/ogg/);
|
||||
assert.equal(replacedAudioFile.toString(), 'audio-two');
|
||||
|
||||
const { body: routeWithMedia } = await requestJson(`${baseUrl}/api/routes/${route.id}`);
|
||||
assert.equal(routeWithMedia.pois[0].images[0].caption, 'Aktualisierte Bildbeschreibung');
|
||||
assert.equal(routeWithMedia.gpxUrl, `/api/routes/${route.id}/gpx`);
|
||||
assert.equal(routeWithMedia.pois[0].audioUrl, `/api/routes/${route.id}/audio/${poi.id}`);
|
||||
assert.equal(routeWithMedia.pois[0].images[0].url, `/api/routes/${route.id}/pictures/${picture.id}`);
|
||||
assert.equal(routeWithMedia.pois[0].audioUrl, `/api/routes/${route.id}/audio?poiId=${poi.id}`);
|
||||
|
||||
const { response: audioContentResponse, bytes: audioBytes } = await requestBytes(
|
||||
`${baseUrl}/api/routes/${route.id}/audio?poiId=${poi.id}`
|
||||
);
|
||||
assert.equal(audioContentResponse.headers.get('content-type'), 'audio/ogg');
|
||||
assert.equal(audioBytes.toString(), 'audio-two');
|
||||
assert.doesNotMatch(JSON.stringify(routeWithMedia), /\/media\//);
|
||||
const legacyMediaResponse = await fetch(`${baseUrl}/media/routes/${route.id}/route.gpx`);
|
||||
assert.equal(legacyMediaResponse.status, 404);
|
||||
|
||||
const { body: deletedAudio } = await requestJson(
|
||||
`${baseUrl}/api/routes/${route.id}/audio?poiId=${poi.id}`,
|
||||
`${baseUrl}/api/routes/${route.id}/audio/${poi.id}`,
|
||||
{ method: 'DELETE' }
|
||||
);
|
||||
assert.equal(deletedAudio.deleted, true);
|
||||
@@ -185,5 +195,5 @@ test('pictures and per-POI audio are delivered and managed through REST API reso
|
||||
assert.equal(deletedPicture.deleted, true);
|
||||
|
||||
await requestJson(`${baseUrl}/api/routes/${route.id}/pictures/${picture.id}`, {}, 404);
|
||||
await requestJson(`${baseUrl}/api/routes/${route.id}/audio?poiId=${poi.id}`, {}, 404);
|
||||
await requestJson(`${baseUrl}/api/routes/${route.id}/audio/${poi.id}`, {}, 404);
|
||||
});
|
||||
|
||||
@@ -152,8 +152,7 @@ test('reaching a POI preloads audio without autoplay and uses the vibration wrap
|
||||
const html = await read('public/index.html');
|
||||
const loader = await read('public/js/bootstrap-loader.js');
|
||||
|
||||
assert.match(app, /ns\.AudioPlayer\.load\(poi\.audioUrl \? ns\.Api\.audioUrl/);
|
||||
assert.match(app, /ns\.Api\.pictureUrl/);
|
||||
assert.match(app, /ns\.AudioPlayer\.load\(poi\.audioUrl\)/);
|
||||
assert.doesNotMatch(app, /AudioPlayer\.play\(/);
|
||||
assert.match(app, /ns\.Vibration\.start\(ns\.Vibration\.Patterns\.ACTIVE_POI\)/);
|
||||
assert.doesNotMatch(app, /navigator\.vibrate/);
|
||||
|
||||
Reference in New Issue
Block a user