Skip to content

Commit e81f1a1

Browse files
committed
Extract getQlPackLanguage function
1 parent c14fa63 commit e81f1a1

File tree

4 files changed

+163
-42
lines changed

4 files changed

+163
-42
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { load } from "js-yaml";
2+
import { readFile } from "fs-extra";
3+
import { QlPackFile } from "../packaging/qlpack-file";
4+
import { QueryLanguage } from "./query-language";
5+
6+
/**
7+
* @param qlpackPath The path to the `qlpack.yml` or `codeql-pack.yml` file.
8+
* @return the language of the given qlpack file, or undefined if the file is
9+
* not a valid qlpack file or does not contain exactly one language.
10+
*/
11+
export async function getQlPackLanguage(
12+
qlpackPath: string,
13+
): Promise<QueryLanguage | undefined> {
14+
const qlPack = load(await readFile(qlpackPath, "utf8")) as
15+
| QlPackFile
16+
| undefined;
17+
const dependencies = qlPack?.dependencies;
18+
if (!dependencies || typeof dependencies !== "object") {
19+
return;
20+
}
21+
22+
const matchingLanguages = Object.values(QueryLanguage).filter(
23+
(language) => `codeql/${language}-all` in dependencies,
24+
);
25+
if (matchingLanguages.length !== 1) {
26+
return undefined;
27+
}
28+
29+
return matchingLanguages[0];
30+
}

extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,15 @@ import {
2828
isCodespacesTemplate,
2929
setQlPackLocation,
3030
} from "../config";
31-
import { lstat, pathExists, readFile } from "fs-extra";
31+
import { lstat, pathExists } from "fs-extra";
3232
import { askForLanguage } from "../codeql-cli/query-language";
3333
import { showInformationMessageWithAction } from "../common/vscode/dialog";
3434
import { redactableError } from "../common/errors";
3535
import { App } from "../common/app";
3636
import { QueryTreeViewItem } from "../queries-panel/query-tree-view-item";
3737
import { containsPath, pathsEqual } from "../common/files";
3838
import { getQlPackPath } from "../common/ql";
39-
import { load } from "js-yaml";
40-
import { QlPackFile } from "../packaging/qlpack-file";
39+
import { getQlPackLanguage } from "../common/qlpack-language";
4140

4241
type QueryLanguagesToDatabaseMap = Record<string, string>;
4342

@@ -253,24 +252,12 @@ export class SkeletonQueryWizard {
253252
return undefined;
254253
}
255254

256-
const qlPack = load(await readFile(qlPackPath, "utf8")) as
257-
| QlPackFile
258-
| undefined;
259-
const dependencies = qlPack?.dependencies;
260-
if (!dependencies || typeof dependencies !== "object") {
261-
return;
262-
}
263-
264-
const matchingLanguages = Object.values(QueryLanguage).filter(
265-
(language) => `codeql/${language}-all` in dependencies,
266-
);
267-
if (matchingLanguages.length !== 1) {
268-
return undefined;
255+
const language = await getQlPackLanguage(qlPackPath);
256+
if (language) {
257+
this.qlPackStoragePath = matchingQueryPackPath;
269258
}
270259

271-
this.qlPackStoragePath = matchingQueryPackPath;
272-
273-
return matchingLanguages[0];
260+
return language;
274261
}
275262

