Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[INTERNAL] ui5-workspace.yaml schema #543

Merged
merged 24 commits into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/graph/Module.js
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,8 @@ class Module {
path: configPath,
source: configFile,
documentIndex
}
},
schemaName: "ui5.json"
});
} catch (error) {
return error;
Expand Down
6 changes: 4 additions & 2 deletions lib/specifications/Specification.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ class Specification {
config,
project: {
id
}
},
schemaName: "ui5.json"
});
} catch (err) {
this._log.verbose(
Expand All @@ -130,7 +131,8 @@ class Specification {
config,
project: {
id
}
},
schemaName: "ui5.json"
});
}

Expand Down
2 changes: 1 addition & 1 deletion lib/validation/ValidationError.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class ValidationError extends Error {
separator += chalk.grey.dim("\u2500".repeat(process.stdout.columns || 80));
}
separator += "\n\n";
let message = chalk.red(`Invalid ui5.yaml configuration for project ${this.project.id}`) + "\n\n";
let message = chalk.red(`Invalid configuration for project ${this.project.id}`) + "\n\n";
d3xter666 marked this conversation as resolved.
Show resolved Hide resolved
message += this.errors.map((error) => {
return this.formatError(error);
}).join(separator);
Expand Down
54 changes: 54 additions & 0 deletions lib/validation/schema/ui5-workspace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "http://ui5.sap/schema/ui5-workspace.json",
"title": "ui5-workspace.yaml",
"description": "Schema for UI5 Tooling Workspace Configuration File (ui5-workspace.yaml)",
"$comment": "See https://sap.github.io/ui5-tooling/",
"type": "object",
"required": ["specVersion", "metadata", "dependencyManagement"],
"properties": {
"additionalProperties": false,
"specVersion": {
"enum": ["workspace/1.0"],
"errorMessage": "Unsupported \"specVersion\"\nYour UI5 CLI installation might be outdated.\nSupported specification versions: \"workspace/1.0\"\nFor details see: // TODO: Add link to Documentation"
},
"metadata": {
"$ref": "#/definitions/metadata"
},
"dependencyManagement": {
"$ref": "#/definitions/dependencyManagement"
}
},
"definitions": {
"metadata": {
"type": "object",
"required": ["name"],
"properties": {
"additionalProperties": false,
"name": {
"type": "string",
"errorMessage": "Workspace name is not provided. There must be a wokrspace name defined."
}
}
},
"dependencyManagement": {
"type": "object",
"properties": {
"additionalProperties": false,
"resolutions": {
"type": "array",
"additionalProperties": false,
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"path": {
"type": "string"
}
}
}
}
}
}
}
}
30 changes: 17 additions & 13 deletions lib/validation/validator.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,33 @@
import {fileURLToPath} from "node:url";
import {readFile} from "node:fs/promises";

async function loadSchema(schemaPath) {
const filePath = schemaPath.replace("http://ui5.sap/schema/", "");
const schemaFile = await readFile(
fileURLToPath(new URL(`./schema/${filePath}`, import.meta.url)), {encoding: "utf8"}
);
return JSON.parse(schemaFile);
}

