aiiinotate 0.2.0
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/LICENSE +661 -0
- package/README.md +61 -0
- package/cli/import.js +142 -0
- package/cli/index.js +26 -0
- package/cli/io.js +105 -0
- package/cli/migrate.js +123 -0
- package/cli/mongoClient.js +11 -0
- package/docs/architecture.md +88 -0
- package/docs/db.md +38 -0
- package/docs/dev_iiif_compatibility.md +43 -0
- package/docs/endpoints.md +48 -0
- package/docs/progress.md +159 -0
- package/docs/specifications/0_w3c_open_annotations.md +332 -0
- package/docs/specifications/1_w3c_web_annotations.md +577 -0
- package/docs/specifications/2_iiif_apis.md +396 -0
- package/docs/specifications/3_iiif_annotations.md +103 -0
- package/docs/specifications/4_search_api.md +135 -0
- package/docs/specifications/5_sas.md +119 -0
- package/docs/specifications/6_mirador.md +119 -0
- package/docs/specifications/7_aikon.md +137 -0
- package/docs/specifications/include/presentation_2.0.webp +0 -0
- package/docs/specifications/include/presentation_2.0_white.png +0 -0
- package/docs/specifications/include/presentation_3.0.png +0 -0
- package/docs/specifications/include/presentation_3.0_resize.png +0 -0
- package/eslint.config.js +27 -0
- package/migrations/baseConfig.js +56 -0
- package/migrations/manageIndex.js +55 -0
- package/migrations/migrate-mongo-config-main.js +8 -0
- package/migrations/migrate-mongo-config-test.js +8 -0
- package/migrations/migrationScripts/20250825185706-collections.js +41 -0
- package/migrations/migrationScripts/20250826194832-annotations2-canvas-index.js +31 -0
- package/migrations/migrationScripts/20250904080710-annotations2-schema.js +42 -0
- package/migrations/migrationScripts/20251002141951-manifest2-schema.js +43 -0
- package/migrations/migrationScripts/20251006212110-manifest-unique-index.js +29 -0
- package/migrations/migrationScripts/20251028115614-annotations2-id-index.js +27 -0
- package/migrations/migrationTemplate.js +25 -0
- package/package.json +78 -0
- package/run.sh +70 -0
- package/scripts/_migrations.sh +79 -0
- package/scripts/_setup.js +31 -0
- package/scripts/setup_mongodb.sh +61 -0
- package/scripts/setup_mongodb_migrate.sh +17 -0
- package/scripts/setup_node.sh +15 -0
- package/scripts/utils.sh +192 -0
- package/setup.sh +20 -0
- package/src/app.js +113 -0
- package/src/config/.env.template +22 -0
- package/src/data/annotations/annotations2.js +419 -0
- package/src/data/annotations/annotations3.js +32 -0
- package/src/data/annotations/routes.js +271 -0
- package/src/data/annotations/routes.test.js +180 -0
- package/src/data/collectionAbstract.js +270 -0
- package/src/data/index.js +29 -0
- package/src/data/manifests/manifests2.js +305 -0
- package/src/data/manifests/manifests2.test.js +53 -0
- package/src/data/manifests/manifests3.js +23 -0
- package/src/data/manifests/routes.js +95 -0
- package/src/data/manifests/routes.test.js +69 -0
- package/src/data/routes.js +141 -0
- package/src/data/routes.test.js +117 -0
- package/src/data/utils/iiif2Utils.js +196 -0
- package/src/data/utils/iiif2Utils.test.js +98 -0
- package/src/data/utils/iiif3Utils.js +0 -0
- package/src/data/utils/iiifUtils.js +18 -0
- package/src/data/utils/routeUtils.js +109 -0
- package/src/data/utils/testUtils.js +253 -0
- package/src/data/utils/utils.js +231 -0
- package/src/db/index.js +48 -0
- package/src/fileServer/annotations.js +39 -0
- package/src/fileServer/data/annotationList_aikon_wit9_man11_anno165_all.jsonld +827 -0
- package/src/fileServer/data/annotationList_vhs_wit250_man250_anno250_all.jsonld +37514 -0
- package/src/fileServer/data/annotationList_vhs_wit253_man253_anno253_all.jsonld +20111 -0
- package/src/fileServer/data/annotations2Invalid.jsonld +39 -0
- package/src/fileServer/data/annotations2Valid.jsonld +39 -0
- package/src/fileServer/data/bnf_invalid_manifest.json +2806 -0
- package/src/fileServer/data/bnf_valid_manifest.json +2817 -0
- package/src/fileServer/data/vhs_wit253_man253_anno253_anno-24.json +1 -0
- package/src/fileServer/index.js +64 -0
- package/src/fileServer/manifests.js +14 -0
- package/src/fileServer/utils.js +35 -0
- package/src/schemas/index.js +20 -0
- package/src/schemas/schemasBase.js +47 -0
- package/src/schemas/schemasPresentation2.js +417 -0
- package/src/schemas/schemasPresentation3.js +57 -0
- package/src/schemas/schemasResolver.js +71 -0
- package/src/schemas/schemasRoutes.js +277 -0
- package/src/server.js +22 -0
- package/src/types.js +93 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
|
|
3
|
+
import build from "#src/app.js";
|
|
4
|
+
import { v4 as uuid4 } from "uuid";
|
|
5
|
+
import { getManifestShortId, getCanvasShortId, getAnnotationTarget, makeTarget, makeAnnotationId } from "#utils/iiif2Utils.js";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
const
|
|
9
|
+
// hash-validating regex
|
|
10
|
+
hashRgx = /^\d+$/;
|
|
11
|
+
|
|
12
|
+
test("test 'iiif2Utils' functions", async (t) => {
|
|
13
|
+
const
|
|
14
|
+
fastify = await build("test"),
|
|
15
|
+
{ annotations2Valid, annotations2Invalid } = fastify.fileServer;
|
|
16
|
+
|
|
17
|
+
await fastify.ready();
|
|
18
|
+
|
|
19
|
+
t.after(() => fastify.close());
|
|
20
|
+
|
|
21
|
+
t.test("test 'getManifestShortId'", (t) => {
|
|
22
|
+
const
|
|
23
|
+
s1 = `manifest_${uuid4()}`,
|
|
24
|
+
s2 = `extra_${uuid4()}`,
|
|
25
|
+
// urls for which the function will manage to extract `s1`
|
|
26
|
+
urlOk = [
|
|
27
|
+
`http://www.example.com/examplePrefix/${s1}/manifest`,
|
|
28
|
+
`http://www.example.com/examplePrefix/${s1}/manifest.json`,
|
|
29
|
+
`http://www.example.com/examplePrefix/${s1}/sequence/${s2}`,
|
|
30
|
+
`http://www.example.com/examplePrefix/${s1}/canvas/${s2}`,
|
|
31
|
+
`http://www.example.com/examplePrefix/${s1}/annotation/${s2}`,
|
|
32
|
+
`http://www.example.com/examplePrefix/${s1}/list/${s2}`,
|
|
33
|
+
`http://www.example.com/examplePrefix/${s1}/range/${s2}`,
|
|
34
|
+
`http://www.example.com/examplePrefix/${s1}/layer/${s2}`,
|
|
35
|
+
`http://www.example.com/examplePrefix/${s1}/res/${s2}.png`,
|
|
36
|
+
],
|
|
37
|
+
// urls for which `s1` won't be returned, and instead a hash will be returned
|
|
38
|
+
urlHash = [
|
|
39
|
+
`http://example.com/example/collection/${s2}`,
|
|
40
|
+
`http://example.com/${s1}`
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
urlOk.map((url) =>
|
|
44
|
+
t.assert.strictEqual(getManifestShortId(url), s1));
|
|
45
|
+
urlHash.map((url) =>
|
|
46
|
+
t.assert.strictEqual(hashRgx.test(getManifestShortId(url)), true));
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
t.test("test 'getCanvasShortId'", (t) => {
|
|
50
|
+
const
|
|
51
|
+
s1 = `manifest_${uuid4()}`,
|
|
52
|
+
s2 = `canvas_${uuid4()}`,
|
|
53
|
+
urlOk = [
|
|
54
|
+
`http://www.example.com/examplePrefix/${s1}/canvas/${s2}`,
|
|
55
|
+
`http://www.example.com/examplePrefix/${s1}/canvas/${s2}.json`,
|
|
56
|
+
|
|
57
|
+
],
|
|
58
|
+
urlHash = [
|
|
59
|
+
`http://example.com/example/${s2}`,
|
|
60
|
+
`http://example.com/${s2}`
|
|
61
|
+
]
|
|
62
|
+
urlOk.map((url) =>
|
|
63
|
+
t.assert.strictEqual(getCanvasShortId(url), s2));
|
|
64
|
+
urlHash.map((url) =>
|
|
65
|
+
t.assert.strictEqual(hashRgx.test(getCanvasShortId(url)), true));
|
|
66
|
+
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
t.test("test 'getAnnotationTarget' and 'makeTarget'", async (t) => {
|
|
70
|
+
await Promise.all(
|
|
71
|
+
[getAnnotationTarget, makeTarget].map((func) =>
|
|
72
|
+
|
|
73
|
+
t.test(`test '${func.name}'`, (t) => {
|
|
74
|
+
annotations2Valid.map((annotation) =>
|
|
75
|
+
// to test for an error, it's necessary to create a new function:
|
|
76
|
+
// https://stackoverflow.com/a/6645586
|
|
77
|
+
t.assert.doesNotThrow(() => func(annotation)));
|
|
78
|
+
annotations2Invalid.map((annotation) =>
|
|
79
|
+
t.assert.throws(() => func(annotation), Error));
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
t.test("test 'makeAnnotationId'", (t) => {
|
|
87
|
+
// https://stackoverflow.com/a/6969486
|
|
88
|
+
const escapeRegExp = (string) =>
|
|
89
|
+
string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
|
|
90
|
+
|
|
91
|
+
const rgx = new RegExp(`^${escapeRegExp(process.env.APP_BASE_URL)}/data/2/[^(\\s|/)]+/annotation/[^\\.]+$`);
|
|
92
|
+
annotations2Valid.map((annotation) =>
|
|
93
|
+
t.assert.strictEqual(rgx.test(makeAnnotationId(annotation)), true));
|
|
94
|
+
annotations2Invalid.map((annotation) =>
|
|
95
|
+
t.assert.throws(() => makeAnnotationId(annotation)));
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
})
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { getHash } from "#utils/utils.js";
|
|
2
|
+
|
|
3
|
+
const IIIF_PRESENTATION_2 = 2;
|
|
4
|
+
const IIIF_PRESENTATION_3 = 3;
|
|
5
|
+
const IIIF_SEARCH_1 = 1;
|
|
6
|
+
const IIIF_SEARCH_2 = 2;
|
|
7
|
+
const IIIF_PRESENTATION_2_CONTEXT = { "@context": "http://iiif.io/api/presentation/2/context.json" };
|
|
8
|
+
const IIIF_PRESENTATION_3_CONTEXT = { "@context": "http://iiif.io/api/presentation/3/context.json" };
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
IIIF_PRESENTATION_2,
|
|
13
|
+
IIIF_PRESENTATION_3,
|
|
14
|
+
IIIF_SEARCH_1,
|
|
15
|
+
IIIF_SEARCH_2,
|
|
16
|
+
IIIF_PRESENTATION_2_CONTEXT,
|
|
17
|
+
IIIF_PRESENTATION_3_CONTEXT,
|
|
18
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { inspectObj, isNonEmptyArray } from "#utils/utils.js";
|
|
2
|
+
|
|
3
|
+
/** @typedef {import("mongodb").UpdateResult} MongoUpdateResultType */
|
|
4
|
+
/** @typedef {import("#types").InsertResponseType} InsertResponseType */
|
|
5
|
+
/** @typedef {import("#types").UpdateResponseType} UpdateResponseType */
|
|
6
|
+
/** @typedef {import("#types").DeleteResponseType} DeleteResponseType */
|
|
7
|
+
/** @typedef {import("#types").FastifyInstanceType} FastifyInstanceType */
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* functionnal alternative to `formatInsertResponse`, that aldready expects a formatted object
|
|
11
|
+
* @param {string[]?} insertedIds
|
|
12
|
+
* @param {string[]?} preExistingIds
|
|
13
|
+
* @param {string[]?} fetchErrorIds
|
|
14
|
+
* @param {string[]?} rejectedIds
|
|
15
|
+
* @returns {InsertResponseType}
|
|
16
|
+
*/
|
|
17
|
+
const formatInsertResponse = (insertedIds, preExistingIds, fetchErrorIds, rejectedIds) => {
|
|
18
|
+
const out = {
|
|
19
|
+
insertedCount: insertedIds?.length || 0,
|
|
20
|
+
insertedIds: insertedIds || [],
|
|
21
|
+
};
|
|
22
|
+
if ( fetchErrorIds?.length ) {
|
|
23
|
+
out.fetchErrorIds = fetchErrorIds;
|
|
24
|
+
}
|
|
25
|
+
if ( rejectedIds?.length ) {
|
|
26
|
+
out.rejectedIds = rejectedIds;
|
|
27
|
+
}
|
|
28
|
+
if ( preExistingIds?.length ) {
|
|
29
|
+
out.preExistingIds = preExistingIds;
|
|
30
|
+
};
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {UpdateResponseType} mongoRes
|
|
36
|
+
* @returns {UpdateResponseType}
|
|
37
|
+
*/
|
|
38
|
+
const formatUpdateResponse = (mongoRes) => ({
|
|
39
|
+
matchedCount: mongoRes.matchedCount,
|
|
40
|
+
modifiedCount: mongoRes.modifiedCount,
|
|
41
|
+
upsertedCount: mongoRes.upsertedCount,
|
|
42
|
+
upsertedId: mongoRes.upsertedId
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @param {DeleteResponseType} mongoRes
|
|
47
|
+
* @returns {DeleteResponseType}
|
|
48
|
+
*/
|
|
49
|
+
const formatDeleteResponse = (mongoRes) => ({
|
|
50
|
+
deletedCount: mongoRes.deletedCount
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* NOTE: fastify only implements top-level `$ref` in responses, using $ref in response schemas is not allowed.
|
|
55
|
+
* in turn, we can't use `{ $ref: makeSchemaUri }` and must resolve schemas instead.
|
|
56
|
+
* @param {FastifyInstanceType} fastify
|
|
57
|
+
* @param {object} okResponseSchema - expected response schema
|
|
58
|
+
*/
|
|
59
|
+
const makeResponseSchema = (fastify, okResponseSchema) => ({
|
|
60
|
+
200: okResponseSchema,
|
|
61
|
+
500: fastify.schemasRoutes.getSchema("routeResponseError")
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const makeResponsePostSchema = (fastify) => makeResponseSchema(
|
|
65
|
+
fastify,
|
|
66
|
+
{
|
|
67
|
+
anyOf: [
|
|
68
|
+
fastify.schemasRoutes.getSchema("routeResponseInsert"),
|
|
69
|
+
fastify.schemasRoutes.getSchema("routeResponseUpdate"),
|
|
70
|
+
fastify.schemasRoutes.getSchema("routeResponseDelete"),
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
*
|
|
77
|
+
* @param {import("fastify").FastifyRequest} request
|
|
78
|
+
* @param {import("fastify").FastifyReply} reply
|
|
79
|
+
* @param {Error} err: the error we're returning
|
|
80
|
+
* @param {number} statusCode - the status code (defaults to 500)
|
|
81
|
+
* @param {any?} requestBody: the data on which the error occurred, for POST requests
|
|
82
|
+
*/
|
|
83
|
+
const returnError = (request, reply, err, requestBody={}, statusCode=500) => {
|
|
84
|
+
// otherwise, the error is not logged, bad for debugging.
|
|
85
|
+
console.error(inspectObj(err));
|
|
86
|
+
|
|
87
|
+
const error = {
|
|
88
|
+
message: `failed ${request.method.toLocaleUpperCase()} request because of error: ${err.message}`,
|
|
89
|
+
info: err.info || {},
|
|
90
|
+
method: request.method,
|
|
91
|
+
url: request.url
|
|
92
|
+
};
|
|
93
|
+
if ( requestBody !== undefined ) {
|
|
94
|
+
error.requestBody = requestBody
|
|
95
|
+
}
|
|
96
|
+
reply
|
|
97
|
+
.status(statusCode)
|
|
98
|
+
.header("Content-Type", "application/json; charset=utf-8")
|
|
99
|
+
.send(error);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export {
|
|
103
|
+
formatInsertResponse,
|
|
104
|
+
formatUpdateResponse,
|
|
105
|
+
formatDeleteResponse,
|
|
106
|
+
makeResponsePostSchema,
|
|
107
|
+
makeResponseSchema,
|
|
108
|
+
returnError
|
|
109
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* utilities and generally useful functions for tests.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { visibleLog } from "#utils/utils.js";
|
|
6
|
+
|
|
7
|
+
/** @typedef {import("#types").NodeTestType} NodeTestType */
|
|
8
|
+
/** @typedef {import("#types").FastifyInstanceType} FastifyInstanceType */
|
|
9
|
+
/** @typedef {import("#types").FastifyReplyType} FastifyReplyType */
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {NodeTestType} t
|
|
13
|
+
* @param {object} obj
|
|
14
|
+
* @param {Array} expectedKeys
|
|
15
|
+
* @returns {void}
|
|
16
|
+
*/
|
|
17
|
+
const assertObjectKeys = (t, obj, expectedKeys) =>
|
|
18
|
+
t.assert.deepStrictEqual(
|
|
19
|
+
Object.keys(obj).sort(),
|
|
20
|
+
expectedKeys.sort()
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* insert responses MUST contain `insertedCount` and MAY contain ["insertedIds", "preExistingIds", "fetchErrorIds", "rejectedIds"]
|
|
25
|
+
* @param {NodeTestType} t
|
|
26
|
+
* @param {object} obj
|
|
27
|
+
* @returns {void}
|
|
28
|
+
*/
|
|
29
|
+
const assertObjectKeysInsert = (t, obj) => {
|
|
30
|
+
t.assert.deepStrictEqual(
|
|
31
|
+
Object.keys(obj).every((k) => ["insertedCount", "insertedIds", "preExistingIds", "fetchErrorIds", "rejectedIds"].includes(k)),
|
|
32
|
+
true
|
|
33
|
+
);
|
|
34
|
+
t.assert.deepStrictEqual(
|
|
35
|
+
Object.keys(obj).includes("insertedCount"), true
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {NodeTestType} t
|
|
41
|
+
* @param {object} obj
|
|
42
|
+
* @returns {void}
|
|
43
|
+
*/
|
|
44
|
+
const assertObjectKeysUpdate = (t, obj) =>
|
|
45
|
+
assertObjectKeys(t, obj, ["matchedCount", "modifiedCount", "upsertedCount", "upsertedId"]);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {NodeTestType} t
|
|
49
|
+
* @param {object} obj
|
|
50
|
+
* @returns {void}
|
|
51
|
+
*/
|
|
52
|
+
const assertObjectKeysDelete = (t, obj) =>
|
|
53
|
+
assertObjectKeys(t, obj, ["deletedCount"]);
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {NodeTestType} t
|
|
57
|
+
* @param {object} obj
|
|
58
|
+
* @returns {void}
|
|
59
|
+
*/
|
|
60
|
+
const assertObjectKeysError = (t, obj) =>
|
|
61
|
+
assertObjectKeys(t, obj, ["message", "info", "method", "url", "requestBody"]);
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {NodeTestType} t
|
|
65
|
+
* @param {FastifyReplyType} r
|
|
66
|
+
* @param {number} expectedStatusCode
|
|
67
|
+
* @returns {void}
|
|
68
|
+
*/
|
|
69
|
+
const assertStatusCode = (t, r, expectedStatusCode) =>
|
|
70
|
+
t.assert.deepStrictEqual(r.statusCode, expectedStatusCode);
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {NodeTestType} t
|
|
74
|
+
* @param {FastifyReplyType} r
|
|
75
|
+
* @param {"insert"|"update"|"delete"|"error"} expectedResponse: keyword to define the response schema to test against.
|
|
76
|
+
* @returns {void}
|
|
77
|
+
*/
|
|
78
|
+
const assertResponseKeys = (t, r, expectedResponse) =>
|
|
79
|
+
expectedResponse === "insert"
|
|
80
|
+
? assertObjectKeysInsert(t, JSON.parse(r.body))
|
|
81
|
+
: expectedResponse === "update"
|
|
82
|
+
? assertObjectKeysUpdate(t, JSON.parse(r.body))
|
|
83
|
+
: expectedResponse === "delete"
|
|
84
|
+
? assertObjectKeysDelete(t, JSON.parse(r.body))
|
|
85
|
+
: assertObjectKeysError(t, JSON.parse(r.body));
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @param {FastifyInstanceType} fastify
|
|
89
|
+
* @param {string} route
|
|
90
|
+
* @param {object} payload
|
|
91
|
+
* @returns {Promise<FastifyReplyType>}
|
|
92
|
+
*/
|
|
93
|
+
const injectPost = (fastify, route, payload) =>
|
|
94
|
+
fastify.inject({
|
|
95
|
+
method: "POST",
|
|
96
|
+
url: route,
|
|
97
|
+
payload: payload,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @param {NodeTestType} t
|
|
102
|
+
* @param {FastifyReplyType} r
|
|
103
|
+
* @returns {void}
|
|
104
|
+
*/
|
|
105
|
+
const assertPostInvalidResponse = (t, r) => {
|
|
106
|
+
assertStatusCode(t, r, 500);
|
|
107
|
+
assertResponseKeys(t, r, "error");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @param {NodeTestType} t
|
|
112
|
+
* @param {FastifyReplyType} r
|
|
113
|
+
* @returns {void}
|
|
114
|
+
*/
|
|
115
|
+
const assertCreateValidResponse = (t, r) => {
|
|
116
|
+
assertStatusCode(t, r, 200);
|
|
117
|
+
assertResponseKeys(t, r, "insert");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @param {NodeTestType} t
|
|
122
|
+
* @param {FastifyReplyType} r
|
|
123
|
+
* @returns {void}
|
|
124
|
+
*/
|
|
125
|
+
const assertUpdateValidResponse = (t,r) => {
|
|
126
|
+
assertStatusCode(t, r, 200);
|
|
127
|
+
assertResponseKeys(t, r, "update");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @param {NodeTestType} t
|
|
132
|
+
* @param {FastifyReplyType} r
|
|
133
|
+
* @returns {void}
|
|
134
|
+
*/
|
|
135
|
+
const assertDeleteValidResponse = (t,r) => {
|
|
136
|
+
assertStatusCode(t, r, 200);
|
|
137
|
+
assertResponseKeys(t, r, "delete");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @param {NodeTestType} t
|
|
142
|
+
* @param {FastifyReplyType} r
|
|
143
|
+
* @param {number} expectedStatusCode
|
|
144
|
+
*/
|
|
145
|
+
const assertErrorValidResponse = (t, r, expectedStatusCode=500) => {
|
|
146
|
+
assertObjectKeysError(t, JSON.parse(r.body));
|
|
147
|
+
assertStatusCode(t, r, expectedStatusCode);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* curried function to test different operations with a post route.
|
|
152
|
+
* @param {FastifyInstanceType} fastify
|
|
153
|
+
*/
|
|
154
|
+
const testPostRouteCurry = (fastify) =>
|
|
155
|
+
/** @param {DataOperationsType} op */
|
|
156
|
+
(op) =>
|
|
157
|
+
/** @param {boolean} success - if `true` test that the query succeeds. else, test that it fails */
|
|
158
|
+
(success) =>
|
|
159
|
+
/**
|
|
160
|
+
* @param {NodeTestType} t
|
|
161
|
+
* @param {string} route: example: /annotations/2/createMany
|
|
162
|
+
* @param {object} payload
|
|
163
|
+
*/
|
|
164
|
+
async (t, route, payload) => {
|
|
165
|
+
const
|
|
166
|
+
r = await injectPost(fastify, route, payload),
|
|
167
|
+
funcInvalid = assertPostInvalidResponse;
|
|
168
|
+
|
|
169
|
+
let funcValid;
|
|
170
|
+
if ( op==="insert" ) {
|
|
171
|
+
funcValid = assertCreateValidResponse;
|
|
172
|
+
} else if ( op==="update" ) {
|
|
173
|
+
funcValid = assertUpdateValidResponse;
|
|
174
|
+
} else {
|
|
175
|
+
throw new Error(`testPostRouteCurry: unimplemented value of 'op': '${op}'.`)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
success
|
|
179
|
+
? funcValid(t, r)
|
|
180
|
+
: funcInvalid(t, r);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const testDeleteRouteCurry =
|
|
185
|
+
/** @param {FastifyInstanceType} */
|
|
186
|
+
(fastify) =>
|
|
187
|
+
/**
|
|
188
|
+
* @param {NodeTestType} t
|
|
189
|
+
* @param {string} deleteRoute - route to delete data, with delete parameters embedded
|
|
190
|
+
* @param {number} expectedDeletedCount - number of documents that should be deleted
|
|
191
|
+
*/
|
|
192
|
+
async (t, deleteRoute, expectedDeletedCount) => {
|
|
193
|
+
const r = await fastify.inject({
|
|
194
|
+
method: "DELETE",
|
|
195
|
+
url: deleteRoute
|
|
196
|
+
})
|
|
197
|
+
assertDeleteValidResponse(t, r);
|
|
198
|
+
t.assert.deepStrictEqual(JSON.parse(r.body).deletedCount, expectedDeletedCount);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* inject a manifest into the database for test purposes
|
|
203
|
+
* @param {FastifyInstanceType} fastify
|
|
204
|
+
* @param {NodeTestType} t
|
|
205
|
+
* @param {object} manifest - the manifest to insert
|
|
206
|
+
* @returns {Promise<Array<number, Array<string>>>}
|
|
207
|
+
*/
|
|
208
|
+
const injectTestManifest = async (fastify, t, manifest) => {
|
|
209
|
+
const
|
|
210
|
+
r = await injectPost(fastify, "/manifests/2/create", manifest),
|
|
211
|
+
{ insertedCount, insertedIds } = JSON.parse(r.body);
|
|
212
|
+
t.assert.deepStrictEqual(insertedCount, 1);
|
|
213
|
+
return [ insertedCount, insertedIds ];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* inject an annotationList into the database for test purposes
|
|
218
|
+
* @param {FastifyInstanceType} fastify
|
|
219
|
+
* @param {NodeTestType} t
|
|
220
|
+
* @param {object} annotationList - a IIIF 2.x annotation list
|
|
221
|
+
* @returns {Promise<Array<number, Array<string>>>}
|
|
222
|
+
*/
|
|
223
|
+
const injectTestAnnotations = async (fastify, t, annotationList) => {
|
|
224
|
+
const
|
|
225
|
+
r = await injectPost(fastify, "/annotations/2/createMany", annotationList),
|
|
226
|
+
rBody = JSON.parse(r.body),
|
|
227
|
+
expectedInsertedCount = annotationList.resources.length,
|
|
228
|
+
{ insertedCount, insertedIds } = rBody;
|
|
229
|
+
t.assert.deepStrictEqual(insertedCount, expectedInsertedCount);
|
|
230
|
+
return [insertedCount, insertedIds];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
export {
|
|
235
|
+
assertObjectKeys,
|
|
236
|
+
assertObjectKeysError,
|
|
237
|
+
assertObjectKeysInsert,
|
|
238
|
+
assertObjectKeysUpdate,
|
|
239
|
+
assertObjectKeysDelete,
|
|
240
|
+
assertStatusCode,
|
|
241
|
+
assertResponseKeys,
|
|
242
|
+
assertErrorValidResponse,
|
|
243
|
+
injectPost,
|
|
244
|
+
assertPostInvalidResponse,
|
|
245
|
+
assertCreateValidResponse,
|
|
246
|
+
assertUpdateValidResponse,
|
|
247
|
+
assertDeleteValidResponse,
|
|
248
|
+
testPostRouteCurry,
|
|
249
|
+
testDeleteRouteCurry,
|
|
250
|
+
injectTestManifest,
|
|
251
|
+
injectTestAnnotations
|
|
252
|
+
}
|
|
253
|
+
|