276263
private async chooseLanguage() {

extensions/ql-vscode/src/queries-panel/query-pack-discovery.ts

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ import { QueryLanguage } from "../common/query-language";
44
import { FALLBACK_QLPACK_FILENAME, QLPACK_FILENAMES } from "../common/ql";
55
import { FilePathDiscovery } from "../common/vscode/file-path-discovery";
66
import { containsPath } from "../common/files";
7-
import { load } from "js-yaml";
8-
import { readFile } from "fs-extra";
9-
import { QlPackFile } from "../packaging/qlpack-file";
7+
import { getQlPackLanguage } from "../common/qlpack-language";
108

119
interface QueryPack {
1210
path: string;
@@ -71,32 +69,13 @@ export class QueryPackDiscovery extends FilePathDiscovery<QueryPack> {
7169
protected async getDataForPath(path: string): Promise<QueryPack> {
7270
let language: QueryLanguage | undefined;
7371
try {
74-
language = await this.determinePackLanguage(path);
72+
language = await getQlPackLanguage(path);
7573
} catch (e) {
7674
language = undefined;
7775
}
7876
return { path, language };
7977
}
8078

81-
private async determinePackLanguage(
82-
path: string,
83-
): Promise<QueryLanguage | undefined> {
84-
const qlPack = load(await readFile(path, "utf8")) as QlPackFile | undefined;
85-
const dependencies = qlPack?.dependencies;
86-
if (!dependencies || typeof dependencies !== "object") {
87-
return;
88-
}
89-
90-
const matchingLanguages = Object.values(QueryLanguage).filter(
91-
(language) => `codeql/${language}-all` in dependencies,
92-
);
93-
if (matchingLanguages.length !== 1) {
94-
return undefined;
95-
}
96-
97-
return matchingLanguages[0];
98-
}
99-
10079
protected pathIsRelevant(path: string): boolean {
10180
return QLPACK_FILENAMES.includes(basename(path));
10281
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { join } from "path";
2+
import { dirSync } from "tmp-promise";
3+
import { DirResult } from "tmp";
4+
import { outputFile } from "fs-extra";
5+
import { dump } from "js-yaml";
6+
import { QueryLanguage } from "../../../src/common/query-language";
7+
import { getQlPackLanguage } from "../../../src/common/qlpack-language";
8+
9+
describe("getQlPackLanguage", () => {
10+
let tmpDir: DirResult;
11+
let qlpackPath: string;
12+
13+
beforeEach(() => {
14+
tmpDir = dirSync({
15+
prefix: "queries_",
16+
keep: false,
17+
unsafeCleanup: true,
18+
});
19+
20+
qlpackPath = join(tmpDir.name, "qlpack.yml");
21+
});
22+
23+
afterEach(() => {
24+
tmpDir.removeCallback();
25+
});
26+
27+
it.each(Object.values(QueryLanguage))(
28+
"should find a single language %s",
29+
async (language) => {
30+
await writeYAML(qlpackPath, {
31+
name: "test",
32+
dependencies: {
33+
[`codeql/${language}-all`]: "^0.7.0",
34+
"my-custom-pack/test": "${workspace}",
35+
},
36+
});
37+
38+
const result = await getQlPackLanguage(qlpackPath);
39+
expect(result).toEqual(language);
40+
},
41+
);
42+
43+
it("should find nothing when there is no dependencies key", async () => {
44+
await writeYAML(qlpackPath, {
45+
name: "test",
46+
});
47+
48+
const result = await getQlPackLanguage(qlpackPath);
49+
expect(result).toEqual(undefined);
50+
});
51+
52+
it("should find nothing when the dependencies are empty", async () => {
53+
await writeYAML(qlpackPath, {
54+
name: "test",
55+
dependencies: {},
56+
});
57+
58+
const result = await getQlPackLanguage(qlpackPath);
59+
expect(result).toEqual(undefined);
60+
});
61+
62+
it("should find nothing when dependencies is a scalar", async () => {
63+
await writeYAML(qlpackPath, {
64+
name: "test",
65+
dependencies: "codeql/java-all",
66+
});
67+
68+
const result = await getQlPackLanguage(qlpackPath);
69+
expect(result).toEqual(undefined);
70+
});
71+
72+
it("should find nothing when dependencies is an array", async () => {
73+
await writeYAML(qlpackPath, {
74+
name: "test",
75+
dependencies: ["codeql/java-all"],
76+
});
77+
78+
const result = await getQlPackLanguage(qlpackPath);
79+
expect(result).toEqual(undefined);
80+
});
81+
82+
it("should find nothing when there are no matching dependencies", async () => {
83+
await writeYAML(qlpackPath, {
84+
name: "test",
85+
dependencies: {
86+
"codeql/java-queries": "*",
87+
"github/my-test-query-pack": "*",
88+
},
89+
});
90+
91+
const result = await getQlPackLanguage(qlpackPath);
92+
expect(result).toEqual(undefined);
93+
});
94+
95+
it("should find nothing when there are multiple matching dependencies", async () => {
96+
await writeYAML(qlpackPath, {
97+
name: "test",
98+
dependencies: {
99+
"codeql/java-all": "*",
100+
"codeql/csharp-all": "*",
101+
},
102+
});
103+
104+
const result = await getQlPackLanguage(qlpackPath);
105+
expect(result).toEqual(undefined);
106+
});
107+
108+
it("should throw when the file does not exist", async () => {
109+
await expect(getQlPackLanguage(qlpackPath)).rejects.toBeDefined();
110+
});
111+
112+
it("should throw when reading a directory", async () => {
113+
await expect(getQlPackLanguage(tmpDir.name)).rejects.toBeDefined();
114+
});
115+
116+
it("should throw when the file is invalid YAML", async () => {
117+
await outputFile(qlpackPath, `name: test\n foo: bar`);
118+
119+
await expect(getQlPackLanguage(tmpDir.name)).rejects.toBeDefined();
120+
});
121+
});
122+
123+
async function writeYAML(path: string, yaml: unknown): Promise<void> {
124+
await outputFile(path, dump(yaml), "utf-8");
125+
}

0 commit comments

Comments
 (0)