Skip to content

Commit 1390821

Browse files
authored
[INTERNAL] ui5-workspace.yaml schema (#543)
JIRA: CPOUI5FOUNDATION-584 Provide ui5-workspace.yaml schema that complies with the [local dependency resolution spec](https://github.com/SAP/ui5-tooling/blob/rfc-local-dependency-resolution/rfcs/0006-local-dependency-resolution.md#workspace-configuration-file) For further details, check the [RFC 0006 Local Dependency Resolution](https://github.com/SAP/ui5-tooling/blob/rfc-local-dependency-resolution/rfcs/0006-local-dependency-resolution.md)
1 parent b721a91 commit 1390821

File tree

15 files changed

+617
-38
lines changed

15 files changed

+617
-38
lines changed

lib/validation/ValidationError.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,14 @@ class ValidationError extends Error {
4949
separator += chalk.grey.dim("\u2500".repeat(process.stdout.columns || 80));
5050
}
5151
separator += "\n\n";
52-
let message = chalk.red(`Invalid ui5.yaml configuration for project ${this.project.id}`) + "\n\n";
52+
let message;
53+
54+
if (this.project) { // ui5-workspace.yaml is project independent, so in that case, no project is available
55+
message = chalk.red(`Invalid ui5.yaml configuration for project ${this.project.id}`) + "\n\n";
56+
} else {
57+
message = chalk.red(`Invalid workspace configuration.`) + "\n\n";
58+
}
59+
5360
message += this.errors.map((error) => {
5461
return this.formatError(error);
5562
}).join(separator);
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema",
3+
"$id": "http://ui5.sap/schema/ui5-workspace.json",
4+
"title": "ui5-workspace.yaml",
5+
"description": "Schema for UI5 Tooling Workspace Configuration File (ui5-workspace.yaml)",
6+
"$comment": "See https://sap.github.io/ui5-tooling/",
7+
"type": "object",
8+
"required": ["specVersion", "metadata", "dependencyManagement"],
9+
"properties": {
10+
"additionalProperties": false,
11+
"specVersion": {
12+
"enum": ["workspace/1.0"],
13+
"errorMessage": "Unsupported \"specVersion\"\nYour UI5 CLI installation might be outdated.\nSupported specification versions: \"workspace/1.0\"\nFor details see: // TODO: Add link to Documentation"
14+
},
15+
"metadata": {
16+
"$ref": "#/definitions/metadata"
17+
},
18+
"dependencyManagement": {
19+
"$ref": "#/definitions/dependencyManagement"
20+
}
21+
},
22+
"definitions": {
23+
"metadata": {
24+
"type": "object",
25+
"required": ["name"],
26+
"properties": {
27+
"additionalProperties": false,
28+
"name": {
29+
"type": "string",
30+
"errorMessage": "Workspace name is not provided. There must be a wokrspace name defined."
31+
}
32+
}
33+
},
34+
"dependencyManagement": {
35+
"type": "object",
36+
"properties": {
37+
"additionalProperties": false,
38+
"resolutions": {
39+
"type": "array",
40+
"additionalProperties": false,
41+
"items": {
42+
"type": "object",
43+
"additionalProperties": false,
44+
"properties": {
45+
"path": {
46+
"type": "string"
47+
}
48+
}
49+
}
50+
}
51+
}
52+
}
53+
}
54+
}

lib/validation/validator.js

Lines changed: 74 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,48 @@
11
import {fileURLToPath} from "node:url";
22
import {readFile} from "node:fs/promises";
33

4-
async function loadSchema(schemaPath) {
5-
const filePath = schemaPath.replace("http://ui5.sap/schema/", "");
6-
const schemaFile = await readFile(
7-
fileURLToPath(new URL(`./schema/${filePath}`, import.meta.url)), {encoding: "utf8"}
8-
);
9-
return JSON.parse(schemaFile);
10-
}
4+
/**
5+
* @module @ui5/project/validation/validator
6+
* @description A collection of validation related APIs
7+
* @public
8+
*/
9+
10+
/**
11+
* @enum {string}
12+
* @private
13+
* @readonly
14+
*/
15+
export const SCHEMA_VARIANTS = {
16+
"ui5": "ui5.json",
17+
"ui5-workspace": "ui5-workspace.json"
18+
};
1119

1220
class Validator {
13-
constructor({Ajv, ajvErrors}) {
21+
constructor({Ajv, ajvErrors, schemaName}) {
22+
if (!schemaName && !SCHEMA_VARIANTS[schemaName]) {
23+
throw new Error(
24+
`"schemaName" is missing or incorrect. The available schemaName variants are ${Object.keys(
25+
SCHEMA_VARIANTS
26+
).join(", ")}`
27+
);
28+
}
29+
30+
this._schemaName = SCHEMA_VARIANTS[schemaName];
31+
1432
this.ajv = new Ajv({
1533
allErrors: true,
1634
jsonPointers: true,
17-
loadSchema
35+
loadSchema: Validator.loadSchema
1836
});
1937
ajvErrors(this.ajv);
2038
}
2139

2240
_compileSchema() {
41+
const schemaName = this._schemaName;
42+
2343
if (!this._compiling) {
2444
this._compiling = Promise.resolve().then(async () => {
25-
const schema = await loadSchema("ui5.json");
45+
const schema = await Validator.loadSchema(schemaName);
2646
const validate = await this.ajv.compileAsync(schema);
2747
return validate;
2848
});
@@ -46,18 +66,33 @@ class Validator {
4666
});
4767
}
4868
}
69+
70+
static async loadSchema(schemaPath) {
71+
const filePath = schemaPath.replace("http://ui5.sap/schema/", "");
72+
const schemaFile = await readFile(
73+
fileURLToPath(new URL(`./schema/${filePath}`, import.meta.url)), {encoding: "utf8"}
74+
);
75+
return JSON.parse(schemaFile);
76+
}
4977
}
5078

51-
let validator;
79+
const validator = Object.create(null);
5280

53-
/**
54-
* @module @ui5/project/validation/validator
55-
* @description A collection of validation related APIs
56-
* @public
57-
*/
81+
async function _validate(schemaName, options) {
82+
if (!validator[schemaName]) {
83+
validator[schemaName] = (async () => {
84+
const {default: Ajv} = await import("ajv");
85+
const {default: ajvErrors} = await import("ajv-errors");
86+
return new Validator({Ajv, ajvErrors, schemaName});
87+
})();
88+
}
89+
90+
const schemaValidator = await validator[schemaName];
91+
await schemaValidator.validate(options);
92+
}
5893

5994
/**
60-
* Validates the given configuration.
95+
* Validates the given ui5 configuration.
6196
*
6297
* @public
6398
* @function
@@ -76,12 +111,28 @@ let validator;
76111
* @returns {Promise<undefined>} Returns a Promise that resolves when the validation succeeds
77112
*/
78113
export async function validate(options) {
79-
if (!validator) {
80-
const {default: Ajv} = await import("ajv");
81-
const {default: ajvErrors} = await import("ajv-errors");
82-
validator = new Validator({Ajv, ajvErrors});
83-
}
84-
await validator.validate(options);
114+
await _validate("ui5", options);
115+
}
116+
117+
/**
118+
* Validates the given ui5-workspace configuration.
119+
*
120+
* @public
121+
* @function
122+
* @static
123+
* @param {object} options
124+
* @param {object} options.config ui5-workspace Configuration to validate
125+
* @param {object} [options.yaml] YAML information
126+
* @param {string} options.yaml.path Path of the YAML file
127+
* @param {string} options.yaml.source Content of the YAML file
128+
* @param {number} [options.yaml.documentIndex=0] Document index in case the YAML file contains multiple documents
129+
* @throws {@ui5/project/validation/ValidationError}
130+
* Rejects with a {@link @ui5/project/validation/ValidationError ValidationError}
131+
* when the validation fails.
132+
* @returns {Promise<undefined>} Returns a Promise that resolves when the validation succeeds
133+
*/
134+
export async function validateWorkspace(options) {
135+
await _validate("ui5-workspace", options);
85136
}
86137

87138
export {

test/lib/validation/schema/specVersion/kind/extension.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ async function assertValidation(t, config, expectedErrors = undefined) {
2222
}
2323

2424
test.before((t) => {
25-
t.context.validator = new Validator({Ajv, ajvErrors});
25+
t.context.validator = new Validator({Ajv, ajvErrors, schemaName: "ui5"});
2626
t.context.ajvCoverage = new AjvCoverage(t.context.validator.ajv, {
2727
includes: ["schema/specVersion/kind/extension.json"]
2828
});

test/lib/validation/schema/specVersion/kind/extension/project-shim.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ async function assertValidation(t, config, expectedErrors = undefined) {
2323
}
2424

2525
test.before((t) => {
26-
t.context.validator = new Validator({Ajv, ajvErrors});
26+
t.context.validator = new Validator({Ajv, ajvErrors, schemaName: "ui5"});
2727
t.context.ajvCoverage = new AjvCoverage(t.context.validator.ajv, {
2828
includes: ["schema/specVersion/kind/extension/project-shim.json"]
2929
});

test/lib/validation/schema/specVersion/kind/extension/server-middleware.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ async function assertValidation(t, config, expectedErrors = undefined) {
2323
}
2424

2525
test.before((t) => {
26-
t.context.validator = new Validator({Ajv, ajvErrors});
26+
t.context.validator = new Validator({Ajv, ajvErrors, schemaName: "ui5"});
2727
t.context.ajvCoverage = new AjvCoverage(t.context.validator.ajv, {
2828
includes: ["schema/specVersion/kind/extension/server-middleware.json"]
2929
});

test/lib/validation/schema/specVersion/kind/extension/task.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ async function assertValidation(t, config, expectedErrors = undefined) {
2323
}
2424

2525
test.before((t) => {
26-
t.context.validator = new Validator({Ajv, ajvErrors});
26+
t.context.validator = new Validator({Ajv, ajvErrors, schemaName: "ui5"});
2727
t.context.ajvCoverage = new AjvCoverage(t.context.validator.ajv, {
2828
includes: ["schema/specVersion/kind/extension/task.json"]
2929
});

test/lib/validation/schema/specVersion/kind/project.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ async function assertValidation(t, config, expectedErrors = undefined) {
2222
}
2323

2424
test.before((t) => {
25-
t.context.validator = new Validator({Ajv, ajvErrors});
25+
t.context.validator = new Validator({Ajv, ajvErrors, schemaName: "ui5"});
2626
t.context.ajvCoverage = new AjvCoverage(t.context.validator.ajv, {
2727
includes: ["schema/specVersion/kind/project.json"]
2828
});

test/lib/validation/schema/specVersion/kind/project/application.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ async function assertValidation(t, config, expectedErrors = undefined) {
2828
}
2929

3030
test.before((t) => {
31-
t.context.validator = new Validator({Ajv, ajvErrors});
31+
t.context.validator = new Validator({Ajv, ajvErrors, schemaName: "ui5"});
3232
t.context.ajvCoverage = new AjvCoverage(t.context.validator.ajv, {
3333
includes: ["schema/specVersion/kind/project/application.json"]
3434
});

test/lib/validation/schema/specVersion/kind/project/library.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ async function assertValidation(t, config, expectedErrors = undefined) {
2828
}
2929

3030
test.before((t) => {
31-
t.context.validator = new Validator({Ajv, ajvErrors});
31+
t.context.validator = new Validator({Ajv, ajvErrors, schemaName: "ui5"});
3232
t.context.ajvCoverage = new AjvCoverage(t.context.validator.ajv, {
3333
includes: ["schema/specVersion/kind/project/library.json"]
3434
});

0 commit comments

Comments
 (0)