Skip to content

Commit 4129962

Browse files
committed
Use generated schema to validate qlpack file
1 parent e81f1a1 commit 4129962

File tree

8 files changed

+153
-22
lines changed

8 files changed

+153
-22
lines changed

extensions/ql-vscode/scripts/generate-schemas.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ import { format, resolveConfig } from "prettier";
66
const extensionDirectory = resolve(__dirname, "..");
77

88
const schemas = [
9+
{
10+
path: join(extensionDirectory, "src", "packaging", "qlpack-file.ts"),
11+
type: "QlPackFile",
12+
schemaPath: join(
13+
extensionDirectory,
14+
"src",
15+
"packaging",
16+
"qlpack-file.schema.json",
17+
),
18+
},
919
{
1020
path: join(
1121
extensionDirectory,

extensions/ql-vscode/src/common/qlpack-language.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import { load } from "js-yaml";
2-
import { readFile } from "fs-extra";
3-
import { QlPackFile } from "../packaging/qlpack-file";
41
import { QueryLanguage } from "./query-language";
2+
import { loadQlpackFile } from "../packaging/qlpack-file-loader";
53

64
/**
75
* @param qlpackPath The path to the `qlpack.yml` or `codeql-pack.yml` file.
@@ -11,11 +9,9 @@ import { QueryLanguage } from "./query-language";
119
export async function getQlPackLanguage(
1210
qlpackPath: string,
1311
): Promise<QueryLanguage | undefined> {
14-
const qlPack = load(await readFile(qlpackPath, "utf8")) as
15-
| QlPackFile
16-
| undefined;
12+
const qlPack = await loadQlpackFile(qlpackPath);
1713
const dependencies = qlPack?.dependencies;
18-
if (!dependencies || typeof dependencies !== "object") {
14+
if (!dependencies) {
1915
return;
2016
}
2117

extensions/ql-vscode/src/model-editor/extension-pack-metadata.schema.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
"ExtensionPackMetadata": {
66
"type": "object",
77
"properties": {
8+
"name": {
9+
"type": "string"
10+
},
11+
"version": {
12+
"type": "string"
13+
},
814
"extensionTargets": {
915
"type": "object",
1016
"additionalProperties": {
@@ -24,12 +30,6 @@
2430
}
2531
]
2632
},
27-
"name": {
28-
"type": "string"
29-
},
30-
"version": {
31-
"type": "string"
32-
},
3333
"dependencies": {
3434
"type": "object",
3535
"additionalProperties": {
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { QlPackFile } from "../packaging/qlpack-file";
22

33
export type ExtensionPackMetadata = QlPackFile & {
4-
// Make both extensionTargets and dataExtensions required
4+
// Make name, version, extensionTargets, and dataExtensions required
5+
name: string;
6+
version: string;
57
extensionTargets: Record<string, string>;
68
dataExtensions: string[] | string;
79
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Ajv from "ajv";
2+
import * as qlpackFileSchemaJson from "./qlpack-file.schema.json";
3+
import { QlPackFile } from "./qlpack-file";
4+
import { load } from "js-yaml";
5+
import { readFile } from "fs-extra";
6+
7+
const ajv = new Ajv({ allErrors: true });
8+
const qlpackFileValidate = ajv.compile(qlpackFileSchemaJson);
9+
10+
export async function loadQlpackFile(path: string): Promise<QlPackFile> {
11+
const qlPack = load(await readFile(path, "utf8")) as QlPackFile | undefined;
12+
13+
qlpackFileValidate(qlPack);
14+
15+
if (qlpackFileValidate.errors) {
16+
throw new Error(
17+
`Invalid extension pack YAML: ${qlpackFileValidate.errors
18+
.map((error) => `${error.instancePath} ${error.message}`)
19+
.join(", ")}`,
20+
);
21+
}
22+
23+
if (!qlPack) {
24+
throw new Error(`Could not parse ${path}`);
25+
}
26+
27+
return qlPack;
28+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$ref": "#/definitions/QlPackFile",
4+
"definitions": {
5+
"QlPackFile": {
6+
"type": "object",
7+
"properties": {
8+
"name": {
9+
"type": "string"
10+
},
11+
"version": {
12+
"type": "string"
13+
},
14+
"dependencies": {
15+
"type": "object",
16+
"additionalProperties": {
17+
"type": "string"
18+
}
19+
},
20+
"extensionTargets": {
21+
"type": "object",
22+
"additionalProperties": {
23+
"type": "string"
24+
}
25+
},
26+
"dbscheme": {
27+
"type": "string"
28+
},
29+
"library": {
30+
"type": "boolean"
31+
},
32+
"defaultSuite": {
33+
"type": "array",
34+
"items": {
35+
"$ref": "#/definitions/SuiteInstruction"
36+
}
37+
},
38+
"defaultSuiteFile": {
39+
"type": "string"
40+
},
41+
"dataExtensions": {
42+
"anyOf": [
43+
{
44+
"type": "array",
45+
"items": {
46+
"type": "string"
47+
}
48+
},
49+
{
50+
"type": "string"
51+
}
52+
]
53+
}
54+
},
55+
"description": "The qlpack pack file, either in qlpack.yml or in codeql-pack.yml."
56+
},
57+
"SuiteInstruction": {
58+
"type": "object",
59+
"properties": {
60+
"qlpack": {
61+
"type": "string"
62+
},
63+
"query": {
64+
"type": "string"
65+
},
66+
"queries": {
67+
"type": "string"
68+
},
69+
"include": {
70+
"type": "object",
71+
"additionalProperties": {
72+
"type": "array",
73+
"items": {
74+
"type": "string"
75+
}
76+
}
77+
},
78+
"exclude": {
79+
"type": "object",
80+
"additionalProperties": {
81+
"type": "array",
82+
"items": {
83+
"type": "string"
84+
}
85+
}
86+
},
87+
"description": {
88+
"type": "string"
89+
},
90+
"from": {
91+
"type": "string"
92+
}
93+
},
94+
"description": "A single entry in a .qls file."
95+
}
96+
}
97+
}

extensions/ql-vscode/src/packaging/qlpack-file.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { SuiteInstruction } from "./suite-instruction";
44
* The qlpack pack file, either in qlpack.yml or in codeql-pack.yml.
55
*/
66
export interface QlPackFile {
7-
name: string;
8-
version: string;
7+
name?: string;
8+
version?: string;
99
dependencies?: Record<string, string>;
1010
extensionTargets?: Record<string, string>;
1111
dbscheme?: string;

extensions/ql-vscode/test/unit-tests/common/qlpack-language.test.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,24 +59,22 @@ describe("getQlPackLanguage", () => {
5959
expect(result).toEqual(undefined);
6060
});
6161

62-
it("should find nothing when dependencies is a scalar", async () => {
62+
it("should throw when dependencies is a scalar", async () => {
6363
await writeYAML(qlpackPath, {
6464
name: "test",
6565
dependencies: "codeql/java-all",
6666
});
6767

68-
const result = await getQlPackLanguage(qlpackPath);
69-
expect(result).toEqual(undefined);
68+
await expect(getQlPackLanguage(qlpackPath)).rejects.toBeDefined();
7069
});
7170

72-
it("should find nothing when dependencies is an array", async () => {
71+
it("should throw when dependencies is an array", async () => {
7372
await writeYAML(qlpackPath, {
7473
name: "test",
7574
dependencies: ["codeql/java-all"],
7675
});
7776

78-
const result = await getQlPackLanguage(qlpackPath);
79-
expect(result).toEqual(undefined);
77+
await expect(getQlPackLanguage(qlpackPath)).rejects.toBeDefined();
8078
});
8179

8280
it("should find nothing when there are no matching dependencies", async () => {

0 commit comments

Comments
 (0)