@bentley/app-schema-validator 0.2.191

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/LICENSE.md ADDED
@@ -0,0 +1,9 @@
1
+ # MIT License
2
+
3
+ Copyright © 2025 Bentley Systems, Incorporated. All rights reserved.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # app-schema-validator
2
+
3
+ Copyright © Bentley Systems, Incorporated. All rights reserved. See LICENSE.md for license terms and full copyright notice.
4
+
5
+ The purpose of this NPM CLI tool is to validate the schemas present within an application installer. It locates the schemas present in the extracted installer and perform the validations.
6
+
7
+ ## Getting Started
8
+
9
+ ### Prerequisites
10
+
11
+ - [Node](https://nodejs.org/en/): an installation of the latest security patch of Node 20. The Node installation also includes the **npm** package manager.
12
+
13
+ ### Installation
14
+
15
+ Install globally:
16
+
17
+ ```sh
18
+ npm install -g @bentley/app-schema-validator
19
+ ```
20
+
21
+ ### Validating an application schemas
22
+
23
+ Following are the arguments required:
24
+
25
+ - **-i, --installerDir**: Path to the extracted installer.
26
+
27
+ - **-b, --baseSchemaRefDir**: Root directory of all released schemas (root of bis-schemas repo).
28
+
29
+ - **-o, --output**: The path where output files will be generated.
30
+
31
+ For help use the '**-h**' option.
32
+
33
+ #### Sample Command:
34
+
35
+ ```sh
36
+ app-schema-validator -i D:\\\\dir1\\\\extracted\\app -b D:\\\\dir1\\\\bis-schemas -o D:\\\\dir1\\\\output
37
+ ```
38
+
39
+ ### Updating to newer version
40
+
41
+ Since the package is installed globally, updating has a different syntax than normal. To update the package globally, run:
42
+
43
+ ```sh
44
+ npm update -g @bentley/app-schema-validator
45
+ ```
package/bin/index.js ADDED
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env node
2
+
3
+ /*---------------------------------------------------------------------------------------------
4
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
5
+ * Licensed under the MIT License. See LICENSE.md in the project root for license terms.
6
+ *--------------------------------------------------------------------------------------------*/
7
+
8
+ const fs = require("fs");
9
+ const commander = require("commander");
10
+ const getResults = require("@bentley/imodel-schema-validator").getResults;
11
+ const verifyAppSchemas = require("../lib/AppSchemaValidator").verifyAppSchemas;
12
+
13
+ const program = new commander.Command("App-Schema-Validator NPM CLI");
14
+ program.option("-i, --installerDir <required>", "Path to the extracted installer.");
15
+ program.option("-b, --baseSchemaRefDir <required>", "Root directory of all released schemas (root of bis-schemas repo).");
16
+ program.option("-o, --output <required>", "Path where output files will be generated.");
17
+
18
+ program.parse(process.argv);
19
+
20
+ async function validate() {
21
+ if (process.argv.length != 8) {
22
+ console.log("usage : index.js");
23
+ console.log(" -i, --installerDir :Path to the extracted installer.");
24
+ console.log(" -b, --baseSchemaRefDir :Root directory of all released schemas (root of bis-schemas repo).");
25
+ console.log(" -o, --output :Path where output files will be generated.");
26
+ throw new Error("Missing from required arguments and their values.");
27
+ }
28
+
29
+ if (!program.installerDir || !program.baseSchemaRefDir || !program.output) {
30
+ console.log(chalk.default.red("Invalid input. For help use the '-h' option."));
31
+ process.exit(1);
32
+ }
33
+
34
+ if (!fs.existsSync(program.installerDir)) {
35
+ const error = "App installer directory do not exist: " + program.installerDir;
36
+ throw new Error(error);
37
+ }
38
+
39
+ if (!fs.existsSync(program.baseSchemaRefDir)) {
40
+ const error = "The baseSchemaRefDir do not exist: " + program.baseSchemaRefDir;
41
+ throw new Error(error);
42
+ }
43
+
44
+ try {
45
+ const results = await verifyAppSchemas(program.installerDir, program.baseSchemaRefDir, program.output);
46
+ await getResults(results, program.baseSchemaRefDir, program.output);
47
+ process.exit(0);
48
+ } catch (err) {
49
+ console.log(err);
50
+ process.exit(1);
51
+ }
52
+ }
53
+
54
+ validate().then()
55
+ .catch((error) => {
56
+ console.error(error);
57
+ process.exit(1);
58
+ });
@@ -0,0 +1,16 @@
1
+ import { IModelValidationResult } from "@bentley/imodel-schema-validator";
2
+ /**
3
+ * Verifies an App Schemas
4
+ * @param appDirectory Path to the extracted installer directory
5
+ * @param baseSchemaRefDir Path to the bis-schemas directory
6
+ * @param output Path to the output directory
7
+ * @returns Array of validation results
8
+ */
9
+ export declare function verifyAppSchemas(appDirectory: string, baseSchemaRefDir: string, output: string): Promise<IModelValidationResult[]>;
10
+ /**
11
+ * Find directories where schema files are present
12
+ * @param appDirectory Directory to search for schema files
13
+ * @returns Array of paths where schema files are found
14
+ */
15
+ export declare function generateAppSchemaDirectoryList(appDirectory: string): Promise<string[]>;
16
+ //# sourceMappingURL=AppSchemaValidator.d.ts.map
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
4
+ * Licensed under the MIT License. See LICENSE.md in the project root for license terms.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.generateAppSchemaDirectoryList = exports.verifyAppSchemas = void 0;
8
+ const fs = require("fs");
9
+ const path = require("path");
10
+ const chalk = require("chalk");
11
+ const readdirp = require("readdirp");
12
+ const Reporter_1 = require("@bentley/imodel-schema-validator/lib/Reporter");
13
+ const ecschema_locaters_1 = require("@itwin/ecschema-locaters");
14
+ const utils_1 = require("./utils");
15
+ const imodel_schema_validator_1 = require("@bentley/imodel-schema-validator");
16
+ /**
17
+ * Verifies an App Schemas
18
+ * @param appDirectory Path to the extracted installer directory
19
+ * @param baseSchemaRefDir Path to the bis-schemas directory
20
+ * @param output Path to the output directory
21
+ * @returns Array of validation results
22
+ */
23
+ async function verifyAppSchemas(appDirectory, baseSchemaRefDir, output) {
24
+ const results = [];
25
+ const releasedSchemaDirectories = await (0, utils_1.generateSchemaDirectoryLists)(baseSchemaRefDir);
26
+ const appSchemaDirs = await generateAppSchemaDirectoryList(appDirectory);
27
+ const excludeSchemas = await (0, utils_1.getExcludeSchemaList)();
28
+ for (const appSchemaDir of appSchemaDirs) {
29
+ for (const appSchemaFile of fs.readdirSync(appSchemaDir)) {
30
+ if (!appSchemaFile.endsWith(".ecschema.xml"))
31
+ continue;
32
+ if (await (0, utils_1.shouldExcludeSchema)(appSchemaFile, excludeSchemas))
33
+ continue;
34
+ const validationResult = await applyValidations(appSchemaDir, appSchemaFile, appSchemaDirs, releasedSchemaDirectories, output);
35
+ results.push(validationResult);
36
+ }
37
+ }
38
+ return results;
39
+ }
40
+ exports.verifyAppSchemas = verifyAppSchemas;
41
+ /**
42
+ * Apply all validations
43
+ * @param schemaDir Directory where schema is located
44
+ * @param schemaFile The schema file
45
+ * @param appSchemaDirs List of paths where app schemas are located
46
+ * @param releasedSchemaDirectories List of paths where released schemas are located
47
+ * @param output Path to the output directory
48
+ * @returns Validation result of a schema
49
+ */
50
+ async function applyValidations(schemaDir, schemaFile, appSchemaDirs, releasedSchemaDirectories, output) {
51
+ const appSchemaPath = path.join(schemaDir, schemaFile);
52
+ const appSchemaKey = await getSchemaInfo(appSchemaPath);
53
+ const name = appSchemaKey.name;
54
+ const version = appSchemaKey.version.toString();
55
+ const validationResult = { name, version };
56
+ console.log("\nBEGIN VALIDATION AND DIFFERENCE AUDIT: %s.%s", name, version);
57
+ Reporter_1.Reporter.writeToLogFile(name, version, `BEGIN VALIDATION AND DIFFERENCE AUDIT: ${name}.${version}\n`, output);
58
+ await (0, imodel_schema_validator_1.validateSchema)(name, version, appSchemaPath, releasedSchemaDirectories, validationResult, output);
59
+ releasedSchemaDirectories = fixReleasedSchemaDirectories(schemaDir, releasedSchemaDirectories);
60
+ if ((0, imodel_schema_validator_1.isDynamicSchema)(appSchemaPath)) {
61
+ console.log(chalk.default.grey(`Skipping difference audit for ${name} ${version}.
62
+ The schema is a dynamic schema and released versions of dynamic schemas are not saved.`));
63
+ Reporter_1.Reporter.writeToLogFile(name, version, `Skipping difference audit for ${name}.${version}.
64
+ The schema is a dynamic schema and released versions of dynamic schemas are not saved.\n`, output);
65
+ validationResult.comparer = imodel_schema_validator_1.iModelValidationResultTypes.Skipped;
66
+ validationResult.approval = imodel_schema_validator_1.iModelValidationResultTypes.Skipped;
67
+ }
68
+ else {
69
+ let releasedSchemaPath = "";
70
+ // Find out Difference
71
+ for (const dir of releasedSchemaDirectories) {
72
+ fs.readdirSync(dir).forEach((releasedSchemaFile) => {
73
+ if (releasedSchemaFile.toLowerCase().includes(appSchemaKey.toString().toLowerCase())) {
74
+ releasedSchemaPath = path.join(dir, releasedSchemaFile);
75
+ }
76
+ });
77
+ }
78
+ if (!releasedSchemaPath) {
79
+ if ((0, imodel_schema_validator_1.isDynamicSchema)(appSchemaPath)) {
80
+ console.log(chalk.default.grey("Skipping difference audit for ", name, version, ". No released schema found."));
81
+ Reporter_1.Reporter.writeToLogFile(name, version, `Skipping difference audit for ${name}.${version}. No released schema found.\n`, output);
82
+ validationResult.comparer = imodel_schema_validator_1.iModelValidationResultTypes.Skipped;
83
+ }
84
+ else {
85
+ console.log(chalk.default.grey("Skipping difference audit for ", name, version, ". No released schema found."));
86
+ Reporter_1.Reporter.writeToLogFile(name, version, `Skipping difference audit for ${name}.${version}. No released schema found.\n`, output);
87
+ validationResult.comparer = imodel_schema_validator_1.iModelValidationResultTypes.NotFound;
88
+ }
89
+ }
90
+ else {
91
+ // The app installer (opensiteplus) is missing several reference schemas. We provide those from bis-schemas.
92
+ const appSchemaReferences = [...appSchemaDirs, ...releasedSchemaDirectories];
93
+ await (0, imodel_schema_validator_1.compareSchema)(name, version, appSchemaPath, releasedSchemaPath, appSchemaReferences, releasedSchemaDirectories, output, validationResult);
94
+ releasedSchemaDirectories = fixReleasedSchemaDirectories(schemaDir, releasedSchemaDirectories);
95
+ }
96
+ }
97
+ console.log("END VALIDATION AND DIFFERENCE AUDIT: ", name, version);
98
+ Reporter_1.Reporter.writeToLogFile(name, version, `END VALIDATION AND DIFFERENCE AUDIT: ${name}.${version}\n`, output);
99
+ return validationResult;
100
+ }
101
+ /**
102
+ * Retrieves schema information
103
+ * @param schemaXMLFilePath Path of schema XML file
104
+ * @returns SchemaKey object containing the schema information
105
+ */
106
+ async function getSchemaInfo(schemaXMLFilePath) {
107
+ const schemaXml = fs.readFileSync(schemaXMLFilePath, "utf-8");
108
+ const locater = new ecschema_locaters_1.StubSchemaXmlFileLocater();
109
+ return locater.getSchemaKey(schemaXml);
110
+ }
111
+ function fixReleasedSchemaDirectories(appSchemaDir, releasedSchemaDirectories) {
112
+ // @bentley/schema-comparer and @bentley/schema-validator are pushing the input schema path to reference array.
113
+ // Removing this path to fix the bug in finding releasedSchemaFile
114
+ const index = releasedSchemaDirectories.indexOf(appSchemaDir);
115
+ if (index !== -1)
116
+ releasedSchemaDirectories.splice(index, 1);
117
+ return releasedSchemaDirectories;
118
+ }
119
+ /**
120
+ * Find directories where schema files are present
121
+ * @param appDirectory Directory to search for schema files
122
+ * @returns Array of paths where schema files are found
123
+ */
124
+ async function generateAppSchemaDirectoryList(appDirectory) {
125
+ // Skip schemas from platform based licensing-addon e.g licensing-win32-x64
126
+ const filter = { fileFilter: "*.ecschema.xml", directoryFilter: ["!.vscode", "!licensing-*"] };
127
+ const schemaPaths = (await readdirp.promise(appDirectory, filter)).map((entry) => path.dirname(entry.fullPath));
128
+ return Array.from(new Set(schemaPaths).keys());
129
+ }
130
+ exports.generateAppSchemaDirectoryList = generateAppSchemaDirectoryList;
131
+ //# sourceMappingURL=AppSchemaValidator.js.map
@@ -0,0 +1,97 @@
1
+ [
2
+ {
3
+ "name": "ChangedElements",
4
+ "version": "01.00.00",
5
+ "reason": "EC2 schema from imodel-native repository"
6
+ },
7
+ {
8
+ "name": "ECDbChange",
9
+ "version": "01.00.01",
10
+ "reason": "EC2 schema from imodel-native repository"
11
+ },
12
+ {
13
+ "name": "USCustomaryUnitSystemDefaults",
14
+ "version": "01.00",
15
+ "reason": "EC2 schema from imodel-native repository"
16
+ },
17
+ {
18
+ "name": "Unit_Attributes",
19
+ "version": "01.00",
20
+ "reason": "EC2 schema from imodel-native repository"
21
+ },
22
+ {
23
+ "name": "Units_Schema",
24
+ "version": "01.00",
25
+ "reason": "EC2 schema from imodel-native repository"
26
+ },
27
+ {
28
+ "name": "SIUnitSystemDefaults",
29
+ "version": "01.00",
30
+ "reason": "EC2 schema from imodel-native repository"
31
+ },
32
+ {
33
+ "name": "rdl_customAttributes",
34
+ "version": "01.00",
35
+ "reason": "EC2 schema from imodel-native repository"
36
+ },
37
+ {
38
+ "name": "KindOfQuantity_Schema",
39
+ "version": "01.01",
40
+ "reason": "EC2 schema from imodel-native repository"
41
+ },
42
+ {
43
+ "name": "iip_mdb_customAttributes",
44
+ "version": "01.00",
45
+ "reason": "EC2 schema from imodel-native repository"
46
+ },
47
+ {
48
+ "name": "EditorCustomAttributes",
49
+ "version": "01.03",
50
+ "reason": "EC2 schema from imodel-native repository"
51
+ },
52
+ {
53
+ "name": "Dimension_Schema",
54
+ "version": "01.00",
55
+ "reason": "EC2 schema from imodel-native repository"
56
+ },
57
+ {
58
+ "name": "Bentley_Standard_CustomAttributes",
59
+ "version": "01.14",
60
+ "reason": "EC2 schema from imodel-native repository"
61
+ },
62
+ {
63
+ "name": "Bentley_Standard_Classes",
64
+ "version": "01.01",
65
+ "reason": "EC2 schema from imodel-native repository"
66
+ },
67
+ {
68
+ "name": "Bentley_ECSchemaMap",
69
+ "version": "01.00",
70
+ "reason": "EC2 schema from imodel-native repository"
71
+ },
72
+ {
73
+ "name": "Bentley_Common_Classes",
74
+ "version": "01.01",
75
+ "reason": "EC2 schema from imodel-native repository"
76
+ },
77
+ {
78
+ "name": "MetaSchema_V3Conversion",
79
+ "version": "02.04",
80
+ "reason": "EC2 schema from imodel-native repository"
81
+ },
82
+ {
83
+ "name": "ECDbMap",
84
+ "version": "01.00",
85
+ "reason": "EC2 schema from imodel-native repository"
86
+ },
87
+ {
88
+ "name": "IModelChange",
89
+ "version": "02.00.00",
90
+ "reason": "It is not inserted into the iModel"
91
+ },
92
+ {
93
+ "name": "SchemaLocalizationCustomAttributes",
94
+ "version": "01.00.00",
95
+ "reason": "This schema version is released but not approved"
96
+ }
97
+ ]
package/lib/utils.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Find released schemas directories
3
+ * @schemaDirectory Directory to search for schema files
4
+ * @returns Array containing released schemas directories
5
+ */
6
+ export declare function generateSchemaDirectoryLists(schemaDirectory: string): Promise<string[]>;
7
+ /**
8
+ * Provides a list of schemas to exclude from validation
9
+ */
10
+ export declare function getExcludeSchemaList(): Promise<any>;
11
+ /**
12
+ * Checks if a schema should be excluded from validation
13
+ * @param schemaFle Schema file
14
+ * @param excludeList List of schemas to exclude
15
+ * @returns True if schema should be excluded, false otherwise
16
+ */
17
+ export declare function shouldExcludeSchema(schemaFle: string, excludeList: any[]): Promise<boolean>;
18
+ //# sourceMappingURL=utils.d.ts.map
package/lib/utils.js ADDED
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
4
+ * Licensed under the MIT License. See LICENSE.md in the project root for license terms.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.shouldExcludeSchema = exports.getExcludeSchemaList = exports.generateSchemaDirectoryLists = void 0;
8
+ const fs = require("fs");
9
+ const path = require("path");
10
+ const readdirp = require("readdirp");
11
+ /**
12
+ * Find released schemas directories
13
+ * @schemaDirectory Directory to search for schema files
14
+ * @returns Array containing released schemas directories
15
+ */
16
+ async function generateSchemaDirectoryLists(schemaDirectory) {
17
+ const filter = { fileFilter: "*.ecschema.xml", directoryFilter: ["!node_modules", "!.vscode", "!tools"] };
18
+ const allSchemaDirs = (await readdirp.promise(schemaDirectory, filter)).map((schemaPath) => path.dirname(schemaPath.fullPath));
19
+ return Array.from(new Set(allSchemaDirs.filter((schemaDir) => /released/i.test(schemaDir))).keys());
20
+ }
21
+ exports.generateSchemaDirectoryLists = generateSchemaDirectoryLists;
22
+ /**
23
+ * Provides a list of schemas to exclude from validation
24
+ */
25
+ async function getExcludeSchemaList() {
26
+ const ignoreListJson = path.resolve(__dirname, "./ignoreSchemaList.json");
27
+ if (!fs.existsSync(ignoreListJson)) {
28
+ console.log("File not found: ", ignoreListJson);
29
+ return;
30
+ }
31
+ const rawdata = fs.readFileSync(ignoreListJson, "utf8");
32
+ const schemas = JSON.parse(rawdata);
33
+ return schemas;
34
+ }
35
+ exports.getExcludeSchemaList = getExcludeSchemaList;
36
+ /**
37
+ * Checks if a schema should be excluded from validation
38
+ * @param schemaFle Schema file
39
+ * @param excludeList List of schemas to exclude
40
+ * @returns True if schema should be excluded, false otherwise
41
+ */
42
+ async function shouldExcludeSchema(schemaFle, excludeList) {
43
+ const rawData = schemaFle.split(".");
44
+ const schemaName = rawData[0];
45
+ const schemaVersion = rawData.slice(1, -2).join(".");
46
+ const matches = excludeList.filter((s) => s.name.toLowerCase() === schemaName.toLowerCase());
47
+ if (matches.length === 0)
48
+ return false;
49
+ if (matches.some((s) => s.version === schemaVersion))
50
+ return true;
51
+ return false;
52
+ }
53
+ exports.shouldExcludeSchema = shouldExcludeSchema;
54
+ //# sourceMappingURL=utils.js.map
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@bentley/app-schema-validator",
3
+ "description": "This tool validates schemas present in an application's installer",
4
+ "main": "./lib/AppSchemaValidator.js",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/iTwin/bis-schema-validation"
8
+ },
9
+ "license": "MIT",
10
+ "author": {
11
+ "name": "Bentley Systems, Inc.",
12
+ "url": "http://www.bentley.com"
13
+ },
14
+ "version": "0.2.191",
15
+ "bin": {
16
+ "app-schema-validator": "./bin/index.js"
17
+ },
18
+ "dependencies": {
19
+ "@itwin/ecschema-locaters": "4.10.10",
20
+ "@itwin/ecschema-metadata": "4.10.10",
21
+ "@bentley/imodel-schema-validator": "0.2.191",
22
+ "chalk": "^2.4.1",
23
+ "commander": "^2.19.0",
24
+ "readdirp": "^3.0.0",
25
+ "rimraf": "^3.0.2"
26
+ },
27
+ "devDependencies": {
28
+ "@itwin/eslint-plugin": "^4.0.2",
29
+ "@itwin/build-tools": "4.10.10",
30
+ "@types/node": "~18.16.20",
31
+ "@types/chai": "4.3.1",
32
+ "@types/mocha": "^10.0.6",
33
+ "cpx2": "^3.0.0",
34
+ "typescript": "~5.3.3",
35
+ "chai": "^4.3.10",
36
+ "mocha": "^10.2.0",
37
+ "nyc": "^15.1.0",
38
+ "eslint": "^8.56.0"
39
+ },
40
+ "scripts": {
41
+ "build": "tsc 1>&2 && npm run copy:assets && npm run copy:test-assets",
42
+ "lint": "eslint -f visualstudio \"./src/**/*.ts\" 1>&2",
43
+ "test": "mocha --timeout=999999999 --check-leaks --global _playwrightInstance",
44
+ "cover": "nyc npm -s test",
45
+ "clean": "rimraf lib",
46
+ "copy:assets": "cpx ./ignoreSchemaList.json ./lib",
47
+ "copy:test-assets": "cpx \"./src/test/assets/**/*\" ./lib/test/assets"
48
+ }
49
+ }