Skip to content

Commit 3b03e5c

Browse files
committed
fix: Validate processing fields everywhere
1 parent 3abfadb commit 3b03e5c

File tree

5 files changed

+157
-46
lines changed

5 files changed

+157
-46
lines changed

json-schema/schema.json

Lines changed: 7 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,7 @@
5454
"properties": {
5555
"type": {
5656
"const": "Collection"
57-
}
58-
}
59-
},
60-
{
61-
"$ref": "#/definitions/stac_extensions"
62-
}
63-
],
64-
"anyOf": [
65-
{
66-
"$comment": "Validate fields in Collection Providers.",
67-
"type": "object",
68-
"required": [
69-
"providers"
70-
],
71-
"properties": {
57+
},
7258
"providers": {
7359
"type": "array",
7460
"minItems": 1,
@@ -110,16 +96,7 @@
11096
}
11197
]
11298
}
113-
}
114-
}
115-
},
116-
{
117-
"type": "object",
118-
"$comment": "This validates the fields in Collection Assets, but does not require them.",
119-
"required": [
120-
"assets"
121-
],
122-
"properties": {
99+
},
123100
"assets": {
124101
"type": "object",
125102
"not": {
@@ -136,16 +113,7 @@
136113
}
137114
}
138115
}
139-
}
140-
}
141-
},
142-
{
143-
"type": "object",
144-
"$comment": "This is the schema for the fields in Item Asset Definitions. It doesn't require any fields.",
145-
"required": [
146-
"item_assets"
147-
],
148-
"properties": {
116+
},
149117
"item_assets": {
150118
"type": "object",
151119
"not": {
@@ -162,20 +130,14 @@
162130
}
163131
}
164132
}
165-
}
166-
}
167-
},
168-
{
169-
"type": "object",
170-
"$comment": "This is the schema for the fields in Summaries. By default, only checks the existance of the properties, but not the schema of the summaries.",
171-
"required": [
172-
"summaries"
173-
],
174-
"properties": {
133+
},
175134
"summaries": {
176135
"$ref": "#/definitions/require_any_field"
177136
}
178137
}
138+
},
139+
{
140+
"$ref": "#/definitions/stac_extensions"
179141
}
180142
]
181143
}

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22
"name": "stac-extensions",
33
"version": "1.0.0",
44
"scripts": {
5-
"test": "npm run check-markdown && npm run check-examples",
5+
"test": "npx ospec && npm run check-markdown && npm run check-examples",
66
"check-markdown": "remark . -f -r .github/remark.yaml",
77
"check-examples": "stac-node-validator . --lint --verbose --schemaMap https://stac-extensions.github.io/processing/v1.0.0/schema.json=./json-schema/schema.json",
88
"format-examples": "stac-node-validator . --format --schemaMap https://stac-extensions.github.io/processing/v1.0.0/schema.json=./json-schema/schema.json"
99
},
10+
"type": "module",
1011
"dependencies": {
12+
"@types/ospec": "^4.0.2",
13+
"ajv": "^8.8.2",
14+
"ospec": "^4.1.1",
1115
"remark-cli": "^8.0.0",
1216
"remark-lint": "^7.0.0",
1317
"remark-lint-no-html": "^2.0.0",

tests/template_collection.test.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import o from 'ospec';
2+
import Ajv from 'ajv';
3+
import { join } from 'path';
4+
import { promises as fs } from 'fs';
5+
import { AjvOptions, rootDirectory, schemaPath } from './validation.js';
6+
7+
const examplePath = join(rootDirectory, 'examples/collection.json');
8+
9+
o.spec('Collection', () => {
10+
let validate;
11+
const ajv = new Ajv(AjvOptions);
12+
13+
o.before(async () => {
14+
const data = JSON.parse(await fs.readFile(schemaPath));
15+
validate = await ajv.compileAsync(data);
16+
});
17+
18+
o('Example should pass validation', async () => {
19+
// given
20+
const example = JSON.parse(await fs.readFile(examplePath));
21+
22+
// when
23+
let valid = validate(example);
24+
25+
// then
26+
o(valid).equals(true)(JSON.stringify(validate.errors, null, 2));
27+
});
28+
29+
o("Example with invalid providers processing:software value should fail validation", async () => {
30+
// given
31+
const example = JSON.parse(await fs.readFile(examplePath));
32+
example['providers'][0]['processing:software'] = null;
33+
34+
// when
35+
let valid = validate(example);
36+
37+
// then
38+
o(valid).equals(false);
39+
o(
40+
validate.errors.some(
41+
(error) =>
42+
error.instancePath === '/providers/0/processing:software' &&
43+
error.message === 'must be object',
44+
),
45+
).equals(true)(JSON.stringify(validate.errors));
46+
});
47+
});

tests/template_item.test.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import o from 'ospec';
2+
import Ajv from 'ajv';
3+
import { join } from 'path';
4+
import { promises as fs } from 'fs';
5+
import { AjvOptions, rootDirectory, schemaPath } from './validation.js';
6+
7+
const examplePath = join(rootDirectory, 'examples/item.json');
8+
9+
o.spec('Item', () => {
10+
let validate;
11+
const ajv = new Ajv(AjvOptions);
12+
13+
o.before(async () => {
14+
const data = JSON.parse(await fs.readFile(schemaPath));
15+
validate = await ajv.compileAsync(data);
16+
});
17+
18+
o('Example should pass validation', async () => {
19+
// given
20+
const example = JSON.parse(await fs.readFile(examplePath));
21+
22+
// when
23+
let valid = validate(example);
24+
25+
// then
26+
o(valid).equals(true)(JSON.stringify(validate.errors, null, 2));
27+
});
28+
29+
o("Example with invalid properties processing:software value should fail validation", async () => {
30+
// given
31+
const example = JSON.parse(await fs.readFile(examplePath));
32+
example['properties']['processing:software'] = null;
33+
34+
// when
35+
let valid = validate(example);
36+
37+
// then
38+
o(valid).equals(false);
39+
o(
40+
validate.errors.some(
41+
(error) =>
42+
error.instancePath === '/properties/processing:software' &&
43+
error.message === 'must be object',
44+
),
45+
).equals(true)(JSON.stringify(validate.errors));
46+
});
47+
48+
o("Example with invalid asset processing:software value should fail validation", async () => {
49+
// given
50+
const example = JSON.parse(await fs.readFile(examplePath));
51+
example['assets']['manifest']['processing:software'] = null;
52+
53+
// when
54+
let valid = validate(example);
55+
56+
// then
57+
o(valid).equals(false);
58+
o(
59+
validate.errors.some(
60+
(error) =>
61+
error.instancePath === '/assets/manifest/processing:software' &&
62+
error.message === 'must be object',
63+
),
64+
).equals(true)(JSON.stringify(validate.errors));
65+
});
66+
});

tests/validation.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import axios from 'axios';
2+
import { dirname, join } from 'path';
3+
import { fileURLToPath } from 'url';
4+
import iriFormats from 'stac-node-validator/iri.js';
5+
6+
const directory = dirname(fileURLToPath(import.meta.url));
7+
8+
const Schemas = new Map();
9+
export function loadSchema(uri) {
10+
let existing = Schemas.get(uri);
11+
if (existing == null) {
12+
existing = loadSchemaFromUri(uri);
13+
Schemas.set(uri, existing);
14+
}
15+
return existing;
16+
}
17+
18+
/**
19+
* function passed in to Ajv instance which allows us to load schemas from a url at run time.
20+
*/
21+
export async function loadSchemaFromUri(uri) {
22+
try {
23+
let response = await axios.get(uri);
24+
return response.data;
25+
} catch (error) {
26+
throw new Error(`-- Schema at '${uri}' not found. Please ensure all entries in 'stac_extensions' are valid.`);
27+
}
28+
}
29+
30+
export const AjvOptions = { loadSchema, formats: Object.assign(iriFormats) };
31+
export const rootDirectory = dirname(directory);
32+
export const schemaPath = join(rootDirectory, 'json-schema/schema.json');

0 commit comments

Comments
 (0)