aiiinotate 0.2.9 → 0.2.11
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/cli/migrate.js +1 -0
- package/cli/utils/mongoClient.js +1 -3
- package/docker/Dockerfile +12 -10
- package/docker/README.md +62 -0
- package/docker/docker-compose.yaml +18 -2
- package/docker/docker.sh +81 -0
- package/docs/notes_quirks_troubleshooting.md +143 -0
- package/docs/progress.md +0 -121
- package/package.json +4 -4
- package/src/data/annotations/routes.test.js +1 -1
- package/src/data/manifests/manifests2.test.js +1 -1
- package/src/data/manifests/routes.test.js +1 -1
- package/src/data/routes.test.js +1 -1
- package/src/server.js +4 -1
package/cli/migrate.js
CHANGED
|
@@ -61,6 +61,7 @@ function migrateMake(migrationName) {
|
|
|
61
61
|
|
|
62
62
|
/** apply all pending migrations */
|
|
63
63
|
function migrateApply() {
|
|
64
|
+
// NOTE: if we don't use a specific version of migrate-mongo, we get docker errors
|
|
64
65
|
migrationConfigs.map((mc) => execSync(`npx -- migrate-mongo@^12.1.3 up -f ${mc}`));
|
|
65
66
|
}
|
|
66
67
|
|
package/cli/utils/mongoClient.js
CHANGED
|
@@ -6,9 +6,7 @@ import { MongoClient } from "mongodb";
|
|
|
6
6
|
*/
|
|
7
7
|
function loadMongoClient() {
|
|
8
8
|
try {
|
|
9
|
-
const client = new MongoClient(
|
|
10
|
-
process.env.MONGODB_CONNSTRING,
|
|
11
|
-
);
|
|
9
|
+
const client = new MongoClient(process.env.MONGODB_CONNSTRING);
|
|
12
10
|
client.db(process.env.MONGODB_DB);
|
|
13
11
|
return client;
|
|
14
12
|
} catch (err) {
|
package/docker/Dockerfile
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# syntax=docker/dockerfile:1
|
|
2
|
-
FROM node:23.11
|
|
2
|
+
FROM node:23.11
|
|
3
3
|
|
|
4
4
|
# aiiinotate port
|
|
5
|
-
ARG PORT
|
|
5
|
+
ARG PORT
|
|
6
6
|
ENV PORT=${PORT}
|
|
7
7
|
|
|
8
8
|
# set up environment
|
|
@@ -11,15 +11,17 @@ SHELL ["/bin/bash", "-c"]
|
|
|
11
11
|
|
|
12
12
|
# root of the app in the docker container
|
|
13
13
|
WORKDIR /aiiinotate
|
|
14
|
-
# copy the .env in the docker container
|
|
15
|
-
COPY ./
|
|
16
|
-
# install
|
|
14
|
+
# copy the docker .env in the docker container
|
|
15
|
+
COPY ./docker/.env /aiiinotate/.env
|
|
16
|
+
# for debug: install `iproute2` which provides CLI `ss` to monitor active ports
|
|
17
|
+
RUN apt update && apt install iproute2 -y
|
|
18
|
+
# install the app as an NPM library. we do a global install as it saves us from issues with `$PATH`.
|
|
17
19
|
RUN npm i -g aiiinotate
|
|
18
20
|
|
|
19
|
-
RUN npm i -g migrate-mongo@^12.1.3
|
|
20
|
-
# migrate the database
|
|
21
|
-
RUN aiiinotate --env=/aiiinotate/.env -- migrate apply
|
|
22
21
|
# expose the used port
|
|
23
22
|
EXPOSE $PORT
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
|
|
24
|
+
# run the migrations and start the app.
|
|
25
|
+
# NOTE migrations must be done in CMD: they need the mongo service to be running.
|
|
26
|
+
CMD aiiinotate --env=/aiiinotate/.env -- migrate apply && \
|
|
27
|
+
aiiinotate --env=/aiiinotate/.env -- serve prod;
|
package/docker/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Docker usage
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
Note that all `docker` commands must be run from the `docker/` directory !
|
|
8
|
+
|
|
9
|
+
1. Clone the repo and `cd` in it
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
git clone git@github.com:Aikon-platform/aiiinotate.git
|
|
13
|
+
cd aiiinotate
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
2. Create a `.env` file at `config/.env` (from the root of the project)
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
cp ./config/.env.template ./config/.env
|
|
20
|
+
vim ./config/.env # do your edits
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
3. Build the containers
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cd docker
|
|
27
|
+
bash docker.sh build
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
4. Start the containers
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bash docker.sh start
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Troubleshooting and useful commands
|
|
39
|
+
|
|
40
|
+
Access `mongosh` within the Mongo container:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
sudo docker exec -it docker-mongo-1 mongosh
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Check running ports in the Web container:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
sudo docker exec -it docker-web-1 ss -tnl
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
View globally installed NPM packages in the Web container:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
sudo docker exec -it docker-web-1 npm list -g --depth=0
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
CURL the Web container
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
sudo docker exec -it docker-web-1 curl http://0.0.0.0:4444 # change 4444 by your $APP_PORT
|
|
62
|
+
```
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
x-proxy-settings: &proxy-settings
|
|
2
|
+
HTTP_PROXY: ${HTTP_PROXY:-}
|
|
3
|
+
HTTPS_PROXY: ${HTTPS_PROXY:-}
|
|
4
|
+
NO_PROXY: "localhost,127.0.0.1,mongo,web,.aiiinotate_network"
|
|
5
|
+
|
|
1
6
|
services:
|
|
2
7
|
mongo:
|
|
3
8
|
image: mongo:8
|
|
4
9
|
restart: always
|
|
5
|
-
|
|
10
|
+
networks:
|
|
11
|
+
- aiiinotate_network
|
|
12
|
+
environment:
|
|
13
|
+
<<: *proxy-settings
|
|
6
14
|
# MONGO_INITDB_ROOT_USERNAME: root
|
|
7
15
|
# MONGO_INITDB_ROOT_PASSWORD: example
|
|
8
16
|
|
|
@@ -15,7 +23,15 @@ services:
|
|
|
15
23
|
env_file:
|
|
16
24
|
- ../config/.env
|
|
17
25
|
ports:
|
|
18
|
-
- ${APP_PORT}:${APP_PORT}
|
|
26
|
+
- "${APP_PORT}:${APP_PORT}"
|
|
19
27
|
depends_on:
|
|
20
28
|
- mongo
|
|
29
|
+
networks:
|
|
30
|
+
- aiiinotate_network
|
|
31
|
+
environment:
|
|
32
|
+
<<: *proxy-settings
|
|
21
33
|
restart: always
|
|
34
|
+
|
|
35
|
+
networks:
|
|
36
|
+
aiiinotate_network:
|
|
37
|
+
driver: bridge
|
package/docker/docker.sh
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -e;
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
|
6
|
+
|
|
7
|
+
# basic / dev config
|
|
8
|
+
ENV_CONFIG="$SCRIPT_DIR/../config/.env"
|
|
9
|
+
# docker specific config, created by `env_to_docker`
|
|
10
|
+
ENV_DOCKER="$SCRIPT_DIR/.env"
|
|
11
|
+
|
|
12
|
+
get_os() {
|
|
13
|
+
unameOut="$(uname -s)"
|
|
14
|
+
case "${unameOut}" in
|
|
15
|
+
Linux*) os=Linux;;
|
|
16
|
+
Darwin*) os=Mac;;
|
|
17
|
+
CYGWIN*) os=Cygwin;;
|
|
18
|
+
MINGW*) os=MinGw;;
|
|
19
|
+
MSYS_NT*) os=Git;;
|
|
20
|
+
*) os="UNKNOWN:${unameOut}"
|
|
21
|
+
esac
|
|
22
|
+
echo "${os}"
|
|
23
|
+
}
|
|
24
|
+
OS=$(get_os)
|
|
25
|
+
|
|
26
|
+
# mac+linux compatible file replacements
|
|
27
|
+
sed_repl () {
|
|
28
|
+
sed_expr="$1"
|
|
29
|
+
file="$2"
|
|
30
|
+
if [ "$OS" = "Linux" ];
|
|
31
|
+
then sed -i -e "$sed_expr" "$file";
|
|
32
|
+
else sed -i "" -e "$sed_expr" "$file";
|
|
33
|
+
fi
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# copy config/.env file to docker/.env and adapt the file to work with docker.
|
|
37
|
+
env_to_docker() {
|
|
38
|
+
# check the env file is found
|
|
39
|
+
if [ ! -f "$ENV_CONFIG" ];
|
|
40
|
+
then
|
|
41
|
+
echo ".env file not found at at '$ENV_CONFIG'. exiting...";
|
|
42
|
+
return 1; # will exit when used with `set -e`
|
|
43
|
+
fi;
|
|
44
|
+
|
|
45
|
+
# NOTE that the MongoDB host in Docker MUST BE the name of the Mongo docker service (defined in docker-compose)
|
|
46
|
+
cp "$ENV_CONFIG" "$ENV_DOCKER";
|
|
47
|
+
sed_repl s~^MONGODB_HOST=.*$~MONGODB_HOST=mongo~ "$ENV_DOCKER";
|
|
48
|
+
sed_repl s~^APP_HOST=.*~APP_HOST=0.0.0.0~ "$ENV_DOCKER";
|
|
49
|
+
}
|
|
50
|
+
env_to_docker;
|
|
51
|
+
|
|
52
|
+
build_containers () {
|
|
53
|
+
sudo docker compose --env-file "$ENV_DOCKER" build --no-cache;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
start_containers() {
|
|
57
|
+
sudo docker compose --env-file "$ENV_DOCKER" up --force-recreate;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
stop_containers() {
|
|
61
|
+
sudo docker compose --env-file "$ENV_DOCKER" down;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
case "$1" in
|
|
65
|
+
start)
|
|
66
|
+
start_containers
|
|
67
|
+
;;
|
|
68
|
+
stop)
|
|
69
|
+
stop_containers
|
|
70
|
+
;;
|
|
71
|
+
build)
|
|
72
|
+
stop_containers
|
|
73
|
+
build_containers
|
|
74
|
+
start_containers
|
|
75
|
+
;;
|
|
76
|
+
*)
|
|
77
|
+
echo "Usage: $0 {build|start|stop}"
|
|
78
|
+
exit 1
|
|
79
|
+
;;
|
|
80
|
+
esac;
|
|
81
|
+
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Notes, quirks and troubleshooting
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Notes
|
|
6
|
+
|
|
7
|
+
### Uniqueness
|
|
8
|
+
|
|
9
|
+
As of writing (09.10.25), there are no uniqueness constraints on annotations. There is only a uniqueness constraint on collection `manifest2` on field `manifest2.@id` (the ID of a manifest).
|
|
10
|
+
|
|
11
|
+
Ideally, we would want to avoid having duplicate annotations in the database. This is more complicated in practice: at least for `annotions2`, an annotation's `@id` field is re-generated and a random part (an UUID) is generated at insert time. This means that, when trying to store the same annotation (with the same `@id`), the `@id` is changed, and so a different value is inserted.
|
|
12
|
+
|
|
13
|
+
This means that we can't have a uniqueness constraint on `@id` or `id` fields of annotations. Another option would be to have a uniqueness constraint on annotation targets (no 2 annotations can annotate the same region), but this behaviour seems brittle in practice, so it's not yet implemented.
|
|
14
|
+
|
|
15
|
+
### Concurrency
|
|
16
|
+
|
|
17
|
+
For clients, concurrency/parrallelization (i.e., with JS `Promise.all()`) on insert/update should be avoided because it can cause a data race: several processes can attempt to write the same thing in parrallel.
|
|
18
|
+
|
|
19
|
+
For example, when inserting annotations, the manifests related to each annotation are inserted in parrallel. Since this is a side effect, 2 processes may unknowingly try to insert the same manifest in the database, which causes a uniqueness constraint to fail. This error can be hard to debug, so it's best to avoid concurrency at write time.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Dev quirks
|
|
24
|
+
|
|
25
|
+
Sometimes, node/fastify can be weird. When scratching your head at dev errors, look here:)
|
|
26
|
+
|
|
27
|
+
### Error swallowing at app build
|
|
28
|
+
|
|
29
|
+
Errors that happen when a plugin is being registered will cause the app's startup to fail, without necessarily throwing an error.
|
|
30
|
+
|
|
31
|
+
This is especially true for test cases that build the fastify app:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
test
|
|
35
|
+
=> build fastify app
|
|
36
|
+
=> build step fails silently
|
|
37
|
+
=> test fails
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
#### Troubleshooting
|
|
41
|
+
|
|
42
|
+
- normal behaviour: when a runtime error happens, the failing test will log the error on the console
|
|
43
|
+
- what this error looks like:
|
|
44
|
+
- tests fail **very quickly**,
|
|
45
|
+
- without throwing an error, seemingly without even running the test suite
|
|
46
|
+
- the proces doesn't exit, although all tests have failed
|
|
47
|
+
- when `npm run test` fails like this, run `npm run start`. See if normal startup throws an error
|
|
48
|
+
- NOTE: normal startup not throwing does NOT mean that the build step necessarily worked
|
|
49
|
+
|
|
50
|
+
#### Possible help/solutions
|
|
51
|
+
|
|
52
|
+
- find a way to stisfyingly use `try...catch` at plugin registration.
|
|
53
|
+
- look into these issues: [2694](https://github.com/fastify/fastify/issues/2694)
|
|
54
|
+
- in particular, it may be an issue specific to async plugins ?
|
|
55
|
+
|
|
56
|
+
### Route response schema definition
|
|
57
|
+
|
|
58
|
+
For some reason, route schema definition is much less flexible for responses than for queries.
|
|
59
|
+
|
|
60
|
+
#### The problem
|
|
61
|
+
|
|
62
|
+
**Query schemas**: In queries (`schema.params` or `schema.querystring`), fastify is very permissive. You can use unresolved schemas (with `$ref`), save entire schemas as JsonSchemas ...
|
|
63
|
+
|
|
64
|
+
**Response schemas**: Response schemas have more constraints.
|
|
65
|
+
- **response schemas are defined at route level** and cannot be stored as full JsonSchemas:
|
|
66
|
+
```js
|
|
67
|
+
// this will be an invalid response schema: it is trying to store a complete response schema as a JsonSchema
|
|
68
|
+
fastify.addSchema({
|
|
69
|
+
$id: makeSchemaUri("routeResponsePost"),
|
|
70
|
+
properties: {
|
|
71
|
+
200: { ... },
|
|
72
|
+
500: { ... },
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// this is a fixed version: an object containing schemas, not a full JsonSchema, to be used inside a Route definition
|
|
77
|
+
schema: {
|
|
78
|
+
response: {
|
|
79
|
+
200: { ... },
|
|
80
|
+
500: { ... }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// if you want to reuse a schema, store it as a JS object and import it.
|
|
84
|
+
```
|
|
85
|
+
- **unresolved response schemas (`$ref`) are forbidden**. You cannot use `$ref`. In the app, use `fastify.schemasResolver` to resolve the schema to a plain `JsonSchema` without `$ref`.
|
|
86
|
+
|
|
87
|
+
#### The fix
|
|
88
|
+
|
|
89
|
+
In short, here's **how to use shared schemas in responses**:
|
|
90
|
+
1. Define payload schemas for different response cases:
|
|
91
|
+
```js
|
|
92
|
+
// in case of a POST success
|
|
93
|
+
fastify.addSchema({
|
|
94
|
+
$id: makeSchemaUri("routeResponseInsert"),
|
|
95
|
+
type: "object",
|
|
96
|
+
// ...properties
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// in case of a POST error
|
|
100
|
+
fastify.addSchema({
|
|
101
|
+
$id: makeSchemaUri("routeResponseError"),
|
|
102
|
+
type: "object",
|
|
103
|
+
// ...properties
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
2. Resolve schemas and use in responses
|
|
107
|
+
```js
|
|
108
|
+
const routeResponseInsert = fastify.getSchema("...");
|
|
109
|
+
const routeResponseError = fastify.getSchema("...");
|
|
110
|
+
fastify.post(
|
|
111
|
+
"/annotations/:iiifPresentationVersion/create",
|
|
112
|
+
{
|
|
113
|
+
schema: {
|
|
114
|
+
body: routeAnnotations2Or3Schema,
|
|
115
|
+
response: {
|
|
116
|
+
200: routeResponseInsert,
|
|
117
|
+
500: routeResponseError
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
async (req, rep) => {}
|
|
122
|
+
)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### `migrate revert-all && migrate apply`
|
|
126
|
+
|
|
127
|
+
#### The problem
|
|
128
|
+
|
|
129
|
+
Sometimes, you want to remove all migrations and then rerun them. This will cause an error like:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
{
|
|
133
|
+
...,
|
|
134
|
+
errmsg: 'namespace aiiinotate.manifests2 already exists, but with different options: { uuid: UUID("65ca87c4-ddce-4c2d-a6f9-08ff6b70c0a7"), validationLevel: "strict", validationAction: "error" }',
|
|
135
|
+
code: 48,
|
|
136
|
+
codeName: 'NamespaceExists',
|
|
137
|
+
...
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### The fix
|
|
142
|
+
|
|
143
|
+
I figure it's an error due to cache or something like that. Either wait or manually delete the collection or the entire database using `mongosh`.
|
package/docs/progress.md
CHANGED
|
@@ -36,124 +36,3 @@ Routes are only implemented with IIIF Presentation API 2.x, not with the 3.0 ver
|
|
|
36
36
|
- fetching and inserting manifests related to an annotation when using inserting annotations.
|
|
37
37
|
|
|
38
38
|
---
|
|
39
|
-
|
|
40
|
-
## Notes
|
|
41
|
-
|
|
42
|
-
### Uniqueness
|
|
43
|
-
|
|
44
|
-
As of writing (09.10.25), there are no uniqueness constraints on annotations. There is only a uniqueness constraint on collection `manifest2` on field `manifest2.@id` (the ID of a manifest).
|
|
45
|
-
|
|
46
|
-
Ideally, we would want to avoid having duplicate annotations in the database. This is more complicated in practice: at least for `annotions2`, an annotation's `@id` field is re-generated and a random part (an UUID) is generated at insert time. This means that, when trying to store the same annotation (with the same `@id`), the `@id` is changed, and so a different value is inserted.
|
|
47
|
-
|
|
48
|
-
This means that we can't have a uniqueness constraint on `@id` or `id` fields of annotations. Another option would be to have a uniqueness constraint on annotation targets (no 2 annotations can annotate the same region), but this behaviour seems brittle in practice, so it's not yet implemented.
|
|
49
|
-
|
|
50
|
-
### Concurrency
|
|
51
|
-
|
|
52
|
-
For clients, concurrency/parrallelization (i.e., with JS `Promise.all()`) on insert/update should be avoided because it can cause a data race: several processes can attempt to write the same thing in parrallel.
|
|
53
|
-
|
|
54
|
-
For example, when inserting annotations, the manifests related to each annotation are inserted in parrallel. Since this is a side effect, 2 processes may unknowingly try to insert the same manifest in the database, which causes a uniqueness constraint to fail. This error can be hard to debug, so it's best to avoid concurrency at write time.
|
|
55
|
-
|
|
56
|
-
---
|
|
57
|
-
|
|
58
|
-
## Dev quirks
|
|
59
|
-
|
|
60
|
-
Sometimes, node/fastify can be weird. When scratching your head at dev errors, look here:)
|
|
61
|
-
|
|
62
|
-
### Error swallowing at app build
|
|
63
|
-
|
|
64
|
-
Errors that happen when a plugin is being registered will cause the app's startup to fail, without necessarily throwing an error.
|
|
65
|
-
|
|
66
|
-
This is especially true for test cases that build the fastify app:
|
|
67
|
-
|
|
68
|
-
```
|
|
69
|
-
test
|
|
70
|
-
=> build fastify app
|
|
71
|
-
=> build step fails silently
|
|
72
|
-
=> test fails
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
#### Troubleshooting
|
|
76
|
-
|
|
77
|
-
- normal behaviour: when a runtime error happens, the failing test will log the error on the console
|
|
78
|
-
- what this error looks like:
|
|
79
|
-
- tests fail **very quickly**,
|
|
80
|
-
- without throwing an error, seemingly without even running the test suite
|
|
81
|
-
- the proces doesn't exit, although all tests have failed
|
|
82
|
-
- when `npm run test` fails like this, run `npm run start`. See if normal startup throws an error
|
|
83
|
-
- NOTE: normal startup not throwing does NOT mean that the build step necessarily worked
|
|
84
|
-
|
|
85
|
-
#### Possible help/solutions
|
|
86
|
-
|
|
87
|
-
- find a way to stisfyingly use `try...catch` at plugin registration.
|
|
88
|
-
- look into these issues: [2694](https://github.com/fastify/fastify/issues/2694)
|
|
89
|
-
- in particular, it may be an issue specific to async plugins ?
|
|
90
|
-
|
|
91
|
-
### Route response schema definition
|
|
92
|
-
|
|
93
|
-
For some reason, route schema definition is much less flexible for responses than for queries.
|
|
94
|
-
|
|
95
|
-
#### The problem
|
|
96
|
-
|
|
97
|
-
**Query schemas**: In queries (`schema.params` or `schema.querystring`), fastify is very permissive. You can use unresolved schemas (with `$ref`), save entire schemas as JsonSchemas ...
|
|
98
|
-
|
|
99
|
-
**Response schemas**: Response schemas have more constraints.
|
|
100
|
-
- **response schemas are defined at route level** and cannot be stored as full JsonSchemas:
|
|
101
|
-
```js
|
|
102
|
-
// this will be an invalid response schema: it is trying to store a complete response schema as a JsonSchema
|
|
103
|
-
fastify.addSchema({
|
|
104
|
-
$id: makeSchemaUri("routeResponsePost"),
|
|
105
|
-
properties: {
|
|
106
|
-
200: { ... },
|
|
107
|
-
500: { ... },
|
|
108
|
-
}
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
// this is a fixed version: an object containing schemas, not a full JsonSchema, to be used inside a Route definition
|
|
112
|
-
schema: {
|
|
113
|
-
response: {
|
|
114
|
-
200: { ... },
|
|
115
|
-
500: { ... }
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
// if you want to reuse a schema, store it as a JS object and import it.
|
|
119
|
-
```
|
|
120
|
-
- **unresolved response schemas (`$ref`) are forbidden**. You cannot use `$ref`. In the app, use `fastify.schemasResolver` to resolve the schema to a plain `JsonSchema` without `$ref`.
|
|
121
|
-
|
|
122
|
-
#### The fix
|
|
123
|
-
|
|
124
|
-
In short, here's **how to use shared schemas in responses**:
|
|
125
|
-
1. Define payload schemas for different response cases:
|
|
126
|
-
```js
|
|
127
|
-
// in case of a POST success
|
|
128
|
-
fastify.addSchema({
|
|
129
|
-
$id: makeSchemaUri("routeResponseInsert"),
|
|
130
|
-
type: "object",
|
|
131
|
-
// ...properties
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
// in case of a POST error
|
|
135
|
-
fastify.addSchema({
|
|
136
|
-
$id: makeSchemaUri("routeResponseError"),
|
|
137
|
-
type: "object",
|
|
138
|
-
// ...properties
|
|
139
|
-
});
|
|
140
|
-
```
|
|
141
|
-
2.
|
|
142
|
-
2. Resolve schemas and use in responses
|
|
143
|
-
```js
|
|
144
|
-
const routeResponseInsert = fastify.getSchema("...");
|
|
145
|
-
const routeResponseError = fastify.getSchema("...");
|
|
146
|
-
fastify.post(
|
|
147
|
-
"/annotations/:iiifPresentationVersion/create",
|
|
148
|
-
{
|
|
149
|
-
schema: {
|
|
150
|
-
body: routeAnnotations2Or3Schema,
|
|
151
|
-
response: {
|
|
152
|
-
200: routeResponseInsert,
|
|
153
|
-
500: routeResponseError
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
async (req, rep) => {}
|
|
158
|
-
)
|
|
159
|
-
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiiinotate",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "a fast IIIF-compliant annotation server",
|
|
5
5
|
"main": "./cli/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
14
|
"cli": "node ./cli/index.js --env=./config/.env",
|
|
15
|
-
"setup": "npm run cli migrate apply",
|
|
16
|
-
"start": "npm run cli serve dev",
|
|
17
|
-
"test": "dotenvx run -f ./config/.env -- node --test --test-isolation=none",
|
|
15
|
+
"setup": "sudo systemctl start mongod && npm run cli migrate apply",
|
|
16
|
+
"start": "sudo systemctl start mongod && npm run cli serve dev",
|
|
17
|
+
"test": "sudo systemctl start mongod && dotenvx run -f ./config/.env -- node --test --test-isolation=none",
|
|
18
18
|
"lint": "npx eslint --fix",
|
|
19
19
|
"migrate": "npm run cli -- migrate"
|
|
20
20
|
},
|
|
@@ -41,7 +41,7 @@ test("test annotation Routes", async (t) => {
|
|
|
41
41
|
|
|
42
42
|
// NOTE: it is necessary to run the app because internally there are fetches to external data.
|
|
43
43
|
try {
|
|
44
|
-
await fastify.listen({ port: process.env.APP_PORT });
|
|
44
|
+
await fastify.listen({ port: process.env.APP_PORT, host: process.env.APP_HOST });
|
|
45
45
|
} catch (err) {
|
|
46
46
|
console.log("FASTIFY ERROR", err);
|
|
47
47
|
throw err;
|
|
@@ -27,7 +27,7 @@ test("test Manifests2 module", async (t) => {
|
|
|
27
27
|
|
|
28
28
|
// NOTE: it is necessary to run the app because internally there are fetches to external data.
|
|
29
29
|
try {
|
|
30
|
-
await fastify.listen({ port: process.env.APP_PORT });
|
|
30
|
+
await fastify.listen({ port: process.env.APP_PORT, host: process.env.APP_HOST });
|
|
31
31
|
} catch (err) {
|
|
32
32
|
console.log("FASTIFY ERROR", err);
|
|
33
33
|
throw err;
|
|
@@ -30,7 +30,7 @@ test("test manifests Routes", async (t) => {
|
|
|
30
30
|
|
|
31
31
|
// NOTE: it is necessary to run the app because internally there are fetches to external data.
|
|
32
32
|
try {
|
|
33
|
-
await fastify.listen({ port: process.env.APP_PORT });
|
|
33
|
+
await fastify.listen({ port: process.env.APP_PORT, host: process.env.APP_HOST });
|
|
34
34
|
} catch (err) {
|
|
35
35
|
console.log("FASTIFY ERROR", err);
|
|
36
36
|
throw err;
|
package/src/data/routes.test.js
CHANGED
|
@@ -26,7 +26,7 @@ test("test common routes", async (t) => {
|
|
|
26
26
|
|
|
27
27
|
// NOTE: it is necessary to run the app because internally there are fetches to external data.
|
|
28
28
|
try {
|
|
29
|
-
await fastify.listen({ port: process.env.APP_PORT });
|
|
29
|
+
await fastify.listen({ port: process.env.APP_PORT, host: process.env.APP_HOST });
|
|
30
30
|
} catch (err) {
|
|
31
31
|
console.log("FASTIFY ERROR", err);
|
|
32
32
|
throw err;
|
package/src/server.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import build from "#src/app.js";
|
|
6
|
+
import { visibleLog } from "#utils/utils.js";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* @param {import("#types").RerveModeType} serveMode
|
|
@@ -14,8 +15,10 @@ async function server (serveMode) {
|
|
|
14
15
|
|
|
15
16
|
const fastify = await build(serveMode);
|
|
16
17
|
|
|
18
|
+
visibleLog([process.env.APP_HOST, process.env.APP_PORT]);
|
|
19
|
+
|
|
17
20
|
try {
|
|
18
|
-
fastify.listen({ port: process.env.APP_PORT });
|
|
21
|
+
fastify.listen({ port: process.env.APP_PORT, host: process.env.APP_HOST });
|
|
19
22
|
} catch(err) {
|
|
20
23
|
fastify.log.error(err);
|
|
21
24
|
process.exit(1);
|