class Validator {
constructor({Ajv, ajvErrors}) {
this.ajv = new Ajv({
allErrors: true,
jsonPointers: true,
loadSchema
loadSchema: Validator.loadSchema
});
ajvErrors(this.ajv);
}

_compileSchema() {
_compileSchema(schemaName) {
d3xter666 marked this conversation as resolved.
Show resolved Hide resolved
if (!schemaName) {
throw new Error("Schema is missing. Schema name must be provided");
}

if (!this._compiling) {
this._compiling = Promise.resolve().then(async () => {
const schema = await loadSchema("ui5.json");
const schema = await Validator.loadSchema(schemaName);
const validate = await this.ajv.compileAsync(schema);
return validate;
});
}
return this._compiling;
}

async validate({config, project, yaml}) {
const fnValidate = await this._compileSchema();
async validate({config, project, yaml, schemaName}) {
const fnValidate = await this._compileSchema(schemaName);
const valid = fnValidate(config);
if (!valid) {
// Read errors/schema from fnValidate before lazy loading ValidationError module.
Expand All @@ -46,6 +42,14 @@ class Validator {
});
}
}

static async loadSchema(schemaPath) {
const filePath = schemaPath.replace("http://ui5.sap/schema/", "");
const schemaFile = await readFile(
fileURLToPath(new URL(`./schema/${filePath}`, import.meta.url)), {encoding: "utf8"}
);
return JSON.parse(schemaFile);
}
}

let validator;
Expand Down
2 changes: 1 addition & 1 deletion test/lib/graph/Module.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ test("Invalid configuration in file", async (t) => {
});
const err = await t.throwsAsync(ui5Module.getSpecifications());

t.true(err.message.includes("Invalid ui5.yaml configuration"), "Threw with validation error");
t.true(err.message.includes("Invalid configuration"), "Threw with validation error");
// Check that config file name is referenced. This validates that the error was not produced by
// the Specification instance but the Module
t.true(err.message.includes("ui5-test-error.yaml"), "Error message references file name");
Expand Down
2 changes: 1 addition & 1 deletion test/lib/specifications/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ test("Invalid configuration", async (t) => {
}
};
const error = await t.throwsAsync(Specification.create(customProjectInput));
t.is(error.message, `${chalk.red("Invalid ui5.yaml configuration for project application.a.id")}
t.is(error.message, `${chalk.red("Invalid configuration for project application.a.id")}

Configuration \
${chalk.underline(chalk.red("resources/configuration/propertiesFileSourceEncoding"))} \
Expand Down
2 changes: 1 addition & 1 deletion test/lib/validation/ValidationError.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ test.serial("ValidationError.formatErrors", (t) => {
const message = ValidationError.prototype.formatErrors.apply(fakeValidationErrorInstance);

const expectedMessage =
`${chalk.red("Invalid ui5.yaml configuration for project my-project")}
`${chalk.red("Invalid configuration for project my-project")}

Error message 1

Expand Down
2 changes: 1 addition & 1 deletion test/lib/validation/schema/specVersion/kind/extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {_Validator as Validator} from "../../../../../../lib/validation/validato
import ValidationError from "../../../../../../lib/validation/ValidationError.js";

async function assertValidation(t, config, expectedErrors = undefined) {
const validation = t.context.validator.validate({config, project: {id: "my-project"}});
const validation = t.context.validator.validate({config, project: {id: "my-project"}, schemaName: "ui5.json"});
if (expectedErrors) {
const validationError = await t.throwsAsync(validation, {
instanceOf: ValidationError,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import ValidationError from "../../../../../../../lib/validation/ValidationError
import extension from "../../../__helper__/extension.js";

async function assertValidation(t, config, expectedErrors = undefined) {
const validation = t.context.validator.validate({config, project: {id: "my-project"}});
const validation = t.context.validator.validate({config, project: {id: "my-project"}, schemaName: "ui5.json"});
if (expectedErrors) {
const validationError = await t.throwsAsync(validation, {
instanceOf: ValidationError,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import ValidationError from "../../../../../../../lib/validation/ValidationError
import extension from "../../../__helper__/extension.js";

async function assertValidation(t, config, expectedErrors = undefined) {
const validation = t.context.validator.validate({config, project: {id: "my-project"}});
const validation = t.context.validator.validate({config, project: {id: "my-project"}, schemaName: "ui5.json"});
if (expectedErrors) {
const validationError = await t.throwsAsync(validation, {
instanceOf: ValidationError,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import ValidationError from "../../../../../../../lib/validation/ValidationError
import extension from "../../../__helper__/extension.js";

async function assertValidation(t, config, expectedErrors = undefined) {
const validation = t.context.validator.validate({config, project: {id: "my-project"}});
const validation = t.context.validator.validate({config, project: {id: "my-project"}, schemaName: "ui5.json"});
if (expectedErrors) {
const validationError = await t.throwsAsync(validation, {
instanceOf: ValidationError,
Expand Down
2 changes: 1 addition & 1 deletion test/lib/validation/schema/specVersion/kind/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {_Validator as Validator} from "../../../../../../lib/validation/validato
import ValidationError from "../../../../../../lib/validation/ValidationError.js";

async function assertValidation(t, config, expectedErrors = undefined) {
const validation = t.context.validator.validate({config, project: {id: "my-project"}});
const validation = t.context.validator.validate({config, project: {id: "my-project"}, schemaName: "ui5.json"});
if (expectedErrors) {
const validationError = await t.throwsAsync(validation, {
instanceOf: ValidationError,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import ValidationError from "../../../../../../../lib/validation/ValidationError
import project from "../../../__helper__/project.js";

async function assertValidation(t, config, expectedErrors = undefined) {
const validation = t.context.validator.validate({config, project: {id: "my-project"}});
const validation = t.context.validator.validate({config, project: {id: "my-project"}, schemaName: "ui5.json"});
if (expectedErrors) {
const validationError = await t.throwsAsync(validation, {
instanceOf: ValidationError,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import ValidationError from "../../../../../../../lib/validation/ValidationError
import project from "../../../__helper__/project.js";

async function assertValidation(t, config, expectedErrors = undefined) {
const validation = t.context.validator.validate({config, project: {id: "my-project"}});
const validation = t.context.validator.validate({config, project: {id: "my-project"}, schemaName: "ui5.json"});
if (expectedErrors) {
const validationError = await t.throwsAsync(validation, {
instanceOf: ValidationError,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import ValidationError from "../../../../../../../lib/validation/ValidationError
import project from "../../../__helper__/project.js";

async function assertValidation(t, config, expectedErrors = undefined) {
const validation = t.context.validator.validate({config, project: {id: "my-project"}});
const validation = t.context.validator.validate({config, project: {id: "my-project"}, schemaName: "ui5.json"});
if (expectedErrors) {
const validationError = await t.throwsAsync(validation, {
instanceOf: ValidationError,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import ValidationError from "../../../../../../../lib/validation/ValidationError
import project from "../../../__helper__/project.js";

async function assertValidation(t, config, expectedErrors = undefined) {
const validation = t.context.validator.validate({config, project: {id: "my-project"}});
const validation = t.context.validator.validate({config, project: {id: "my-project"}, schemaName: "ui5.json"});
if (expectedErrors) {
const validationError = await t.throwsAsync(validation, {
instanceOf: ValidationError,
Expand Down
Loading