Merge branch 'main' into starcke/local-query-lang-dto
This commit is contained in:
2
.github/workflows/cli-test.yml
vendored
2
.github/workflows/cli-test.yml
vendored
@@ -62,7 +62,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.17.1'
|
||||
node-version: '18.15.0'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: extensions/ql-vscode/package-lock.json
|
||||
|
||||
|
||||
12
.github/workflows/main.yml
vendored
12
.github/workflows/main.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.17.1'
|
||||
node-version: '18.15.0'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: extensions/ql-vscode/package-lock.json
|
||||
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.17.1'
|
||||
node-version: '18.15.0'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: extensions/ql-vscode/package-lock.json
|
||||
|
||||
@@ -110,7 +110,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.17.1'
|
||||
node-version: '18.15.0'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: extensions/ql-vscode/package-lock.json
|
||||
|
||||
@@ -149,7 +149,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.17.1'
|
||||
node-version: '18.15.0'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: extensions/ql-vscode/package-lock.json
|
||||
|
||||
@@ -183,7 +183,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.17.1'
|
||||
node-version: '18.15.0'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: extensions/ql-vscode/package-lock.json
|
||||
|
||||
@@ -251,7 +251,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.17.1'
|
||||
node-version: '18.15.0'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: extensions/ql-vscode/package-lock.json
|
||||
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.17.1'
|
||||
node-version: '18.15.0'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
|
||||
@@ -21,6 +21,7 @@ The following files will need to be updated:
|
||||
- `extensions/ql-vscode/.nvmrc` - this will enable nvm to automatically switch to the correct node version when you're in the project folder
|
||||
- `extensions/ql-vscode/package-lock.json` - the "engines.node: '[VERSION]'" setting
|
||||
- `extensions/ql-vscode/package.json` - the "engines.node: '[VERSION]'" setting
|
||||
- `extensions/ql-vscode/package.json` - the "@types/node: '[VERSION]'" dependency
|
||||
|
||||
## Node.js version used in tests
|
||||
|
||||
|
||||
@@ -173,6 +173,8 @@ Note that this test requires the feature flag: `codeQL.model.llmGeneration`
|
||||
|
||||
#### Test Case 4: Model as dependency
|
||||
|
||||
Note that this test requires the feature flag: `codeQL.model.flowGeneration`
|
||||
|
||||
1. Click "Model as dependency"
|
||||
- Check that grouping are now per package (e.g. `com.alipay.sofa.rraft.option` or `com.google.protobuf`)
|
||||
2. Click "Generate".
|
||||
|
||||
@@ -1 +1 @@
|
||||
v16.17.1
|
||||
v18.15.0
|
||||
|
||||
@@ -3,6 +3,15 @@
|
||||
## [UNRELEASED]
|
||||
|
||||
- It is now possible to show the language of query history items using the `%l` specifier in the `codeQL.queryHistory.format` setting. Note that this only works queries run after this upgrade, and older items will show `unknown` as a language. [#2892](https://github.com/github/vscode-codeql/pull/2892)
|
||||
- Increase the required version of VS Code to 1.82.0. [#2877](https://github.com/github/vscode-codeql/pull/2877)
|
||||
- Fix a bug where the query server was restarted twice after configuration changes. [#2884](https://github.com/github/vscode-codeql/pull/2884).
|
||||
|
||||
## 1.9.1 - 29 September 2023
|
||||
|
||||
- Add warning when using a VS Code version older than 1.82.0. [#2854](https://github.com/github/vscode-codeql/pull/2854)
|
||||
- Fix a bug when parsing large evaluation log summaries. [#2858](https://github.com/github/vscode-codeql/pull/2858)
|
||||
- Right-align and format numbers in raw result tables. [#2864](https://github.com/github/vscode-codeql/pull/2864)
|
||||
- Remove rate limit warning notifications when using Code Search to add repositories to a variant analysis list. [#2812](https://github.com/github/vscode-codeql/pull/2812)
|
||||
|
||||
## 1.9.0 - 19 September 2023
|
||||
|
||||
|
||||
949
extensions/ql-vscode/package-lock.json
generated
949
extensions/ql-vscode/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
||||
"description": "CodeQL for Visual Studio Code",
|
||||
"author": "GitHub",
|
||||
"private": true,
|
||||
"version": "1.9.1",
|
||||
"version": "1.9.2",
|
||||
"publisher": "GitHub",
|
||||
"license": "MIT",
|
||||
"icon": "media/VS-marketplace-CodeQL-icon.png",
|
||||
@@ -13,8 +13,8 @@
|
||||
"url": "https://github.com/github/vscode-codeql"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.67.0",
|
||||
"node": "^16.17.1",
|
||||
"vscode": "^1.82.0",
|
||||
"node": "^18.15.0",
|
||||
"npm": ">=7.20.6"
|
||||
},
|
||||
"categories": [
|
||||
@@ -2070,8 +2070,8 @@
|
||||
"prepare": "cd ../.. && husky install"
|
||||
},
|
||||
"dependencies": {
|
||||
"@octokit/plugin-retry": "^4.1.6",
|
||||
"@octokit/rest": "^19.0.4",
|
||||
"@octokit/plugin-retry": "^6.0.1",
|
||||
"@octokit/rest": "^20.0.2",
|
||||
"@vscode/codicons": "^0.0.31",
|
||||
"@vscode/debugadapter": "^1.59.0",
|
||||
"@vscode/debugprotocol": "^1.59.0",
|
||||
@@ -2085,7 +2085,7 @@
|
||||
"fs-extra": "^11.1.1",
|
||||
"immutable": "^4.0.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"msw": "^1.2.0",
|
||||
"msw": "^0.0.0-fetch.rc-20",
|
||||
"nanoid": "^3.2.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"p-queue": "^6.0.0",
|
||||
@@ -2115,7 +2115,7 @@
|
||||
"@babel/preset-typescript": "^7.21.4",
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@github/markdownlint-github": "^0.3.0",
|
||||
"@octokit/plugin-throttling": "^5.0.1",
|
||||
"@octokit/plugin-throttling": "^8.0.0",
|
||||
"@storybook/addon-actions": "^7.1.0",
|
||||
"@storybook/addon-essentials": "^7.1.0",
|
||||
"@storybook/addon-interactions": "^7.1.0",
|
||||
@@ -2141,7 +2141,7 @@
|
||||
"@types/jest": "^29.0.2",
|
||||
"@types/js-yaml": "^4.0.6",
|
||||
"@types/nanoid": "^3.0.0",
|
||||
"@types/node": "^16.11.25",
|
||||
"@types/node": "18.15.0",
|
||||
"@types/node-fetch": "^2.5.2",
|
||||
"@types/react": "^18.0.28",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
@@ -2153,7 +2153,7 @@
|
||||
"@types/through2": "^2.0.36",
|
||||
"@types/tmp": "^0.1.0",
|
||||
"@types/unzipper": "^0.10.1",
|
||||
"@types/vscode": "^1.67.0",
|
||||
"@types/vscode": "^1.82.0",
|
||||
"@types/webpack": "^5.28.0",
|
||||
"@types/webpack-env": "^1.18.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.2.1",
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
import { pathExists, readJson, writeJson } from "fs-extra";
|
||||
import { resolve, relative } from "path";
|
||||
|
||||
import { Octokit, type RestEndpointMethodTypes } from "@octokit/rest";
|
||||
import { Octokit } from "@octokit/core";
|
||||
import { type RestEndpointMethodTypes } from "@octokit/rest";
|
||||
import { throttling } from "@octokit/plugin-throttling";
|
||||
|
||||
import { getFiles } from "./util/files";
|
||||
@@ -22,6 +23,7 @@ import type { GitHubApiRequest } from "../src/common/mock-gh-api/gh-api-request"
|
||||
import { isGetVariantAnalysisRequest } from "../src/common/mock-gh-api/gh-api-request";
|
||||
import { VariantAnalysis } from "../src/variant-analysis/gh-api/variant-analysis";
|
||||
import { RepositoryWithMetadata } from "../src/variant-analysis/gh-api/repository";
|
||||
import { AppOctokit } from "../src/common/octokit";
|
||||
|
||||
const extensionDirectory = resolve(__dirname, "..");
|
||||
const scenariosDirectory = resolve(
|
||||
@@ -31,7 +33,7 @@ const scenariosDirectory = resolve(
|
||||
|
||||
// Make sure we don't run into rate limits by automatically waiting until we can
|
||||
// make another request.
|
||||
const MyOctokit = Octokit.plugin(throttling);
|
||||
const MyOctokit = AppOctokit.plugin(throttling);
|
||||
|
||||
const auth = process.env.GITHUB_TOKEN;
|
||||
|
||||
|
||||
@@ -600,8 +600,7 @@ export type FromModelEditorMessage =
|
||||
| SetModeledMethodMessage;
|
||||
|
||||
export type FromMethodModelingMessage =
|
||||
| TelemetryMessage
|
||||
| UnhandledErrorMessage
|
||||
| CommonFromViewMessages
|
||||
| SetModeledMethodMessage;
|
||||
|
||||
interface SetMethodMessage {
|
||||
|
||||
@@ -17,7 +17,7 @@ export enum RequestKind {
|
||||
AutoModel = "autoModel",
|
||||
}
|
||||
|
||||
interface BasicErorResponse {
|
||||
export interface BasicErrorResponse {
|
||||
message: string;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ interface GetRepoRequest {
|
||||
};
|
||||
response: {
|
||||
status: number;
|
||||
body: Repository | BasicErorResponse | undefined;
|
||||
body: Repository | BasicErrorResponse | undefined;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ interface SubmitVariantAnalysisRequest {
|
||||
};
|
||||
response: {
|
||||
status: number;
|
||||
body?: VariantAnalysis | BasicErorResponse;
|
||||
body?: VariantAnalysis | BasicErrorResponse;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ interface GetVariantAnalysisRequest {
|
||||
};
|
||||
response: {
|
||||
status: number;
|
||||
body?: VariantAnalysis | BasicErorResponse;
|
||||
body?: VariantAnalysis | BasicErrorResponse;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ interface GetVariantAnalysisRepoRequest {
|
||||
};
|
||||
response: {
|
||||
status: number;
|
||||
body?: VariantAnalysisRepoTask | BasicErorResponse;
|
||||
body?: VariantAnalysisRepoTask | BasicErrorResponse;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -74,6 +74,13 @@ export interface GetVariantAnalysisRepoResultRequest {
|
||||
};
|
||||
}
|
||||
|
||||
export interface CodeSearchResponse {
|
||||
total_count: number;
|
||||
items: Array<{
|
||||
repository: Repository;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface CodeSearchRequest {
|
||||
request: {
|
||||
kind: RequestKind.CodeSearch;
|
||||
@@ -81,16 +88,14 @@ interface CodeSearchRequest {
|
||||
};
|
||||
response: {
|
||||
status: number;
|
||||
body?: {
|
||||
total_count?: number;
|
||||
items?: Array<{
|
||||
repository: Repository;
|
||||
}>;
|
||||
};
|
||||
message?: string;
|
||||
body?: CodeSearchResponse | BasicErrorResponse;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AutoModelResponse {
|
||||
models: string;
|
||||
}
|
||||
|
||||
interface AutoModelRequest {
|
||||
request: {
|
||||
kind: RequestKind.AutoModel;
|
||||
@@ -100,10 +105,7 @@ interface AutoModelRequest {
|
||||
};
|
||||
response: {
|
||||
status: number;
|
||||
body?: {
|
||||
models: string;
|
||||
};
|
||||
message?: string;
|
||||
body?: AutoModelResponse | BasicErrorResponse;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +1,33 @@
|
||||
import { ensureDir, writeFile } from "fs-extra";
|
||||
import { join } from "path";
|
||||
|
||||
import { MockedRequest } from "msw";
|
||||
import { SetupServer } from "msw/node";
|
||||
import { IsomorphicResponse } from "@mswjs/interceptors";
|
||||
|
||||
import { Headers } from "headers-polyfill";
|
||||
import fetch from "node-fetch";
|
||||
import { SetupServer } from "msw/node";
|
||||
|
||||
import { DisposableObject } from "../disposable-object";
|
||||
import { gzipDecode } from "../zlib";
|
||||
|
||||
import {
|
||||
AutoModelResponse,
|
||||
BasicErrorResponse,
|
||||
CodeSearchResponse,
|
||||
GetVariantAnalysisRepoResultRequest,
|
||||
GitHubApiRequest,
|
||||
RequestKind,
|
||||
} from "./gh-api-request";
|
||||
import {
|
||||
VariantAnalysis,
|
||||
VariantAnalysisRepoTask,
|
||||
} from "../../variant-analysis/gh-api/variant-analysis";
|
||||
import { Repository } from "../../variant-analysis/gh-api/repository";
|
||||
|
||||
export class Recorder extends DisposableObject {
|
||||
private readonly allRequests = new Map<string, MockedRequest>();
|
||||
private currentRecordedScenario: GitHubApiRequest[] = [];
|
||||
|
||||
private _isRecording = false;
|
||||
|
||||
constructor(private readonly server: SetupServer) {
|
||||
super();
|
||||
this.onRequestStart = this.onRequestStart.bind(this);
|
||||
this.onResponseBypass = this.onResponseBypass.bind(this);
|
||||
}
|
||||
|
||||
@@ -45,7 +48,6 @@ export class Recorder extends DisposableObject {
|
||||
|
||||
this.clear();
|
||||
|
||||
this.server.events.on("request:start", this.onRequestStart);
|
||||
this.server.events.on("response:bypass", this.onResponseBypass);
|
||||
}
|
||||
|
||||
@@ -56,13 +58,11 @@ export class Recorder extends DisposableObject {
|
||||
|
||||
this._isRecording = false;
|
||||
|
||||
this.server.events.removeListener("request:start", this.onRequestStart);
|
||||
this.server.events.removeListener("response:bypass", this.onResponseBypass);
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this.currentRecordedScenario = [];
|
||||
this.allRequests.clear();
|
||||
}
|
||||
|
||||
public async save(scenariosPath: string, name: string): Promise<string> {
|
||||
@@ -91,7 +91,7 @@ export class Recorder extends DisposableObject {
|
||||
|
||||
let bodyFileLink = undefined;
|
||||
if (writtenRequest.response.body) {
|
||||
await writeFile(bodyFilePath, writtenRequest.response.body || "");
|
||||
await writeFile(bodyFilePath, writtenRequest.response.body);
|
||||
bodyFileLink = `file:${bodyFileName}`;
|
||||
}
|
||||
|
||||
@@ -112,33 +112,18 @@ export class Recorder extends DisposableObject {
|
||||
return scenarioDirectory;
|
||||
}
|
||||
|
||||
private onRequestStart(request: MockedRequest): void {
|
||||
private async onResponseBypass(
|
||||
response: Response,
|
||||
request: Request,
|
||||
_requestId: string,
|
||||
): Promise<void> {
|
||||
if (request.headers.has("x-vscode-codeql-msw-bypass")) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.allRequests.set(request.id, request);
|
||||
}
|
||||
|
||||
private async onResponseBypass(
|
||||
response: IsomorphicResponse,
|
||||
requestId: string,
|
||||
): Promise<void> {
|
||||
const request = this.allRequests.get(requestId);
|
||||
this.allRequests.delete(requestId);
|
||||
if (!request) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.body === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const gitHubApiRequest = await createGitHubApiRequest(
|
||||
request.url.toString(),
|
||||
response.status,
|
||||
response.body,
|
||||
response.headers,
|
||||
request.url,
|
||||
response,
|
||||
);
|
||||
if (!gitHubApiRequest) {
|
||||
return;
|
||||
@@ -150,14 +135,14 @@ export class Recorder extends DisposableObject {
|
||||
|
||||
async function createGitHubApiRequest(
|
||||
url: string,
|
||||
status: number,
|
||||
body: string,
|
||||
headers: Headers,
|
||||
response: Response,
|
||||
): Promise<GitHubApiRequest | undefined> {
|
||||
if (!url) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const status = response.status;
|
||||
|
||||
if (url.match(/\/repos\/[a-zA-Z0-9-_.]+\/[a-zA-Z0-9-_.]+$/)) {
|
||||
return {
|
||||
request: {
|
||||
@@ -165,7 +150,9 @@ async function createGitHubApiRequest(
|
||||
},
|
||||
response: {
|
||||
status,
|
||||
body: JSON.parse(body),
|
||||
body: await jsonResponseBody<
|
||||
Repository | BasicErrorResponse | undefined
|
||||
>(response),
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -179,7 +166,9 @@ async function createGitHubApiRequest(
|
||||
},
|
||||
response: {
|
||||
status,
|
||||
body: JSON.parse(body),
|
||||
body: await jsonResponseBody<
|
||||
VariantAnalysis | BasicErrorResponse | undefined
|
||||
>(response),
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -195,7 +184,9 @@ async function createGitHubApiRequest(
|
||||
},
|
||||
response: {
|
||||
status,
|
||||
body: JSON.parse(body),
|
||||
body: await jsonResponseBody<
|
||||
VariantAnalysis | BasicErrorResponse | undefined
|
||||
>(response),
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -211,7 +202,9 @@ async function createGitHubApiRequest(
|
||||
},
|
||||
response: {
|
||||
status,
|
||||
body: JSON.parse(body),
|
||||
body: await jsonResponseBody<
|
||||
VariantAnalysisRepoTask | BasicErrorResponse | undefined
|
||||
>(response),
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -238,9 +231,10 @@ async function createGitHubApiRequest(
|
||||
repositoryId: parseInt(repoDownloadMatch.groups.repositoryId, 10),
|
||||
},
|
||||
response: {
|
||||
status,
|
||||
status: response.status,
|
||||
body: responseBuffer,
|
||||
contentType: headers.get("content-type") ?? "application/octet-stream",
|
||||
contentType:
|
||||
response.headers.get("content-type") ?? "application/octet-stream",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -254,7 +248,9 @@ async function createGitHubApiRequest(
|
||||
},
|
||||
response: {
|
||||
status,
|
||||
body: JSON.parse(body),
|
||||
body: await jsonResponseBody<
|
||||
CodeSearchResponse | BasicErrorResponse | undefined
|
||||
>(response),
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -269,7 +265,9 @@ async function createGitHubApiRequest(
|
||||
},
|
||||
response: {
|
||||
status,
|
||||
body: JSON.parse(body),
|
||||
body: await jsonResponseBody<
|
||||
BasicErrorResponse | AutoModelResponse | undefined
|
||||
>(response),
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -277,6 +275,26 @@ async function createGitHubApiRequest(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function responseBody(response: Response): Promise<Uint8Array> {
|
||||
const body = await response.arrayBuffer();
|
||||
const view = new Uint8Array(body);
|
||||
|
||||
if (view[0] === 0x1f && view[1] === 0x8b) {
|
||||
// Response body is gzipped, so we need to un-gzip it.
|
||||
|
||||
return await gzipDecode(view);
|
||||
} else {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
async function jsonResponseBody<T>(response: Response): Promise<T> {
|
||||
const body = await responseBody(response);
|
||||
const text = new TextDecoder("utf-8").decode(body);
|
||||
|
||||
return JSON.parse(text);
|
||||
}
|
||||
|
||||
function shouldWriteBodyToFile(
|
||||
request: GitHubApiRequest,
|
||||
): request is GetVariantAnalysisRepoResultRequest {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { join } from "path";
|
||||
import { readdir, readJson, readFile } from "fs-extra";
|
||||
import { DefaultBodyType, MockedRequest, rest, RestHandler } from "msw";
|
||||
import { RequestHandler, rest } from "msw";
|
||||
import {
|
||||
GitHubApiRequest,
|
||||
isAutoModelRequest,
|
||||
@@ -14,7 +14,19 @@ import {
|
||||
|
||||
const baseUrl = "https://api.github.com";
|
||||
|
||||
type RequestHandler = RestHandler<MockedRequest<DefaultBodyType>>;
|
||||
const jsonResponse = <T>(
|
||||
body: T,
|
||||
init?: ResponseInit,
|
||||
contentType = "application/json",
|
||||
): Response => {
|
||||
return new Response(JSON.stringify(body), {
|
||||
...init,
|
||||
headers: {
|
||||
"Content-Type": contentType,
|
||||
...init?.headers,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export async function createRequestHandlers(
|
||||
scenarioDirPath: string,
|
||||
@@ -82,11 +94,10 @@ function createGetRepoRequestHandler(
|
||||
|
||||
const getRepoRequest = getRepoRequests[0];
|
||||
|
||||
return rest.get(`${baseUrl}/repos/:owner/:name`, (_req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(getRepoRequest.response.status),
|
||||
ctx.json(getRepoRequest.response.body),
|
||||
);
|
||||
return rest.get(`${baseUrl}/repos/:owner/:name`, () => {
|
||||
return jsonResponse(getRepoRequest.response.body, {
|
||||
status: getRepoRequest.response.status,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -105,11 +116,10 @@ function createSubmitVariantAnalysisRequestHandler(
|
||||
|
||||
return rest.post(
|
||||
`${baseUrl}/repositories/:controllerRepoId/code-scanning/codeql/variant-analyses`,
|
||||
(_req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(getRepoRequest.response.status),
|
||||
ctx.json(getRepoRequest.response.body),
|
||||
);
|
||||
() => {
|
||||
return jsonResponse(getRepoRequest.response.body, {
|
||||
status: getRepoRequest.response.status,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -127,7 +137,7 @@ function createGetVariantAnalysisRequestHandler(
|
||||
// request, so keep an index of the request and return the appropriate response.
|
||||
return rest.get(
|
||||
`${baseUrl}/repositories/:controllerRepoId/code-scanning/codeql/variant-analyses/:variantAnalysisId`,
|
||||
(_req, res, ctx) => {
|
||||
() => {
|
||||
const request = getVariantAnalysisRequests[requestIndex];
|
||||
|
||||
if (requestIndex < getVariantAnalysisRequests.length - 1) {
|
||||
@@ -135,10 +145,9 @@ function createGetVariantAnalysisRequestHandler(
|
||||
requestIndex++;
|
||||
}
|
||||
|
||||
return res(
|
||||
ctx.status(request.response.status),
|
||||
ctx.json(request.response.body),
|
||||
);
|
||||
return jsonResponse(request.response.body, {
|
||||
status: request.response.status,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -152,18 +161,17 @@ function createGetVariantAnalysisRepoRequestHandler(
|
||||
|
||||
return rest.get(
|
||||
`${baseUrl}/repositories/:controllerRepoId/code-scanning/codeql/variant-analyses/:variantAnalysisId/repositories/:repoId`,
|
||||
(req, res, ctx) => {
|
||||
({ request, params }) => {
|
||||
const scenarioRequest = getVariantAnalysisRepoRequests.find(
|
||||
(r) => r.request.repositoryId.toString() === req.params.repoId,
|
||||
(r) => r.request.repositoryId.toString() === params.repoId,
|
||||
);
|
||||
if (!scenarioRequest) {
|
||||
throw Error(`No scenario request found for ${req.url}`);
|
||||
throw Error(`No scenario request found for ${request.url}`);
|
||||
}
|
||||
|
||||
return res(
|
||||
ctx.status(scenarioRequest.response.status),
|
||||
ctx.json(scenarioRequest.response.body),
|
||||
);
|
||||
return jsonResponse(scenarioRequest.response.body, {
|
||||
status: scenarioRequest.response.status,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -177,22 +185,23 @@ function createGetVariantAnalysisRepoResultRequestHandler(
|
||||
|
||||
return rest.get(
|
||||
"https://objects-origin.githubusercontent.com/codeql-query-console/codeql-variant-analysis-repo-tasks/:variantAnalysisId/:repoId/*",
|
||||
(req, res, ctx) => {
|
||||
({ request, params }) => {
|
||||
const scenarioRequest = getVariantAnalysisRepoResultRequests.find(
|
||||
(r) => r.request.repositoryId.toString() === req.params.repoId,
|
||||
(r) => r.request.repositoryId.toString() === params.repoId,
|
||||
);
|
||||
if (!scenarioRequest) {
|
||||
throw Error(`No scenario request found for ${req.url}`);
|
||||
throw Error(`No scenario request found for ${request.url}`);
|
||||
}
|
||||
|
||||
if (scenarioRequest.response.body) {
|
||||
return res(
|
||||
ctx.status(scenarioRequest.response.status),
|
||||
ctx.set("Content-Type", scenarioRequest.response.contentType),
|
||||
ctx.body(scenarioRequest.response.body),
|
||||
);
|
||||
return new Response(scenarioRequest.response.body, {
|
||||
status: scenarioRequest.response.status,
|
||||
headers: {
|
||||
"Content-Type": scenarioRequest.response.contentType,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return res(ctx.status(scenarioRequest.response.status));
|
||||
return new Response(null, { status: scenarioRequest.response.status });
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -207,7 +216,7 @@ function createCodeSearchRequestHandler(
|
||||
// During a code search, there are multiple request to get pages of results. We
|
||||
// need to return different responses for each request, so keep an index of the
|
||||
// request and return the appropriate response.
|
||||
return rest.get(`${baseUrl}/search/code?q=*`, (_req, res, ctx) => {
|
||||
return rest.get(`${baseUrl}/search/code`, () => {
|
||||
const request = codeSearchRequests[requestIndex];
|
||||
|
||||
if (requestIndex < codeSearchRequests.length - 1) {
|
||||
@@ -215,10 +224,9 @@ function createCodeSearchRequestHandler(
|
||||
requestIndex++;
|
||||
}
|
||||
|
||||
return res(
|
||||
ctx.status(request.response.status),
|
||||
ctx.json(request.response.body),
|
||||
);
|
||||
return jsonResponse(request.response.body, {
|
||||
status: request.response.status,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -233,7 +241,7 @@ function createAutoModelRequestHandler(
|
||||
// so keep an index of the request and return the appropriate response.
|
||||
return rest.post(
|
||||
`${baseUrl}/repos/github/codeql/code-scanning/codeql/auto-model`,
|
||||
(_req, res, ctx) => {
|
||||
() => {
|
||||
const request = autoModelRequests[requestIndex];
|
||||
|
||||
if (requestIndex < autoModelRequests.length - 1) {
|
||||
@@ -241,10 +249,9 @@ function createAutoModelRequestHandler(
|
||||
requestIndex++;
|
||||
}
|
||||
|
||||
return res(
|
||||
ctx.status(request.response.status),
|
||||
ctx.json(request.response.body),
|
||||
);
|
||||
return jsonResponse(request.response.body, {
|
||||
status: request.response.status,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
10
extensions/ql-vscode/src/common/octokit.ts
Normal file
10
extensions/ql-vscode/src/common/octokit.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import * as Octokit from "@octokit/rest";
|
||||
import { retry } from "@octokit/plugin-retry";
|
||||
import fetch from "node-fetch";
|
||||
|
||||
export const AppOctokit = Octokit.Octokit.defaults({
|
||||
request: {
|
||||
fetch,
|
||||
},
|
||||
retry,
|
||||
});
|
||||
@@ -0,0 +1,85 @@
|
||||
import * as vscode from "vscode";
|
||||
import { Uri, WebviewViewProvider } from "vscode";
|
||||
import { WebviewKind, WebviewMessage, getHtmlForWebview } from "./webview-html";
|
||||
import { Disposable } from "../disposable-object";
|
||||
import { App } from "../app";
|
||||
|
||||
export abstract class AbstractWebviewViewProvider<
|
||||
ToMessage extends WebviewMessage,
|
||||
FromMessage extends WebviewMessage,
|
||||
> implements WebviewViewProvider
|
||||
{
|
||||
protected webviewView: vscode.WebviewView | undefined = undefined;
|
||||
private disposables: Disposable[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly app: App,
|
||||
private readonly webviewKind: WebviewKind,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* This is called when a view first becomes visible. This may happen when the view is
|
||||
* first loaded or when the user hides and then shows a view again.
|
||||
*/
|
||||
public resolveWebviewView(
|
||||
webviewView: vscode.WebviewView,
|
||||
_context: vscode.WebviewViewResolveContext,
|
||||
_token: vscode.CancellationToken,
|
||||
) {
|
||||
webviewView.webview.options = {
|
||||
enableScripts: true,
|
||||
localResourceRoots: [Uri.file(this.app.extensionPath)],
|
||||
};
|
||||
|
||||
const html = getHtmlForWebview(
|
||||
this.app,
|
||||
webviewView.webview,
|
||||
this.webviewKind,
|
||||
{
|
||||
allowInlineStyles: true,
|
||||
allowWasmEval: false,
|
||||
},
|
||||
);
|
||||
|
||||
webviewView.webview.html = html;
|
||||
|
||||
this.webviewView = webviewView;
|
||||
|
||||
webviewView.webview.onDidReceiveMessage(async (msg) => this.onMessage(msg));
|
||||
webviewView.onDidDispose(() => this.dispose());
|
||||
}
|
||||
|
||||
protected get isShowingView() {
|
||||
return this.webviewView?.visible ?? false;
|
||||
}
|
||||
|
||||
protected async postMessage(msg: ToMessage): Promise<void> {
|
||||
await this.webviewView?.webview.postMessage(msg);
|
||||
}
|
||||
|
||||
protected dispose() {
|
||||
while (this.disposables.length > 0) {
|
||||
const disposable = this.disposables.pop()!;
|
||||
disposable.dispose();
|
||||
}
|
||||
|
||||
this.webviewView = undefined;
|
||||
}
|
||||
|
||||
protected push<T extends Disposable>(obj: T): T {
|
||||
if (obj !== undefined) {
|
||||
this.disposables.push(obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
protected abstract onMessage(msg: FromMessage): Promise<void>;
|
||||
|
||||
/**
|
||||
* This is called when a view first becomes visible. This may happen when the view is
|
||||
* first loaded or when the user hides and then shows a view again.
|
||||
*/
|
||||
protected onWebViewLoaded(): void {
|
||||
// Do nothing by default.
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
import { join } from "path";
|
||||
|
||||
import { App } from "../app";
|
||||
import { DisposableObject, DisposeHandler } from "../disposable-object";
|
||||
import { Disposable } from "../disposable-object";
|
||||
import { tmpDir } from "../../tmp-dir";
|
||||
import { getHtmlForWebview, WebviewMessage, WebviewKind } from "./webview-html";
|
||||
|
||||
@@ -27,16 +27,16 @@ export type WebviewPanelConfig = {
|
||||
export abstract class AbstractWebview<
|
||||
ToMessage extends WebviewMessage,
|
||||
FromMessage extends WebviewMessage,
|
||||
> extends DisposableObject {
|
||||
> {
|
||||
protected panel: WebviewPanel | undefined;
|
||||
protected panelLoaded = false;
|
||||
protected panelLoadedCallBacks: Array<() => void> = [];
|
||||
|
||||
private panelResolves?: Array<(panel: WebviewPanel) => void>;
|
||||
|
||||
constructor(protected readonly app: App) {
|
||||
super();
|
||||
}
|
||||
private disposables: Disposable[] = [];
|
||||
|
||||
constructor(protected readonly app: App) {}
|
||||
|
||||
public async restoreView(panel: WebviewPanel): Promise<void> {
|
||||
this.panel = panel;
|
||||
@@ -101,6 +101,7 @@ export abstract class AbstractWebview<
|
||||
this.panel = undefined;
|
||||
this.panelLoaded = false;
|
||||
this.onPanelDispose();
|
||||
this.disposeAll();
|
||||
}, null),
|
||||
);
|
||||
|
||||
@@ -150,8 +151,27 @@ export abstract class AbstractWebview<
|
||||
return panel.webview.postMessage(msg);
|
||||
}
|
||||
|
||||
public dispose(disposeHandler?: DisposeHandler) {
|
||||
public dispose() {
|
||||
this.panel?.dispose();
|
||||
super.dispose(disposeHandler);
|
||||
this.disposeAll();
|
||||
}
|
||||
|
||||
private disposeAll() {
|
||||
while (this.disposables.length > 0) {
|
||||
const disposable = this.disposables.pop()!;
|
||||
disposable.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds `obj` to a list of objects to dispose when the panel is disposed. Objects added by `push` are
|
||||
* disposed in reverse order of being added.
|
||||
* @param obj The object to take ownership of.
|
||||
*/
|
||||
protected push<T extends Disposable>(obj: T): T {
|
||||
if (obj !== undefined) {
|
||||
this.disposables.push(obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as vscode from "vscode";
|
||||
import * as Octokit from "@octokit/rest";
|
||||
import { retry } from "@octokit/plugin-retry";
|
||||
import { Credentials } from "../authentication";
|
||||
import { AppOctokit } from "../octokit";
|
||||
|
||||
export const GITHUB_AUTH_PROVIDER_ID = "github";
|
||||
|
||||
@@ -32,9 +32,8 @@ export class VSCodeCredentials implements Credentials {
|
||||
|
||||
const accessToken = await this.getAccessToken();
|
||||
|
||||
return new Octokit.Octokit({
|
||||
return new AppOctokit({
|
||||
auth: accessToken,
|
||||
retry,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { retry } from "@octokit/plugin-retry";
|
||||
import { throttling } from "@octokit/plugin-throttling";
|
||||
import { Octokit } from "@octokit/rest";
|
||||
import { Progress, CancellationToken } from "vscode";
|
||||
import { Credentials } from "../common/authentication";
|
||||
import { BaseLogger } from "../common/logging";
|
||||
import { AppOctokit } from "../common/octokit";
|
||||
|
||||
export async function getCodeSearchRepositories(
|
||||
query: string,
|
||||
@@ -46,12 +46,11 @@ async function provideOctokitWithThrottling(
|
||||
credentials: Credentials,
|
||||
logger: BaseLogger,
|
||||
): Promise<Octokit> {
|
||||
const MyOctokit = Octokit.plugin(throttling);
|
||||
const MyOctokit = AppOctokit.plugin(throttling);
|
||||
const auth = await credentials.getAccessToken();
|
||||
|
||||
const octokit = new MyOctokit({
|
||||
auth,
|
||||
retry,
|
||||
throttle: {
|
||||
onRateLimit: (retryAfter: number, options: any): boolean => {
|
||||
void logger.log(
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
} from "fs-extra";
|
||||
import { basename, join } from "path";
|
||||
import * as Octokit from "@octokit/rest";
|
||||
import { retry } from "@octokit/plugin-retry";
|
||||
|
||||
import { DatabaseManager, DatabaseItem } from "./local-databases";
|
||||
import { tmpDir } from "../tmp-dir";
|
||||
@@ -32,6 +31,7 @@ import { Credentials } from "../common/authentication";
|
||||
import { AppCommandManager } from "../common/commands";
|
||||
import { allowHttp } from "../config";
|
||||
import { showAndLogInformationMessage } from "../common/logging";
|
||||
import { AppOctokit } from "../common/octokit";
|
||||
|
||||
/**
|
||||
* Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file.
|
||||
@@ -186,7 +186,7 @@ export async function downloadGitHubDatabase(
|
||||
|
||||
const octokit = credentials
|
||||
? await credentials.getOctokit()
|
||||
: new Octokit.Octokit({ retry });
|
||||
: new AppOctokit();
|
||||
|
||||
const result = await convertGithubNwoToDatabaseUrl(
|
||||
nwo,
|
||||
|
||||
@@ -790,7 +790,7 @@ async function activateWithInstalledDistribution(
|
||||
);
|
||||
ctx.subscriptions.push(databaseUI);
|
||||
|
||||
QueriesModule.initialize(app, cliServer);
|
||||
QueriesModule.initialize(app, languageContext, cliServer);
|
||||
|
||||
void extLogger.log("Initializing evaluator log viewer.");
|
||||
const evalLogViewer = new EvalLogViewer();
|
||||
|
||||
@@ -75,6 +75,7 @@ import { telemetryListener } from "../common/vscode/telemetry";
|
||||
import { redactableError } from "../common/errors";
|
||||
import { ResultsViewCommands } from "../common/commands";
|
||||
import { App } from "../common/app";
|
||||
import { Disposable } from "../common/disposable-object";
|
||||
|
||||
/**
|
||||
* results-view.ts
|
||||
@@ -157,6 +158,12 @@ function numInterpretedPages(
|
||||
return Math.ceil(n / pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* The results view is used for displaying the results of a local query. It is a singleton; only 1 results view exists
|
||||
* in the extension. It is created when the extension is activated and disposed of when the extension is deactivated.
|
||||
* There can be multiple panels linked to this view over the lifetime of the extension, but there is only ever 1 panel
|
||||
* active at a time.
|
||||
*/
|
||||
export class ResultsView extends AbstractWebview<
|
||||
IntoResultsViewMsg,
|
||||
FromResultsViewMsg
|
||||
@@ -168,6 +175,9 @@ export class ResultsView extends AbstractWebview<
|
||||
"codeql-query-results",
|
||||
);
|
||||
|
||||
// Event listeners that should be disposed of when the view is disposed.
|
||||
private disposableEventListeners: Disposable[] = [];
|
||||
|
||||
constructor(
|
||||
app: App,
|
||||
private databaseManager: DatabaseManager,
|
||||
@@ -176,14 +186,16 @@ export class ResultsView extends AbstractWebview<
|
||||
private labelProvider: HistoryItemLabelProvider,
|
||||
) {
|
||||
super(app);
|
||||
this.push(this._diagnosticCollection);
|
||||
this.push(
|
||||
|
||||
// We can't use this.push for these two event listeners because they need to be disposed of when the view is
|
||||
// disposed, not when the panel is disposed. The results view is a singleton, so we shouldn't be calling this.push.
|
||||
this.disposableEventListeners.push(
|
||||
vscode.window.onDidChangeTextEditorSelection(
|
||||
this.handleSelectionChange.bind(this),
|
||||
),
|
||||
);
|
||||
|
||||
this.push(
|
||||
this.disposableEventListeners.push(
|
||||
this.databaseManager.onDidChangeDatabaseItem(({ kind }) => {
|
||||
if (kind === DatabaseEventKind.Remove) {
|
||||
this._diagnosticCollection.clear();
|
||||
@@ -981,4 +993,12 @@ export class ResultsView extends AbstractWebview<
|
||||
editor.setDecorations(shownLocationLineDecoration, []);
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
super.dispose();
|
||||
|
||||
this._diagnosticCollection.dispose();
|
||||
this.disposableEventListeners.forEach((d) => d.dispose());
|
||||
this.disposableEventListeners = [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ export class MethodModelingPanel extends DisposableObject {
|
||||
super();
|
||||
|
||||
this.provider = new MethodModelingViewProvider(app, modelingStore);
|
||||
this.push(this.provider);
|
||||
this.push(
|
||||
window.registerWebviewViewProvider(
|
||||
MethodModelingViewProvider.viewType,
|
||||
|
||||
@@ -1,82 +1,51 @@
|
||||
import * as vscode from "vscode";
|
||||
import { Uri, WebviewViewProvider } from "vscode";
|
||||
import { getHtmlForWebview } from "../../common/vscode/webview-html";
|
||||
import { FromMethodModelingMessage } from "../../common/interface-types";
|
||||
import {
|
||||
FromMethodModelingMessage,
|
||||
ToMethodModelingMessage,
|
||||
} from "../../common/interface-types";
|
||||
import { telemetryListener } from "../../common/vscode/telemetry";
|
||||
import { showAndLogExceptionWithTelemetry } from "../../common/logging/notifications";
|
||||
import { extLogger } from "../../common/logging/vscode/loggers";
|
||||
import { App } from "../../common/app";
|
||||
import { redactableError } from "../../common/errors";
|
||||
import { Method } from "../method";
|
||||
import { DisposableObject } from "../../common/disposable-object";
|
||||
import { ModelingStore } from "../modeling-store";
|
||||
import { AbstractWebviewViewProvider } from "../../common/vscode/abstract-webview-view-provider";
|
||||
|
||||
export class MethodModelingViewProvider
|
||||
extends DisposableObject
|
||||
implements WebviewViewProvider
|
||||
{
|
||||
export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
|
||||
ToMethodModelingMessage,
|
||||
FromMethodModelingMessage
|
||||
> {
|
||||
public static readonly viewType = "codeQLMethodModeling";
|
||||
|
||||
private webviewView: vscode.WebviewView | undefined = undefined;
|
||||
|
||||
private method: Method | undefined = undefined;
|
||||
|
||||
constructor(
|
||||
private readonly app: App,
|
||||
app: App,
|
||||
private readonly modelingStore: ModelingStore,
|
||||
) {
|
||||
super();
|
||||
super(app, "method-modeling");
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when a view first becomes visible. This may happen when the view is
|
||||
* first loaded or when the user hides and then shows a view again.
|
||||
*/
|
||||
public resolveWebviewView(
|
||||
webviewView: vscode.WebviewView,
|
||||
_context: vscode.WebviewViewResolveContext,
|
||||
_token: vscode.CancellationToken,
|
||||
) {
|
||||
webviewView.webview.options = {
|
||||
enableScripts: true,
|
||||
localResourceRoots: [Uri.file(this.app.extensionPath)],
|
||||
};
|
||||
|
||||
const html = getHtmlForWebview(
|
||||
this.app,
|
||||
webviewView.webview,
|
||||
"method-modeling",
|
||||
{
|
||||
allowInlineStyles: true,
|
||||
allowWasmEval: false,
|
||||
},
|
||||
);
|
||||
|
||||
webviewView.webview.html = html;
|
||||
|
||||
webviewView.webview.onDidReceiveMessage(async (msg) => this.onMessage(msg));
|
||||
|
||||
this.webviewView = webviewView;
|
||||
|
||||
this.setInitialState(webviewView);
|
||||
protected override onWebViewLoaded(): void {
|
||||
this.setInitialState();
|
||||
this.registerToModelingStoreEvents();
|
||||
}
|
||||
|
||||
public async setMethod(method: Method): Promise<void> {
|
||||
this.method = method;
|
||||
|
||||
if (this.webviewView) {
|
||||
await this.webviewView.webview.postMessage({
|
||||
if (this.isShowingView) {
|
||||
await this.postMessage({
|
||||
t: "setMethod",
|
||||
method,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private setInitialState(webviewView: vscode.WebviewView): void {
|
||||
private setInitialState(): void {
|
||||
const selectedMethod = this.modelingStore.getSelectedMethodDetails();
|
||||
if (selectedMethod) {
|
||||
void webviewView.webview.postMessage({
|
||||
void this.postMessage({
|
||||
t: "setSelectedMethod",
|
||||
method: selectedMethod.method,
|
||||
modeledMethod: selectedMethod.modeledMethod,
|
||||
@@ -85,8 +54,28 @@ export class MethodModelingViewProvider
|
||||
}
|
||||
}
|
||||
|
||||
private async onMessage(msg: FromMethodModelingMessage): Promise<void> {
|
||||
protected override async onMessage(
|
||||
msg: FromMethodModelingMessage,
|
||||
): Promise<void> {
|
||||
switch (msg.t) {
|
||||
case "viewLoaded":
|
||||
this.onWebViewLoaded();
|
||||
break;
|
||||
|
||||
case "telemetry":
|
||||
telemetryListener?.sendUIInteraction(msg.action);
|
||||
break;
|
||||
|
||||
case "unhandledError":
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
extLogger,
|
||||
telemetryListener,
|
||||
redactableError(
|
||||
msg.error,
|
||||
)`Unhandled error in method modeling view: ${msg.error.message}`,
|
||||
);
|
||||
break;
|
||||
|
||||
case "setModeledMethod": {
|
||||
const activeState = this.modelingStore.getStateForActiveDb();
|
||||
if (!activeState) {
|
||||
@@ -98,56 +87,48 @@ export class MethodModelingViewProvider
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case "telemetry": {
|
||||
telemetryListener?.sendUIInteraction(msg.action);
|
||||
break;
|
||||
}
|
||||
case "unhandledError":
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
extLogger,
|
||||
telemetryListener,
|
||||
redactableError(
|
||||
msg.error,
|
||||
)`Unhandled error in method modeling view: ${msg.error.message}`,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private registerToModelingStoreEvents(): void {
|
||||
this.modelingStore.onModeledMethodsChanged(async (e) => {
|
||||
if (this.webviewView && e.isActiveDb) {
|
||||
const modeledMethod = e.modeledMethods[this.method?.signature ?? ""];
|
||||
if (modeledMethod) {
|
||||
this.push(
|
||||
this.modelingStore.onModeledMethodsChanged(async (e) => {
|
||||
if (this.webviewView && e.isActiveDb) {
|
||||
const modeledMethod = e.modeledMethods[this.method?.signature ?? ""];
|
||||
if (modeledMethod) {
|
||||
await this.webviewView.webview.postMessage({
|
||||
t: "setModeledMethod",
|
||||
method: modeledMethod,
|
||||
});
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
this.push(
|
||||
this.modelingStore.onModifiedMethodsChanged(async (e) => {
|
||||
if (this.webviewView && e.isActiveDb && this.method) {
|
||||
const isModified = e.modifiedMethods.has(this.method.signature);
|
||||
await this.webviewView.webview.postMessage({
|
||||
t: "setModeledMethod",
|
||||
method: modeledMethod,
|
||||
t: "setMethodModified",
|
||||
isModified,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
this.modelingStore.onModifiedMethodsChanged(async (e) => {
|
||||
if (this.webviewView && e.isActiveDb && this.method) {
|
||||
const isModified = e.modifiedMethods.has(this.method.signature);
|
||||
await this.webviewView.webview.postMessage({
|
||||
t: "setMethodModified",
|
||||
isModified,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.modelingStore.onSelectedMethodChanged(async (e) => {
|
||||
if (this.webviewView) {
|
||||
this.method = e.method;
|
||||
await this.webviewView.webview.postMessage({
|
||||
t: "setSelectedMethod",
|
||||
method: e.method,
|
||||
modeledMethod: e.modeledMethod,
|
||||
isModified: e.isModified,
|
||||
});
|
||||
}
|
||||
});
|
||||
this.push(
|
||||
this.modelingStore.onSelectedMethodChanged(async (e) => {
|
||||
if (this.webviewView) {
|
||||
this.method = e.method;
|
||||
await this.webviewView.webview.postMessage({
|
||||
t: "setSelectedMethod",
|
||||
method: e.method,
|
||||
modeledMethod: e.modeledMethod,
|
||||
isModified: e.isModified,
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { DisposableObject } from "../common/disposable-object";
|
||||
import { QueriesPanel } from "./queries-panel";
|
||||
import { QueryDiscovery } from "./query-discovery";
|
||||
import { QueryPackDiscovery } from "./query-pack-discovery";
|
||||
import { LanguageContextStore } from "../language-context-store";
|
||||
|
||||
export class QueriesModule extends DisposableObject {
|
||||
private queriesPanel: QueriesPanel | undefined;
|
||||
@@ -16,16 +17,21 @@ export class QueriesModule extends DisposableObject {
|
||||
|
||||
public static initialize(
|
||||
app: App,
|
||||
languageContext: LanguageContextStore,
|
||||
cliServer: CodeQLCliServer,
|
||||
): QueriesModule {
|
||||
const queriesModule = new QueriesModule(app);
|
||||
app.subscriptions.push(queriesModule);
|
||||
|
||||
queriesModule.initialize(app, cliServer);
|
||||
queriesModule.initialize(app, languageContext, cliServer);
|
||||
return queriesModule;
|
||||
}
|
||||
|
||||
private initialize(app: App, cliServer: CodeQLCliServer): void {
|
||||
private initialize(
|
||||
app: App,
|
||||
langauageContext: LanguageContextStore,
|
||||
cliServer: CodeQLCliServer,
|
||||
): void {
|
||||
// Currently, we only want to expose the new panel when we are in canary mode
|
||||
// and the user has enabled the "Show queries panel" flag.
|
||||
if (!isCanary() || !showQueriesPanel()) {
|
||||
@@ -38,8 +44,9 @@ export class QueriesModule extends DisposableObject {
|
||||
void queryPackDiscovery.initialRefresh();
|
||||
|
||||
const queryDiscovery = new QueryDiscovery(
|
||||
app.environment,
|
||||
app,
|
||||
queryPackDiscovery,
|
||||
langauageContext,
|
||||
);
|
||||
this.push(queryDiscovery);
|
||||
void queryDiscovery.initialRefresh();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { dirname, basename, normalize, relative } from "path";
|
||||
import { Event } from "vscode";
|
||||
import { EnvironmentContext } from "../common/app";
|
||||
import { App } from "../common/app";
|
||||
import {
|
||||
FileTreeDirectory,
|
||||
FileTreeLeaf,
|
||||
@@ -11,6 +11,8 @@ import { FilePathDiscovery } from "../common/vscode/file-path-discovery";
|
||||
import { containsPath } from "../common/files";
|
||||
import { getOnDiskWorkspaceFoldersObjects } from "../common/vscode/workspace-folders";
|
||||
import { QueryLanguage } from "../common/query-language";
|
||||
import { LanguageContextStore } from "../language-context-store";
|
||||
import { AppEvent, AppEventEmitter } from "../common/events";
|
||||
|
||||
const QUERY_FILE_EXTENSION = ".ql";
|
||||
|
||||
@@ -31,24 +33,36 @@ export class QueryDiscovery
|
||||
extends FilePathDiscovery<Query>
|
||||
implements QueryDiscoverer
|
||||
{
|
||||
public readonly onDidChangeQueries: AppEvent<void>;
|
||||
private readonly onDidChangeQueriesEmitter: AppEventEmitter<void>;
|
||||
|
||||
constructor(
|
||||
private readonly env: EnvironmentContext,
|
||||
private readonly app: App,
|
||||
private readonly queryPackDiscovery: QueryPackDiscoverer,
|
||||
private readonly languageContext: LanguageContextStore,
|
||||
) {
|
||||
super("Query Discovery", `**/*${QUERY_FILE_EXTENSION}`);
|
||||
|
||||
// Set up event emitters
|
||||
this.onDidChangeQueriesEmitter = this.push(app.createEventEmitter<void>());
|
||||
this.onDidChangeQueries = this.onDidChangeQueriesEmitter.event;
|
||||
|
||||
// Handlers
|
||||
this.push(
|
||||
this.queryPackDiscovery.onDidChangeQueryPacks(
|
||||
this.recomputeAllData.bind(this),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Event that fires when the set of queries in the workspace changes.
|
||||
*/
|
||||
public get onDidChangeQueries(): Event<void> {
|
||||
return this.onDidChangePathData;
|
||||
this.push(
|
||||
this.onDidChangePathData(() => {
|
||||
this.onDidChangeQueriesEmitter.fire();
|
||||
}),
|
||||
);
|
||||
this.push(
|
||||
this.languageContext.onLanguageContextChanged(() => {
|
||||
this.onDidChangeQueriesEmitter.fire();
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,8 +78,10 @@ export class QueryDiscovery
|
||||
|
||||
const roots = [];
|
||||
for (const workspaceFolder of getOnDiskWorkspaceFoldersObjects()) {
|
||||
const queriesInRoot = pathData.filter((query) =>
|
||||
containsPath(workspaceFolder.uri.fsPath, query.path),
|
||||
const queriesInRoot = pathData.filter(
|
||||
(query) =>
|
||||
containsPath(workspaceFolder.uri.fsPath, query.path) &&
|
||||
this.languageContext.shouldInclude(query.language),
|
||||
);
|
||||
if (queriesInRoot.length === 0) {
|
||||
continue;
|
||||
@@ -73,7 +89,7 @@ export class QueryDiscovery
|
||||
const root = new FileTreeDirectory<string>(
|
||||
workspaceFolder.uri.fsPath,
|
||||
workspaceFolder.name,
|
||||
this.env,
|
||||
this.app.environment,
|
||||
);
|
||||
for (const query of queriesInRoot) {
|
||||
const dirName = dirname(normalize(relative(root.path, query.path)));
|
||||
|
||||
@@ -26,6 +26,7 @@ export class ServerProcess implements Disposable {
|
||||
this.connection.end();
|
||||
this.child.stdin!.end();
|
||||
this.child.stderr!.destroy();
|
||||
this.child.removeAllListeners();
|
||||
// TODO kill the process if it doesn't terminate after a certain time limit.
|
||||
|
||||
// On Windows, we usually have to terminate the process before closing its stdout.
|
||||
|
||||
@@ -27,10 +27,12 @@ const DependencyContainer = styled.div`
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
background-color: var(--vscode-textBlockQuote-background);
|
||||
background-color: var(--vscode-editor-background);
|
||||
border: 0.05rem solid var(--vscode-panelSection-border);
|
||||
border-radius: 0.3rem;
|
||||
border-color: var(--vscode-textBlockQuote-border);
|
||||
padding: 0.5rem;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
`;
|
||||
|
||||
export type MethodModelingProps = {
|
||||
|
||||
@@ -56,7 +56,7 @@ class MockAppEventEmitter<T> implements AppEventEmitter<T> {
|
||||
|
||||
constructor() {
|
||||
this.event = () => {
|
||||
return {} as Disposable;
|
||||
return new MockAppEvent();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -69,7 +69,17 @@ class MockAppEventEmitter<T> implements AppEventEmitter<T> {
|
||||
}
|
||||
}
|
||||
|
||||
export function createMockEnvironmentContext(): EnvironmentContext {
|
||||
class MockAppEvent implements Disposable {
|
||||
public fire(): void {
|
||||
// no-op
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
function createMockEnvironmentContext(): EnvironmentContext {
|
||||
return {
|
||||
language: "en-US",
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { retry } from "@octokit/plugin-retry";
|
||||
import * as Octokit from "@octokit/rest";
|
||||
import { RequestInterface } from "@octokit/types/dist-types/RequestInterface";
|
||||
|
||||
import { Credentials } from "../../src/common/authentication";
|
||||
import { AppOctokit } from "../../src/common/octokit";
|
||||
|
||||
function makeTestOctokit(octokit: Octokit.Octokit): Credentials {
|
||||
return {
|
||||
@@ -38,5 +38,5 @@ export function testCredentialsWithStub(
|
||||
* optionally authenticated with a given token.
|
||||
*/
|
||||
export function testCredentialsWithRealOctokit(token?: string): Credentials {
|
||||
return makeTestOctokit(new Octokit.Octokit({ auth: token, retry }));
|
||||
return makeTestOctokit(new AppOctokit({ auth: token }));
|
||||
}
|
||||
|
||||
@@ -12,9 +12,7 @@ const rootDir = path.resolve(__dirname, "../..");
|
||||
|
||||
/** @type import("jest-runner-vscode").RunnerOptions */
|
||||
const config = {
|
||||
// Temporary until https://github.com/github/vscode-codeql/issues/2402 is fixed
|
||||
// version: "stable",
|
||||
version: "1.77.3",
|
||||
version: "stable",
|
||||
launchArgs: [
|
||||
"--disable-gpu",
|
||||
"--extensions-dir=" + path.join(rootDir, ".vscode-test", "extensions"),
|
||||
|
||||
@@ -3,8 +3,8 @@ import {
|
||||
QueryDiscovery,
|
||||
QueryPackDiscoverer,
|
||||
} from "../../../../src/queries-panel/query-discovery";
|
||||
import { createMockEnvironmentContext } from "../../../__mocks__/appMock";
|
||||
import { dirname, join } from "path";
|
||||
import { createMockApp } from "../../../__mocks__/appMock";
|
||||
import { basename, dirname, join } from "path";
|
||||
import * as tmp from "tmp";
|
||||
import {
|
||||
FileTreeDirectory,
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
import { mkdirSync, writeFileSync } from "fs";
|
||||
import { QueryLanguage } from "../../../../src/common/query-language";
|
||||
import { sleep } from "../../../../src/common/time";
|
||||
import { LanguageContextStore } from "../../../../src/language-context-store";
|
||||
|
||||
describe("Query pack discovery", () => {
|
||||
let tmpDir: string;
|
||||
@@ -20,7 +21,10 @@ describe("Query pack discovery", () => {
|
||||
|
||||
let workspacePath: string;
|
||||
|
||||
const env = createMockEnvironmentContext();
|
||||
const app = createMockApp({});
|
||||
const env = app.environment;
|
||||
|
||||
const languageContext = new LanguageContextStore(app);
|
||||
|
||||
const onDidChangeQueryPacks = new EventEmitter<void>();
|
||||
let queryPackDiscoverer: QueryPackDiscoverer;
|
||||
@@ -45,7 +49,7 @@ describe("Query pack discovery", () => {
|
||||
getLanguageForQueryFile: () => QueryLanguage.Java,
|
||||
onDidChangeQueryPacks: onDidChangeQueryPacks.event,
|
||||
};
|
||||
discovery = new QueryDiscovery(env, queryPackDiscoverer);
|
||||
discovery = new QueryDiscovery(app, queryPackDiscoverer, languageContext);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -160,6 +164,52 @@ describe("Query pack discovery", () => {
|
||||
]),
|
||||
]);
|
||||
});
|
||||
|
||||
it("should respect the language context filter", async () => {
|
||||
makeTestFile(join(workspacePath, "query1.ql"));
|
||||
makeTestFile(join(workspacePath, "query2.ql"));
|
||||
|
||||
queryPackDiscoverer.getLanguageForQueryFile = (path) => {
|
||||
if (basename(path) === "query1.ql") {
|
||||
return QueryLanguage.Java;
|
||||
} else {
|
||||
return QueryLanguage.Python;
|
||||
}
|
||||
};
|
||||
|
||||
await discovery.initialRefresh();
|
||||
|
||||
// Set the language to python-only
|
||||
await languageContext.setLanguageContext(QueryLanguage.Python);
|
||||
|
||||
expect(discovery.buildQueryTree()).toEqual([
|
||||
new FileTreeDirectory(workspacePath, "workspace", env, [
|
||||
new FileTreeLeaf(
|
||||
join(workspacePath, "query2.ql"),
|
||||
"query2.ql",
|
||||
"python",
|
||||
),
|
||||
]),
|
||||
]);
|
||||
|
||||
// Clear the language context filter
|
||||
await languageContext.clearLanguageContext();
|
||||
|
||||
expect(discovery.buildQueryTree()).toEqual([
|
||||
new FileTreeDirectory(workspacePath, "workspace", env, [
|
||||
new FileTreeLeaf(
|
||||
join(workspacePath, "query1.ql"),
|
||||
"query1.ql",
|
||||
"java",
|
||||
),
|
||||
new FileTreeLeaf(
|
||||
join(workspacePath, "query2.ql"),
|
||||
"query2.ql",
|
||||
"python",
|
||||
),
|
||||
]),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("recomputeAllQueryLanguages", () => {
|
||||
|
||||
Reference in New Issue
Block a user