aiiinotate 0.3.2 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/endpoints.md CHANGED
@@ -1,8 +1,273 @@
1
1
  # Endpoints
2
2
 
3
+ ## Introductory notes
4
+
5
+ **aiiinotate** is meant to be able to handle both IIIF presentation APIs: the most common [2.x](https://iiif.io/api/presentation/2.1) and the more recent [3.x](https://iiif.io/api/presentation/3.0). Both APIs define a data structure for manifests, annotations, lists of annotations and collections of manifests.
6
+
7
+ **HOWEVER, in aiiinotate, IIIF Presentation v2 and v3 data are isolated**: they form two separate collections, and no conversion is done between IIIF 2.x and 3.x data. This means that:
8
+ - **when communicating with aiiinotate**, you must specify a **IIIF presentation version in the query URL**. In the docs, this is described by the `iiif_version` keyword.
9
+ - **when inserting/updating data**, the data structure you provide must match the URL's `iiif_version`: you can't insert an annotation in v3 if your `iiif_version` is `2`.
10
+ - **when searching for data**, if you inserted an annotation in v3, you must search for it with `iiif_version = 3`.
11
+ - **TLDR**:
12
+ - your data must match the `iiif_version` argument
13
+ - if you insert an Annotation following the API v3.x, you can't search for it using `iiif_version=2`.
14
+
15
+ This is because
16
+ - the IIIF standard is quite complex and there are breaking changes between v2 and v3
17
+ - handling conversions between v2 and v3 is error prone, would increase calculations and slow the app down
18
+
19
+ ---
20
+
21
+ ## Generic routes
22
+
23
+ ### IIIF search API
24
+
25
+ ```
26
+ GET /search-api/{iiif_version}/manifests/{manifest_short_id}/search
27
+ ```
28
+
29
+ Implementation of the [IIIF Search API](https://iiif.io/api/search/2.0/), to search one or several annotations within a manifest.
30
+
31
+ #### Request
32
+
33
+ - Variables:
34
+ - `iiif_version` (`2 | 3`): the IIIF aearch API version. 2 is for IIIF Presentation API 3.x, 1 is for IIIF Presentation API 2.x
35
+ - `manifest_short_id` (`string`): the ID of the manifest. See the *IIIF URIs* section.
36
+ - Parameters:
37
+ - `q` (`string`): query string.
38
+ - if `iiif_version=1`, `q` is searched in the fields: `@id`, `resource.@id` or `resource.chars` fields
39
+ - `motivation` (`painting | non-painting | commenting | describing | tagging | linking`): values for the `motivation` field of an annotation
40
+
41
+ #### Response
42
+
43
+ Returns a JSON. If `iiif_version` is `1`, an `AnnotationList` is returned. Otherwise, an `AnnotationPage` is returned.
44
+
45
+ #### Notes
46
+
47
+ - if `q` and `motivation` are unused, it will return all annotations for the manifest
48
+ - only exact matches are allowed for `q` and `motivation`
49
+
50
+ ---
51
+
52
+ ### Delete an annotation or a manifest
53
+
54
+ ```
55
+ DELETE /{collection_name}/{iiif_version}/delete
56
+ ```
57
+
58
+ #### Request
59
+
60
+ - Variables
61
+ - `collection_name` (`annotations | manifests`): delete an annotation or a manifest
62
+ - `iiif_version` (`2 | 3`): IIIF presentation version
63
+ - Parameters:
64
+ - if `collection_name = manifests`:
65
+ - `uri`: the full URI of the manifest to delete
66
+ - `manifestShortId`: the manifest's identifier
67
+ - if `collection_name = annotation`:
68
+ - `uri`: the full URI of the annotation to delete
69
+ - `manifestShortId`: a manifest's identifier, to delete all annotations for a manifest
70
+ - `canvasUri`: the full URI to an annotation's target canvas, to delete all annotatons for the canvas
71
+
72
+ #### Response
73
+
74
+ ```
75
+ { deletedCount: <integer> }
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Manifests routes
81
+
82
+ ### Get an index of all manifests
83
+
84
+ ```
85
+ GET /manifests/{iiif_version}
86
+ ```
87
+
88
+ Returns a Collection of all manifests in your **aiiinotate** instance.
89
+
90
+ #### Request
91
+
92
+ - Variables:
93
+ - `iiif_version` (`2 | 3`): the IIIF Presentation API version
94
+
95
+ #### Response
96
+
97
+ A IIIF `Collection`, following the IIIF Presentation API 2 or 3, depending of the value of `iiif_version`.
98
+
99
+ ---
100
+
101
+ ### Insert a manifest
102
+
103
+ ```
104
+ POST /manifests/{iiif_version}/create
105
+ ```
106
+
107
+ #### Request
108
+
109
+ - Variable:
110
+ - `iiif_version` (`2 | 3`): the IIIF Presentation API version of your manifest
111
+ - Body (`JSON`): the manifest to index in the database
112
+
113
+ #### Response
114
+
115
+ ```
116
+ {
117
+ insertedIds: string[],
118
+ preExistingIds: string[],
119
+ rejectedIds: []
120
+ }
121
+ ```
122
+
123
+ - `insertedIds`: the list of IDs of inserted manifests
124
+ - `preExisingIds`: the IDs of manifests that were aldready in the database
125
+ - `rejectedIds`: the IDs of manifests on which an error occurred
126
+
127
+ ---
128
+
129
+ ## Annotation routes
130
+
131
+ ### Get all annotations for a canvas
132
+
133
+ ```
134
+ GET /annotations/{iiif_version}/search
135
+ ```
136
+
137
+ #### Request
138
+
139
+ - Variables:
140
+ - `iiif_version` (`2 | 3`): the IIIF Presentation API of your manifests
141
+ - Parameters:
142
+ - `uri` (`string`): the URI of the target canvas
143
+ - `asAnnotationList` (`true | false`): format of the response
144
+
145
+ #### Response
146
+
147
+ `Object[] | Object`: if `true`, return an array of annotations. Otherwise, return an `AnnotationList`.
148
+
149
+ ---
150
+
151
+ ### Count annotations
152
+
153
+ ```
154
+ GET /annotations/{iiif_version}/count
155
+ ```
156
+
157
+ #### Request
158
+
159
+ - Variables:
160
+ - `iiif_version` (`2 | 3`): the IIIF Presentation API of your manifests
161
+ - Parameters:
162
+ - `uri` (`string`): the annotation's `@id`
163
+ - `canvasUri` (`string`): the annotation's target canvas (`on.full`)
164
+ - `manifestShortId` (`string`): the short ID of the annotation's target manifest (`on.manifestShortId`)
165
+
166
+ #### Response
167
+
168
+ ```
169
+ { count: integer }
170
+ ```
171
+
172
+ ---
173
+
174
+ ### Get a single annotation
175
+
176
+ ```
177
+ GET /data/{iiif_version}/{manifest_short_id}/annotation/{annotation_short_id}
178
+ ```
179
+
180
+ This route allows to query an annotation by its ID by defering its `@id | id` field. This URL follows the IIIF specification
181
+
182
+ #### Request
183
+
184
+ - Variables:
185
+ - `iiif_version` (`2 | 3`): the IIIF version of the annotation
186
+ - `manifest_short_id` (`string`): the identifier of the manifest the annotation is related to
187
+ - `annotation_short_id`: the unique part of the annotation URL
188
+
189
+ #### Response
190
+
191
+ `Object`: the annotation. Its format follows the IIIF Presentation specification 2 or 3, based on the value of `iiif_version`.
192
+
193
+ ---
194
+
195
+ ### Create/update an annotation
196
+
197
+ ```
198
+ POST /annotations/{iiif_version}/{action}
199
+ ```
200
+
201
+ Create or update a single annotation
202
+
203
+ #### Request
204
+
205
+ - Variables:
206
+ - `iiif_version` (`2 | 3`): the IIIF version of the annotation
207
+ - `action` (`create | update`): the action to perform: create or update an annotation
208
+ - Body (`Object`): a IIIF annotation that follows the IIIF Presentation API 2 or 3 (depending on the value of `iiif_version`)
209
+
210
+ #### Response
211
+
212
+ ```
213
+ {
214
+ insertedIds: string[],
215
+ preExistingIds: string[],
216
+ rejectedIds: []
217
+ }
218
+ ```
219
+
220
+ #### Notes
221
+
222
+ - A side effect of inserting annotations is inserting the related manifests.
223
+ - When inserting an annotation, the annotation's target manifest is also fetched and inserted in the database
224
+ - Annotations in `aiiinotate` contain 3 nonstandard fields. In IIIF presentation 2.x,
225
+ - `annotation.on[0].manifestUri`: the URI of the manifest on which is an annotation
226
+ - `annotation.on[0].manifestShortId`: the unique identifier of the manifest on which is an annotation
227
+ - `annotation.on[0].canvasIdx`: the position of an annotation's target canvas within the target manifest, as an integer
228
+ - this depends on reconstructing an annotation's target manifest URL and fetching it. If this process fails, the fields above will be `undefined`.
229
+ - the annotation's target's manifest is fetched and inserted in the database, if possible, and stored in `annotation.on[0].manifestShortId`
230
+
231
+
232
+ ---
233
+
234
+ ### Insert several annotations
235
+
236
+ ```
237
+ POST /annotations/{iiif_version}/createMany
238
+ ```
239
+
240
+ Batch insert multiple annotations.
241
+
242
+ #### Request
243
+
244
+ - Parameters:
245
+ - `iiif_version` (`2 | 3`): the IIIF version of the annotation
246
+ - Body: either:
247
+ - a full `AnnotationList | AnnotationPage` embedded in the body (type must match `iiif_version`: AnnotationPage for IIIF 3, AnnotationList for IIIF 2).
248
+ - `AnnotationList[] | AnnotationPage[]` (type must match `iiif_version`): an array of annotation lists or pages
249
+ - `{ uri: string }`: an object containing a reference to an `AnnotationList` or `AnnotationPage`
250
+ - `{ uri: string }[]`: an array of objects containing a reference to an `AnnotationList` or `AnnotationPage`.
251
+
252
+ #### Response
253
+
254
+ ```
255
+ {
256
+ insertedIds: string[],
257
+ preExistingIds: string[],
258
+ rejectedIds: []
259
+ }
260
+ ```
261
+
262
+ #### Notes
263
+
264
+ - Be wary of maximum body size, especially when sending AnnotationLists in your body. If possible, using `{ uri: string }` is better.
265
+ - All annotations within a single AnnotationList/Page may have different target canvases or manifests.
266
+ - See **Create/update an annotation**.
267
+
3
268
  ---
4
269
 
5
- ## URL prefixes
270
+ ## Appending 1: App logic: URL prefixes
6
271
 
7
272
  URL anatomy is a mix of [SAS endpoints](./specifications/4_sas.md) and IIIF specifications. In turn, we define the following prefixes:
8
273
 
@@ -27,22 +292,23 @@ Where:
27
292
 
28
293
  There is an extra URL prefix: `schemas`. It is only used internally (not accessible to clients or accessible through HTTP) to define the IDs of all JsonSchemas, so we won't talk about it here.
29
294
 
295
+
30
296
  ---
31
297
 
32
- ## IIIF URIs
298
+ ## Appendix 2: IIIF URIs
33
299
 
34
300
  IIIF URIs in the Presentation 2.1 API are:
35
301
 
36
302
  ```
37
303
  Collection {scheme}://{host}/{prefix}/collection/{name}
38
- Manifest {scheme}://{host}/{prefix}/{identifier}/manifest
39
- Sequence {scheme}://{host}/{prefix}/{identifier}/sequence/{name}
40
- Canvas {scheme}://{host}/{prefix}/{identifier}/canvas/{name}
41
- Annotation (incl images) {scheme}://{host}/{prefix}/{identifier}/annotation/{name}
42
- AnnotationList {scheme}://{host}/{prefix}/{identifier}/list/{name}
43
- Range {scheme}://{host}/{prefix}/{identifier}/range/{name}
44
- Layer {scheme}://{host}/{prefix}/{identifier}/layer/{name}
45
- Content {scheme}://{host}/{prefix}/{identifier}/res/{name}.{format}
304
+ Manifest {scheme}://{host}/{prefix}/{manifest_short_id}/manifest
305
+ Sequence {scheme}://{host}/{prefix}/{manifest_short_id}/sequence/{name}
306
+ Canvas {scheme}://{host}/{prefix}/{manifest_short_id}/canvas/{name}
307
+ Annotation (incl images) {scheme}://{host}/{prefix}/{manifest_short_id}/annotation/{name}
308
+ AnnotationList {scheme}://{host}/{prefix}/{manifest_short_id}/list/{name}
309
+ Range {scheme}://{host}/{prefix}/{manifest_short_id}/range/{name}
310
+ Layer {scheme}://{host}/{prefix}/{manifest_short_id}/layer/{name}
311
+ Content {scheme}://{host}/{prefix}/{manifest_short_id}/res/{name}.{format}
46
312
  ```
47
313
 
48
314
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiiinotate",
3
- "version": "0.3.2",
3
+ "version": "0.4.1",
4
4
  "description": "a fast IIIF-compliant annotation server",
5
5
  "main": "./cli/index.js",
6
6
  "type": "module",
@@ -0,0 +1,12 @@
1
+ import re
2
+ import sys
3
+ import json
4
+ import shutil
5
+ import pathlib
6
+
7
+ curdir = pathlib.Path(__file__).parent.resolve()
8
+ pkg_file = curdir.parent.joinpath("package.json").resolve()
9
+
10
+ with open(pkg_file, mode="r") as fh:
11
+ data = json.load(fh)
12
+ print(f"\naiiinotate current version: {data['version']}\n")
@@ -27,4 +27,4 @@ for fp in [pkg_file, pkg_lock_file]:
27
27
  with open(fp, mode="w") as fh:
28
28
  json.dump(data, fh, indent=2)
29
29
 
30
- print(f"\nUpdated NPM package to version: {version}.")
30
+ print(f"\nUpdated NPM package to version: {version}.\n")
@@ -58,6 +58,24 @@ class Annotations2 extends CollectionAbstract {
58
58
  ////////////////////////////////////////////////////////////////
59
59
  // utils
60
60
 
61
+ /**
62
+ * expand a pair of `filterKey`, `filterVal` following the schema `routeAnnotationFilter` into a proper filter for the `annotations2` collection.
63
+ * @param {string} filterKey
64
+ * @param {string} filterVal
65
+ * @returns
66
+ */
67
+ #expandRouteAnnotationFilter(filterKey, filterVal) {
68
+ const allowedFilterKeys = [ "uri", "manifestShortId", "canvasUri" ];
69
+ if ( !allowedFilterKeys.includes(filterKey) ) {
70
+ throw new Error(`${this.funcname(this.#expandRouteAnnotationFilter)}: expected one of ${allowedFilterKeys} for param 'deleteKey', got '${filterKey}'`)
71
+ }
72
+ return filterKey==="uri"
73
+ ? { "@id": filterVal }
74
+ : filterKey==="canvasUri"
75
+ ? { "on.full": filterVal }
76
+ : { "on.manifestShortId": filterVal };
77
+ }
78
+
61
79
  /**
62
80
  * clean the body of an annotation (annotation.resource).
63
81
  * if `annotation.resource` is an array (there are several bodies associated to that annotation), this function must be called on each item of the array
@@ -183,7 +201,7 @@ class Annotations2 extends CollectionAbstract {
183
201
  // 1. get all distinct manifest URIs
184
202
  const manifestUris = [];
185
203
  annotationData.map((ann) => ann.on.map((target) => {
186
- if ( target.manifestUri !== undefined && !manifestUris.includes(target.manifestUri) ) {
204
+ if ( target.manifestUri != null && !manifestUris.includes(target.manifestUri) ) {
187
205
  manifestUris.push(target.manifestUri);
188
206
  }
189
207
  }));
@@ -275,19 +293,12 @@ class Annotations2 extends CollectionAbstract {
275
293
  * @returns {Promise<DeleteResponseType>}
276
294
  */
277
295
  async deleteAnnotations(deleteKey, deleteVal) {
278
-
279
- const allowedDeleteKey = [ "uri", "manifestShortId", "canvasUri" ];
280
- if ( !allowedDeleteKey.includes(deleteKey) ) {
281
- throw this.deleteError(`${this.funcName(this.deleteAnnotations)}: expected one of ${allowedDeleteKey} for param 'deleteKey', got '${deleteKey}'`)
296
+ try {
297
+ const deleteFilter = this.#expandRouteAnnotationFilter(deleteKey, deleteVal);
298
+ return this.delete(deleteFilter);
299
+ } catch (err) {
300
+ throw this.deleteError(`${this.funcName(this.deleteAnnotations)}: ${err.message}`)
282
301
  }
283
-
284
- const deleteFilter =
285
- deleteKey==="uri"
286
- ? { "@id": deleteVal }
287
- : deleteKey==="canvasUri"
288
- ? { "on.full": deleteVal }
289
- : { "on.manifestShortId": deleteVal };
290
- return this.delete(deleteFilter);
291
302
  }
292
303
 
293
304
  ////////////////////////////////////////////////////////////////
@@ -340,7 +351,7 @@ class Annotations2 extends CollectionAbstract {
340
351
  *
341
352
  * NOTE:
342
353
  * - only `motivation` and `q` search params are implemented
343
- * - to increase search execution, ONLY EXACT STRING MACHES are
354
+ * - to increase search execution speed, ONLY EXACT STRING MACHES are
344
355
  * implemented for `q` and `motivation` (in the IIIF specs, you can supply
345
356
  * multiple space-separated values and the server should return all partial
346
357
  * matches to any of those strings.)
@@ -410,6 +421,23 @@ class Annotations2 extends CollectionAbstract {
410
421
  return this.collection.findOne({ "@id": annotationUri })
411
422
  }
412
423
 
424
+ /**
425
+ * count number of annotations.
426
+ * @param {string} filterKey
427
+ * @param {string} filterVal
428
+ * @returns
429
+ */
430
+ async count(filterKey, filterVal) {
431
+ try {
432
+ const
433
+ countFilter = this.#expandRouteAnnotationFilter(filterKey, filterVal),
434
+ count = await this.collection.countDocuments(countFilter);
435
+ return { count: count }
436
+ } catch (err) {
437
+ throw this.readError(`${this.funcName(this.count)}: ${err.message}`)
438
+ }
439
+ }
440
+
413
441
  }
414
442
 
415
443
  export default fastifyPlugin((fastify, options, done) => {
@@ -64,9 +64,11 @@ function annotationsRoutes(fastify, options, done) {
64
64
  annotations2 = fastify.annotations2,
65
65
  /** @type {Annotations3InstanceType} */
66
66
  annotations3 = fastify.annotations3,
67
- iiifPresentationVersionSchema = fastify.schemasBase.getSchema("presentation"),
68
67
  routeAnnotation2Or3Schema = fastify.schemasRoutes.getSchema("routeAnnotation2Or3"),
69
68
  routeAnnotationCreateManySchema = fastify.schemasRoutes.getSchema("routeAnnotationCreateMany"),
69
+ routeAnnotationFilterSchema = fastify.schemasRoutes.getSchema("routeAnnotationFilter"),
70
+ routeResponseCountSchema = fastify.schemasRoutes.getSchema("routeResponseCount"),
71
+ iiifPresentationVersionSchema = fastify.schemasBase.getSchema("presentation"),
70
72
  iiifAnnotationListSchema = fastify.schemasPresentation2.getSchema("annotationList"),
71
73
  iiifAnnotation2ArraySchema = fastify.schemasPresentation2.getSchema("annotationArray"),
72
74
  iiifAnnotation2Schema = fastify.schemasPresentation2.getSchema("annotation"),
@@ -122,6 +124,36 @@ function annotationsRoutes(fastify, options, done) {
122
124
  }
123
125
  );
124
126
 
127
+ fastify.get(
128
+ "/annotations/:iiifPresentationVersion/count",
129
+ {
130
+ schema: {
131
+ params: {
132
+ type: "object",
133
+ properties: {
134
+ iiifPresentationVersion: iiifPresentationVersionSchema
135
+ }
136
+ },
137
+ querystring: routeAnnotationFilterSchema,
138
+ response: makeResponseSchema(
139
+ fastify, routeResponseCountSchema
140
+ )
141
+ }
142
+ },
143
+ async (request, reply) => {
144
+ const
145
+ { iiifPresentationVersion } = request.params,
146
+ [ filterKey, filterVal ] = getFirstNonEmptyPair(request.query);
147
+ try {
148
+ return iiifPresentationVersion === 2
149
+ ? await annotations2.count(filterKey, filterVal)
150
+ : annotations3.notImplementedError();
151
+ } catch (err) {
152
+ returnError(request, reply, err);
153
+ }
154
+ }
155
+ )
156
+
125
157
  /** retrieve a single annotation by its "@id"|"id". this route defers an annotation */
126
158
  fastify.get(
127
159
  "/data/:iiifPresentationVersion/:manifestShortId/annotation/:annotationShortId",
@@ -176,5 +176,39 @@ test("test annotation Routes", async (t) => {
176
176
  )
177
177
  })
178
178
 
179
+ await t.test("test route /annotations/:iiifPresentationVersion/count", async (t) => {
180
+ const expectedOnCount = (annotationArray, onKey, expectedOnVal) =>
181
+ annotationArray.filter((anno) => anno.on.some(x => x[onKey] === expectedOnVal)).length
182
+
183
+ await injectTestAnnotations(fastify, t, annotationList);
184
+ const
185
+ annotationArray = await fastify.mongo.db.collection("annotations2").find().toArray(),
186
+ annotationId = getRandomItem(annotationArray)["@id"],
187
+ canvasUri = getRandomItem(annotationArray).on[0].full,
188
+ manifestShortId = getRandomItem(annotationArray).on[0].manifestShortId,
189
+ expectedAnnotationIdCount = 1,
190
+ expectedCanvasUriCount = expectedOnCount(annotationArray, "full", canvasUri),
191
+ expectedManifestShortIdCount = expectedOnCount(annotationArray, "manifestShortId", manifestShortId),
192
+ mapper = [
193
+ ["uri", annotationId, expectedAnnotationIdCount],
194
+ ["canvasUri", canvasUri, expectedCanvasUriCount],
195
+ ["manifestShortId", manifestShortId, expectedManifestShortIdCount]
196
+ ];
197
+
198
+ await Promise.all(
199
+ mapper.map(async ([filterKey, filterVal, expectedCount]) => {
200
+ const
201
+ r = await fastify.inject({
202
+ method: "GET",
203
+ url: `/annotations/2/count?${filterKey}=${filterVal}`
204
+ }),
205
+ body = await r.json();
206
+ t.assert.deepStrictEqual(r.statusCode, 200);
207
+ t.assert.deepStrictEqual(body.count, expectedCount);
208
+ })
209
+ )
210
+
211
+ })
212
+
179
213
  return
180
214
  })
@@ -147,9 +147,11 @@ class Manifests2 extends CollectionAbstract {
147
147
  const
148
148
  mongoResponse =
149
149
  await this.collection.find(
150
- { "@id": {
151
- $in: cleanManifestArray.map((manifest) => manifest["@id"])
152
- }},
150
+ {
151
+ "@id": {
152
+ $in: cleanManifestArray.map((manifest) => manifest["@id"])
153
+ }
154
+ },
153
155
  { projection: { "@id": 1 } }
154
156
  )
155
157
  .toArray(),
@@ -206,7 +208,10 @@ class Manifests2 extends CollectionAbstract {
206
208
  await Promise.all(
207
209
  manifestUriArray.map(async (manifestUri) => {
208
210
  try {
209
- manifestArray.push(await this.#fetchManifestFromUri(manifestUri));
211
+ const r = await this.#fetchManifestFromUri(manifestUri);
212
+ if ( ! r.error ) {
213
+ manifestArray.push(r);
214
+ }
210
215
  } catch (err) {
211
216
  if ( throwOnError ) {
212
217
  throw err;
@@ -29,10 +29,10 @@ function commonRoutes(fastify, options, done) {
29
29
  routeDeleteSchema = fastify.schemasRoutes.getSchema("routeDelete"),
30
30
  responsePostSchema = makeResponsePostSchema(fastify),
31
31
  validatorRouteAnnotationDeleteSchema = ajvCompile(fastify.schemasResolver(
32
- fastify.schemasRoutes.getSchema("routeAnnotationDelete")
32
+ fastify.schemasRoutes.getSchema("routeAnnotationFilter")
33
33
  )),
34
34
  validatorRouteManifestDeleteSchema = ajvCompile(fastify.schemasResolver(
35
- fastify.schemasRoutes.getSchema("routeManifestDelete")
35
+ fastify.schemasRoutes.getSchema("routeManifestFilter")
36
36
  ));
37
37
 
38
38
  fastify.get(
@@ -163,8 +163,9 @@ function addSchemas(fastify, options, done) {
163
163
  ]
164
164
  })
165
165
 
166
+ // for annotations: key-value pairs to filter a manifests collection by
166
167
  fastify.addSchema({
167
- $id: makeSchemaUri("routeAnnotationDelete"),
168
+ $id: makeSchemaUri("routeAnnotationFilter"),
168
169
  oneOf: [
169
170
  {
170
171
  type: "object",
@@ -200,8 +201,9 @@ function addSchemas(fastify, options, done) {
200
201
  ]
201
202
  });
202
203
 
204
+ // for manifests: key-value pairs to filter a manifests collection by
203
205
  fastify.addSchema({
204
- $id: makeSchemaUri("routeManifestDelete"),
206
+ $id: makeSchemaUri("routeManifestFilter"),
205
207
  type: "object",
206
208
  oneOf: [
207
209
  {
@@ -224,8 +226,8 @@ function addSchemas(fastify, options, done) {
224
226
  $id: makeSchemaUri("routeDelete"),
225
227
  type: "object",
226
228
  oneOf: [
227
- { $ref: makeSchemaUri("routeAnnotationDelete") },
228
- { $ref: makeSchemaUri("routeManifestDelete") },
229
+ { $ref: makeSchemaUri("routeAnnotationFilter") },
230
+ { $ref: makeSchemaUri("routeManifestFilter") },
229
231
  ]
230
232
  });
231
233
 
@@ -288,6 +290,18 @@ function addSchemas(fastify, options, done) {
288
290
  }
289
291
  });
290
292
 
293
+ fastify.addSchema({
294
+ $id: makeSchemaUri("routeResponseCount"),
295
+ type: "object",
296
+ required: [ "count" ],
297
+ properties: {
298
+ count: {
299
+ type: "integer",
300
+ minimum: 0
301
+ }
302
+ }
303
+ })
304
+
291
305
  ////////////////////////////////////////////////////////
292
306
 
293
307
  fastify.decorate("schemasRoutes", {