Skip to content

Commit 3888b01

Browse files
authored
Add command to the CLI to validate the collection metadata file (#1134)
2 parents d16808d + 5e1a746 commit 3888b01

File tree

10 files changed

+256
-76
lines changed

10 files changed

+256
-76
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
All changes that impact users of this module are documented in this file, in the [Common Changelog](https://common-changelog.org) format with some additional specifications defined in the CONTRIBUTING file. This codebase adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
44

5+
## Unreleased [minor]
6+
7+
> Development of this release was supported by the [French Ministry for Foreign Affairs](https://www.diplomatie.gouv.fr/fr/politique-etrangere-de-la-france/diplomatie-numerique/) through its ministerial [State Startups incubator](https://beta.gouv.fr/startups/open-terms-archive.html) under the aegis of the Ambassador for Digital Affairs.
8+
9+
### Added
10+
11+
- Add `ota validate metadata` command to the CLI to validate the collection metadata file
12+
513
## 4.0.2 - 2025-02-04
614

715
_Full changeset and discussions: [#1133](https://github.com/OpenTermsArchive/engine/pull/1133)._

bin/ota-lint.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
1212
const LINT_TEST_FILEPATH = '../scripts/declarations/lint/index.mocha.js';
1313
const LINT_PATH = path.resolve(__dirname, LINT_TEST_FILEPATH);
1414

15-
// Mocha catches unhandled rejection from the user code and re-emits them to the process (see https://github.com/mochajs/mocha/blob/master/lib/runner.js#L198)
15+
// Mocha catches unhandled rejection from the user code and re-emits them to the process
1616
process.on('unhandledRejection', reason => {
1717
// Re-throw them so that the validation command fails in these cases (for example, if there is a syntax error when parsing JSON declaration files)
1818
throw reason;

bin/ota-validate.js

100755100644
Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#! /usr/bin/env node
22
import './env.js';
3-
43
import path from 'path';
54
import { fileURLToPath } from 'url';
65

@@ -9,49 +8,71 @@ import Mocha from 'mocha';
98

109
const __dirname = path.dirname(fileURLToPath(import.meta.url));
1110

12-
const VALIDATE_TEST_FILEPATH = '../scripts/declarations/validate/index.mocha.js';
13-
const VALIDATE_PATH = path.resolve(__dirname, VALIDATE_TEST_FILEPATH);
11+
export function createMocha({ delay = false, reporter = 'spec' } = {}) {
12+
return new Mocha({
13+
delay,
14+
failZero: true,
15+
reporter,
16+
});
17+
}
18+
19+
export async function runMochaTests(mocha, testPath) {
20+
try {
21+
mocha.addFile(testPath); // With `delay` option, this statement will not load the file directly, `loadFilesAsync` is required.
22+
await mocha.loadFilesAsync(); // Load files previously added to the Mocha cache with `addFile`.
23+
24+
return new Promise(resolve => {
25+
let hasFailedTests = false;
26+
27+
mocha.run()
28+
.on('fail', () => { hasFailedTests = true; })
29+
.on('end', () => { resolve(hasFailedTests ? 1 : 0); });
30+
});
31+
} catch (error) {
32+
console.error('Error running tests:', error);
33+
34+
return 2;
35+
}
36+
}
1437

15-
// Mocha catches unhandled rejection from the user code and re-emits them to the process (see https://github.com/mochajs/mocha/blob/master/lib/runner.js#L198)
16-
process.on('unhandledRejection', reason => {
17-
// Re-throw them so that the validation command fails in these cases (for example, if there is a syntax error when parsing JSON declaration files)
18-
throw reason;
38+
process.on('unhandledRejection', reason => { // Mocha catches unhandled rejection from the user code and re-emits them to the process
39+
throw reason; // Re-throw them so that the validation command fails in these cases (for example, if there is a syntax error when parsing JSON declaration files)
1940
});
2041

2142
program
2243
.name('ota validate')
44+
.description('Validate terms declarations and metadata files');
45+
46+
program.command('declarations')
2347
.description('Run a series of tests to check the validity of terms declarations')
2448
.option('-s, --services [serviceId...]', 'service IDs of services to validate')
2549
.option('-t, --types [termsType...]', 'terms types to validate')
2650
.option('-m, --modified', 'target only services modified in the current git branch')
27-
.option('-o, --schema-only', 'much faster check of declarations, but does not check that the documents are actually accessible');
51+
.option('-o, --schema-only', 'much faster check of declarations, but does not check that the documents are actually accessible')
52+
.action(async options => {
53+
const VALIDATE_TEST_FILEPATH = '../scripts/declarations/validate/index.mocha.js';
54+
const VALIDATE_PATH = path.resolve(__dirname, VALIDATE_TEST_FILEPATH);
2855

29-
const mocha = new Mocha({
30-
delay: true, // as the validation script performs an asynchronous load before running the tests, the execution of the tests are delayed until run() is called
31-
failZero: true, // consider that being called with no service to validate is a failure
32-
});
56+
const mocha = createMocha({ delay: true }); // as the validation script performs an asynchronous load before running the tests, the execution of the tests are delayed until run() is called
57+
const generateValidationTestSuite = (await import(VALIDATE_TEST_FILEPATH)).default;
3358

34-
(async () => {
35-
mocha.addFile(VALIDATE_PATH); // As `delay` has been called, this statement will not load the file directly, `loadFilesAsync` is required.
36-
await mocha.loadFilesAsync() // Load files previously added to the Mocha cache with `addFile`.
37-
.catch(error => {
38-
console.error(error);
39-
process.exit(2);
40-
});
59+
generateValidationTestSuite(options);
4160

42-
let hasFailedTests = false;
61+
const exitCode = await runMochaTests(mocha, VALIDATE_PATH);
4362

44-
const generateValidationTestSuite = (await import(VALIDATE_TEST_FILEPATH)).default;
63+
process.exit(exitCode);
64+
});
4565

46-
generateValidationTestSuite(program.parse().opts());
66+
program.command('metadata')
67+
.description('Validate the metadata file structure')
68+
.action(async () => {
69+
const VALIDATE_TEST_FILEPATH = '../scripts/metadata/index.mocha.js';
70+
const VALIDATE_PATH = path.resolve(__dirname, VALIDATE_TEST_FILEPATH);
4771

48-
mocha.run()
49-
.on('fail', () => { hasFailedTests = true; })
50-
.on('end', () => {
51-
if (hasFailedTests) {
52-
process.exit(1);
53-
}
72+
const mocha = createMocha();
73+
const exitCode = await runMochaTests(mocha, VALIDATE_PATH);
5474

55-
process.exit(0);
56-
});
57-
})();
75+
process.exit(exitCode);
76+
});
77+
78+
program.parse();

package-lock.json

Lines changed: 83 additions & 34 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@
3636
"dataset:release": "node bin/ota.js dataset --publish --remove-local-copy",
3737
"dataset:scheduler": "npm run dataset:release -- --schedule",
3838
"declarations:lint": "node bin/ota.js lint",
39-
"declarations:validate": "node bin/ota.js validate",
39+
"declarations:validate": "node bin/ota.js validate declarations",
4040
"declarations:validate:schema": "npm run declarations:validate -- --schema-only",
4141
"lint": "eslint src test scripts bin",
4242
"lint:fix": "npm run lint -- --fix",
43+
"metadata:validate": "node bin/ota.js validate metadata",
4344
"start": "node -r dotenv/config --max-http-header-size=32768 bin/ota.js track",
4445
"start:api": "node bin/ota.js serve",
4546
"start:scheduler": "npm start -- --schedule",
@@ -54,7 +55,8 @@
5455
"@opentermsarchive/turndown": "^7.1.3",
5556
"@stylistic/eslint-plugin-js": "^1.4.1",
5657
"abort-controller": "^3.0.0",
57-
"ajv": "^6.12.6",
58+
"ajv": "^8.17.1",
59+
"ajv-formats": "^3.0.1",
5860
"archiver": "^5.3.0",
5961
"async": "^3.2.2",
6062
"chai": "^4.3.4",

scripts/declarations/validate/index.mocha.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import fsApi from 'fs';
22
import path from 'path';
33

44
import Ajv from 'ajv';
5+
import addFormats from 'ajv-formats';
56
import { expect } from 'chai';
67
import config from 'config';
78
import jsonSourceMap from 'json-source-map';
@@ -178,10 +179,9 @@ export default async options => {
178179
run();
179180
};
180181

181-
const validator = new Ajv({
182-
allErrors: true,
183-
jsonPointers: true,
184-
});
182+
const validator = new Ajv({ allErrors: true });
183+
184+
addFormats(validator);
185185

186186
function assertValid(schema, subject) {
187187
const valid = validator.validate(schema, subject);
@@ -193,7 +193,6 @@ function assertValid(schema, subject) {
193193
const jsonLines = sourceMap.json.split('\n');
194194

195195
validator.errors.forEach(error => {
196-
console.log('error', error);
197196
errorMessage += `\n\n${validator.errorsText([error])}`;
198197
const errorPointer = sourceMap.pointers[error.dataPath];
199198

scripts/declarations/validate/service.schema.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ const schema = {
6767
singleSourceDocumentTerms: {
6868
allOf: [
6969
{ $ref: '#/definitions/sourceDocument' },
70-
{ required: [ 'fetch', 'select' ] },
70+
{
71+
type: 'object',
72+
required: [ 'fetch', 'select' ],
73+
},
7174
],
7275
},
7376
multipleSourceDocumentsTerms: {

0 commit comments

Comments
 (0)