Merge remote-tracking branch 'origin/main' into koesie10/npm-upgrade
This commit is contained in:
@@ -2,6 +2,11 @@
|
||||
|
||||
## [UNRELEASED]
|
||||
|
||||
## 1.11.0 - 13 December 2023
|
||||
|
||||
- Add a new method modeling panel to classify methods as sources/sinks/summaries while in the context of the source code. [#3128](https://github.com/github/vscode-codeql/pull/3128)
|
||||
- Adds the ability to add multiple classifications per method in the CodeQL Model Editor. [#3128](https://github.com/github/vscode-codeql/pull/3128)
|
||||
- Switch add and delete button positions in the CodeQL Model Editor. [#3123](https://github.com/github/vscode-codeql/pull/3123)
|
||||
- Add a prompt to the "Quick query" command to encourage users in single-folder workspaces to use "Create query" instead. [#3082](https://github.com/github/vscode-codeql/pull/3082)
|
||||
- Remove support for CodeQL CLI versions older than 2.11.6. [#3087](https://github.com/github/vscode-codeql/pull/3087)
|
||||
- Preserve focus on results viewer when showing a location in a file. [#3088](https://github.com/github/vscode-codeql/pull/3088)
|
||||
|
||||
16
extensions/ql-vscode/package-lock.json
generated
16
extensions/ql-vscode/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "vscode-codeql",
|
||||
"version": "1.10.1",
|
||||
"version": "1.11.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "vscode-codeql",
|
||||
"version": "1.10.1",
|
||||
"version": "1.11.1",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -21385,6 +21385,12 @@
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip/node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/just-debounce": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.1.0.tgz",
|
||||
@@ -24137,12 +24143,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/param-case": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": "CodeQL for Visual Studio Code",
|
||||
"author": "GitHub",
|
||||
"private": true,
|
||||
"version": "1.10.1",
|
||||
"version": "1.11.1",
|
||||
"publisher": "GitHub",
|
||||
"license": "MIT",
|
||||
"icon": "media/VS-marketplace-CodeQL-icon.png",
|
||||
@@ -1840,15 +1840,14 @@
|
||||
{
|
||||
"id": "codeQLMethodModeling",
|
||||
"type": "webview",
|
||||
"name": "CodeQL Method Modeling",
|
||||
"when": "config.codeQL.canary"
|
||||
"name": "CodeQL Method Modeling"
|
||||
}
|
||||
],
|
||||
"codeql-methods-usage": [
|
||||
{
|
||||
"id": "codeQLMethodsUsage",
|
||||
"name": "CodeQL Methods Usage",
|
||||
"when": "config.codeQL.canary && codeql.modelEditorOpen"
|
||||
"when": "codeql.modelEditorOpen"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ import { promisify } from "util";
|
||||
import { CancellationToken, Disposable, Uri } from "vscode";
|
||||
|
||||
import {
|
||||
BQRSInfo,
|
||||
BqrsInfo,
|
||||
DecodedBqrs,
|
||||
DecodedBqrsChunk,
|
||||
} from "../common/bqrs-cli-types";
|
||||
@@ -928,11 +928,11 @@ export class CodeQLCliServer implements Disposable {
|
||||
* @param bqrsPath The path to the bqrs.
|
||||
* @param pageSize The page size to precompute offsets into the binary file for.
|
||||
*/
|
||||
async bqrsInfo(bqrsPath: string, pageSize?: number): Promise<BQRSInfo> {
|
||||
async bqrsInfo(bqrsPath: string, pageSize?: number): Promise<BqrsInfo> {
|
||||
const subcommandArgs = (
|
||||
pageSize ? ["--paginate-rows", pageSize.toString()] : []
|
||||
).concat(bqrsPath);
|
||||
return await this.runJsonCodeQlCliCommand<BQRSInfo>(
|
||||
return await this.runJsonCodeQlCliCommand<BqrsInfo>(
|
||||
["bqrs", "info"],
|
||||
subcommandArgs,
|
||||
"Reading bqrs header",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* the "for the sake of extensibility" comment in messages.ts.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace ColumnKindCode {
|
||||
export namespace BqrsColumnKindCode {
|
||||
export const FLOAT = "f";
|
||||
export const INTEGER = "i";
|
||||
export const STRING = "s";
|
||||
@@ -13,55 +13,44 @@ export namespace ColumnKindCode {
|
||||
export const ENTITY = "e";
|
||||
}
|
||||
|
||||
type ColumnKind =
|
||||
| typeof ColumnKindCode.FLOAT
|
||||
| typeof ColumnKindCode.INTEGER
|
||||
| typeof ColumnKindCode.STRING
|
||||
| typeof ColumnKindCode.BOOLEAN
|
||||
| typeof ColumnKindCode.DATE
|
||||
| typeof ColumnKindCode.ENTITY;
|
||||
export type BqrsColumnKind =
|
||||
| typeof BqrsColumnKindCode.FLOAT
|
||||
| typeof BqrsColumnKindCode.INTEGER
|
||||
| typeof BqrsColumnKindCode.STRING
|
||||
| typeof BqrsColumnKindCode.BOOLEAN
|
||||
| typeof BqrsColumnKindCode.DATE
|
||||
| typeof BqrsColumnKindCode.ENTITY;
|
||||
|
||||
interface Column {
|
||||
export interface BqrsSchemaColumn {
|
||||
name?: string;
|
||||
kind: ColumnKind;
|
||||
kind: BqrsColumnKind;
|
||||
}
|
||||
|
||||
export interface ResultSetSchema {
|
||||
export interface BqrsResultSetSchema {
|
||||
name: string;
|
||||
rows: number;
|
||||
columns: Column[];
|
||||
pagination?: PaginationInfo;
|
||||
columns: BqrsSchemaColumn[];
|
||||
pagination?: BqrsPaginationInfo;
|
||||
}
|
||||
|
||||
export function getResultSetSchema(
|
||||
resultSetName: string,
|
||||
resultSets: BQRSInfo,
|
||||
): ResultSetSchema | undefined {
|
||||
for (const schema of resultSets["result-sets"]) {
|
||||
if (schema.name === resultSetName) {
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
interface PaginationInfo {
|
||||
interface BqrsPaginationInfo {
|
||||
"step-size": number;
|
||||
offsets: number[];
|
||||
}
|
||||
|
||||
export interface BQRSInfo {
|
||||
"result-sets": ResultSetSchema[];
|
||||
export interface BqrsInfo {
|
||||
"result-sets": BqrsResultSetSchema[];
|
||||
}
|
||||
|
||||
export type BqrsId = number;
|
||||
|
||||
export interface EntityValue {
|
||||
url?: UrlValue;
|
||||
export interface BqrsEntityValue {
|
||||
url?: BqrsUrlValue;
|
||||
label?: string;
|
||||
id?: BqrsId;
|
||||
}
|
||||
|
||||
export interface LineColumnLocation {
|
||||
export interface BqrsLineColumnLocation {
|
||||
uri: string;
|
||||
startLine: number;
|
||||
startColumn: number;
|
||||
@@ -69,7 +58,7 @@ export interface LineColumnLocation {
|
||||
endColumn: number;
|
||||
}
|
||||
|
||||
export interface WholeFileLocation {
|
||||
export interface BqrsWholeFileLocation {
|
||||
uri: string;
|
||||
startLine: never;
|
||||
startColumn: never;
|
||||
@@ -77,47 +66,28 @@ export interface WholeFileLocation {
|
||||
endColumn: never;
|
||||
}
|
||||
|
||||
export type ResolvableLocationValue = WholeFileLocation | LineColumnLocation;
|
||||
export type BqrsUrlValue =
|
||||
| BqrsWholeFileLocation
|
||||
| BqrsLineColumnLocation
|
||||
| string;
|
||||
|
||||
export type UrlValue = ResolvableLocationValue | string;
|
||||
|
||||
export type CellValue = EntityValue | number | string | boolean;
|
||||
|
||||
export type ResultRow = CellValue[];
|
||||
|
||||
export interface RawResultSet {
|
||||
readonly schema: ResultSetSchema;
|
||||
readonly rows: readonly ResultRow[];
|
||||
}
|
||||
|
||||
// TODO: This function is not necessary. It generates a tuple that is slightly easier
|
||||
// to handle than the ResultSetSchema and DecodedBqrsChunk. But perhaps it is unnecessary
|
||||
// boilerplate.
|
||||
export function transformBqrsResultSet(
|
||||
schema: ResultSetSchema,
|
||||
page: DecodedBqrsChunk,
|
||||
): RawResultSet {
|
||||
return {
|
||||
schema,
|
||||
rows: Array.from(page.tuples),
|
||||
};
|
||||
}
|
||||
export type BqrsCellValue = BqrsEntityValue | number | string | boolean;
|
||||
|
||||
export type BqrsKind =
|
||||
| "String"
|
||||
| "Float"
|
||||
| "Integer"
|
||||
| "String"
|
||||
| "Boolean"
|
||||
| "Date"
|
||||
| "Entity";
|
||||
|
||||
export interface BqrsColumn {
|
||||
interface BqrsColumn {
|
||||
name?: string;
|
||||
kind: BqrsKind;
|
||||
}
|
||||
|
||||
export interface DecodedBqrsChunk {
|
||||
tuples: CellValue[][];
|
||||
tuples: BqrsCellValue[][];
|
||||
next?: number;
|
||||
columns: BqrsColumn[];
|
||||
}
|
||||
|
||||
216
extensions/ql-vscode/src/common/bqrs-raw-results-mapper.ts
Normal file
216
extensions/ql-vscode/src/common/bqrs-raw-results-mapper.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
import {
|
||||
BqrsCellValue as BqrsCellValue,
|
||||
BqrsColumnKind as BqrsColumnKind,
|
||||
BqrsColumnKindCode,
|
||||
DecodedBqrsChunk,
|
||||
BqrsEntityValue as BqrsEntityValue,
|
||||
BqrsLineColumnLocation,
|
||||
BqrsResultSetSchema,
|
||||
BqrsUrlValue as BqrsUrlValue,
|
||||
BqrsWholeFileLocation,
|
||||
BqrsSchemaColumn,
|
||||
} from "./bqrs-cli-types";
|
||||
import {
|
||||
CellValue,
|
||||
Column,
|
||||
ColumnKind,
|
||||
EntityValue,
|
||||
RawResultSet,
|
||||
Row,
|
||||
UrlValue,
|
||||
UrlValueResolvable,
|
||||
} from "./raw-result-types";
|
||||
import { assertNever } from "./helpers-pure";
|
||||
import { isEmptyPath } from "./bqrs-utils";
|
||||
|
||||
export function bqrsToResultSet(
|
||||
schema: BqrsResultSetSchema,
|
||||
chunk: DecodedBqrsChunk,
|
||||
): RawResultSet {
|
||||
const name = schema.name;
|
||||
const totalRowCount = schema.rows;
|
||||
|
||||
const columns = schema.columns.map(mapColumn);
|
||||
|
||||
const rows = chunk.tuples.map(
|
||||
(tuple): Row => tuple.map((cell): CellValue => mapCellValue(cell)),
|
||||
);
|
||||
|
||||
const resultSet: RawResultSet = {
|
||||
name,
|
||||
totalRowCount,
|
||||
columns,
|
||||
rows,
|
||||
};
|
||||
|
||||
if (chunk.next) {
|
||||
resultSet.nextPageOffset = chunk.next;
|
||||
}
|
||||
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
function mapColumn(column: BqrsSchemaColumn): Column {
|
||||
const result: Column = {
|
||||
kind: mapColumnKind(column.kind),
|
||||
};
|
||||
|
||||
if (column.name) {
|
||||
result.name = column.name;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function mapColumnKind(kind: BqrsColumnKind): ColumnKind {
|
||||
switch (kind) {
|
||||
case BqrsColumnKindCode.STRING:
|
||||
return ColumnKind.String;
|
||||
case BqrsColumnKindCode.FLOAT:
|
||||
return ColumnKind.Float;
|
||||
case BqrsColumnKindCode.INTEGER:
|
||||
return ColumnKind.Integer;
|
||||
case BqrsColumnKindCode.BOOLEAN:
|
||||
return ColumnKind.Boolean;
|
||||
case BqrsColumnKindCode.DATE:
|
||||
return ColumnKind.Date;
|
||||
case BqrsColumnKindCode.ENTITY:
|
||||
return ColumnKind.Entity;
|
||||
default:
|
||||
assertNever(kind);
|
||||
}
|
||||
}
|
||||
|
||||
function mapCellValue(cellValue: BqrsCellValue): CellValue {
|
||||
switch (typeof cellValue) {
|
||||
case "string":
|
||||
return {
|
||||
type: "string",
|
||||
value: cellValue,
|
||||
};
|
||||
case "number":
|
||||
return {
|
||||
type: "number",
|
||||
value: cellValue,
|
||||
};
|
||||
case "boolean":
|
||||
return {
|
||||
type: "boolean",
|
||||
value: cellValue,
|
||||
};
|
||||
case "object":
|
||||
return {
|
||||
type: "entity",
|
||||
value: mapEntityValue(cellValue),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function mapEntityValue(cellValue: BqrsEntityValue): EntityValue {
|
||||
const result: EntityValue = {};
|
||||
|
||||
if (cellValue.id) {
|
||||
result.id = cellValue.id;
|
||||
}
|
||||
if (cellValue.label) {
|
||||
result.label = cellValue.label;
|
||||
}
|
||||
if (cellValue.url) {
|
||||
result.url = mapUrlValue(cellValue.url);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function mapUrlValue(urlValue: BqrsUrlValue): UrlValue | undefined {
|
||||
if (typeof urlValue === "string") {
|
||||
const location = tryGetLocationFromString(urlValue);
|
||||
if (location !== undefined) {
|
||||
return location;
|
||||
}
|
||||
|
||||
return {
|
||||
type: "string",
|
||||
value: urlValue,
|
||||
};
|
||||
}
|
||||
|
||||
if (isWholeFileLoc(urlValue)) {
|
||||
return {
|
||||
type: "wholeFileLocation",
|
||||
uri: urlValue.uri,
|
||||
};
|
||||
}
|
||||
|
||||
if (isLineColumnLoc(urlValue)) {
|
||||
return {
|
||||
type: "lineColumnLocation",
|
||||
uri: urlValue.uri,
|
||||
startLine: urlValue.startLine,
|
||||
startColumn: urlValue.startColumn,
|
||||
endLine: urlValue.endLine,
|
||||
endColumn: urlValue.endColumn,
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isLineColumnLoc(loc: BqrsUrlValue): loc is BqrsLineColumnLocation {
|
||||
return (
|
||||
typeof loc !== "string" &&
|
||||
!isEmptyPath(loc.uri) &&
|
||||
"startLine" in loc &&
|
||||
"startColumn" in loc &&
|
||||
"endLine" in loc &&
|
||||
"endColumn" in loc
|
||||
);
|
||||
}
|
||||
|
||||
function isWholeFileLoc(loc: BqrsUrlValue): loc is BqrsWholeFileLocation {
|
||||
return (
|
||||
typeof loc !== "string" && !isEmptyPath(loc.uri) && !isLineColumnLoc(loc)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The CodeQL filesystem libraries use this pattern in `getURL()` predicates
|
||||
* to describe the location of an entire filesystem resource.
|
||||
* Such locations appear as `StringLocation`s instead of `FivePartLocation`s.
|
||||
*
|
||||
* Folder resources also get similar URLs, but with the `folder` scheme.
|
||||
* They are deliberately ignored here, since there is no suitable location to show the user.
|
||||
*/
|
||||
const FILE_LOCATION_REGEX = /file:\/\/(.+):([0-9]+):([0-9]+):([0-9]+):([0-9]+)/;
|
||||
|
||||
function tryGetLocationFromString(loc: string): UrlValueResolvable | undefined {
|
||||
const matches = FILE_LOCATION_REGEX.exec(loc);
|
||||
if (matches && matches.length > 1 && matches[1]) {
|
||||
if (isWholeFileMatch(matches)) {
|
||||
return {
|
||||
type: "wholeFileLocation",
|
||||
uri: matches[1],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: "lineColumnLocation",
|
||||
uri: matches[1],
|
||||
startLine: Number(matches[2]),
|
||||
startColumn: Number(matches[3]),
|
||||
endLine: Number(matches[4]),
|
||||
endColumn: Number(matches[5]),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isWholeFileMatch(matches: RegExpExecArray): boolean {
|
||||
return (
|
||||
matches[2] === "0" &&
|
||||
matches[3] === "0" &&
|
||||
matches[4] === "0" &&
|
||||
matches[5] === "0"
|
||||
);
|
||||
}
|
||||
@@ -1,111 +1,20 @@
|
||||
import {
|
||||
UrlValue,
|
||||
ResolvableLocationValue,
|
||||
LineColumnLocation,
|
||||
WholeFileLocation,
|
||||
} from "./bqrs-cli-types";
|
||||
import { createRemoteFileRef } from "../common/location-link-utils";
|
||||
|
||||
/**
|
||||
* The CodeQL filesystem libraries use this pattern in `getURL()` predicates
|
||||
* to describe the location of an entire filesystem resource.
|
||||
* Such locations appear as `StringLocation`s instead of `FivePartLocation`s.
|
||||
*
|
||||
* Folder resources also get similar URLs, but with the `folder` scheme.
|
||||
* They are deliberately ignored here, since there is no suitable location to show the user.
|
||||
*/
|
||||
const FILE_LOCATION_REGEX = /file:\/\/(.+):([0-9]+):([0-9]+):([0-9]+):([0-9]+)/;
|
||||
/**
|
||||
* Gets a resolvable source file location for the specified `LocationValue`, if possible.
|
||||
* @param loc The location to test.
|
||||
*/
|
||||
export function tryGetResolvableLocation(
|
||||
loc: UrlValue | undefined,
|
||||
): ResolvableLocationValue | undefined {
|
||||
let resolvedLoc;
|
||||
if (loc === undefined) {
|
||||
resolvedLoc = undefined;
|
||||
} else if (isWholeFileLoc(loc) || isLineColumnLoc(loc)) {
|
||||
resolvedLoc = loc as ResolvableLocationValue;
|
||||
} else if (isStringLoc(loc)) {
|
||||
resolvedLoc = tryGetLocationFromString(loc);
|
||||
} else {
|
||||
resolvedLoc = undefined;
|
||||
}
|
||||
|
||||
return resolvedLoc;
|
||||
}
|
||||
|
||||
export function tryGetLocationFromString(
|
||||
loc: string,
|
||||
): ResolvableLocationValue | undefined {
|
||||
const matches = FILE_LOCATION_REGEX.exec(loc);
|
||||
if (matches && matches.length > 1 && matches[1]) {
|
||||
if (isWholeFileMatch(matches)) {
|
||||
return {
|
||||
uri: matches[1],
|
||||
} as WholeFileLocation;
|
||||
} else {
|
||||
return {
|
||||
uri: matches[1],
|
||||
startLine: Number(matches[2]),
|
||||
startColumn: Number(matches[3]),
|
||||
endLine: Number(matches[4]),
|
||||
endColumn: Number(matches[5]),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function isWholeFileMatch(matches: RegExpExecArray): boolean {
|
||||
return (
|
||||
matches[2] === "0" &&
|
||||
matches[3] === "0" &&
|
||||
matches[4] === "0" &&
|
||||
matches[5] === "0"
|
||||
);
|
||||
}
|
||||
import { isUrlValueResolvable, UrlValue } from "./raw-result-types";
|
||||
|
||||
/**
|
||||
* Checks whether the file path is empty. If so, we do not want to render this location
|
||||
* as a link.
|
||||
*
|
||||
* @param uri A file uri
|
||||
*/
|
||||
export function isEmptyPath(uriStr: string) {
|
||||
return !uriStr || uriStr === "file:/";
|
||||
}
|
||||
|
||||
export function isLineColumnLoc(loc: UrlValue): loc is LineColumnLocation {
|
||||
return (
|
||||
typeof loc !== "string" &&
|
||||
!isEmptyPath(loc.uri) &&
|
||||
"startLine" in loc &&
|
||||
"startColumn" in loc &&
|
||||
"endLine" in loc &&
|
||||
"endColumn" in loc
|
||||
);
|
||||
}
|
||||
|
||||
export function isWholeFileLoc(loc: UrlValue): loc is WholeFileLocation {
|
||||
return (
|
||||
typeof loc !== "string" && !isEmptyPath(loc.uri) && !isLineColumnLoc(loc)
|
||||
);
|
||||
}
|
||||
|
||||
export function isStringLoc(loc: UrlValue): loc is string {
|
||||
return typeof loc === "string";
|
||||
}
|
||||
|
||||
export function tryGetRemoteLocation(
|
||||
loc: UrlValue | undefined,
|
||||
fileLinkPrefix: string,
|
||||
sourceLocationPrefix: string | undefined,
|
||||
): string | undefined {
|
||||
const resolvableLocation = tryGetResolvableLocation(loc);
|
||||
if (!resolvableLocation) {
|
||||
if (!loc || !isUrlValueResolvable(loc)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -115,22 +24,19 @@ export function tryGetRemoteLocation(
|
||||
// "file:${sourceLocationPrefix}/relative/path/to/file"
|
||||
// So we need to strip off the first part to get the relative path.
|
||||
if (sourceLocationPrefix) {
|
||||
if (!resolvableLocation.uri.startsWith(`file:${sourceLocationPrefix}/`)) {
|
||||
if (!loc.uri.startsWith(`file:${sourceLocationPrefix}/`)) {
|
||||
return undefined;
|
||||
}
|
||||
trimmedLocation = resolvableLocation.uri.replace(
|
||||
`file:${sourceLocationPrefix}/`,
|
||||
"",
|
||||
);
|
||||
trimmedLocation = loc.uri.replace(`file:${sourceLocationPrefix}/`, "");
|
||||
} else {
|
||||
// If the source location prefix is empty (e.g. for older remote queries), we assume that the database
|
||||
// was created on a Linux actions runner and has the format:
|
||||
// "file:/home/runner/work/<repo>/<repo>/relative/path/to/file"
|
||||
// So we need to drop the first 6 parts of the path.
|
||||
if (!resolvableLocation.uri.startsWith("file:/home/runner/work/")) {
|
||||
if (!loc.uri.startsWith("file:/home/runner/work/")) {
|
||||
return undefined;
|
||||
}
|
||||
const locationParts = resolvableLocation.uri.split("/");
|
||||
const locationParts = loc.uri.split("/");
|
||||
trimmedLocation = locationParts.slice(6, locationParts.length).join("/");
|
||||
}
|
||||
|
||||
@@ -138,11 +44,16 @@ export function tryGetRemoteLocation(
|
||||
fileLinkPrefix,
|
||||
filePath: trimmedLocation,
|
||||
};
|
||||
|
||||
if (loc.type === "wholeFileLocation") {
|
||||
return createRemoteFileRef(fileLink);
|
||||
}
|
||||
|
||||
return createRemoteFileRef(
|
||||
fileLink,
|
||||
resolvableLocation.startLine,
|
||||
resolvableLocation.endLine,
|
||||
resolvableLocation.startColumn,
|
||||
resolvableLocation.endColumn,
|
||||
loc.startLine,
|
||||
loc.endLine,
|
||||
loc.startColumn,
|
||||
loc.endColumn,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import * as sarif from "sarif";
|
||||
import {
|
||||
RawResultSet,
|
||||
ResultRow,
|
||||
ResultSetSchema,
|
||||
ResolvableLocationValue,
|
||||
BqrsColumn,
|
||||
} from "../common/bqrs-cli-types";
|
||||
import {
|
||||
VariantAnalysis,
|
||||
VariantAnalysisScannedRepositoryResult,
|
||||
@@ -25,6 +18,12 @@ import {
|
||||
} from "../model-editor/shared/view-state";
|
||||
import { Mode } from "../model-editor/shared/mode";
|
||||
import { QueryLanguage } from "./query-language";
|
||||
import {
|
||||
Column,
|
||||
RawResultSet,
|
||||
Row,
|
||||
UrlValueResolvable,
|
||||
} from "./raw-result-types";
|
||||
|
||||
/**
|
||||
* This module contains types and code that are shared between
|
||||
@@ -35,10 +34,13 @@ export const SELECT_TABLE_NAME = "#select";
|
||||
export const ALERTS_TABLE_NAME = "alerts";
|
||||
export const GRAPH_TABLE_NAME = "graph";
|
||||
|
||||
export type RawTableResultSet = { t: "RawResultSet" } & RawResultSet;
|
||||
type RawTableResultSet = {
|
||||
t: "RawResultSet";
|
||||
resultSet: RawResultSet;
|
||||
};
|
||||
|
||||
export type InterpretedResultSet<T> = {
|
||||
t: "InterpretedResultSet";
|
||||
readonly schema: ResultSetSchema;
|
||||
name: string;
|
||||
interpretation: InterpretationT<T>;
|
||||
};
|
||||
@@ -208,7 +210,7 @@ export type FromResultsViewMsg =
|
||||
*/
|
||||
interface ViewSourceFileMsg {
|
||||
t: "viewSourceFile";
|
||||
loc: ResolvableLocationValue;
|
||||
loc: UrlValueResolvable;
|
||||
databaseUri: string;
|
||||
}
|
||||
|
||||
@@ -377,9 +379,9 @@ type QueryCompareResult = RawQueryCompareResult | InterpretedQueryCompareResult;
|
||||
*/
|
||||
export type RawQueryCompareResult = {
|
||||
kind: "raw";
|
||||
columns: readonly BqrsColumn[];
|
||||
from: ResultRow[];
|
||||
to: ResultRow[];
|
||||
columns: readonly Column[];
|
||||
from: Row[];
|
||||
to: Row[];
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
90
extensions/ql-vscode/src/common/raw-result-types.ts
Normal file
90
extensions/ql-vscode/src/common/raw-result-types.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
export enum ColumnKind {
|
||||
String = "string",
|
||||
Float = "float",
|
||||
Integer = "integer",
|
||||
Boolean = "boolean",
|
||||
Date = "date",
|
||||
Entity = "entity",
|
||||
}
|
||||
|
||||
export type Column = {
|
||||
name?: string;
|
||||
kind: ColumnKind;
|
||||
};
|
||||
|
||||
type UrlValueString = {
|
||||
type: "string";
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type UrlValueWholeFileLocation = {
|
||||
type: "wholeFileLocation";
|
||||
uri: string;
|
||||
};
|
||||
|
||||
export type UrlValueLineColumnLocation = {
|
||||
type: "lineColumnLocation";
|
||||
uri: string;
|
||||
startLine: number;
|
||||
startColumn: number;
|
||||
endLine: number;
|
||||
endColumn: number;
|
||||
};
|
||||
|
||||
export type UrlValueResolvable =
|
||||
| UrlValueWholeFileLocation
|
||||
| UrlValueLineColumnLocation;
|
||||
|
||||
export function isUrlValueResolvable(
|
||||
value: UrlValue,
|
||||
): value is UrlValueResolvable {
|
||||
return (
|
||||
value.type === "wholeFileLocation" || value.type === "lineColumnLocation"
|
||||
);
|
||||
}
|
||||
|
||||
export type UrlValue = UrlValueString | UrlValueResolvable;
|
||||
|
||||
export type EntityValue = {
|
||||
url?: UrlValue;
|
||||
label?: string;
|
||||
id?: number;
|
||||
};
|
||||
|
||||
type CellValueEntity = {
|
||||
type: "entity";
|
||||
value: EntityValue;
|
||||
};
|
||||
|
||||
type CellValueNumber = {
|
||||
type: "number";
|
||||
value: number;
|
||||
};
|
||||
|
||||
type CellValueString = {
|
||||
type: "string";
|
||||
value: string;
|
||||
};
|
||||
|
||||
type CellValueBoolean = {
|
||||
type: "boolean";
|
||||
value: boolean;
|
||||
};
|
||||
|
||||
export type CellValue =
|
||||
| CellValueEntity
|
||||
| CellValueNumber
|
||||
| CellValueString
|
||||
| CellValueBoolean;
|
||||
|
||||
export type Row = CellValue[];
|
||||
|
||||
export type RawResultSet = {
|
||||
name: string;
|
||||
totalRowCount: number;
|
||||
|
||||
columns: Column[];
|
||||
rows: Row[];
|
||||
|
||||
nextPageOffset?: number;
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as Sarif from "sarif";
|
||||
import type { HighlightedRegion } from "../variant-analysis/shared/analysis-result";
|
||||
import { ResolvableLocationValue } from "../common/bqrs-cli-types";
|
||||
import { UrlValueResolvable } from "./raw-result-types";
|
||||
import { isEmptyPath } from "./bqrs-utils";
|
||||
|
||||
export interface SarifLink {
|
||||
@@ -16,7 +16,7 @@ interface NoLocation {
|
||||
}
|
||||
|
||||
type ParsedSarifLocation =
|
||||
| (ResolvableLocationValue & {
|
||||
| (UrlValueResolvable & {
|
||||
userVisibleFile: string;
|
||||
})
|
||||
// Resolvable locations have a `uri` field, but it will sometimes include
|
||||
@@ -137,6 +137,7 @@ export function parseSarifLocation(
|
||||
// If the region property is absent, the physicalLocation object refers to the entire file.
|
||||
// Source: https://docs.oasis-open.org/sarif/sarif/v2.1.0/cs01/sarif-v2.1.0-cs01.html#_Toc16012638.
|
||||
return {
|
||||
type: "wholeFileLocation",
|
||||
uri: effectiveLocation,
|
||||
userVisibleFile,
|
||||
} as ParsedSarifLocation;
|
||||
@@ -144,6 +145,7 @@ export function parseSarifLocation(
|
||||
const region = parseSarifRegion(physicalLocation.region);
|
||||
|
||||
return {
|
||||
type: "lineColumnLocation",
|
||||
uri: effectiveLocation,
|
||||
userVisibleFile,
|
||||
...region,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { extLogger } from "../common/logging/vscode";
|
||||
import { CodeQLCliServer } from "../codeql-cli/cli";
|
||||
import { DatabaseManager } from "../databases/local-databases";
|
||||
import { jumpToLocation } from "../databases/local-databases/locations";
|
||||
import { BQRSInfo, DecodedBqrsChunk } from "../common/bqrs-cli-types";
|
||||
import { BqrsInfo } from "../common/bqrs-cli-types";
|
||||
import resultsDiff from "./resultsDiff";
|
||||
import { CompletedLocalQueryInfo } from "../query-results";
|
||||
import { assertNever, getErrorMessage } from "../common/helpers-pure";
|
||||
@@ -22,6 +22,8 @@ import {
|
||||
import { telemetryListener } from "../common/vscode/telemetry";
|
||||
import { redactableError } from "../common/errors";
|
||||
import { App } from "../common/app";
|
||||
import { bqrsToResultSet } from "../common/bqrs-raw-results-mapper";
|
||||
import { RawResultSet } from "../common/raw-result-types";
|
||||
import {
|
||||
findCommonResultSetNames,
|
||||
findResultSetNames,
|
||||
@@ -29,9 +31,9 @@ import {
|
||||
|
||||
interface ComparePair {
|
||||
from: CompletedLocalQueryInfo;
|
||||
fromSchemas: BQRSInfo;
|
||||
fromSchemas: BqrsInfo;
|
||||
to: CompletedLocalQueryInfo;
|
||||
toSchemas: BQRSInfo;
|
||||
toSchemas: BqrsInfo;
|
||||
|
||||
commonResultSetNames: readonly string[];
|
||||
}
|
||||
@@ -236,22 +238,23 @@ export class CompareView extends AbstractWebview<
|
||||
}
|
||||
|
||||
private async getResultSet(
|
||||
bqrsInfo: BQRSInfo,
|
||||
bqrsInfo: BqrsInfo,
|
||||
resultSetName: string,
|
||||
resultsPath: string,
|
||||
): Promise<DecodedBqrsChunk> {
|
||||
): Promise<RawResultSet> {
|
||||
const schema = bqrsInfo["result-sets"].find(
|
||||
(schema) => schema.name === resultSetName,
|
||||
);
|
||||
if (!schema) {
|
||||
throw new Error(`Schema ${resultSetName} not found.`);
|
||||
}
|
||||
return await this.cliServer.bqrsDecode(resultsPath, resultSetName);
|
||||
const chunk = await this.cliServer.bqrsDecode(resultsPath, resultSetName);
|
||||
return bqrsToResultSet(schema, chunk);
|
||||
}
|
||||
|
||||
private compareResults(
|
||||
fromResults: DecodedBqrsChunk,
|
||||
toResults: DecodedBqrsChunk,
|
||||
fromResults: RawResultSet,
|
||||
toResults: RawResultSet,
|
||||
): RawQueryCompareResult {
|
||||
// Only compare columns that have the same name
|
||||
return resultsDiff(fromResults, toResults);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { BQRSInfo } from "../common/bqrs-cli-types";
|
||||
import { BqrsInfo } from "../common/bqrs-cli-types";
|
||||
import { getDefaultResultSetName } from "../common/interface-types";
|
||||
|
||||
export async function findCommonResultSetNames(
|
||||
fromSchemas: BQRSInfo,
|
||||
toSchemas: BQRSInfo,
|
||||
fromSchemas: BqrsInfo,
|
||||
toSchemas: BqrsInfo,
|
||||
): Promise<string[]> {
|
||||
const fromSchemaNames = fromSchemas["result-sets"].map(
|
||||
(schema) => schema.name,
|
||||
@@ -14,8 +14,8 @@ export async function findCommonResultSetNames(
|
||||
}
|
||||
|
||||
export async function findResultSetNames(
|
||||
fromSchemas: BQRSInfo,
|
||||
toSchemas: BQRSInfo,
|
||||
fromSchemas: BqrsInfo,
|
||||
toSchemas: BqrsInfo,
|
||||
commonResultSetNames: readonly string[],
|
||||
selectedResultSetName: string | undefined,
|
||||
) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DecodedBqrsChunk } from "../common/bqrs-cli-types";
|
||||
import { RawQueryCompareResult } from "../common/interface-types";
|
||||
import { RawResultSet } from "../common/raw-result-types";
|
||||
|
||||
/**
|
||||
* Compare the rows of two queries. Use deep equality to determine if
|
||||
@@ -20,31 +20,31 @@ import { RawQueryCompareResult } from "../common/interface-types";
|
||||
* 3. If the queries are 100% disjoint
|
||||
*/
|
||||
export default function resultsDiff(
|
||||
fromResults: DecodedBqrsChunk,
|
||||
toResults: DecodedBqrsChunk,
|
||||
fromResults: RawResultSet,
|
||||
toResults: RawResultSet,
|
||||
): RawQueryCompareResult {
|
||||
if (fromResults.columns.length !== toResults.columns.length) {
|
||||
throw new Error("CodeQL Compare: Columns do not match.");
|
||||
}
|
||||
|
||||
if (!fromResults.tuples.length) {
|
||||
if (!fromResults.rows.length) {
|
||||
throw new Error("CodeQL Compare: Source query has no results.");
|
||||
}
|
||||
|
||||
if (!toResults.tuples.length) {
|
||||
if (!toResults.rows.length) {
|
||||
throw new Error("CodeQL Compare: Target query has no results.");
|
||||
}
|
||||
|
||||
const results: RawQueryCompareResult = {
|
||||
kind: "raw",
|
||||
columns: fromResults.columns,
|
||||
from: arrayDiff(fromResults.tuples, toResults.tuples),
|
||||
to: arrayDiff(toResults.tuples, fromResults.tuples),
|
||||
from: arrayDiff(fromResults.rows, toResults.rows),
|
||||
to: arrayDiff(toResults.rows, fromResults.rows),
|
||||
};
|
||||
|
||||
if (
|
||||
fromResults.tuples.length === results.from.length &&
|
||||
toResults.tuples.length === results.to.length
|
||||
fromResults.rows.length === results.from.length &&
|
||||
toResults.rows.length === results.to.length
|
||||
) {
|
||||
throw new Error("CodeQL Compare: No overlap between the selected queries.");
|
||||
}
|
||||
|
||||
@@ -726,7 +726,6 @@ export interface ModelConfig {
|
||||
flowGeneration: boolean;
|
||||
llmGeneration: boolean;
|
||||
getExtensionsDirectory(languageId: string): string | undefined;
|
||||
showMultipleModels: boolean;
|
||||
enableRuby: boolean;
|
||||
}
|
||||
|
||||
@@ -765,10 +764,6 @@ export class ModelConfigListener extends ConfigListener implements ModelConfig {
|
||||
});
|
||||
}
|
||||
|
||||
public get showMultipleModels(): boolean {
|
||||
return isCanary();
|
||||
}
|
||||
|
||||
public get enableRuby(): boolean {
|
||||
return !!ENABLE_RUBY.getValue<boolean>();
|
||||
}
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import { pathExists, outputJSON, readJSON, readJSONSync } from "fs-extra";
|
||||
import { join } from "path";
|
||||
import {
|
||||
clearLocalDbConfig,
|
||||
cloneDbConfig,
|
||||
DbConfig,
|
||||
initializeLocalDbConfig,
|
||||
removeLocalDb,
|
||||
removeLocalList,
|
||||
removeRemoteList,
|
||||
removeRemoteOwner,
|
||||
removeRemoteRepo,
|
||||
renameLocalDb,
|
||||
renameLocalList,
|
||||
renameRemoteList,
|
||||
SelectedDbItem,
|
||||
DB_CONFIG_VERSION,
|
||||
@@ -30,13 +24,7 @@ import {
|
||||
DbConfigValidationErrorKind,
|
||||
} from "../db-validation-errors";
|
||||
import { ValueResult } from "../../common/value-result";
|
||||
import {
|
||||
LocalDatabaseDbItem,
|
||||
LocalListDbItem,
|
||||
RemoteUserDefinedListDbItem,
|
||||
DbItem,
|
||||
DbItemKind,
|
||||
} from "../db-item";
|
||||
import { RemoteUserDefinedListDbItem, DbItem, DbItemKind } from "../db-item";
|
||||
|
||||
export class DbConfigStore extends DisposableObject {
|
||||
public static readonly databaseConfigFileName = "databases.json";
|
||||
@@ -119,20 +107,9 @@ export class DbConfigStore extends DisposableObject {
|
||||
let config: DbConfig;
|
||||
|
||||
switch (dbItem.kind) {
|
||||
case DbItemKind.LocalList:
|
||||
config = removeLocalList(this.config, dbItem.listName);
|
||||
break;
|
||||
case DbItemKind.RemoteUserDefinedList:
|
||||
config = removeRemoteList(this.config, dbItem.listName);
|
||||
break;
|
||||
case DbItemKind.LocalDatabase:
|
||||
// When we start using local databases these need to be removed from disk as well.
|
||||
config = removeLocalDb(
|
||||
this.config,
|
||||
dbItem.databaseName,
|
||||
dbItem.parentListName,
|
||||
);
|
||||
break;
|
||||
case DbItemKind.RemoteRepo:
|
||||
config = removeRemoteRepo(
|
||||
this.config,
|
||||
@@ -229,22 +206,6 @@ export class DbConfigStore extends DisposableObject {
|
||||
await this.writeConfig(config);
|
||||
}
|
||||
|
||||
public async addLocalList(listName: string): Promise<void> {
|
||||
if (!this.config) {
|
||||
throw Error("Cannot add local list if config is not loaded");
|
||||
}
|
||||
|
||||
this.validateLocalListName(listName);
|
||||
|
||||
const config = cloneDbConfig(this.config);
|
||||
config.databases.local.lists.push({
|
||||
name: listName,
|
||||
databases: [],
|
||||
});
|
||||
|
||||
await this.writeConfig(config);
|
||||
}
|
||||
|
||||
public async addRemoteList(listName: string): Promise<void> {
|
||||
if (!this.config) {
|
||||
throw Error("Cannot add variant analysis list if config is not loaded");
|
||||
@@ -261,25 +222,6 @@ export class DbConfigStore extends DisposableObject {
|
||||
await this.writeConfig(config);
|
||||
}
|
||||
|
||||
public async renameLocalList(
|
||||
currentDbItem: LocalListDbItem,
|
||||
newName: string,
|
||||
) {
|
||||
if (!this.config) {
|
||||
throw Error("Cannot rename local list if config is not loaded");
|
||||
}
|
||||
|
||||
this.validateLocalListName(newName);
|
||||
|
||||
const updatedConfig = renameLocalList(
|
||||
this.config,
|
||||
currentDbItem.listName,
|
||||
newName,
|
||||
);
|
||||
|
||||
await this.writeConfig(updatedConfig);
|
||||
}
|
||||
|
||||
public async renameRemoteList(
|
||||
currentDbItem: RemoteUserDefinedListDbItem,
|
||||
newName: string,
|
||||
@@ -301,27 +243,6 @@ export class DbConfigStore extends DisposableObject {
|
||||
await this.writeConfig(updatedConfig);
|
||||
}
|
||||
|
||||
public async renameLocalDb(
|
||||
currentDbItem: LocalDatabaseDbItem,
|
||||
newName: string,
|
||||
parentListName?: string,
|
||||
): Promise<void> {
|
||||
if (!this.config) {
|
||||
throw Error("Cannot rename local db if config is not loaded");
|
||||
}
|
||||
|
||||
this.validateLocalDbName(newName);
|
||||
|
||||
const updatedConfig = renameLocalDb(
|
||||
this.config,
|
||||
currentDbItem.databaseName,
|
||||
newName,
|
||||
parentListName,
|
||||
);
|
||||
|
||||
await this.writeConfig(updatedConfig);
|
||||
}
|
||||
|
||||
public doesRemoteListExist(listName: string): boolean {
|
||||
if (!this.config) {
|
||||
throw Error(
|
||||
@@ -334,31 +255,6 @@ export class DbConfigStore extends DisposableObject {
|
||||
);
|
||||
}
|
||||
|
||||
public doesLocalListExist(listName: string): boolean {
|
||||
if (!this.config) {
|
||||
throw Error("Cannot check local list existence if config is not loaded");
|
||||
}
|
||||
|
||||
return this.config.databases.local.lists.some((l) => l.name === listName);
|
||||
}
|
||||
|
||||
public doesLocalDbExist(dbName: string, listName?: string): boolean {
|
||||
if (!this.config) {
|
||||
throw Error(
|
||||
"Cannot check variant analysis repository existence if config is not loaded",
|
||||
);
|
||||
}
|
||||
|
||||
if (listName) {
|
||||
return this.config.databases.local.lists.some(
|
||||
(l) =>
|
||||
l.name === listName && l.databases.some((d) => d.name === dbName),
|
||||
);
|
||||
}
|
||||
|
||||
return this.config.databases.local.databases.some((d) => d.name === dbName);
|
||||
}
|
||||
|
||||
public doesRemoteDbExist(dbName: string, listName?: string): boolean {
|
||||
if (!this.config) {
|
||||
throw Error(
|
||||
@@ -384,7 +280,6 @@ export class DbConfigStore extends DisposableObject {
|
||||
}
|
||||
|
||||
private async writeConfig(config: DbConfig): Promise<void> {
|
||||
clearLocalDbConfig(config);
|
||||
await outputJSON(this.configPath, config, {
|
||||
spaces: 2,
|
||||
});
|
||||
@@ -416,7 +311,6 @@ export class DbConfigStore extends DisposableObject {
|
||||
}
|
||||
|
||||
if (newConfig) {
|
||||
initializeLocalDbConfig(newConfig);
|
||||
this.configErrors = this.configValidator.validate(newConfig);
|
||||
}
|
||||
|
||||
@@ -451,7 +345,6 @@ export class DbConfigStore extends DisposableObject {
|
||||
}
|
||||
|
||||
if (newConfig) {
|
||||
initializeLocalDbConfig(newConfig);
|
||||
this.configErrors = this.configValidator.validate(newConfig);
|
||||
}
|
||||
|
||||
@@ -499,10 +392,6 @@ export class DbConfigStore extends DisposableObject {
|
||||
owners: [],
|
||||
repositories: [],
|
||||
},
|
||||
local: {
|
||||
lists: [],
|
||||
databases: [],
|
||||
},
|
||||
},
|
||||
selected: {
|
||||
kind: SelectedDbItemKind.VariantAnalysisSystemDefinedList,
|
||||
@@ -511,16 +400,6 @@ export class DbConfigStore extends DisposableObject {
|
||||
};
|
||||
}
|
||||
|
||||
private validateLocalListName(listName: string): void {
|
||||
if (listName === "") {
|
||||
throw Error("List name cannot be empty");
|
||||
}
|
||||
|
||||
if (this.doesLocalListExist(listName)) {
|
||||
throw Error(`A local list with the name '${listName}' already exists`);
|
||||
}
|
||||
}
|
||||
|
||||
private validateRemoteListName(listName: string): void {
|
||||
if (listName === "") {
|
||||
throw Error("List name cannot be empty");
|
||||
@@ -532,14 +411,4 @@ export class DbConfigStore extends DisposableObject {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private validateLocalDbName(dbName: string): void {
|
||||
if (dbName === "") {
|
||||
throw Error("Database name cannot be empty");
|
||||
}
|
||||
|
||||
if (this.doesLocalDbExist(dbName)) {
|
||||
throw Error(`A local database with the name '${dbName}' already exists`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { readJsonSync } from "fs-extra";
|
||||
import { resolve } from "path";
|
||||
import Ajv, { ValidateFunction } from "ajv";
|
||||
import { clearLocalDbConfig, DbConfig } from "./db-config";
|
||||
import { DbConfig } from "./db-config";
|
||||
import { findDuplicateStrings } from "../../common/text-utils";
|
||||
import {
|
||||
DbConfigValidationError,
|
||||
@@ -19,8 +19,6 @@ export class DbConfigValidator {
|
||||
}
|
||||
|
||||
public validate(dbConfig: DbConfig): DbConfigValidationError[] {
|
||||
const localDbs = clearLocalDbConfig(dbConfig);
|
||||
|
||||
this.validateSchemaFn(dbConfig);
|
||||
|
||||
if (this.validateSchemaFn.errors) {
|
||||
@@ -30,13 +28,6 @@ export class DbConfigValidator {
|
||||
}));
|
||||
}
|
||||
|
||||
// Add any local db config back so that we have a config
|
||||
// object that respects its type and validation can happen
|
||||
// as normal.
|
||||
if (localDbs) {
|
||||
dbConfig.databases.local = localDbs;
|
||||
}
|
||||
|
||||
return [
|
||||
...this.validateDbListNames(dbConfig),
|
||||
...this.validateDbNames(dbConfig),
|
||||
@@ -55,14 +46,6 @@ export class DbConfigValidator {
|
||||
)}`,
|
||||
});
|
||||
|
||||
const duplicateLocalDbLists = findDuplicateStrings(
|
||||
dbConfig.databases.local.lists.map((n) => n.name),
|
||||
);
|
||||
|
||||
if (duplicateLocalDbLists.length > 0) {
|
||||
errors.push(buildError(duplicateLocalDbLists));
|
||||
}
|
||||
|
||||
const duplicateRemoteDbLists = findDuplicateStrings(
|
||||
dbConfig.databases.variantAnalysis.repositoryLists.map((n) => n.name),
|
||||
);
|
||||
@@ -81,14 +64,6 @@ export class DbConfigValidator {
|
||||
message: `There are databases with the same name: ${dups.join(", ")}`,
|
||||
});
|
||||
|
||||
const duplicateLocalDbs = findDuplicateStrings(
|
||||
dbConfig.databases.local.databases.map((d) => d.name),
|
||||
);
|
||||
|
||||
if (duplicateLocalDbs.length > 0) {
|
||||
errors.push(buildError(duplicateLocalDbs));
|
||||
}
|
||||
|
||||
const duplicateRemoteDbs = findDuplicateStrings(
|
||||
dbConfig.databases.variantAnalysis.repositories,
|
||||
);
|
||||
@@ -111,13 +86,6 @@ export class DbConfigValidator {
|
||||
)}`,
|
||||
});
|
||||
|
||||
for (const list of dbConfig.databases.local.lists) {
|
||||
const dups = findDuplicateStrings(list.databases.map((d) => d.name));
|
||||
if (dups.length > 0) {
|
||||
errors.push(buildError(list.name, dups));
|
||||
}
|
||||
}
|
||||
|
||||
for (const list of dbConfig.databases.variantAnalysis.repositoryLists) {
|
||||
const dups = findDuplicateStrings(list.repositories);
|
||||
if (dups.length > 0) {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Contains models and consts for the data we want to store in the database config.
|
||||
// Changes to these models should be done carefully and account for backwards compatibility of data.
|
||||
|
||||
import { DatabaseOrigin } from "../local-databases/database-origin";
|
||||
|
||||
export const DB_CONFIG_VERSION = 1;
|
||||
|
||||
export interface DbConfig {
|
||||
@@ -13,37 +11,21 @@ export interface DbConfig {
|
||||
|
||||
interface DbConfigDatabases {
|
||||
variantAnalysis: RemoteDbConfig;
|
||||
local: LocalDbConfig;
|
||||
}
|
||||
|
||||
export type SelectedDbItem =
|
||||
| SelectedLocalUserDefinedList
|
||||
| SelectedLocalDatabase
|
||||
| SelectedRemoteSystemDefinedList
|
||||
| SelectedVariantAnalysisUserDefinedList
|
||||
| SelectedRemoteOwner
|
||||
| SelectedRemoteRepository;
|
||||
|
||||
export enum SelectedDbItemKind {
|
||||
LocalUserDefinedList = "localUserDefinedList",
|
||||
LocalDatabase = "localDatabase",
|
||||
VariantAnalysisSystemDefinedList = "variantAnalysisSystemDefinedList",
|
||||
VariantAnalysisUserDefinedList = "variantAnalysisUserDefinedList",
|
||||
VariantAnalysisOwner = "variantAnalysisOwner",
|
||||
VariantAnalysisRepository = "variantAnalysisRepository",
|
||||
}
|
||||
|
||||
interface SelectedLocalUserDefinedList {
|
||||
kind: SelectedDbItemKind.LocalUserDefinedList;
|
||||
listName: string;
|
||||
}
|
||||
|
||||
interface SelectedLocalDatabase {
|
||||
kind: SelectedDbItemKind.LocalDatabase;
|
||||
databaseName: string;
|
||||
listName?: string;
|
||||
}
|
||||
|
||||
interface SelectedRemoteSystemDefinedList {
|
||||
kind: SelectedDbItemKind.VariantAnalysisSystemDefinedList;
|
||||
listName: string;
|
||||
@@ -76,24 +58,6 @@ export interface RemoteRepositoryList {
|
||||
repositories: string[];
|
||||
}
|
||||
|
||||
interface LocalDbConfig {
|
||||
lists: LocalList[];
|
||||
databases: LocalDatabase[];
|
||||
}
|
||||
|
||||
export interface LocalList {
|
||||
name: string;
|
||||
databases: LocalDatabase[];
|
||||
}
|
||||
|
||||
export interface LocalDatabase {
|
||||
name: string;
|
||||
dateAdded: number;
|
||||
language: string;
|
||||
origin: DatabaseOrigin;
|
||||
storagePath: string;
|
||||
}
|
||||
|
||||
export function cloneDbConfig(config: DbConfig): DbConfig {
|
||||
return {
|
||||
version: config.version,
|
||||
@@ -108,13 +72,6 @@ export function cloneDbConfig(config: DbConfig): DbConfig {
|
||||
owners: [...config.databases.variantAnalysis.owners],
|
||||
repositories: [...config.databases.variantAnalysis.repositories],
|
||||
},
|
||||
local: {
|
||||
lists: config.databases.local.lists.map((list) => ({
|
||||
name: list.name,
|
||||
databases: list.databases.map((db) => ({ ...db })),
|
||||
})),
|
||||
databases: config.databases.local.databases.map((db) => ({ ...db })),
|
||||
},
|
||||
},
|
||||
selected: config.selected
|
||||
? cloneDbConfigSelectedItem(config.selected)
|
||||
@@ -122,28 +79,6 @@ export function cloneDbConfig(config: DbConfig): DbConfig {
|
||||
};
|
||||
}
|
||||
|
||||
export function renameLocalList(
|
||||
originalConfig: DbConfig,
|
||||
currentListName: string,
|
||||
newListName: string,
|
||||
): DbConfig {
|
||||
const config = cloneDbConfig(originalConfig);
|
||||
|
||||
const list = getLocalList(config, currentListName);
|
||||
list.name = newListName;
|
||||
|
||||
if (
|
||||
config.selected?.kind === SelectedDbItemKind.LocalUserDefinedList ||
|
||||
config.selected?.kind === SelectedDbItemKind.LocalDatabase
|
||||
) {
|
||||
if (config.selected.listName === currentListName) {
|
||||
config.selected.listName = newListName;
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
export function renameRemoteList(
|
||||
originalConfig: DbConfig,
|
||||
currentListName: string,
|
||||
@@ -167,67 +102,6 @@ export function renameRemoteList(
|
||||
return config;
|
||||
}
|
||||
|
||||
export function renameLocalDb(
|
||||
originalConfig: DbConfig,
|
||||
currentDbName: string,
|
||||
newDbName: string,
|
||||
parentListName?: string,
|
||||
): DbConfig {
|
||||
const config = cloneDbConfig(originalConfig);
|
||||
|
||||
if (parentListName) {
|
||||
const list = getLocalList(config, parentListName);
|
||||
const dbIndex = list.databases.findIndex((db) => db.name === currentDbName);
|
||||
if (dbIndex === -1) {
|
||||
throw Error(
|
||||
`Cannot find database '${currentDbName}' in list '${parentListName}'`,
|
||||
);
|
||||
}
|
||||
list.databases[dbIndex].name = newDbName;
|
||||
} else {
|
||||
const dbIndex = config.databases.local.databases.findIndex(
|
||||
(db) => db.name === currentDbName,
|
||||
);
|
||||
if (dbIndex === -1) {
|
||||
throw Error(`Cannot find database '${currentDbName}' in local databases`);
|
||||
}
|
||||
config.databases.local.databases[dbIndex].name = newDbName;
|
||||
}
|
||||
|
||||
if (
|
||||
config.selected?.kind === SelectedDbItemKind.LocalDatabase &&
|
||||
config.selected.databaseName === currentDbName
|
||||
) {
|
||||
config.selected.databaseName = newDbName;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
export function removeLocalList(
|
||||
originalConfig: DbConfig,
|
||||
listName: string,
|
||||
): DbConfig {
|
||||
const config = cloneDbConfig(originalConfig);
|
||||
|
||||
config.databases.local.lists = config.databases.local.lists.filter(
|
||||
(list) => list.name !== listName,
|
||||
);
|
||||
|
||||
if (config.selected?.kind === SelectedDbItemKind.LocalUserDefinedList) {
|
||||
config.selected = undefined;
|
||||
}
|
||||
|
||||
if (
|
||||
config.selected?.kind === SelectedDbItemKind.LocalDatabase &&
|
||||
config.selected?.listName === listName
|
||||
) {
|
||||
config.selected = undefined;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
export function removeRemoteList(
|
||||
originalConfig: DbConfig,
|
||||
listName: string,
|
||||
@@ -255,35 +129,6 @@ export function removeRemoteList(
|
||||
return config;
|
||||
}
|
||||
|
||||
export function removeLocalDb(
|
||||
originalConfig: DbConfig,
|
||||
databaseName: string,
|
||||
parentListName?: string,
|
||||
): DbConfig {
|
||||
const config = cloneDbConfig(originalConfig);
|
||||
|
||||
if (parentListName) {
|
||||
const parentList = getLocalList(config, parentListName);
|
||||
parentList.databases = parentList.databases.filter(
|
||||
(db) => db.name !== databaseName,
|
||||
);
|
||||
} else {
|
||||
config.databases.local.databases = config.databases.local.databases.filter(
|
||||
(db) => db.name !== databaseName,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
config.selected?.kind === SelectedDbItemKind.LocalDatabase &&
|
||||
config.selected?.databaseName === databaseName &&
|
||||
config.selected?.listName === parentListName
|
||||
) {
|
||||
config.selected = undefined;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
export function removeRemoteRepo(
|
||||
originalConfig: DbConfig,
|
||||
repoFullName: string,
|
||||
@@ -333,51 +178,8 @@ export function removeRemoteOwner(
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes local db config from a db config object, if one is set.
|
||||
* We do this because we don't want to expose this feature to users
|
||||
* yet (since it's only partially implemented), but we also don't want
|
||||
* to remove all the code we've already implemented.
|
||||
* @param config The config object to change.
|
||||
* @returns Any removed local db config.
|
||||
*/
|
||||
export function clearLocalDbConfig(
|
||||
config: DbConfig,
|
||||
): LocalDbConfig | undefined {
|
||||
let localDbs = undefined;
|
||||
|
||||
if (config && config.databases && config.databases.local) {
|
||||
localDbs = config.databases.local;
|
||||
delete (config.databases as any).local;
|
||||
}
|
||||
|
||||
return localDbs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the local db config, if the config object contains
|
||||
* database configuration.
|
||||
* @param config The config object to change.
|
||||
*/
|
||||
export function initializeLocalDbConfig(config: DbConfig): void {
|
||||
if (config.databases) {
|
||||
config.databases.local = { lists: [], databases: [] };
|
||||
}
|
||||
}
|
||||
|
||||
function cloneDbConfigSelectedItem(selected: SelectedDbItem): SelectedDbItem {
|
||||
switch (selected.kind) {
|
||||
case SelectedDbItemKind.LocalUserDefinedList:
|
||||
return {
|
||||
kind: SelectedDbItemKind.LocalUserDefinedList,
|
||||
listName: selected.listName,
|
||||
};
|
||||
case SelectedDbItemKind.LocalDatabase:
|
||||
return {
|
||||
kind: SelectedDbItemKind.LocalDatabase,
|
||||
databaseName: selected.databaseName,
|
||||
listName: selected.listName,
|
||||
};
|
||||
case SelectedDbItemKind.VariantAnalysisSystemDefinedList:
|
||||
return {
|
||||
kind: SelectedDbItemKind.VariantAnalysisSystemDefinedList,
|
||||
@@ -402,16 +204,6 @@ function cloneDbConfigSelectedItem(selected: SelectedDbItem): SelectedDbItem {
|
||||
}
|
||||
}
|
||||
|
||||
function getLocalList(config: DbConfig, listName: string): LocalList {
|
||||
const list = config.databases.local.lists.find((l) => l.name === listName);
|
||||
|
||||
if (!list) {
|
||||
throw Error(`Cannot find local list '${listName}'`);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
function getRemoteList(
|
||||
config: DbConfig,
|
||||
listName: string,
|
||||
|
||||
@@ -1,27 +1,14 @@
|
||||
import { DbItem, DbItemKind, flattenDbItems } from "./db-item";
|
||||
|
||||
export type ExpandedDbItem =
|
||||
| RootLocalExpandedDbItem
|
||||
| LocalUserDefinedListExpandedDbItem
|
||||
| RootRemoteExpandedDbItem
|
||||
| RemoteUserDefinedListExpandedDbItem;
|
||||
|
||||
export enum ExpandedDbItemKind {
|
||||
RootLocal = "rootLocal",
|
||||
LocalUserDefinedList = "localUserDefinedList",
|
||||
RootRemote = "rootRemote",
|
||||
RemoteUserDefinedList = "remoteUserDefinedList",
|
||||
}
|
||||
|
||||
interface RootLocalExpandedDbItem {
|
||||
kind: ExpandedDbItemKind.RootLocal;
|
||||
}
|
||||
|
||||
interface LocalUserDefinedListExpandedDbItem {
|
||||
kind: ExpandedDbItemKind.LocalUserDefinedList;
|
||||
listName: string;
|
||||
}
|
||||
|
||||
interface RootRemoteExpandedDbItem {
|
||||
kind: ExpandedDbItemKind.RootRemote;
|
||||
}
|
||||
@@ -80,13 +67,6 @@ export function cleanNonExistentExpandedItems(
|
||||
|
||||
function mapDbItemToExpandedDbItem(dbItem: DbItem): ExpandedDbItem {
|
||||
switch (dbItem.kind) {
|
||||
case DbItemKind.RootLocal:
|
||||
return { kind: ExpandedDbItemKind.RootLocal };
|
||||
case DbItemKind.LocalList:
|
||||
return {
|
||||
kind: ExpandedDbItemKind.LocalUserDefinedList,
|
||||
listName: dbItem.listName,
|
||||
};
|
||||
case DbItemKind.RootRemote:
|
||||
return { kind: ExpandedDbItemKind.RootRemote };
|
||||
case DbItemKind.RemoteUserDefinedList:
|
||||
@@ -104,13 +84,6 @@ function isDbItemEqualToExpandedDbItem(
|
||||
expandedDbItem: ExpandedDbItem,
|
||||
) {
|
||||
switch (dbItem.kind) {
|
||||
case DbItemKind.RootLocal:
|
||||
return expandedDbItem.kind === ExpandedDbItemKind.RootLocal;
|
||||
case DbItemKind.LocalList:
|
||||
return (
|
||||
expandedDbItem.kind === ExpandedDbItemKind.LocalUserDefinedList &&
|
||||
expandedDbItem.listName === dbItem.listName
|
||||
);
|
||||
case DbItemKind.RootRemote:
|
||||
return expandedDbItem.kind === ExpandedDbItemKind.RootRemote;
|
||||
case DbItemKind.RemoteUserDefinedList:
|
||||
@@ -118,7 +91,6 @@ function isDbItemEqualToExpandedDbItem(
|
||||
expandedDbItem.kind === ExpandedDbItemKind.RemoteUserDefinedList &&
|
||||
expandedDbItem.listName === dbItem.listName
|
||||
);
|
||||
case DbItemKind.LocalDatabase:
|
||||
case DbItemKind.RemoteSystemDefinedList:
|
||||
case DbItemKind.RemoteOwner:
|
||||
case DbItemKind.RemoteRepo:
|
||||
|
||||
@@ -2,17 +2,13 @@ import { DbItem, DbItemKind } from "./db-item";
|
||||
|
||||
export function getDbItemName(dbItem: DbItem): string | undefined {
|
||||
switch (dbItem.kind) {
|
||||
case DbItemKind.RootLocal:
|
||||
case DbItemKind.RootRemote:
|
||||
return undefined;
|
||||
case DbItemKind.LocalList:
|
||||
case DbItemKind.RemoteUserDefinedList:
|
||||
case DbItemKind.RemoteSystemDefinedList:
|
||||
return dbItem.listName;
|
||||
case DbItemKind.RemoteOwner:
|
||||
return dbItem.ownerName;
|
||||
case DbItemKind.LocalDatabase:
|
||||
return dbItem.databaseName;
|
||||
case DbItemKind.RemoteRepo:
|
||||
return dbItem.repoFullName;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { DbItem, DbItemKind, LocalDbItem, RemoteDbItem } from "./db-item";
|
||||
import { DbItem, DbItemKind, RemoteDbItem } from "./db-item";
|
||||
import { SelectedDbItem, SelectedDbItemKind } from "./config/db-config";
|
||||
|
||||
export function getSelectedDbItem(dbItems: DbItem[]): DbItem | undefined {
|
||||
for (const dbItem of dbItems) {
|
||||
if (
|
||||
dbItem.kind === DbItemKind.RootRemote ||
|
||||
dbItem.kind === DbItemKind.RootLocal
|
||||
) {
|
||||
if (dbItem.kind === DbItemKind.RootRemote) {
|
||||
for (const child of dbItem.children) {
|
||||
const selectedItem = extractSelected(child);
|
||||
if (selectedItem) {
|
||||
@@ -23,20 +20,11 @@ export function getSelectedDbItem(dbItems: DbItem[]): DbItem | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function extractSelected(
|
||||
dbItem: RemoteDbItem | LocalDbItem,
|
||||
): DbItem | undefined {
|
||||
function extractSelected(dbItem: RemoteDbItem): DbItem | undefined {
|
||||
if (dbItem.selected) {
|
||||
return dbItem;
|
||||
}
|
||||
switch (dbItem.kind) {
|
||||
case DbItemKind.LocalList:
|
||||
for (const database of dbItem.databases) {
|
||||
if (database.selected) {
|
||||
return database;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DbItemKind.RemoteUserDefinedList:
|
||||
for (const repo of dbItem.repos) {
|
||||
if (repo.selected) {
|
||||
@@ -52,17 +40,10 @@ export function mapDbItemToSelectedDbItem(
|
||||
dbItem: DbItem,
|
||||
): SelectedDbItem | undefined {
|
||||
switch (dbItem.kind) {
|
||||
case DbItemKind.RootLocal:
|
||||
case DbItemKind.RootRemote:
|
||||
// Root items are not selectable.
|
||||
return undefined;
|
||||
|
||||
case DbItemKind.LocalList:
|
||||
return {
|
||||
kind: SelectedDbItemKind.LocalUserDefinedList,
|
||||
listName: dbItem.listName,
|
||||
};
|
||||
|
||||
case DbItemKind.RemoteUserDefinedList:
|
||||
return {
|
||||
kind: SelectedDbItemKind.VariantAnalysisUserDefinedList,
|
||||
@@ -81,13 +62,6 @@ export function mapDbItemToSelectedDbItem(
|
||||
ownerName: dbItem.ownerName,
|
||||
};
|
||||
|
||||
case DbItemKind.LocalDatabase:
|
||||
return {
|
||||
kind: SelectedDbItemKind.LocalDatabase,
|
||||
databaseName: dbItem.databaseName,
|
||||
listName: dbItem?.parentListName,
|
||||
};
|
||||
|
||||
case DbItemKind.RemoteRepo:
|
||||
return {
|
||||
kind: SelectedDbItemKind.VariantAnalysisRepository,
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
// This file contains models that are used to represent the databases.
|
||||
|
||||
import { DatabaseOrigin } from "./local-databases/database-origin";
|
||||
|
||||
export enum DbItemKind {
|
||||
RootLocal = "RootLocal",
|
||||
LocalList = "LocalList",
|
||||
LocalDatabase = "LocalDatabase",
|
||||
RootRemote = "RootRemote",
|
||||
RemoteSystemDefinedList = "RemoteSystemDefinedList",
|
||||
RemoteUserDefinedList = "RemoteUserDefinedList",
|
||||
@@ -13,49 +8,13 @@ export enum DbItemKind {
|
||||
RemoteRepo = "RemoteRepo",
|
||||
}
|
||||
|
||||
export enum DbListKind {
|
||||
Local = "Local",
|
||||
Remote = "Remote",
|
||||
}
|
||||
|
||||
export interface RootLocalDbItem {
|
||||
kind: DbItemKind.RootLocal;
|
||||
expanded: boolean;
|
||||
children: LocalDbItem[];
|
||||
}
|
||||
|
||||
export type LocalDbItem = LocalListDbItem | LocalDatabaseDbItem;
|
||||
|
||||
export interface LocalListDbItem {
|
||||
kind: DbItemKind.LocalList;
|
||||
expanded: boolean;
|
||||
selected: boolean;
|
||||
listName: string;
|
||||
databases: LocalDatabaseDbItem[];
|
||||
}
|
||||
|
||||
export interface LocalDatabaseDbItem {
|
||||
kind: DbItemKind.LocalDatabase;
|
||||
selected: boolean;
|
||||
databaseName: string;
|
||||
dateAdded: number;
|
||||
language: string;
|
||||
origin: DatabaseOrigin;
|
||||
storagePath: string;
|
||||
parentListName?: string;
|
||||
}
|
||||
|
||||
export interface RootRemoteDbItem {
|
||||
kind: DbItemKind.RootRemote;
|
||||
expanded: boolean;
|
||||
children: RemoteDbItem[];
|
||||
}
|
||||
|
||||
export type DbItem =
|
||||
| RootLocalDbItem
|
||||
| RootRemoteDbItem
|
||||
| RemoteDbItem
|
||||
| LocalDbItem;
|
||||
export type DbItem = RootRemoteDbItem | RemoteDbItem;
|
||||
|
||||
export type RemoteDbItem =
|
||||
| RemoteSystemDefinedListDbItem
|
||||
@@ -108,25 +67,13 @@ export function isRemoteRepoDbItem(dbItem: DbItem): dbItem is RemoteRepoDbItem {
|
||||
return dbItem.kind === DbItemKind.RemoteRepo;
|
||||
}
|
||||
|
||||
export function isLocalListDbItem(dbItem: DbItem): dbItem is LocalListDbItem {
|
||||
return dbItem.kind === DbItemKind.LocalList;
|
||||
}
|
||||
|
||||
export function isLocalDatabaseDbItem(
|
||||
dbItem: DbItem,
|
||||
): dbItem is LocalDatabaseDbItem {
|
||||
return dbItem.kind === DbItemKind.LocalDatabase;
|
||||
}
|
||||
|
||||
type SelectableDbItem = RemoteDbItem | LocalDbItem;
|
||||
type SelectableDbItem = RemoteDbItem;
|
||||
|
||||
export function isSelectableDbItem(dbItem: DbItem): dbItem is SelectableDbItem {
|
||||
return SelectableDbItemKinds.includes(dbItem.kind);
|
||||
}
|
||||
|
||||
const SelectableDbItemKinds = [
|
||||
DbItemKind.LocalList,
|
||||
DbItemKind.LocalDatabase,
|
||||
DbItemKind.RemoteSystemDefinedList,
|
||||
DbItemKind.RemoteUserDefinedList,
|
||||
DbItemKind.RemoteOwner,
|
||||
@@ -139,19 +86,12 @@ export function flattenDbItems(dbItems: DbItem[]): DbItem[] {
|
||||
for (const dbItem of dbItems) {
|
||||
allItems.push(dbItem);
|
||||
switch (dbItem.kind) {
|
||||
case DbItemKind.RootLocal:
|
||||
allItems.push(...flattenDbItems(dbItem.children));
|
||||
break;
|
||||
case DbItemKind.LocalList:
|
||||
allItems.push(...flattenDbItems(dbItem.databases));
|
||||
break;
|
||||
case DbItemKind.RootRemote:
|
||||
allItems.push(...flattenDbItems(dbItem.children));
|
||||
break;
|
||||
case DbItemKind.RemoteUserDefinedList:
|
||||
allItems.push(...dbItem.repos);
|
||||
break;
|
||||
case DbItemKind.LocalDatabase:
|
||||
case DbItemKind.RemoteSystemDefinedList:
|
||||
case DbItemKind.RemoteOwner:
|
||||
case DbItemKind.RemoteRepo:
|
||||
|
||||
@@ -3,14 +3,7 @@ import { AppEvent, AppEventEmitter } from "../common/events";
|
||||
import { ValueResult } from "../common/value-result";
|
||||
import { DisposableObject } from "../common/disposable-object";
|
||||
import { DbConfigStore } from "./config/db-config-store";
|
||||
import {
|
||||
DbItem,
|
||||
DbItemKind,
|
||||
DbListKind,
|
||||
LocalDatabaseDbItem,
|
||||
LocalListDbItem,
|
||||
RemoteUserDefinedListDbItem,
|
||||
} from "./db-item";
|
||||
import { DbItem, RemoteUserDefinedListDbItem } from "./db-item";
|
||||
import {
|
||||
updateExpandedItem,
|
||||
replaceExpandedItem,
|
||||
@@ -116,31 +109,15 @@ export class DbManager extends DisposableObject {
|
||||
await this.dbConfigStore.addRemoteOwner(owner);
|
||||
}
|
||||
|
||||
public async addNewList(
|
||||
listKind: DbListKind,
|
||||
listName: string,
|
||||
): Promise<void> {
|
||||
switch (listKind) {
|
||||
case DbListKind.Local:
|
||||
await this.dbConfigStore.addLocalList(listName);
|
||||
break;
|
||||
case DbListKind.Remote:
|
||||
await this.dbConfigStore.addRemoteList(listName);
|
||||
break;
|
||||
default:
|
||||
throw Error(`Unknown list kind '${listKind}'`);
|
||||
}
|
||||
public async addNewList(listName: string): Promise<void> {
|
||||
await this.dbConfigStore.addRemoteList(listName);
|
||||
}
|
||||
|
||||
public async renameList(
|
||||
currentDbItem: LocalListDbItem | RemoteUserDefinedListDbItem,
|
||||
currentDbItem: RemoteUserDefinedListDbItem,
|
||||
newName: string,
|
||||
): Promise<void> {
|
||||
if (currentDbItem.kind === DbItemKind.LocalList) {
|
||||
await this.dbConfigStore.renameLocalList(currentDbItem, newName);
|
||||
} else if (currentDbItem.kind === DbItemKind.RemoteUserDefinedList) {
|
||||
await this.dbConfigStore.renameRemoteList(currentDbItem, newName);
|
||||
}
|
||||
await this.dbConfigStore.renameRemoteList(currentDbItem, newName);
|
||||
|
||||
const newDbItem = { ...currentDbItem, listName: newName };
|
||||
const newExpandedItems = replaceExpandedItem(
|
||||
@@ -152,26 +129,8 @@ export class DbManager extends DisposableObject {
|
||||
await this.setExpandedItems(newExpandedItems);
|
||||
}
|
||||
|
||||
public async renameLocalDb(
|
||||
currentDbItem: LocalDatabaseDbItem,
|
||||
newName: string,
|
||||
): Promise<void> {
|
||||
await this.dbConfigStore.renameLocalDb(
|
||||
currentDbItem,
|
||||
newName,
|
||||
currentDbItem.parentListName,
|
||||
);
|
||||
}
|
||||
|
||||
public doesListExist(listKind: DbListKind, listName: string): boolean {
|
||||
switch (listKind) {
|
||||
case DbListKind.Local:
|
||||
return this.dbConfigStore.doesLocalListExist(listName);
|
||||
case DbListKind.Remote:
|
||||
return this.dbConfigStore.doesRemoteListExist(listName);
|
||||
default:
|
||||
throw Error(`Unknown list kind '${listKind}'`);
|
||||
}
|
||||
public doesListExist(listName: string): boolean {
|
||||
return this.dbConfigStore.doesRemoteListExist(listName);
|
||||
}
|
||||
|
||||
public doesRemoteOwnerExist(owner: string): boolean {
|
||||
@@ -182,10 +141,6 @@ export class DbManager extends DisposableObject {
|
||||
return this.dbConfigStore.doesRemoteDbExist(nwo, listName);
|
||||
}
|
||||
|
||||
public doesLocalDbExist(dbName: string, listName?: string): boolean {
|
||||
return this.dbConfigStore.doesLocalDbExist(dbName, listName);
|
||||
}
|
||||
|
||||
private getExpandedItems(): ExpandedDbItem[] {
|
||||
const items = this.app.workspaceState.get<ExpandedDbItem[]>(
|
||||
DbManager.DB_EXPANDED_STATE_KEY,
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
import {
|
||||
DbConfig,
|
||||
LocalDatabase,
|
||||
LocalList,
|
||||
RemoteRepositoryList,
|
||||
SelectedDbItemKind,
|
||||
} from "./config/db-config";
|
||||
import {
|
||||
DbItemKind,
|
||||
LocalDatabaseDbItem,
|
||||
LocalListDbItem,
|
||||
RemoteOwnerDbItem,
|
||||
RemoteRepoDbItem,
|
||||
RemoteSystemDefinedListDbItem,
|
||||
RemoteUserDefinedListDbItem,
|
||||
RootLocalDbItem,
|
||||
RootRemoteDbItem,
|
||||
} from "./db-item";
|
||||
import { ExpandedDbItem, ExpandedDbItemKind } from "./db-item-expansion";
|
||||
@@ -55,28 +50,6 @@ export function createRemoteTree(
|
||||
};
|
||||
}
|
||||
|
||||
export function createLocalTree(
|
||||
dbConfig: DbConfig,
|
||||
expandedItems: ExpandedDbItem[],
|
||||
): RootLocalDbItem {
|
||||
const localLists = dbConfig.databases.local.lists.map((l) =>
|
||||
createLocalList(l, dbConfig, expandedItems),
|
||||
);
|
||||
const localDbs = dbConfig.databases.local.databases.map((l) =>
|
||||
createLocalDb(l, dbConfig),
|
||||
);
|
||||
|
||||
const expanded = expandedItems.some(
|
||||
(e) => e.kind === ExpandedDbItemKind.RootLocal,
|
||||
);
|
||||
|
||||
return {
|
||||
kind: DbItemKind.RootLocal,
|
||||
children: [...localLists, ...localDbs],
|
||||
expanded: !!expanded,
|
||||
};
|
||||
}
|
||||
|
||||
function createSystemDefinedList(
|
||||
n: number,
|
||||
dbConfig: DbConfig,
|
||||
@@ -155,51 +128,3 @@ function createRepoItem(
|
||||
parentListName: listName,
|
||||
};
|
||||
}
|
||||
|
||||
function createLocalList(
|
||||
list: LocalList,
|
||||
dbConfig: DbConfig,
|
||||
expandedItems: ExpandedDbItem[],
|
||||
): LocalListDbItem {
|
||||
const selected =
|
||||
dbConfig.selected &&
|
||||
dbConfig.selected.kind === SelectedDbItemKind.LocalUserDefinedList &&
|
||||
dbConfig.selected.listName === list.name;
|
||||
|
||||
const expanded = expandedItems.some(
|
||||
(e) =>
|
||||
e.kind === ExpandedDbItemKind.LocalUserDefinedList &&
|
||||
e.listName === list.name,
|
||||
);
|
||||
|
||||
return {
|
||||
kind: DbItemKind.LocalList,
|
||||
listName: list.name,
|
||||
databases: list.databases.map((d) => createLocalDb(d, dbConfig, list.name)),
|
||||
selected: !!selected,
|
||||
expanded: !!expanded,
|
||||
};
|
||||
}
|
||||
|
||||
function createLocalDb(
|
||||
db: LocalDatabase,
|
||||
dbConfig: DbConfig,
|
||||
listName?: string,
|
||||
): LocalDatabaseDbItem {
|
||||
const selected =
|
||||
dbConfig.selected &&
|
||||
dbConfig.selected.kind === SelectedDbItemKind.LocalDatabase &&
|
||||
dbConfig.selected.databaseName === db.name &&
|
||||
dbConfig.selected.listName === listName;
|
||||
|
||||
return {
|
||||
kind: DbItemKind.LocalDatabase,
|
||||
databaseName: db.name,
|
||||
dateAdded: db.dateAdded,
|
||||
language: db.language,
|
||||
origin: db.origin,
|
||||
storagePath: db.storagePath,
|
||||
selected: !!selected,
|
||||
parentListName: listName,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,20 +9,15 @@ import {
|
||||
window as Window,
|
||||
workspace,
|
||||
} from "vscode";
|
||||
import {
|
||||
LineColumnLocation,
|
||||
ResolvableLocationValue,
|
||||
UrlValue,
|
||||
WholeFileLocation,
|
||||
} from "../../common/bqrs-cli-types";
|
||||
import {
|
||||
isLineColumnLoc,
|
||||
tryGetResolvableLocation,
|
||||
} from "../../common/bqrs-utils";
|
||||
import { getErrorMessage } from "../../common/helpers-pure";
|
||||
import { assertNever, getErrorMessage } from "../../common/helpers-pure";
|
||||
import { Logger } from "../../common/logging";
|
||||
import { DatabaseItem } from "./database-item";
|
||||
import { DatabaseManager } from "./database-manager";
|
||||
import {
|
||||
UrlValueLineColumnLocation,
|
||||
UrlValueResolvable,
|
||||
UrlValueWholeFileLocation,
|
||||
} from "../../common/raw-result-types";
|
||||
|
||||
const findMatchBackground = new ThemeColor("editor.findMatchBackground");
|
||||
const findRangeHighlightBackground = new ThemeColor(
|
||||
@@ -45,7 +40,7 @@ export const shownLocationLineDecoration =
|
||||
* @param databaseItem Database in which to resolve the file location.
|
||||
*/
|
||||
function resolveFivePartLocation(
|
||||
loc: LineColumnLocation,
|
||||
loc: UrlValueLineColumnLocation,
|
||||
databaseItem: DatabaseItem,
|
||||
): Location {
|
||||
// `Range` is a half-open interval, and is zero-based. CodeQL locations are closed intervals, and
|
||||
@@ -66,7 +61,7 @@ function resolveFivePartLocation(
|
||||
* @param databaseItem Database in which to resolve the filesystem resource location.
|
||||
*/
|
||||
function resolveWholeFileLocation(
|
||||
loc: WholeFileLocation,
|
||||
loc: UrlValueWholeFileLocation,
|
||||
databaseItem: DatabaseItem,
|
||||
): Location {
|
||||
// A location corresponding to the start of the file.
|
||||
@@ -81,21 +76,25 @@ function resolveWholeFileLocation(
|
||||
* @param databaseItem Database in which to resolve the file location.
|
||||
*/
|
||||
export function tryResolveLocation(
|
||||
loc: UrlValue | undefined,
|
||||
loc: UrlValueResolvable | undefined,
|
||||
databaseItem: DatabaseItem,
|
||||
): Location | undefined {
|
||||
const resolvableLoc = tryGetResolvableLocation(loc);
|
||||
if (!resolvableLoc || typeof resolvableLoc === "string") {
|
||||
if (!loc) {
|
||||
return;
|
||||
} else if (isLineColumnLoc(resolvableLoc)) {
|
||||
return resolveFivePartLocation(resolvableLoc, databaseItem);
|
||||
} else {
|
||||
return resolveWholeFileLocation(resolvableLoc, databaseItem);
|
||||
}
|
||||
|
||||
switch (loc.type) {
|
||||
case "wholeFileLocation":
|
||||
return resolveWholeFileLocation(loc, databaseItem);
|
||||
case "lineColumnLocation":
|
||||
return resolveFivePartLocation(loc, databaseItem);
|
||||
default:
|
||||
assertNever(loc);
|
||||
}
|
||||
}
|
||||
|
||||
export async function showResolvableLocation(
|
||||
loc: ResolvableLocationValue,
|
||||
loc: UrlValueResolvable,
|
||||
databaseItem: DatabaseItem,
|
||||
logger: Logger,
|
||||
): Promise<void> {
|
||||
@@ -153,7 +152,7 @@ export async function showLocation(location?: Location) {
|
||||
|
||||
export async function jumpToLocation(
|
||||
databaseUri: string,
|
||||
loc: ResolvableLocationValue,
|
||||
loc: UrlValueResolvable,
|
||||
databaseManager: DatabaseManager,
|
||||
logger: Logger,
|
||||
) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { DbItem, DbItemKind } from "../db-item";
|
||||
import {
|
||||
createDbTreeViewItemLocalDatabase,
|
||||
createDbTreeViewItemOwner,
|
||||
createDbTreeViewItemRepo,
|
||||
createDbTreeViewItemRoot,
|
||||
@@ -11,14 +10,6 @@ import {
|
||||
|
||||
export function mapDbItemToTreeViewItem(dbItem: DbItem): DbTreeViewItem {
|
||||
switch (dbItem.kind) {
|
||||
case DbItemKind.RootLocal:
|
||||
return createDbTreeViewItemRoot(
|
||||
dbItem,
|
||||
"local",
|
||||
"Local databases",
|
||||
dbItem.children.map((c) => mapDbItemToTreeViewItem(c)),
|
||||
);
|
||||
|
||||
case DbItemKind.RootRemote:
|
||||
return createDbTreeViewItemRoot(
|
||||
dbItem,
|
||||
@@ -46,19 +37,5 @@ export function mapDbItemToTreeViewItem(dbItem: DbItem): DbTreeViewItem {
|
||||
|
||||
case DbItemKind.RemoteRepo:
|
||||
return createDbTreeViewItemRepo(dbItem, dbItem.repoFullName);
|
||||
|
||||
case DbItemKind.LocalList:
|
||||
return createDbTreeViewItemUserDefinedList(
|
||||
dbItem,
|
||||
dbItem.listName,
|
||||
dbItem.databases.map(mapDbItemToTreeViewItem),
|
||||
);
|
||||
|
||||
case DbItemKind.LocalDatabase:
|
||||
return createDbTreeViewItemLocalDatabase(
|
||||
dbItem,
|
||||
dbItem.databaseName,
|
||||
dbItem.language,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,14 +17,7 @@ import {
|
||||
isValidGitHubOwner,
|
||||
} from "../../common/github-url-identifier-helper";
|
||||
import { DisposableObject } from "../../common/disposable-object";
|
||||
import {
|
||||
DbItem,
|
||||
DbItemKind,
|
||||
DbListKind,
|
||||
LocalDatabaseDbItem,
|
||||
LocalListDbItem,
|
||||
RemoteUserDefinedListDbItem,
|
||||
} from "../db-item";
|
||||
import { DbItem, DbItemKind, RemoteUserDefinedListDbItem } from "../db-item";
|
||||
import { getDbItemName } from "../db-item-naming";
|
||||
import { DbManager } from "../db-manager";
|
||||
import { DbTreeDataProvider } from "./db-tree-data-provider";
|
||||
@@ -42,10 +35,6 @@ export interface RemoteDatabaseQuickPickItem extends QuickPickItem {
|
||||
remoteDatabaseKind: string;
|
||||
}
|
||||
|
||||
export interface AddListQuickPickItem extends QuickPickItem {
|
||||
databaseKind: DbListKind;
|
||||
}
|
||||
|
||||
interface CodeSearchQuickPickItem extends QuickPickItem {
|
||||
language: string;
|
||||
}
|
||||
@@ -223,8 +212,6 @@ export class DbPanel extends DisposableObject {
|
||||
}
|
||||
|
||||
private async addNewList(): Promise<void> {
|
||||
const listKind = DbListKind.Remote;
|
||||
|
||||
const listName = await window.showInputBox({
|
||||
prompt: "Enter a name for the new list",
|
||||
placeHolder: "example-list",
|
||||
@@ -233,7 +220,7 @@ export class DbPanel extends DisposableObject {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.dbManager.doesListExist(listKind, listName)) {
|
||||
if (this.dbManager.doesListExist(listName)) {
|
||||
void showAndLogErrorMessage(
|
||||
this.app.logger,
|
||||
`The list '${listName}' already exists`,
|
||||
@@ -241,7 +228,7 @@ export class DbPanel extends DisposableObject {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.dbManager.addNewList(listKind, listName);
|
||||
await this.dbManager.addNewList(listName);
|
||||
}
|
||||
|
||||
private async setSelectedItem(treeViewItem: DbTreeViewItem): Promise<void> {
|
||||
@@ -277,59 +264,13 @@ export class DbPanel extends DisposableObject {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (dbItem.kind) {
|
||||
case DbItemKind.LocalList:
|
||||
await this.renameLocalListItem(dbItem, newName);
|
||||
break;
|
||||
case DbItemKind.LocalDatabase:
|
||||
await this.renameLocalDatabaseItem(dbItem, newName);
|
||||
break;
|
||||
case DbItemKind.RemoteUserDefinedList:
|
||||
await this.renameVariantAnalysisUserDefinedListItem(dbItem, newName);
|
||||
break;
|
||||
default:
|
||||
throw Error(`Action not allowed for the '${dbItem.kind}' db item kind`);
|
||||
if (dbItem.kind === DbItemKind.RemoteUserDefinedList) {
|
||||
await this.renameVariantAnalysisUserDefinedListItem(dbItem, newName);
|
||||
} else {
|
||||
throw Error(`Action not allowed for the '${dbItem.kind}' db item kind`);
|
||||
}
|
||||
}
|
||||
|
||||
private async renameLocalListItem(
|
||||
dbItem: LocalListDbItem,
|
||||
newName: string,
|
||||
): Promise<void> {
|
||||
if (dbItem.listName === newName) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.dbManager.doesListExist(DbListKind.Local, newName)) {
|
||||
void showAndLogErrorMessage(
|
||||
this.app.logger,
|
||||
`The list '${newName}' already exists`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.dbManager.renameList(dbItem, newName);
|
||||
}
|
||||
|
||||
private async renameLocalDatabaseItem(
|
||||
dbItem: LocalDatabaseDbItem,
|
||||
newName: string,
|
||||
): Promise<void> {
|
||||
if (dbItem.databaseName === newName) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.dbManager.doesLocalDbExist(newName, dbItem.parentListName)) {
|
||||
void showAndLogErrorMessage(
|
||||
this.app.logger,
|
||||
`The database '${newName}' already exists`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.dbManager.renameLocalDb(dbItem, newName);
|
||||
}
|
||||
|
||||
private async renameVariantAnalysisUserDefinedListItem(
|
||||
dbItem: RemoteUserDefinedListDbItem,
|
||||
newName: string,
|
||||
@@ -338,7 +279,7 @@ export class DbPanel extends DisposableObject {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.dbManager.doesListExist(DbListKind.Remote, newName)) {
|
||||
if (this.dbManager.doesListExist(newName)) {
|
||||
void showAndLogErrorMessage(
|
||||
this.app.logger,
|
||||
`The list '${newName}' already exists`,
|
||||
|
||||
@@ -29,18 +29,12 @@ export function getDbItemActions(dbItem: DbItem): DbTreeViewItemAction[] {
|
||||
}
|
||||
|
||||
const dbItemKindsThatCanBeRemoved = [
|
||||
DbItemKind.LocalList,
|
||||
DbItemKind.RemoteUserDefinedList,
|
||||
DbItemKind.LocalDatabase,
|
||||
DbItemKind.RemoteRepo,
|
||||
DbItemKind.RemoteOwner,
|
||||
];
|
||||
|
||||
const dbItemKindsThatCanBeRenamed = [
|
||||
DbItemKind.LocalList,
|
||||
DbItemKind.RemoteUserDefinedList,
|
||||
DbItemKind.LocalDatabase,
|
||||
];
|
||||
const dbItemKindsThatCanBeRenamed = [DbItemKind.RemoteUserDefinedList];
|
||||
|
||||
const dbItemKindsThatCanBeOpenedOnGitHub = [
|
||||
DbItemKind.RemoteOwner,
|
||||
|
||||
@@ -2,13 +2,10 @@ import * as vscode from "vscode";
|
||||
import {
|
||||
DbItem,
|
||||
isSelectableDbItem,
|
||||
LocalDatabaseDbItem,
|
||||
LocalListDbItem,
|
||||
RemoteOwnerDbItem,
|
||||
RemoteRepoDbItem,
|
||||
RemoteSystemDefinedListDbItem,
|
||||
RemoteUserDefinedListDbItem,
|
||||
RootLocalDbItem,
|
||||
RootRemoteDbItem,
|
||||
} from "../db-item";
|
||||
import { getDbItemActions } from "./db-tree-view-item-action";
|
||||
@@ -74,7 +71,7 @@ export function createDbTreeViewItemError(
|
||||
}
|
||||
|
||||
export function createDbTreeViewItemRoot(
|
||||
dbItem: RootLocalDbItem | RootRemoteDbItem,
|
||||
dbItem: RootRemoteDbItem,
|
||||
label: string,
|
||||
tooltip: string,
|
||||
children: DbTreeViewItem[],
|
||||
@@ -105,7 +102,7 @@ export function createDbTreeViewItemSystemDefinedList(
|
||||
}
|
||||
|
||||
export function createDbTreeViewItemUserDefinedList(
|
||||
dbItem: LocalListDbItem | RemoteUserDefinedListDbItem,
|
||||
dbItem: RemoteUserDefinedListDbItem,
|
||||
listName: string,
|
||||
children: DbTreeViewItem[],
|
||||
): DbTreeViewItem {
|
||||
@@ -147,21 +144,6 @@ export function createDbTreeViewItemRepo(
|
||||
);
|
||||
}
|
||||
|
||||
export function createDbTreeViewItemLocalDatabase(
|
||||
dbItem: LocalDatabaseDbItem,
|
||||
databaseName: string,
|
||||
language: string,
|
||||
): DbTreeViewItem {
|
||||
return new DbTreeViewItem(
|
||||
dbItem,
|
||||
new vscode.ThemeIcon("database"),
|
||||
databaseName,
|
||||
`Language: ${language}`,
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
function getCollapsibleState(
|
||||
expanded: boolean,
|
||||
): vscode.TreeItemCollapsibleState {
|
||||
|
||||
@@ -2,13 +2,14 @@ import { CodeQLCliServer } from "../../codeql-cli/cli";
|
||||
import {
|
||||
DecodedBqrsChunk,
|
||||
BqrsId,
|
||||
EntityValue,
|
||||
BqrsEntityValue,
|
||||
} from "../../common/bqrs-cli-types";
|
||||
import { DatabaseItem } from "../../databases/local-databases";
|
||||
import { ChildAstItem, AstItem } from "./ast-viewer";
|
||||
import { Uri } from "vscode";
|
||||
import { QueryOutputDir } from "../../run-queries-shared";
|
||||
import { fileRangeFromURI } from "../contextual/file-range-from-uri";
|
||||
import { mapUrlValue } from "../../common/bqrs-raw-results-mapper";
|
||||
|
||||
/**
|
||||
* A class that wraps a tree of QL results from a query that
|
||||
@@ -55,8 +56,8 @@ export class AstBuilder {
|
||||
// Build up the parent-child relationships
|
||||
edgeTuples.tuples.forEach((tuple) => {
|
||||
const [source, target, tupleType, value] = tuple as [
|
||||
EntityValue,
|
||||
EntityValue,
|
||||
BqrsEntityValue,
|
||||
BqrsEntityValue,
|
||||
string,
|
||||
string,
|
||||
];
|
||||
@@ -90,7 +91,11 @@ export class AstBuilder {
|
||||
|
||||
// populate parents and children
|
||||
nodeTuples.tuples.forEach((tuple) => {
|
||||
const [entity, tupleType, value] = tuple as [EntityValue, string, string];
|
||||
const [entity, tupleType, value] = tuple as [
|
||||
BqrsEntityValue,
|
||||
string,
|
||||
string,
|
||||
];
|
||||
const id = entity.id!;
|
||||
|
||||
switch (tupleType) {
|
||||
@@ -106,7 +111,7 @@ export class AstBuilder {
|
||||
const item = {
|
||||
id,
|
||||
label,
|
||||
location: entity.url,
|
||||
location: entity.url ? mapUrlValue(entity.url) : undefined,
|
||||
fileLocation: fileRangeFromURI(entity.url, this.db),
|
||||
children: [] as ChildAstItem[],
|
||||
order: Number.MAX_SAFE_INTEGER,
|
||||
|
||||
@@ -16,20 +16,20 @@ import {
|
||||
import { basename } from "path";
|
||||
|
||||
import { DatabaseItem } from "../../databases/local-databases";
|
||||
import { UrlValue, BqrsId } from "../../common/bqrs-cli-types";
|
||||
import { BqrsId } from "../../common/bqrs-cli-types";
|
||||
import { showLocation } from "../../databases/local-databases/locations";
|
||||
import {
|
||||
isStringLoc,
|
||||
isWholeFileLoc,
|
||||
isLineColumnLoc,
|
||||
} from "../../common/bqrs-utils";
|
||||
import { DisposableObject } from "../../common/disposable-object";
|
||||
import { asError, getErrorMessage } from "../../common/helpers-pure";
|
||||
import {
|
||||
asError,
|
||||
assertNever,
|
||||
getErrorMessage,
|
||||
} from "../../common/helpers-pure";
|
||||
import { redactableError } from "../../common/errors";
|
||||
import { AstViewerCommands } from "../../common/commands";
|
||||
import { extLogger } from "../../common/logging/vscode";
|
||||
import { showAndLogExceptionWithTelemetry } from "../../common/logging";
|
||||
import { telemetryListener } from "../../common/vscode/telemetry";
|
||||
import { UrlValue } from "../../common/raw-result-types";
|
||||
|
||||
export interface AstItem {
|
||||
id: BqrsId;
|
||||
@@ -90,15 +90,18 @@ class AstViewerDataProvider
|
||||
|
||||
private extractLineInfo(loc?: UrlValue) {
|
||||
if (!loc) {
|
||||
return "";
|
||||
} else if (isStringLoc(loc)) {
|
||||
return loc;
|
||||
} else if (isWholeFileLoc(loc)) {
|
||||
return loc.uri;
|
||||
} else if (isLineColumnLoc(loc)) {
|
||||
return loc.startLine;
|
||||
} else {
|
||||
return "";
|
||||
return;
|
||||
}
|
||||
|
||||
switch (loc.type) {
|
||||
case "string":
|
||||
return loc.value;
|
||||
case "wholeFileLocation":
|
||||
return loc.uri;
|
||||
case "lineColumnLocation":
|
||||
return loc.startLine;
|
||||
default:
|
||||
assertNever(loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import * as vscode from "vscode";
|
||||
|
||||
import { UrlValue, LineColumnLocation } from "../../common/bqrs-cli-types";
|
||||
import {
|
||||
BqrsUrlValue,
|
||||
BqrsLineColumnLocation,
|
||||
} from "../../common/bqrs-cli-types";
|
||||
import { isEmptyPath } from "../../common/bqrs-utils";
|
||||
import { DatabaseItem } from "../../databases/local-databases";
|
||||
|
||||
export function fileRangeFromURI(
|
||||
uri: UrlValue | undefined,
|
||||
uri: BqrsUrlValue | undefined,
|
||||
db: DatabaseItem,
|
||||
): vscode.Location | undefined {
|
||||
if (!uri || typeof uri === "string") {
|
||||
@@ -13,7 +16,7 @@ export function fileRangeFromURI(
|
||||
} else if ("startOffset" in uri) {
|
||||
return undefined;
|
||||
} else {
|
||||
const loc = uri as LineColumnLocation;
|
||||
const loc = uri as BqrsLineColumnLocation;
|
||||
if (isEmptyPath(loc.uri)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,9 @@ import {
|
||||
encodeArchiveBasePath,
|
||||
} from "../../common/vscode/archive-filesystem-provider";
|
||||
import {
|
||||
ColumnKindCode,
|
||||
EntityValue,
|
||||
getResultSetSchema,
|
||||
ResultSetSchema,
|
||||
BqrsColumnKindCode,
|
||||
BqrsEntityValue,
|
||||
BqrsResultSetSchema,
|
||||
} from "../../common/bqrs-cli-types";
|
||||
import { CodeQLCliServer } from "../../codeql-cli/cli";
|
||||
import { DatabaseItem, DatabaseManager } from "../../databases/local-databases";
|
||||
@@ -99,12 +98,14 @@ async function getLinksFromResults(
|
||||
const localLinks: FullLocationLink[] = [];
|
||||
const bqrsPath = outputDir.bqrsPath;
|
||||
const info = await cli.bqrsInfo(bqrsPath);
|
||||
const selectInfo = getResultSetSchema(SELECT_QUERY_NAME, info);
|
||||
const selectInfo = info["result-sets"].find(
|
||||
(schema) => schema.name === SELECT_QUERY_NAME,
|
||||
);
|
||||
if (isValidSelect(selectInfo)) {
|
||||
// TODO: Page this
|
||||
const allTuples = await cli.bqrsDecode(bqrsPath, SELECT_QUERY_NAME);
|
||||
for (const tuple of allTuples.tuples) {
|
||||
const [src, dest] = tuple as [EntityValue, EntityValue];
|
||||
const [src, dest] = tuple as [BqrsEntityValue, BqrsEntityValue];
|
||||
const srcFile = src.url && fileRangeFromURI(src.url, db);
|
||||
const destFile = dest.url && fileRangeFromURI(dest.url, db);
|
||||
if (
|
||||
@@ -130,12 +131,12 @@ function createTemplates(path: string): Record<string, string> {
|
||||
};
|
||||
}
|
||||
|
||||
function isValidSelect(selectInfo: ResultSetSchema | undefined) {
|
||||
function isValidSelect(selectInfo: BqrsResultSetSchema | undefined) {
|
||||
return (
|
||||
selectInfo &&
|
||||
selectInfo.columns.length === 3 &&
|
||||
selectInfo.columns[0].kind === ColumnKindCode.ENTITY &&
|
||||
selectInfo.columns[1].kind === ColumnKindCode.ENTITY &&
|
||||
selectInfo.columns[2].kind === ColumnKindCode.STRING
|
||||
selectInfo.columns[0].kind === BqrsColumnKindCode.ENTITY &&
|
||||
selectInfo.columns[1].kind === BqrsColumnKindCode.ENTITY &&
|
||||
selectInfo.columns[2].kind === BqrsColumnKindCode.STRING
|
||||
);
|
||||
}
|
||||
|
||||
@@ -60,11 +60,7 @@ import {
|
||||
shownLocationLineDecoration,
|
||||
jumpToLocation,
|
||||
} from "../databases/local-databases/locations";
|
||||
import {
|
||||
RawResultSet,
|
||||
transformBqrsResultSet,
|
||||
ResultSetSchema,
|
||||
} from "../common/bqrs-cli-types";
|
||||
import { bqrsToResultSet } from "../common/bqrs-raw-results-mapper";
|
||||
import {
|
||||
AbstractWebview,
|
||||
WebviewPanelConfig,
|
||||
@@ -76,6 +72,8 @@ import { redactableError } from "../common/errors";
|
||||
import { ResultsViewCommands } from "../common/commands";
|
||||
import { App } from "../common/app";
|
||||
import { Disposable } from "../common/disposable-object";
|
||||
import { RawResultSet } from "../common/raw-result-types";
|
||||
import { BqrsResultSetSchema } from "../common/bqrs-cli-types";
|
||||
|
||||
/**
|
||||
* results-view.ts
|
||||
@@ -136,7 +134,7 @@ function numPagesOfResultSet(
|
||||
const n =
|
||||
interpretation?.data.t === "GraphInterpretationData"
|
||||
? interpretation.data.dot.length
|
||||
: resultSet.schema.rows;
|
||||
: resultSet.totalRowCount;
|
||||
|
||||
return Math.ceil(n / pageSize);
|
||||
}
|
||||
@@ -524,16 +522,16 @@ export class ResultsView extends AbstractWebview<
|
||||
offset: schema.pagination?.offsets[0],
|
||||
pageSize,
|
||||
});
|
||||
const resultSet = transformBqrsResultSet(schema, chunk);
|
||||
const resultSet = bqrsToResultSet(schema, chunk);
|
||||
fullQuery.completedQuery.setResultCount(
|
||||
interpretationPage?.numTotalResults || resultSet.schema.rows,
|
||||
interpretationPage?.numTotalResults || resultSet.totalRowCount,
|
||||
);
|
||||
const parsedResultSets: ParsedResultSets = {
|
||||
pageNumber: 0,
|
||||
pageSize,
|
||||
numPages: numPagesOfResultSet(resultSet, this._interpretation),
|
||||
numInterpretedPages: numInterpretedPages(this._interpretation),
|
||||
resultSet: { ...resultSet, t: "RawResultSet" },
|
||||
resultSet: { t: "RawResultSet", resultSet },
|
||||
selectedTable: undefined,
|
||||
resultSetNames,
|
||||
};
|
||||
@@ -601,7 +599,7 @@ export class ResultsView extends AbstractWebview<
|
||||
private async getResultSetSchemas(
|
||||
completedQuery: CompletedQueryInfo,
|
||||
selectedTable = "",
|
||||
): Promise<ResultSetSchema[]> {
|
||||
): Promise<BqrsResultSetSchema[]> {
|
||||
const resultsPath = completedQuery.getResultsPath(selectedTable);
|
||||
const schemas = await this.cliServer.bqrsInfo(
|
||||
resultsPath,
|
||||
@@ -668,12 +666,12 @@ export class ResultsView extends AbstractWebview<
|
||||
pageSize,
|
||||
},
|
||||
);
|
||||
const resultSet = transformBqrsResultSet(schema, chunk);
|
||||
const resultSet = bqrsToResultSet(schema, chunk);
|
||||
|
||||
const parsedResultSets: ParsedResultSets = {
|
||||
pageNumber,
|
||||
pageSize,
|
||||
resultSet: { t: "RawResultSet", ...resultSet },
|
||||
resultSet: { t: "RawResultSet", resultSet },
|
||||
numPages: numPagesOfResultSet(resultSet),
|
||||
numInterpretedPages: numInterpretedPages(this._interpretation),
|
||||
selectedTable,
|
||||
|
||||
@@ -37,45 +37,49 @@ function makeKey(
|
||||
return `${queryCausingWork}:${predicate}${suffix ? ` ${suffix}` : ""}`;
|
||||
}
|
||||
|
||||
const DEPENDENT_PREDICATES_REGEXP = (() => {
|
||||
function getDependentPredicates(operations: string[]): I.List<string> {
|
||||
const id = String.raw`[0-9a-zA-Z:#_\./]+`;
|
||||
const idWithAngleBrackets = String.raw`[0-9a-zA-Z:#_<>\./]+`;
|
||||
const quotedId = String.raw`\`[^\`\r\n]*\``;
|
||||
const regexps = [
|
||||
// SCAN id
|
||||
String.raw`SCAN\s+([0-9a-zA-Z:#_]+|\`[^\`\r\n]*\`)\s`,
|
||||
String.raw`SCAN\s+(${id}|${quotedId})\s`,
|
||||
// JOIN id WITH id
|
||||
String.raw`JOIN\s+([0-9a-zA-Z:#_]+|\`[^\`\r\n]*\`)\s+WITH\s+([0-9a-zA-Z:#_]+|\`[^\`\r\n]*\`)\s`,
|
||||
String.raw`JOIN\s+(${id}|${quotedId})\s+WITH\s+(${id}|${quotedId})\s`,
|
||||
// JOIN WITH id
|
||||
String.raw`JOIN\s+WITH\s+(${id}|${quotedId})\s`,
|
||||
// AGGREGATE id, id
|
||||
String.raw`AGGREGATE\s+([0-9a-zA-Z:#_]+|\`[^\`\r\n]*\`)\s*,\s+([0-9a-zA-Z:#_]+|\`[^\`\r\n]*\`)`,
|
||||
String.raw`AGGREGATE\s+(${id}|${quotedId})\s*,\s+(${id}|${quotedId})`,
|
||||
// id AND NOT id
|
||||
String.raw`([0-9a-zA-Z:#_]+|\`[^\`\r\n]*\`)\s+AND\s+NOT\s+([0-9a-zA-Z:#_]+|\`[^\`\r\n]*\`)`,
|
||||
String.raw`(${id}|${quotedId})\s+AND\s+NOT\s+(${id}|${quotedId})`,
|
||||
// AND NOT id
|
||||
String.raw`AND\s+NOT\s+(${id}|${quotedId})`,
|
||||
// INVOKE HIGHER-ORDER RELATION rel ON <id, ..., id>
|
||||
String.raw`INVOKE\s+HIGHER-ORDER\s+RELATION\s[^\s]+\sON\s+<([0-9a-zA-Z:#_<>]+|\`[^\`\r\n]*\`)((?:,[0-9a-zA-Z:#_<>]+|,\`[^\`\r\n]*\`)*)>`,
|
||||
String.raw`INVOKE\s+HIGHER-ORDER\s+RELATION\s[^\s]+\sON\s+<(${idWithAngleBrackets}|${quotedId})((?:,${idWithAngleBrackets}|,${quotedId})*)>`,
|
||||
// SELECT id
|
||||
String.raw`SELECT\s+([0-9a-zA-Z:#_]+|\`[^\`\r\n]*\`)`,
|
||||
String.raw`SELECT\s+(${id}|${quotedId})`,
|
||||
// REWRITE id WITH
|
||||
String.raw`REWRITE\s+([0-9a-zA-Z:#_]+|\`[^\`\r\n]*\`)\s+WITH\s`,
|
||||
String.raw`REWRITE\s+(${id}|${quotedId})\s+WITH\s`,
|
||||
// id UNION id UNION ... UNION id
|
||||
String.raw`(${id}|${quotedId})((?:\s+UNION\s+${id}|${quotedId})+)`,
|
||||
];
|
||||
return new RegExp(
|
||||
`${String.raw`\{[0-9]+\}\s+[0-9a-zA-Z]+\s=\s(?:` + regexps.join("|")})`,
|
||||
const r = new RegExp(
|
||||
`${
|
||||
String.raw`\{[0-9]+\}\s+(?:[0-9a-zA-Z]+\s=|\|)\s(?:` + regexps.join("|")
|
||||
})`,
|
||||
);
|
||||
})();
|
||||
|
||||
function getDependentPredicates(operations: string[]): I.List<string> {
|
||||
return I.List(operations).flatMap((operation) => {
|
||||
const matches = DEPENDENT_PREDICATES_REGEXP.exec(operation.trim());
|
||||
if (matches !== null) {
|
||||
return I.List(matches)
|
||||
.rest() // Skip the first group as it's just the entire string
|
||||
.filter((x) => !!x && !x.match("r[0-9]+|PRIMITIVE")) // Only keep the references to predicates.
|
||||
.flatMap((x) => x.split(",")) // Group 2 in the INVOKE HIGHER_ORDER RELATION case is a comma-separated list of identifiers.
|
||||
.filter((x) => !!x) // Remove empty strings
|
||||
.map((x) =>
|
||||
x.startsWith("`") && x.endsWith("`")
|
||||
? x.substring(1, x.length - 1)
|
||||
: x,
|
||||
); // Remove quotes from quoted identifiers
|
||||
} else {
|
||||
return I.List();
|
||||
}
|
||||
const matches = r.exec(operation.trim()) || [];
|
||||
return I.List(matches)
|
||||
.rest() // Skip the first group as it's just the entire string
|
||||
.filter((x) => !!x)
|
||||
.flatMap((x) => x.split(",")) // Group 2 in the INVOKE HIGHER_ORDER RELATION case is a comma-separated list of identifiers.
|
||||
.flatMap((x) => x.split(" UNION ")) // Split n-ary unions into individual arguments.
|
||||
.filter((x) => !x.match("r[0-9]+|PRIMITIVE")) // Only keep the references to predicates.
|
||||
.filter((x) => !!x) // Remove empty strings
|
||||
.map((x) =>
|
||||
x.startsWith("`") && x.endsWith("`") ? x.substring(1, x.length - 1) : x,
|
||||
); // Remove quotes from quoted identifiers
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { DecodedBqrsChunk } from "../common/bqrs-cli-types";
|
||||
import { Call, CallClassification, Method } from "./method";
|
||||
import { DecodedBqrsChunk, BqrsEntityValue } from "../common/bqrs-cli-types";
|
||||
import { CallClassification, Method, Usage } from "./method";
|
||||
import { ModeledMethodType } from "./modeled-method";
|
||||
import { parseLibraryFilename } from "./library";
|
||||
import { Mode } from "./shared/mode";
|
||||
import { ApplicationModeTuple, FrameworkModeTuple } from "./queries/query";
|
||||
import { QueryLanguage } from "../common/query-language";
|
||||
import { getModelsAsDataLanguage } from "./languages";
|
||||
import { mapUrlValue } from "../common/bqrs-raw-results-mapper";
|
||||
import { isUrlValueResolvable } from "../common/raw-result-types";
|
||||
|
||||
export function decodeBqrsToMethods(
|
||||
chunk: DecodedBqrsChunk,
|
||||
@@ -17,7 +19,7 @@ export function decodeBqrsToMethods(
|
||||
const definition = getModelsAsDataLanguage(language);
|
||||
|
||||
chunk?.tuples.forEach((tuple) => {
|
||||
let usage: Call;
|
||||
let usageEntityValue: BqrsEntityValue;
|
||||
let packageName: string;
|
||||
let typeName: string;
|
||||
let methodName: string;
|
||||
@@ -30,7 +32,7 @@ export function decodeBqrsToMethods(
|
||||
|
||||
if (mode === Mode.Application) {
|
||||
[
|
||||
usage,
|
||||
usageEntityValue,
|
||||
packageName,
|
||||
typeName,
|
||||
methodName,
|
||||
@@ -43,7 +45,7 @@ export function decodeBqrsToMethods(
|
||||
] = tuple as ApplicationModeTuple;
|
||||
} else {
|
||||
[
|
||||
usage,
|
||||
usageEntityValue,
|
||||
packageName,
|
||||
typeName,
|
||||
methodName,
|
||||
@@ -97,11 +99,25 @@ export function decodeBqrsToMethods(
|
||||
});
|
||||
}
|
||||
|
||||
if (usageEntityValue.url === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const usageUrl = mapUrlValue(usageEntityValue.url);
|
||||
if (!usageUrl || !isUrlValueResolvable(usageUrl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!usageEntityValue.label) {
|
||||
return;
|
||||
}
|
||||
|
||||
const method = methodsByApiName.get(signature)!;
|
||||
const usages = [
|
||||
const usages: Usage[] = [
|
||||
...method.usages,
|
||||
{
|
||||
...usage,
|
||||
label: usageEntityValue.label,
|
||||
url: usageUrl,
|
||||
classification,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -48,7 +48,6 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
|
||||
t: "setMethodModelingPanelViewState",
|
||||
viewState: {
|
||||
language: this.language,
|
||||
showMultipleModels: this.modelConfig.showMultipleModels,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ResolvableLocationValue } from "../common/bqrs-cli-types";
|
||||
import { ModeledMethod, ModeledMethodType } from "./modeled-method";
|
||||
import { UrlValueResolvable } from "../common/raw-result-types";
|
||||
|
||||
export type Call = {
|
||||
type Call = {
|
||||
readonly label: string;
|
||||
readonly url: Readonly<ResolvableLocationValue>;
|
||||
readonly url: Readonly<UrlValueResolvable>;
|
||||
};
|
||||
|
||||
export enum CallClassification {
|
||||
|
||||
@@ -19,6 +19,7 @@ import { assertNever } from "../../common/helpers-pure";
|
||||
import { ModeledMethod } from "../modeled-method";
|
||||
import { groupMethods, sortGroupNames, sortMethods } from "../shared/sorting";
|
||||
import { INITIAL_MODE, Mode } from "../shared/mode";
|
||||
import { UrlValueResolvable } from "../../common/raw-result-types";
|
||||
|
||||
export class MethodsUsageDataProvider
|
||||
extends DisposableObject
|
||||
@@ -99,11 +100,16 @@ export class MethodsUsageDataProvider
|
||||
} else {
|
||||
const { method, usage } = item;
|
||||
|
||||
const description =
|
||||
usage.url.type === "wholeFileLocation"
|
||||
? this.relativePathWithinDatabase(usage.url.uri)
|
||||
: `${this.relativePathWithinDatabase(usage.url.uri)} [${
|
||||
usage.url.startLine
|
||||
}, ${usage.url.endLine}]`;
|
||||
|
||||
return {
|
||||
label: usage.label,
|
||||
description: `${this.relativePathWithinDatabase(usage.url.uri)} [${
|
||||
usage.url.startLine
|
||||
}, ${usage.url.endLine}]`,
|
||||
description,
|
||||
collapsibleState: TreeItemCollapsibleState.None,
|
||||
command: {
|
||||
title: "Show usage",
|
||||
@@ -211,14 +217,35 @@ function usagesAreEqual(u1: Usage, u2: Usage): boolean {
|
||||
return (
|
||||
u1.label === u2.label &&
|
||||
u1.classification === u2.classification &&
|
||||
u1.url.uri === u2.url.uri &&
|
||||
u1.url.startLine === u2.url.startLine &&
|
||||
u1.url.startColumn === u2.url.startColumn &&
|
||||
u1.url.endLine === u2.url.endLine &&
|
||||
u1.url.endColumn === u2.url.endColumn
|
||||
urlValueResolvablesAreEqual(u1.url, u2.url)
|
||||
);
|
||||
}
|
||||
|
||||
function urlValueResolvablesAreEqual(
|
||||
u1: UrlValueResolvable,
|
||||
u2: UrlValueResolvable,
|
||||
): boolean {
|
||||
if (u1.type !== u2.type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (u1.type === "wholeFileLocation" && u2.type === "wholeFileLocation") {
|
||||
return u1.uri === u2.uri;
|
||||
}
|
||||
|
||||
if (u1.type === "lineColumnLocation" && u2.type === "lineColumnLocation") {
|
||||
return (
|
||||
u1.uri === u2.uri &&
|
||||
u1.startLine === u2.startLine &&
|
||||
u1.startColumn === u2.startColumn &&
|
||||
u1.endLine === u2.endLine &&
|
||||
u1.endColumn === u2.endColumn
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function sortMethodsInGroups(methods: readonly Method[], mode: Mode): Method[] {
|
||||
const grouped = groupMethods(methods, mode);
|
||||
|
||||
|
||||
@@ -385,7 +385,6 @@ export class ModelEditorView extends AbstractWebview<
|
||||
language: this.language,
|
||||
showGenerateButton,
|
||||
showLlmButton,
|
||||
showMultipleModels: this.modelConfig.showMultipleModels,
|
||||
mode: this.modelingStore.getMode(this.databaseItem),
|
||||
showModeSwitchButton,
|
||||
sourceArchiveAvailable,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Call, CallClassification } from "../method";
|
||||
import { CallClassification } from "../method";
|
||||
import { ModeledMethodType } from "../modeled-method";
|
||||
import { BqrsEntityValue } from "../../common/bqrs-cli-types";
|
||||
|
||||
export type Query = {
|
||||
/**
|
||||
@@ -39,7 +40,7 @@ export type Query = {
|
||||
};
|
||||
|
||||
export type ApplicationModeTuple = [
|
||||
Call,
|
||||
BqrsEntityValue,
|
||||
string,
|
||||
string,
|
||||
string,
|
||||
@@ -52,7 +53,7 @@ export type ApplicationModeTuple = [
|
||||
];
|
||||
|
||||
export type FrameworkModeTuple = [
|
||||
Call,
|
||||
BqrsEntityValue,
|
||||
string,
|
||||
string,
|
||||
string,
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { ModeledMethod } from "../modeled-method";
|
||||
|
||||
/**
|
||||
* Converts a ModeledMethod[] to a single ModeledMethod for legacy usage. This function should always be used instead
|
||||
* of the trivial conversion to track usages of this conversion.
|
||||
*
|
||||
* This method should only be called inside a `postMessage` call. If it's used anywhere else, consider whether the
|
||||
* boundary is correct: the boundary should as close as possible to the extension host -> webview boundary.
|
||||
*
|
||||
* @param modeledMethods The ModeledMethod[]
|
||||
*/
|
||||
export function convertToLegacyModeledMethod(
|
||||
modeledMethods: ModeledMethod[],
|
||||
): ModeledMethod | undefined {
|
||||
return modeledMethods[0];
|
||||
}
|
||||
@@ -7,7 +7,6 @@ export interface ModelEditorViewState {
|
||||
language: QueryLanguage;
|
||||
showGenerateButton: boolean;
|
||||
showLlmButton: boolean;
|
||||
showMultipleModels: boolean;
|
||||
mode: Mode;
|
||||
showModeSwitchButton: boolean;
|
||||
sourceArchiveAvailable: boolean;
|
||||
@@ -15,5 +14,4 @@ export interface ModelEditorViewState {
|
||||
|
||||
export interface MethodModelingPanelViewState {
|
||||
language: QueryLanguage | undefined;
|
||||
showMultipleModels: boolean;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import { nanoid } from "nanoid";
|
||||
import { CodeQLCliServer } from "./codeql-cli/cli";
|
||||
import { SELECT_QUERY_NAME } from "./language-support";
|
||||
import { DatabaseManager } from "./databases/local-databases";
|
||||
import { DecodedBqrsChunk, EntityValue } from "./common/bqrs-cli-types";
|
||||
import { DecodedBqrsChunk, BqrsEntityValue } from "./common/bqrs-cli-types";
|
||||
import { BaseLogger, showAndLogWarningMessage } from "./common/logging";
|
||||
import { extLogger } from "./common/logging/vscode";
|
||||
import { generateSummarySymbolsFile } from "./log-insights/summary-parser";
|
||||
@@ -287,7 +287,7 @@ export class QueryEvaluationInfo extends QueryOutputDir {
|
||||
typeof v === "string" ? v.replaceAll('"', '""') : v
|
||||
}"`;
|
||||
} else if (chunk.columns[i].kind === "Entity") {
|
||||
return (v as EntityValue).label;
|
||||
return (v as BqrsEntityValue).label;
|
||||
} else {
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Meta, StoryFn } from "@storybook/react";
|
||||
import CompareTableComponent from "../../view/compare/CompareTable";
|
||||
|
||||
import "../../view/results/resultsView.css";
|
||||
import { ColumnKind } from "../../common/raw-result-types";
|
||||
|
||||
export default {
|
||||
title: "Compare/Compare Table",
|
||||
@@ -40,30 +41,38 @@ CompareTable.args = {
|
||||
result: {
|
||||
kind: "raw",
|
||||
columns: [
|
||||
{ name: "a", kind: "Entity" },
|
||||
{ name: "b", kind: "Entity" },
|
||||
{ name: "a", kind: ColumnKind.Entity },
|
||||
{ name: "b", kind: ColumnKind.Entity },
|
||||
],
|
||||
from: [],
|
||||
to: [
|
||||
[
|
||||
{
|
||||
label: "url : String",
|
||||
url: {
|
||||
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
|
||||
startLine: 22,
|
||||
startColumn: 27,
|
||||
endLine: 22,
|
||||
endColumn: 57,
|
||||
type: "entity",
|
||||
value: {
|
||||
label: "url : String",
|
||||
url: {
|
||||
type: "lineColumnLocation",
|
||||
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
|
||||
startLine: 22,
|
||||
startColumn: 27,
|
||||
endLine: 22,
|
||||
endColumn: 57,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "url",
|
||||
url: {
|
||||
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
|
||||
startLine: 23,
|
||||
startColumn: 33,
|
||||
endLine: 23,
|
||||
endColumn: 35,
|
||||
type: "entity",
|
||||
value: {
|
||||
label: "url",
|
||||
url: {
|
||||
type: "lineColumnLocation",
|
||||
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
|
||||
startLine: 23,
|
||||
startColumn: 33,
|
||||
endLine: 23,
|
||||
endColumn: 35,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
{
|
||||
"schema": {
|
||||
"resultSet": {
|
||||
"name": "#select",
|
||||
"rows": 1,
|
||||
"totalRowCount": 1,
|
||||
"columns": [
|
||||
{
|
||||
"kind": "i"
|
||||
"kind": "integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
"resultSet": {
|
||||
"schema": {
|
||||
"name": "#select",
|
||||
"rows": 1,
|
||||
"columns": [
|
||||
],
|
||||
"rows": [
|
||||
[
|
||||
{
|
||||
"kind": "i"
|
||||
"type": "number",
|
||||
"value": 60688
|
||||
}
|
||||
]
|
||||
},
|
||||
"rows": [[60688]]
|
||||
]
|
||||
},
|
||||
"fileLinkPrefix": "https://github.com/facebook/create-react-app/blob/d960b9e38c062584ff6cfb1a70e1512509a966e7",
|
||||
"sourceLocationPrefix": "/home/runner/work/bulk-builder/bulk-builder",
|
||||
|
||||
@@ -47,26 +47,16 @@ MethodSaved.args = {
|
||||
modelingStatus: "saved",
|
||||
};
|
||||
|
||||
export const MultipleModelingsUnmodeled = Template.bind({});
|
||||
MultipleModelingsUnmodeled.args = {
|
||||
language,
|
||||
method,
|
||||
modeledMethods: [],
|
||||
modelingStatus: "saved",
|
||||
showMultipleModels: true,
|
||||
};
|
||||
|
||||
export const MultipleModelingsModeledSingle = Template.bind({});
|
||||
MultipleModelingsModeledSingle.args = {
|
||||
export const ModeledSingle = Template.bind({});
|
||||
ModeledSingle.args = {
|
||||
language,
|
||||
method,
|
||||
modeledMethods: [createSinkModeledMethod(method)],
|
||||
modelingStatus: "saved",
|
||||
showMultipleModels: true,
|
||||
};
|
||||
|
||||
export const MultipleModelingsModeledMultiple = Template.bind({});
|
||||
MultipleModelingsModeledMultiple.args = {
|
||||
export const ModeledMultiple = Template.bind({});
|
||||
ModeledMultiple.args = {
|
||||
language,
|
||||
method,
|
||||
modeledMethods: [
|
||||
@@ -79,11 +69,10 @@ MultipleModelingsModeledMultiple.args = {
|
||||
}),
|
||||
],
|
||||
modelingStatus: "saved",
|
||||
showMultipleModels: true,
|
||||
};
|
||||
|
||||
export const MultipleModelingsValidationFailedNeutral = Template.bind({});
|
||||
MultipleModelingsValidationFailedNeutral.args = {
|
||||
export const ValidationFailedNeutral = Template.bind({});
|
||||
ValidationFailedNeutral.args = {
|
||||
language,
|
||||
method,
|
||||
modeledMethods: [
|
||||
@@ -91,11 +80,10 @@ MultipleModelingsValidationFailedNeutral.args = {
|
||||
createNeutralModeledMethod(method),
|
||||
],
|
||||
modelingStatus: "unsaved",
|
||||
showMultipleModels: true,
|
||||
};
|
||||
|
||||
export const MultipleModelingsValidationFailedDuplicate = Template.bind({});
|
||||
MultipleModelingsValidationFailedDuplicate.args = {
|
||||
export const ValidationFailedDuplicate = Template.bind({});
|
||||
ValidationFailedDuplicate.args = {
|
||||
language,
|
||||
method,
|
||||
modeledMethods: [
|
||||
@@ -108,5 +96,4 @@ MultipleModelingsValidationFailedDuplicate.args = {
|
||||
createSinkModeledMethod(method),
|
||||
],
|
||||
modelingStatus: "unsaved",
|
||||
showMultipleModels: true,
|
||||
};
|
||||
|
||||
@@ -216,7 +216,6 @@ LibraryRow.args = {
|
||||
viewState: createMockModelEditorViewState({
|
||||
showGenerateButton: true,
|
||||
showLlmButton: true,
|
||||
showMultipleModels: true,
|
||||
}),
|
||||
hideModeledMethods: false,
|
||||
};
|
||||
|
||||
@@ -6,10 +6,7 @@ import { Meta, StoryFn } from "@storybook/react";
|
||||
import { MethodRow as MethodRowComponent } from "../../view/model-editor/MethodRow";
|
||||
import { CallClassification, Method } from "../../model-editor/method";
|
||||
import { ModeledMethod } from "../../model-editor/modeled-method";
|
||||
import {
|
||||
MULTIPLE_MODELS_GRID_TEMPLATE_COLUMNS,
|
||||
SINGLE_MODEL_GRID_TEMPLATE_COLUMNS,
|
||||
} from "../../view/model-editor/ModeledMethodDataGrid";
|
||||
import { MULTIPLE_MODELS_GRID_TEMPLATE_COLUMNS } from "../../view/model-editor/ModeledMethodDataGrid";
|
||||
import { DataGrid } from "../../view/common/DataGrid";
|
||||
import { createMockModelEditorViewState } from "../../../test/factories/model-editor/view-state";
|
||||
|
||||
@@ -35,12 +32,8 @@ const Template: StoryFn<typeof MethodRowComponent> = (args) => {
|
||||
[args],
|
||||
);
|
||||
|
||||
const gridTemplateColumns = args.viewState?.showMultipleModels
|
||||
? MULTIPLE_MODELS_GRID_TEMPLATE_COLUMNS
|
||||
: SINGLE_MODEL_GRID_TEMPLATE_COLUMNS;
|
||||
|
||||
return (
|
||||
<DataGrid gridTemplateColumns={gridTemplateColumns}>
|
||||
<DataGrid gridTemplateColumns={MULTIPLE_MODELS_GRID_TEMPLATE_COLUMNS}>
|
||||
<MethodRowComponent
|
||||
{...args}
|
||||
modeledMethods={modeledMethods}
|
||||
@@ -63,6 +56,7 @@ const method: Method = {
|
||||
{
|
||||
label: "open(...)",
|
||||
url: {
|
||||
type: "lineColumnLocation",
|
||||
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
|
||||
startLine: 14,
|
||||
startColumn: 24,
|
||||
@@ -74,6 +68,7 @@ const method: Method = {
|
||||
{
|
||||
label: "open(...)",
|
||||
url: {
|
||||
type: "lineColumnLocation",
|
||||
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
|
||||
startLine: 25,
|
||||
startColumn: 24,
|
||||
@@ -100,7 +95,6 @@ const modeledMethod: ModeledMethod = {
|
||||
const viewState = createMockModelEditorViewState({
|
||||
showGenerateButton: true,
|
||||
showLlmButton: true,
|
||||
showMultipleModels: true,
|
||||
});
|
||||
|
||||
export const Unmodeled = Template.bind({});
|
||||
|
||||
@@ -30,7 +30,6 @@ ModelEditor.args = {
|
||||
},
|
||||
showGenerateButton: true,
|
||||
showLlmButton: true,
|
||||
showMultipleModels: true,
|
||||
}),
|
||||
initialMethods: [
|
||||
{
|
||||
@@ -112,6 +111,7 @@ ModelEditor.args = {
|
||||
{
|
||||
label: "println(...)",
|
||||
url: {
|
||||
type: "lineColumnLocation",
|
||||
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
|
||||
startLine: 29,
|
||||
startColumn: 9,
|
||||
@@ -123,6 +123,7 @@ ModelEditor.args = {
|
||||
{
|
||||
label: "println(...)",
|
||||
url: {
|
||||
type: "lineColumnLocation",
|
||||
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/test/java/org/example/HelloControllerTest.java",
|
||||
startLine: 29,
|
||||
startColumn: 9,
|
||||
|
||||
@@ -19,7 +19,6 @@ export const WithoutCodeFlows = Template.bind({});
|
||||
WithoutCodeFlows.args = {
|
||||
resultSet: {
|
||||
t: "InterpretedResultSet",
|
||||
schema: { name: "alerts", rows: 1, columns: [] },
|
||||
name: "alerts",
|
||||
interpretation: {
|
||||
data: {
|
||||
@@ -335,7 +334,6 @@ export const WithCodeFlows = Template.bind({});
|
||||
WithCodeFlows.args = {
|
||||
resultSet: {
|
||||
t: "InterpretedResultSet",
|
||||
schema: { name: "alerts", rows: 1, columns: [] },
|
||||
name: "alerts",
|
||||
interpretation: {
|
||||
data: {
|
||||
|
||||
@@ -28,16 +28,6 @@ ResultTablesHeader.args = {
|
||||
resultSetNames: ["#select", "alerts"],
|
||||
resultSet: {
|
||||
t: "InterpretedResultSet",
|
||||
schema: {
|
||||
name: "#select",
|
||||
rows: 15,
|
||||
columns: [
|
||||
{
|
||||
name: "x",
|
||||
kind: "s",
|
||||
},
|
||||
],
|
||||
},
|
||||
name: "#select",
|
||||
interpretation: {
|
||||
sourceLocationPrefix: "/home/bulk-builder/bulk-builder",
|
||||
|
||||
@@ -18,6 +18,7 @@ const Template: StoryFn<typeof ClickableLocationComponent> = (args) => (
|
||||
export const ClickableLocation = Template.bind({});
|
||||
ClickableLocation.args = {
|
||||
loc: {
|
||||
type: "lineColumnLocation",
|
||||
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
|
||||
startLine: 22,
|
||||
startColumn: 27,
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
} from "../../variant-analysis/shared/variant-analysis";
|
||||
import { createMockVariantAnalysis } from "../../../test/factories/variant-analysis/shared/variant-analysis";
|
||||
import { createMockRepositoryWithMetadata } from "../../../test/factories/variant-analysis/shared/repository";
|
||||
import { ColumnKind } from "../../common/raw-result-types";
|
||||
|
||||
export default {
|
||||
title: "Variant Analysis/Variant Analysis",
|
||||
@@ -207,26 +208,22 @@ const repoResults: VariantAnalysisScannedRepositoryResult[] = [
|
||||
variantAnalysisId: 1,
|
||||
repositoryId: 1,
|
||||
rawResults: {
|
||||
schema: {
|
||||
resultSet: {
|
||||
name: "#select",
|
||||
rows: 1,
|
||||
totalRowCount: 1,
|
||||
columns: [
|
||||
{
|
||||
kind: "i",
|
||||
kind: ColumnKind.Integer,
|
||||
},
|
||||
],
|
||||
},
|
||||
resultSet: {
|
||||
schema: {
|
||||
name: "#select",
|
||||
rows: 1,
|
||||
columns: [
|
||||
rows: [
|
||||
[
|
||||
{
|
||||
kind: "i",
|
||||
type: "number",
|
||||
value: 60688,
|
||||
},
|
||||
],
|
||||
},
|
||||
rows: [[60688]],
|
||||
],
|
||||
},
|
||||
fileLinkPrefix:
|
||||
"https://github.com/octodemo/hello-world-1/blob/59a2a6c7d9dde7a6ecb77c2f7e8197d6925c143b",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { CodeQLCliServer } from "../codeql-cli/cli";
|
||||
import { Logger } from "../common/logging";
|
||||
import { transformBqrsResultSet } from "../common/bqrs-cli-types";
|
||||
import { AnalysisRawResults } from "./shared/analysis-result";
|
||||
import { MAX_RAW_RESULTS } from "./shared/result-limits";
|
||||
import { SELECT_TABLE_NAME } from "../common/interface-types";
|
||||
import { bqrsToResultSet } from "../common/bqrs-raw-results-mapper";
|
||||
|
||||
export async function extractRawResults(
|
||||
cliServer: CodeQLCliServer,
|
||||
@@ -34,9 +34,9 @@ export async function extractRawResults(
|
||||
pageSize: MAX_RAW_RESULTS,
|
||||
});
|
||||
|
||||
const resultSet = transformBqrsResultSet(schema, chunk);
|
||||
const resultSet = bqrsToResultSet(schema, chunk);
|
||||
|
||||
const capped = !!chunk.next;
|
||||
|
||||
return { schema, resultSet, fileLinkPrefix, sourceLocationPrefix, capped };
|
||||
return { resultSet, fileLinkPrefix, sourceLocationPrefix, capped };
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { CellValue } from "../common/bqrs-cli-types";
|
||||
import { tryGetRemoteLocation } from "../common/bqrs-utils";
|
||||
import { createRemoteFileRef } from "../common/location-link-utils";
|
||||
import {
|
||||
@@ -19,6 +18,7 @@ import type {
|
||||
VariantAnalysisScannedRepositoryResult,
|
||||
} from "./shared/variant-analysis";
|
||||
import type { RepositoryWithMetadata } from "./shared/repository";
|
||||
import { CellValue } from "../common/raw-result-types";
|
||||
|
||||
type MarkdownLinkType = "local" | "gist";
|
||||
|
||||
@@ -298,9 +298,9 @@ function generateMarkdownForRawResults(
|
||||
analysisRawResults: AnalysisRawResults,
|
||||
): string[] {
|
||||
const tableRows: string[] = [];
|
||||
const columnCount = analysisRawResults.schema.columns.length;
|
||||
const columnCount = analysisRawResults.resultSet.columns.length;
|
||||
// Table headers are the column names if they exist, and empty otherwise
|
||||
const headers = analysisRawResults.schema.columns.map(
|
||||
const headers = analysisRawResults.resultSet.columns.map(
|
||||
(column) => column.name || "",
|
||||
);
|
||||
const tableHeader = `| ${headers.join(" | ")} |`;
|
||||
@@ -327,23 +327,25 @@ function generateMarkdownForRawTableCell(
|
||||
sourceLocationPrefix: string,
|
||||
) {
|
||||
let cellValue: string;
|
||||
switch (typeof value) {
|
||||
switch (value.type) {
|
||||
case "string":
|
||||
case "number":
|
||||
case "boolean":
|
||||
cellValue = `\`${convertNonPrintableChars(value.toString())}\``;
|
||||
cellValue = `\`${convertNonPrintableChars(value.value.toString())}\``;
|
||||
break;
|
||||
case "object":
|
||||
case "entity":
|
||||
{
|
||||
const url = tryGetRemoteLocation(
|
||||
value.url,
|
||||
value.value.url,
|
||||
fileLinkPrefix,
|
||||
sourceLocationPrefix,
|
||||
);
|
||||
if (url) {
|
||||
cellValue = `[\`${convertNonPrintableChars(value.label)}\`](${url})`;
|
||||
cellValue = `[\`${convertNonPrintableChars(
|
||||
value.value.label,
|
||||
)}\`](${url})`;
|
||||
} else {
|
||||
cellValue = `\`${convertNonPrintableChars(value.label)}\``;
|
||||
cellValue = `\`${convertNonPrintableChars(value.value.label)}\``;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -18,10 +18,6 @@ export async function getRepositorySelection(
|
||||
const selectedDbItem = dbManager.getSelectedDbItem();
|
||||
if (selectedDbItem) {
|
||||
switch (selectedDbItem.kind) {
|
||||
case DbItemKind.LocalDatabase || DbItemKind.LocalList:
|
||||
throw new UserCancellationException(
|
||||
"Local databases and lists are not supported yet.",
|
||||
);
|
||||
case DbItemKind.RemoteSystemDefinedList:
|
||||
return { repositoryLists: [selectedDbItem.listName] };
|
||||
case DbItemKind.RemoteUserDefinedList:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { RawResultSet, ResultSetSchema } from "../../common/bqrs-cli-types";
|
||||
import { RawResultSet } from "../../common/raw-result-types";
|
||||
|
||||
export interface AnalysisRawResults {
|
||||
schema: ResultSetSchema;
|
||||
resultSet: RawResultSet;
|
||||
fileLinkPrefix: string;
|
||||
sourceLocationPrefix: string;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import * as React from "react";
|
||||
import { ResultRow } from "../../common/bqrs-cli-types";
|
||||
import { sendTelemetry } from "../common/telemetry";
|
||||
import { Column, Row } from "../../common/raw-result-types";
|
||||
import RawTableHeader from "../results/RawTableHeader";
|
||||
import RawTableRow from "../results/RawTableRow";
|
||||
|
||||
interface Props {
|
||||
columns: ReadonlyArray<{ name?: string }>;
|
||||
columns: readonly Column[];
|
||||
schemaName: string;
|
||||
rows: ResultRow[];
|
||||
rows: Row[];
|
||||
databaseUri: string;
|
||||
|
||||
className?: string;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { MethodName } from "../model-editor/MethodName";
|
||||
import { ModeledMethod } from "../../model-editor/modeled-method";
|
||||
import { VSCodeTag } from "@vscode/webview-ui-toolkit/react";
|
||||
import { ReviewInEditorButton } from "./ReviewInEditorButton";
|
||||
import { ModeledMethodsPanel } from "./ModeledMethodsPanel";
|
||||
import { MultipleModeledMethodsPanel } from "./MultipleModeledMethodsPanel";
|
||||
import { QueryLanguage } from "../../common/query-language";
|
||||
|
||||
const Container = styled.div`
|
||||
@@ -55,7 +55,6 @@ export type MethodModelingProps = {
|
||||
method: Method;
|
||||
modeledMethods: ModeledMethod[];
|
||||
isModelingInProgress: boolean;
|
||||
showMultipleModels?: boolean;
|
||||
onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void;
|
||||
};
|
||||
|
||||
@@ -65,7 +64,6 @@ export const MethodModeling = ({
|
||||
modeledMethods,
|
||||
method,
|
||||
isModelingInProgress,
|
||||
showMultipleModels = false,
|
||||
onChange,
|
||||
}: MethodModelingProps): JSX.Element => {
|
||||
return (
|
||||
@@ -79,11 +77,10 @@ export const MethodModeling = ({
|
||||
<ModelingStatusIndicator status={modelingStatus} />
|
||||
<MethodName {...method} />
|
||||
</DependencyContainer>
|
||||
<ModeledMethodsPanel
|
||||
<MultipleModeledMethodsPanel
|
||||
language={language}
|
||||
method={method}
|
||||
modeledMethods={modeledMethods}
|
||||
showMultipleModels={showMultipleModels}
|
||||
isModelingInProgress={isModelingInProgress}
|
||||
modelingStatus={modelingStatus}
|
||||
onChange={onChange}
|
||||
|
||||
@@ -110,7 +110,6 @@ export function MethodModelingView({ initialViewState }: Props): JSX.Element {
|
||||
method={method}
|
||||
modeledMethods={modeledMethods}
|
||||
isModelingInProgress={isModelingInProgress}
|
||||
showMultipleModels={viewState?.showMultipleModels}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import * as React from "react";
|
||||
import { useCallback } from "react";
|
||||
import { ModeledMethod } from "../../model-editor/modeled-method";
|
||||
import { MethodModelingInputs } from "./MethodModelingInputs";
|
||||
import { Method } from "../../model-editor/method";
|
||||
import { styled } from "styled-components";
|
||||
import { MultipleModeledMethodsPanel } from "./MultipleModeledMethodsPanel";
|
||||
import { convertToLegacyModeledMethod } from "../../model-editor/shared/modeled-methods-legacy";
|
||||
import { QueryLanguage } from "../../common/query-language";
|
||||
import { ModelingStatus } from "../../model-editor/shared/modeling-status";
|
||||
|
||||
export type ModeledMethodsPanelProps = {
|
||||
language: QueryLanguage;
|
||||
method: Method;
|
||||
modeledMethods: ModeledMethod[];
|
||||
modelingStatus: ModelingStatus;
|
||||
isModelingInProgress: boolean;
|
||||
showMultipleModels: boolean;
|
||||
onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void;
|
||||
};
|
||||
|
||||
const SingleMethodModelingInputs = styled(MethodModelingInputs)`
|
||||
padding-bottom: 0.5rem;
|
||||
`;
|
||||
|
||||
export const ModeledMethodsPanel = ({
|
||||
language,
|
||||
method,
|
||||
modeledMethods,
|
||||
modelingStatus,
|
||||
isModelingInProgress,
|
||||
showMultipleModels,
|
||||
onChange,
|
||||
}: ModeledMethodsPanelProps) => {
|
||||
const handleSingleChange = useCallback(
|
||||
(modeledMethod: ModeledMethod) => {
|
||||
onChange(modeledMethod.signature, [modeledMethod]);
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
if (!showMultipleModels) {
|
||||
return (
|
||||
<SingleMethodModelingInputs
|
||||
language={language}
|
||||
method={method}
|
||||
modeledMethod={convertToLegacyModeledMethod(modeledMethods)}
|
||||
modelingStatus={modelingStatus}
|
||||
isModelingInProgress={isModelingInProgress}
|
||||
onChange={handleSingleChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<MultipleModeledMethodsPanel
|
||||
language={language}
|
||||
method={method}
|
||||
modeledMethods={modeledMethods}
|
||||
modelingStatus={modelingStatus}
|
||||
isModelingInProgress={isModelingInProgress}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,90 +0,0 @@
|
||||
import * as React from "react";
|
||||
import { render as reactRender, screen } from "@testing-library/react";
|
||||
import { createMethod } from "../../../../test/factories/model-editor/method-factories";
|
||||
import { createSinkModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories";
|
||||
import {
|
||||
ModeledMethodsPanel,
|
||||
ModeledMethodsPanelProps,
|
||||
} from "../ModeledMethodsPanel";
|
||||
import { QueryLanguage } from "../../../common/query-language";
|
||||
|
||||
describe(ModeledMethodsPanel.name, () => {
|
||||
const render = (props: ModeledMethodsPanelProps) =>
|
||||
reactRender(<ModeledMethodsPanel {...props} />);
|
||||
|
||||
const language = QueryLanguage.Java;
|
||||
const method = createMethod();
|
||||
const modeledMethods = [createSinkModeledMethod(), createSinkModeledMethod()];
|
||||
const modelingStatus = "unmodeled";
|
||||
const isModelingInProgress = false;
|
||||
const onChange = jest.fn();
|
||||
|
||||
describe("when show multiple models is disabled", () => {
|
||||
const showMultipleModels = false;
|
||||
|
||||
it("renders the method modeling inputs", () => {
|
||||
render({
|
||||
language,
|
||||
method,
|
||||
modeledMethods,
|
||||
isModelingInProgress,
|
||||
modelingStatus,
|
||||
onChange,
|
||||
showMultipleModels,
|
||||
});
|
||||
|
||||
expect(screen.getAllByRole("combobox")).toHaveLength(4);
|
||||
});
|
||||
|
||||
it("does not render the pagination", () => {
|
||||
render({
|
||||
language,
|
||||
method,
|
||||
modeledMethods,
|
||||
isModelingInProgress,
|
||||
modelingStatus,
|
||||
onChange,
|
||||
showMultipleModels,
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.queryByLabelText("Previous modeling"),
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByLabelText("Next modeling")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when show multiple models is enabled", () => {
|
||||
const showMultipleModels = true;
|
||||
|
||||
it("renders the method modeling inputs once", () => {
|
||||
render({
|
||||
language,
|
||||
method,
|
||||
modeledMethods,
|
||||
isModelingInProgress,
|
||||
modelingStatus,
|
||||
onChange,
|
||||
showMultipleModels,
|
||||
});
|
||||
|
||||
expect(screen.getAllByRole("combobox")).toHaveLength(4);
|
||||
});
|
||||
|
||||
it("renders the pagination", () => {
|
||||
render({
|
||||
language,
|
||||
method,
|
||||
modeledMethods,
|
||||
isModelingInProgress,
|
||||
modelingStatus,
|
||||
onChange,
|
||||
showMultipleModels,
|
||||
});
|
||||
|
||||
expect(screen.getByLabelText("Previous modeling")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Next modeling")).toBeInTheDocument();
|
||||
expect(screen.getByText("1/2")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2,7 +2,6 @@ import * as React from "react";
|
||||
import { styled } from "styled-components";
|
||||
import { pluralize } from "../../common/word";
|
||||
import { DataGridCell, DataGridRow } from "../common/DataGrid";
|
||||
import { ModelEditorViewState } from "../../model-editor/shared/view-state";
|
||||
|
||||
const HiddenMethodsCell = styled(DataGridCell)`
|
||||
text-align: center;
|
||||
@@ -11,23 +10,19 @@ const HiddenMethodsCell = styled(DataGridCell)`
|
||||
interface Props {
|
||||
numHiddenMethods: number;
|
||||
someMethodsAreVisible: boolean;
|
||||
viewState: ModelEditorViewState;
|
||||
}
|
||||
|
||||
export function HiddenMethodsRow({
|
||||
numHiddenMethods,
|
||||
someMethodsAreVisible,
|
||||
viewState,
|
||||
}: Props) {
|
||||
if (numHiddenMethods === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const gridColumn = viewState.showMultipleModels ? "span 6" : "span 5";
|
||||
|
||||
return (
|
||||
<DataGridRow>
|
||||
<HiddenMethodsCell gridColumn={gridColumn}>
|
||||
<HiddenMethodsCell gridColumn="span 6">
|
||||
{someMethodsAreVisible && "And "}
|
||||
{pluralize(numHiddenMethods, "method", "methods")} modeled in other
|
||||
CodeQL packs
|
||||
|
||||
@@ -136,8 +136,8 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
|
||||
}, [focusedIndex]);
|
||||
|
||||
const modeledMethods = useMemo(
|
||||
() => modeledMethodsToDisplay(modeledMethodsProp, method, viewState),
|
||||
[modeledMethodsProp, method, viewState],
|
||||
() => modeledMethodsToDisplay(modeledMethodsProp, method),
|
||||
[modeledMethodsProp, method],
|
||||
);
|
||||
|
||||
const validationErrors = useMemo(
|
||||
@@ -219,13 +219,11 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
|
||||
<DataGridCell>
|
||||
<InProgressDropdown />
|
||||
</DataGridCell>
|
||||
{viewState.showMultipleModels && (
|
||||
<DataGridCell>
|
||||
<CodiconRow appearance="icon" disabled={true}>
|
||||
<Codicon name="add" label="Add new model" />
|
||||
</CodiconRow>
|
||||
</DataGridCell>
|
||||
)}
|
||||
<DataGridCell>
|
||||
<CodiconRow appearance="icon" disabled={true}>
|
||||
<Codicon name="add" label="Add new model" />
|
||||
</CodiconRow>
|
||||
</DataGridCell>
|
||||
</>
|
||||
)}
|
||||
{!props.modelingInProgress && (
|
||||
@@ -267,28 +265,26 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
|
||||
onChange={modeledMethodChangedHandlers[index]}
|
||||
/>
|
||||
</DataGridCell>
|
||||
{viewState.showMultipleModels && (
|
||||
<DataGridCell>
|
||||
{index === modeledMethods.length - 1 ? (
|
||||
<CodiconRow
|
||||
appearance="icon"
|
||||
aria-label="Add new model"
|
||||
onClick={handleAddModelClick}
|
||||
disabled={addModelButtonDisabled}
|
||||
>
|
||||
<Codicon name="add" />
|
||||
</CodiconRow>
|
||||
) : (
|
||||
<CodiconRow
|
||||
appearance="icon"
|
||||
aria-label="Remove model"
|
||||
onClick={removeModelClickedHandlers[index]}
|
||||
>
|
||||
<Codicon name="trash" />
|
||||
</CodiconRow>
|
||||
)}
|
||||
</DataGridCell>
|
||||
)}
|
||||
<DataGridCell>
|
||||
{index === 0 ? (
|
||||
<CodiconRow
|
||||
appearance="icon"
|
||||
aria-label="Add new model"
|
||||
onClick={handleAddModelClick}
|
||||
disabled={addModelButtonDisabled}
|
||||
>
|
||||
<Codicon name="add" />
|
||||
</CodiconRow>
|
||||
) : (
|
||||
<CodiconRow
|
||||
appearance="icon"
|
||||
aria-label="Remove model"
|
||||
onClick={removeModelClickedHandlers[index]}
|
||||
>
|
||||
<Codicon name="trash" />
|
||||
</CodiconRow>
|
||||
)}
|
||||
</DataGridCell>
|
||||
</DataGridRow>
|
||||
))}
|
||||
{validationErrors.map((error, index) => (
|
||||
@@ -336,9 +332,7 @@ const UnmodelableMethodRow = forwardRef<
|
||||
<ViewLink onClick={jumpToMethod}>View</ViewLink>
|
||||
</ApiOrMethodRow>
|
||||
</DataGridCell>
|
||||
<DataGridCell gridColumn={`span ${viewState.showMultipleModels ? 5 : 4}`}>
|
||||
Method already modeled
|
||||
</DataGridCell>
|
||||
<DataGridCell gridColumn="span 5">Method already modeled</DataGridCell>
|
||||
</DataGridRow>
|
||||
);
|
||||
});
|
||||
@@ -354,15 +348,10 @@ function sendJumpToMethodMessage(method: Method) {
|
||||
function modeledMethodsToDisplay(
|
||||
modeledMethods: ModeledMethod[],
|
||||
method: Method,
|
||||
viewState: ModelEditorViewState,
|
||||
): ModeledMethod[] {
|
||||
if (modeledMethods.length === 0) {
|
||||
return [createEmptyModeledMethod("none", method)];
|
||||
}
|
||||
|
||||
if (viewState.showMultipleModels) {
|
||||
return modeledMethods;
|
||||
} else {
|
||||
return modeledMethods.slice(0, 1);
|
||||
}
|
||||
return modeledMethods;
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ import { ModelEditorViewState } from "../../model-editor/shared/view-state";
|
||||
import { ScreenReaderOnly } from "../common/ScreenReaderOnly";
|
||||
import { DataGrid, DataGridCell } from "../common/DataGrid";
|
||||
|
||||
export const SINGLE_MODEL_GRID_TEMPLATE_COLUMNS =
|
||||
"0.5fr 0.125fr 0.125fr 0.125fr 0.125fr";
|
||||
export const MULTIPLE_MODELS_GRID_TEMPLATE_COLUMNS =
|
||||
"0.5fr 0.125fr 0.125fr 0.125fr 0.125fr max-content";
|
||||
|
||||
@@ -61,12 +59,8 @@ export const ModeledMethodDataGrid = ({
|
||||
|
||||
const someMethodsAreVisible = methodsWithModelability.length > 0;
|
||||
|
||||
const gridTemplateColumns = viewState.showMultipleModels
|
||||
? MULTIPLE_MODELS_GRID_TEMPLATE_COLUMNS
|
||||
: SINGLE_MODEL_GRID_TEMPLATE_COLUMNS;
|
||||
|
||||
return (
|
||||
<DataGrid gridTemplateColumns={gridTemplateColumns}>
|
||||
<DataGrid gridTemplateColumns={MULTIPLE_MODELS_GRID_TEMPLATE_COLUMNS}>
|
||||
{someMethodsAreVisible && (
|
||||
<>
|
||||
<DataGridCell rowType="header">API or method</DataGridCell>
|
||||
@@ -74,11 +68,9 @@ export const ModeledMethodDataGrid = ({
|
||||
<DataGridCell rowType="header">Input</DataGridCell>
|
||||
<DataGridCell rowType="header">Output</DataGridCell>
|
||||
<DataGridCell rowType="header">Kind</DataGridCell>
|
||||
{viewState.showMultipleModels && (
|
||||
<DataGridCell rowType="header">
|
||||
<ScreenReaderOnly>Add or remove models</ScreenReaderOnly>
|
||||
</DataGridCell>
|
||||
)}
|
||||
<DataGridCell rowType="header">
|
||||
<ScreenReaderOnly>Add or remove models</ScreenReaderOnly>
|
||||
</DataGridCell>
|
||||
{methodsWithModelability.map(({ method, methodCanBeModeled }) => {
|
||||
const modeledMethods = modeledMethodsMap[method.signature] ?? [];
|
||||
return (
|
||||
@@ -100,7 +92,6 @@ export const ModeledMethodDataGrid = ({
|
||||
<HiddenMethodsRow
|
||||
numHiddenMethods={numHiddenMethods}
|
||||
someMethodsAreVisible={someMethodsAreVisible}
|
||||
viewState={viewState}
|
||||
/>
|
||||
</DataGrid>
|
||||
);
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
import * as React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { HiddenMethodsRow } from "../HiddenMethodsRow";
|
||||
import { createMockModelEditorViewState } from "../../../../test/factories/model-editor/view-state";
|
||||
|
||||
describe(HiddenMethodsRow.name, () => {
|
||||
const viewState = createMockModelEditorViewState();
|
||||
|
||||
it("does not render with 0 hidden methods", () => {
|
||||
const { container } = render(
|
||||
<HiddenMethodsRow
|
||||
numHiddenMethods={0}
|
||||
someMethodsAreVisible={true}
|
||||
viewState={viewState}
|
||||
/>,
|
||||
<HiddenMethodsRow numHiddenMethods={0} someMethodsAreVisible={true} />,
|
||||
);
|
||||
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
@@ -20,11 +13,7 @@ describe(HiddenMethodsRow.name, () => {
|
||||
|
||||
it("renders with 1 hidden methods and no visible methods", () => {
|
||||
render(
|
||||
<HiddenMethodsRow
|
||||
numHiddenMethods={1}
|
||||
someMethodsAreVisible={false}
|
||||
viewState={viewState}
|
||||
/>,
|
||||
<HiddenMethodsRow numHiddenMethods={1} someMethodsAreVisible={false} />,
|
||||
);
|
||||
|
||||
expect(
|
||||
@@ -34,11 +23,7 @@ describe(HiddenMethodsRow.name, () => {
|
||||
|
||||
it("renders with 1 hidden methods and visible methods", () => {
|
||||
render(
|
||||
<HiddenMethodsRow
|
||||
numHiddenMethods={1}
|
||||
someMethodsAreVisible={true}
|
||||
viewState={viewState}
|
||||
/>,
|
||||
<HiddenMethodsRow numHiddenMethods={1} someMethodsAreVisible={true} />,
|
||||
);
|
||||
|
||||
expect(
|
||||
@@ -48,11 +33,7 @@ describe(HiddenMethodsRow.name, () => {
|
||||
|
||||
it("renders with 3 hidden methods and no visible methods", () => {
|
||||
render(
|
||||
<HiddenMethodsRow
|
||||
numHiddenMethods={3}
|
||||
someMethodsAreVisible={false}
|
||||
viewState={viewState}
|
||||
/>,
|
||||
<HiddenMethodsRow numHiddenMethods={3} someMethodsAreVisible={false} />,
|
||||
);
|
||||
|
||||
expect(
|
||||
@@ -62,11 +43,7 @@ describe(HiddenMethodsRow.name, () => {
|
||||
|
||||
it("renders with 3 hidden methods and visible methods", () => {
|
||||
render(
|
||||
<HiddenMethodsRow
|
||||
numHiddenMethods={3}
|
||||
someMethodsAreVisible={true}
|
||||
viewState={viewState}
|
||||
/>,
|
||||
<HiddenMethodsRow numHiddenMethods={3} someMethodsAreVisible={true} />,
|
||||
);
|
||||
|
||||
expect(
|
||||
|
||||
@@ -195,10 +195,6 @@ describe(MethodRow.name, () => {
|
||||
{ ...modeledMethod, type: "sink" },
|
||||
{ ...modeledMethod, type: "summary" },
|
||||
],
|
||||
viewState: {
|
||||
...viewState,
|
||||
showMultipleModels: true,
|
||||
},
|
||||
});
|
||||
|
||||
const kindInputs = screen.getAllByRole("combobox", { name: "Model type" });
|
||||
@@ -208,24 +204,6 @@ describe(MethodRow.name, () => {
|
||||
expect(kindInputs[2]).toHaveValue("summary");
|
||||
});
|
||||
|
||||
it("renders only first model when showMultipleModels feature flag is disabled", () => {
|
||||
render({
|
||||
modeledMethods: [
|
||||
{ ...modeledMethod, type: "source" },
|
||||
{ ...modeledMethod, type: "sink" },
|
||||
{ ...modeledMethod, type: "summary" },
|
||||
],
|
||||
viewState: {
|
||||
...viewState,
|
||||
showMultipleModels: false,
|
||||
},
|
||||
});
|
||||
|
||||
const kindInputs = screen.getAllByRole("combobox", { name: "Model type" });
|
||||
expect(kindInputs.length).toBe(1);
|
||||
expect(kindInputs[0]).toHaveValue("source");
|
||||
});
|
||||
|
||||
it("can update fields when there are multiple models", async () => {
|
||||
render({
|
||||
modeledMethods: [
|
||||
@@ -233,10 +211,6 @@ describe(MethodRow.name, () => {
|
||||
{ ...modeledMethod, type: "sink", kind: "code-injection" },
|
||||
{ ...modeledMethod, type: "summary" },
|
||||
],
|
||||
viewState: {
|
||||
...viewState,
|
||||
showMultipleModels: true,
|
||||
},
|
||||
});
|
||||
|
||||
onChange.mockReset();
|
||||
@@ -268,26 +242,9 @@ describe(MethodRow.name, () => {
|
||||
expect(screen.getByText("Method already modeled")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("doesn't show add/remove buttons when multiple methods feature flag is disabled", async () => {
|
||||
render({
|
||||
modeledMethods: [modeledMethod],
|
||||
viewState: {
|
||||
...viewState,
|
||||
showMultipleModels: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.queryByLabelText("Add new model")).not.toBeInTheDocument();
|
||||
expect(screen.queryByLabelText("Remove model")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows disabled button add new model when there are no modeled methods", async () => {
|
||||
render({
|
||||
modeledMethods: [],
|
||||
viewState: {
|
||||
...viewState,
|
||||
showMultipleModels: true,
|
||||
},
|
||||
});
|
||||
|
||||
const addButton = screen.queryByLabelText("Add new model");
|
||||
@@ -300,10 +257,6 @@ describe(MethodRow.name, () => {
|
||||
it("disabled button to add new model when there is one unmodeled method", async () => {
|
||||
render({
|
||||
modeledMethods: [{ ...modeledMethod, type: "none" }],
|
||||
viewState: {
|
||||
...viewState,
|
||||
showMultipleModels: true,
|
||||
},
|
||||
});
|
||||
|
||||
const addButton = screen.queryByLabelText("Add new model");
|
||||
@@ -316,10 +269,6 @@ describe(MethodRow.name, () => {
|
||||
it("enabled button to add new model when there is one modeled method", async () => {
|
||||
render({
|
||||
modeledMethods: [modeledMethod],
|
||||
viewState: {
|
||||
...viewState,
|
||||
showMultipleModels: true,
|
||||
},
|
||||
});
|
||||
|
||||
const addButton = screen.queryByLabelText("Add new model");
|
||||
@@ -335,10 +284,6 @@ describe(MethodRow.name, () => {
|
||||
{ ...modeledMethod, type: "source" },
|
||||
{ ...modeledMethod, type: "none" },
|
||||
],
|
||||
viewState: {
|
||||
...viewState,
|
||||
showMultipleModels: true,
|
||||
},
|
||||
});
|
||||
|
||||
const addButton = screen.queryByLabelText("Add new model");
|
||||
@@ -350,7 +295,7 @@ describe(MethodRow.name, () => {
|
||||
expect(removeButton?.getElementsByTagName("input")[0]).toBeEnabled();
|
||||
});
|
||||
|
||||
it("shows add model button on last row and remove model button on all other rows", async () => {
|
||||
it("shows add model button on first row and remove model button on all other rows", async () => {
|
||||
render({
|
||||
modeledMethods: [
|
||||
{ ...modeledMethod, type: "source" },
|
||||
@@ -358,10 +303,6 @@ describe(MethodRow.name, () => {
|
||||
{ ...modeledMethod, type: "summary" },
|
||||
{ ...modeledMethod, type: "none" },
|
||||
],
|
||||
viewState: {
|
||||
...viewState,
|
||||
showMultipleModels: true,
|
||||
},
|
||||
});
|
||||
|
||||
const addButtons = screen.queryAllByLabelText("Add new model");
|
||||
@@ -378,10 +319,6 @@ describe(MethodRow.name, () => {
|
||||
it("can add a new model", async () => {
|
||||
render({
|
||||
modeledMethods: [modeledMethod],
|
||||
viewState: {
|
||||
...viewState,
|
||||
showMultipleModels: true,
|
||||
},
|
||||
});
|
||||
|
||||
onChange.mockReset();
|
||||
@@ -401,7 +338,7 @@ describe(MethodRow.name, () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("can delete the first modeled method", async () => {
|
||||
it("cannot delete the first modeled method (but delete second instead)", async () => {
|
||||
render({
|
||||
modeledMethods: [
|
||||
{ ...modeledMethod, type: "source" },
|
||||
@@ -409,10 +346,6 @@ describe(MethodRow.name, () => {
|
||||
{ ...modeledMethod, type: "none" },
|
||||
{ ...modeledMethod, type: "summary" },
|
||||
],
|
||||
viewState: {
|
||||
...viewState,
|
||||
showMultipleModels: true,
|
||||
},
|
||||
});
|
||||
|
||||
onChange.mockReset();
|
||||
@@ -420,7 +353,7 @@ describe(MethodRow.name, () => {
|
||||
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
expect(onChange).toHaveBeenCalledWith(method.signature, [
|
||||
{ ...modeledMethod, type: "sink" },
|
||||
{ ...modeledMethod, type: "source" },
|
||||
{ ...modeledMethod, type: "none" },
|
||||
{ ...modeledMethod, type: "summary" },
|
||||
]);
|
||||
@@ -434,14 +367,10 @@ describe(MethodRow.name, () => {
|
||||
{ ...modeledMethod, type: "none" },
|
||||
{ ...modeledMethod, type: "summary" },
|
||||
],
|
||||
viewState: {
|
||||
...viewState,
|
||||
showMultipleModels: true,
|
||||
},
|
||||
});
|
||||
|
||||
onChange.mockReset();
|
||||
await userEvent.click(screen.getAllByLabelText("Remove model")[2]);
|
||||
await userEvent.click(screen.getAllByLabelText("Remove model")[1]);
|
||||
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
expect(onChange).toHaveBeenCalledWith(method.signature, [
|
||||
@@ -457,10 +386,6 @@ describe(MethodRow.name, () => {
|
||||
{ ...modeledMethod, type: "source" },
|
||||
{ ...modeledMethod, type: "sink" },
|
||||
],
|
||||
viewState: {
|
||||
...viewState,
|
||||
showMultipleModels: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.queryByRole("alert")).not.toBeInTheDocument();
|
||||
@@ -472,10 +397,6 @@ describe(MethodRow.name, () => {
|
||||
{ ...modeledMethod, type: "source" },
|
||||
{ ...modeledMethod, type: "source" },
|
||||
],
|
||||
viewState: {
|
||||
...viewState,
|
||||
showMultipleModels: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.getByRole("alert")).toBeInTheDocument();
|
||||
@@ -494,10 +415,6 @@ describe(MethodRow.name, () => {
|
||||
{ ...modeledMethod, type: "source" },
|
||||
{ ...modeledMethod, type: "neutral", kind: "source" },
|
||||
],
|
||||
viewState: {
|
||||
...viewState,
|
||||
showMultipleModels: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.getAllByRole("alert").length).toBe(2);
|
||||
|
||||
@@ -2,8 +2,9 @@ import * as React from "react";
|
||||
import { select } from "d3";
|
||||
import { jumpToLocation } from "./result-table-utils";
|
||||
import { graphviz, GraphvizOptions } from "d3-graphviz";
|
||||
import { tryGetLocationFromString } from "../../common/bqrs-utils";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import { mapUrlValue } from "../../common/bqrs-raw-results-mapper";
|
||||
import { isUrlValueResolvable } from "../../common/raw-result-types";
|
||||
|
||||
type GraphProps = {
|
||||
graphData: string;
|
||||
@@ -42,8 +43,8 @@ export function Graph({ graphData, databaseUri }: GraphProps) {
|
||||
.attributer(function (d) {
|
||||
if (d.tag === "a") {
|
||||
const url = d.attributes["xlink:href"] || d.attributes["href"];
|
||||
const loc = tryGetLocationFromString(url);
|
||||
if (loc !== undefined) {
|
||||
const loc = mapUrlValue(url);
|
||||
if (loc !== undefined && isUrlValueResolvable(loc)) {
|
||||
d.attributes["xlink:href"] = "#";
|
||||
d.attributes["href"] = "#";
|
||||
loc.uri = `file://${loc.uri}`;
|
||||
|
||||
@@ -6,21 +6,22 @@ import {
|
||||
RawResultsSortState,
|
||||
NavigateMsg,
|
||||
NavigationDirection,
|
||||
RawTableResultSet,
|
||||
} from "../../common/interface-types";
|
||||
import RawTableHeader from "./RawTableHeader";
|
||||
import RawTableRow from "./RawTableRow";
|
||||
import { ResultRow } from "../../common/bqrs-cli-types";
|
||||
import { onNavigation } from "./ResultsApp";
|
||||
import { tryGetResolvableLocation } from "../../common/bqrs-utils";
|
||||
import { sendTelemetry } from "../common/telemetry";
|
||||
import { assertNever } from "../../common/helpers-pure";
|
||||
import { EmptyQueryResultsMessage } from "./EmptyQueryResultsMessage";
|
||||
import { useScrollIntoView } from "./useScrollIntoView";
|
||||
import {
|
||||
isUrlValueResolvable,
|
||||
RawResultSet,
|
||||
} from "../../common/raw-result-types";
|
||||
|
||||
type RawTableProps = {
|
||||
databaseUri: string;
|
||||
resultSet: RawTableResultSet;
|
||||
resultSet: RawResultSet;
|
||||
sortState?: RawResultsSortState;
|
||||
offset: number;
|
||||
};
|
||||
@@ -67,11 +68,12 @@ export function RawTable({
|
||||
return prevSelectedItem;
|
||||
}
|
||||
const cellData = rowData[nextColumn];
|
||||
if (cellData != null && typeof cellData === "object") {
|
||||
const location = tryGetResolvableLocation(cellData.url);
|
||||
if (location !== undefined) {
|
||||
jumpToLocation(location, databaseUri);
|
||||
}
|
||||
if (
|
||||
cellData?.type === "entity" &&
|
||||
cellData.value.url &&
|
||||
isUrlValueResolvable(cellData.value.url)
|
||||
) {
|
||||
jumpToLocation(cellData.value.url, databaseUri);
|
||||
}
|
||||
return { row: nextRow, column: nextColumn };
|
||||
});
|
||||
@@ -126,7 +128,7 @@ export function RawTable({
|
||||
return <EmptyQueryResultsMessage />;
|
||||
}
|
||||
|
||||
const tableRows = dataRows.map((row: ResultRow, rowIndex: number) => (
|
||||
const tableRows = dataRows.map((row, rowIndex) => (
|
||||
<RawTableRow
|
||||
key={rowIndex}
|
||||
rowIndex={rowIndex + offset}
|
||||
@@ -159,8 +161,8 @@ export function RawTable({
|
||||
return (
|
||||
<table className={className}>
|
||||
<RawTableHeader
|
||||
columns={resultSet.schema.columns}
|
||||
schemaName={resultSet.schema.name}
|
||||
columns={resultSet.columns}
|
||||
schemaName={resultSet.name}
|
||||
sortState={sortState}
|
||||
/>
|
||||
<tbody>{tableRows}</tbody>
|
||||
|
||||
@@ -6,11 +6,10 @@ import {
|
||||
SortDirection,
|
||||
} from "../../common/interface-types";
|
||||
import { nextSortDirection } from "./result-table-utils";
|
||||
import { Column } from "../../common/raw-result-types";
|
||||
|
||||
interface Props {
|
||||
readonly columns: ReadonlyArray<{
|
||||
name?: string;
|
||||
}>;
|
||||
readonly columns: readonly Column[];
|
||||
readonly schemaName: string;
|
||||
readonly sortState?: RawResultsSortState;
|
||||
readonly preventSort?: boolean;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import * as React from "react";
|
||||
import { ResultRow } from "../../common/bqrs-cli-types";
|
||||
import { selectedRowClassName, zebraStripe } from "./result-table-utils";
|
||||
import RawTableValue from "./RawTableValue";
|
||||
import { Row } from "../../common/raw-result-types";
|
||||
|
||||
interface Props {
|
||||
rowIndex: number;
|
||||
row: ResultRow;
|
||||
row: Row;
|
||||
databaseUri: string;
|
||||
className?: string;
|
||||
selectedColumn?: number;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { Location } from "./locations/Location";
|
||||
import { CellValue } from "../../common/bqrs-cli-types";
|
||||
import { RawNumberValue } from "../common/RawNumberValue";
|
||||
import { CellValue } from "../../common/raw-result-types";
|
||||
import { assertNever } from "../../common/helpers-pure";
|
||||
|
||||
interface Props {
|
||||
value: CellValue;
|
||||
@@ -15,21 +16,23 @@ export default function RawTableValue({
|
||||
databaseUri,
|
||||
onSelected,
|
||||
}: Props): JSX.Element {
|
||||
switch (typeof value) {
|
||||
switch (value.type) {
|
||||
case "boolean":
|
||||
return <span>{value.toString()}</span>;
|
||||
return <span>{value.value.toString()}</span>;
|
||||
case "number":
|
||||
return <RawNumberValue value={value} />;
|
||||
return <RawNumberValue value={value.value} />;
|
||||
case "string":
|
||||
return <Location label={value.toString()} />;
|
||||
default:
|
||||
return <Location label={value.value} />;
|
||||
case "entity":
|
||||
return (
|
||||
<Location
|
||||
loc={value.url}
|
||||
label={value.label}
|
||||
loc={value.value.url}
|
||||
label={value.value.label}
|
||||
databaseUri={databaseUri}
|
||||
onClick={onSelected}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
assertNever(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ interface Props {
|
||||
function getResultCount(resultSet: ResultSet): number {
|
||||
switch (resultSet.t) {
|
||||
case "RawResultSet":
|
||||
return resultSet.schema.rows;
|
||||
return resultSet.resultSet.totalRowCount;
|
||||
case "InterpretedResultSet":
|
||||
return resultSet.interpretation.numTotalResults;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export function ResultTable(props: ResultTableProps) {
|
||||
const { resultSet } = props;
|
||||
switch (resultSet.t) {
|
||||
case "RawResultSet":
|
||||
return <RawTable {...props} resultSet={resultSet} />;
|
||||
return <RawTable {...props} resultSet={resultSet.resultSet} />;
|
||||
case "InterpretedResultSet": {
|
||||
const data = resultSet.interpretation.data;
|
||||
switch (data.t) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import { ResultTablesHeader } from "./ResultTablesHeader";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { ResultCount } from "./ResultCount";
|
||||
import { ProblemsViewCheckbox } from "./ProblemsViewCheckbox";
|
||||
import { assertNever } from "../../common/helpers-pure";
|
||||
|
||||
/**
|
||||
* Properties for the `ResultTables` component.
|
||||
@@ -74,15 +75,6 @@ function getResultSets(
|
||||
const tableName = getInterpretedTableName(interpretation);
|
||||
resultSets.push({
|
||||
t: "InterpretedResultSet",
|
||||
// FIXME: The values of version, columns, tupleCount are
|
||||
// unused stubs because a InterpretedResultSet schema isn't used the
|
||||
// same way as a RawResultSet. Probably should pull `name` field
|
||||
// out.
|
||||
schema: {
|
||||
name: tableName,
|
||||
rows: 1,
|
||||
columns: [],
|
||||
},
|
||||
name: tableName,
|
||||
interpretation,
|
||||
});
|
||||
@@ -149,7 +141,7 @@ export function ResultTables(props: ResultTablesProps) {
|
||||
const resultSetExists =
|
||||
parsedResultSets.resultSetNames.some((v) => selectedTable === v) ||
|
||||
getResultSets(rawResultSets, interpretation).some(
|
||||
(v) => selectedTable === v.schema.name,
|
||||
(v) => selectedTable === getResultSetName(v),
|
||||
);
|
||||
|
||||
// If the selected result set does not exist, select the default result set.
|
||||
@@ -207,11 +199,14 @@ export function ResultTables(props: ResultTablesProps) {
|
||||
|
||||
const resultSet = useMemo(
|
||||
() =>
|
||||
resultSets.find((resultSet) => resultSet.schema.name === selectedTable),
|
||||
resultSets.find(
|
||||
(resultSet) => selectedTable === getResultSetName(resultSet),
|
||||
),
|
||||
[resultSets, selectedTable],
|
||||
);
|
||||
const nonemptyRawResults = resultSets.some(
|
||||
(resultSet) => resultSet.t === "RawResultSet" && resultSet.rows.length > 0,
|
||||
(resultSet) =>
|
||||
resultSet.t === "RawResultSet" && resultSet.resultSet.rows.length > 0,
|
||||
);
|
||||
|
||||
const resultSetOptions = resultSetNames.map((name) => (
|
||||
@@ -219,6 +214,9 @@ export function ResultTables(props: ResultTablesProps) {
|
||||
{name}
|
||||
</option>
|
||||
));
|
||||
|
||||
const resultSetName = resultSet ? getResultSetName(resultSet) : undefined;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ResultTablesHeader {...props} selectedTable={selectedTable} />
|
||||
@@ -239,13 +237,13 @@ export function ResultTables(props: ResultTablesProps) {
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
{resultSet && (
|
||||
{resultSet && resultSetName && (
|
||||
<ResultTable
|
||||
key={resultSet.schema.name}
|
||||
key={resultSetName}
|
||||
resultSet={resultSet}
|
||||
databaseUri={database.databaseUri}
|
||||
resultsPath={resultsPath}
|
||||
sortState={sortStates.get(resultSet.schema.name)}
|
||||
sortState={sortStates.get(resultSetName)}
|
||||
nonemptyRawResults={nonemptyRawResults}
|
||||
showRawResults={() => {
|
||||
setSelectedTable(SELECT_TABLE_NAME);
|
||||
@@ -260,6 +258,17 @@ export function ResultTables(props: ResultTablesProps) {
|
||||
|
||||
function getDefaultResultSet(resultSets: readonly ResultSet[]): string {
|
||||
return getDefaultResultSetName(
|
||||
resultSets.map((resultSet) => resultSet.schema.name),
|
||||
resultSets.map((resultSet) => getResultSetName(resultSet)),
|
||||
);
|
||||
}
|
||||
|
||||
function getResultSetName(resultSet: ResultSet): string {
|
||||
switch (resultSet.t) {
|
||||
case "RawResultSet":
|
||||
return resultSet.resultSet.name;
|
||||
case "InterpretedResultSet":
|
||||
return resultSet.name;
|
||||
default:
|
||||
assertNever(resultSet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,11 +174,6 @@ export function ResultsApp() {
|
||||
resultSet: {
|
||||
t: "InterpretedResultSet",
|
||||
name: tableName,
|
||||
schema: {
|
||||
name: tableName,
|
||||
rows: 1,
|
||||
columns: [],
|
||||
},
|
||||
interpretation: msg.interpretation,
|
||||
},
|
||||
selectedTable: tableName,
|
||||
|
||||
@@ -8,8 +8,8 @@ import {
|
||||
} from "../../../common/interface-types";
|
||||
import * as fs from "fs-extra";
|
||||
import { resolve } from "path";
|
||||
import { ColumnKindCode } from "../../../common/bqrs-cli-types";
|
||||
import { postMessage } from "../../common/post-message";
|
||||
import { ColumnKind } from "../../../common/raw-result-types";
|
||||
|
||||
const exampleSarif = fs.readJSONSync(
|
||||
resolve(__dirname, "../../../../test/data/sarif/validSarif.sarif"),
|
||||
@@ -64,16 +64,6 @@ describe(ResultsApp.name, () => {
|
||||
resultSetNames: ["#select"],
|
||||
resultSet: {
|
||||
t: "InterpretedResultSet",
|
||||
schema: {
|
||||
name: "#select",
|
||||
rows: 1,
|
||||
columns: [
|
||||
{
|
||||
name: "Path",
|
||||
kind: ColumnKindCode.STRING,
|
||||
},
|
||||
],
|
||||
},
|
||||
name: "#select",
|
||||
interpretation,
|
||||
},
|
||||
@@ -120,13 +110,19 @@ describe(ResultsApp.name, () => {
|
||||
numPages: 1,
|
||||
numInterpretedPages: 0,
|
||||
resultSet: {
|
||||
schema: {
|
||||
resultSet: {
|
||||
name: "#select",
|
||||
rows: 1,
|
||||
columns: [{ kind: "s" }],
|
||||
pagination: { "step-size": 200, offsets: [13] },
|
||||
totalRowCount: 1,
|
||||
columns: [{ kind: ColumnKind.String }],
|
||||
rows: [
|
||||
[
|
||||
{
|
||||
type: "string",
|
||||
value: "foobar1",
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
rows: [["foobar1"]],
|
||||
t: "RawResultSet",
|
||||
},
|
||||
resultSetNames: ["#select"],
|
||||
@@ -158,13 +154,19 @@ describe(ResultsApp.name, () => {
|
||||
numPages: 1,
|
||||
numInterpretedPages: 0,
|
||||
resultSet: {
|
||||
schema: {
|
||||
resultSet: {
|
||||
name: "#Quick_evaluation_of_expression",
|
||||
rows: 1,
|
||||
columns: [{ name: "#expr_result", kind: "s" }],
|
||||
pagination: { "step-size": 200, offsets: [49] },
|
||||
totalRowCount: 1,
|
||||
columns: [{ name: "#expr_result", kind: ColumnKind.String }],
|
||||
rows: [
|
||||
[
|
||||
{
|
||||
type: "string",
|
||||
value: "foobar2",
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
rows: [["foobar2"]],
|
||||
t: "RawResultSet",
|
||||
},
|
||||
resultSetNames: ["#Quick_evaluation_of_expression"],
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import * as React from "react";
|
||||
import { useCallback } from "react";
|
||||
import { ResolvableLocationValue } from "../../../common/bqrs-cli-types";
|
||||
import { jumpToLocation } from "../result-table-utils";
|
||||
import TextButton from "../../common/TextButton";
|
||||
import { styled } from "styled-components";
|
||||
import { UrlValueResolvable } from "../../../common/raw-result-types";
|
||||
|
||||
interface Props {
|
||||
loc: ResolvableLocationValue;
|
||||
loc: UrlValueResolvable;
|
||||
label: string;
|
||||
databaseUri: string;
|
||||
onClick?: () => void;
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import * as React from "react";
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { UrlValue } from "../../../common/bqrs-cli-types";
|
||||
import {
|
||||
isStringLoc,
|
||||
tryGetResolvableLocation,
|
||||
} from "../../../common/bqrs-utils";
|
||||
import { UrlValue } from "../../../common/raw-result-types";
|
||||
import { convertNonPrintableChars } from "../../../common/text-utils";
|
||||
import { NonClickableLocation } from "./NonClickableLocation";
|
||||
import { ClickableLocation } from "./ClickableLocation";
|
||||
@@ -28,24 +24,23 @@ export function Location({
|
||||
title,
|
||||
onClick,
|
||||
}: Props): JSX.Element {
|
||||
const resolvableLoc = useMemo(() => tryGetResolvableLocation(loc), [loc]);
|
||||
const displayLabel = useMemo(() => convertNonPrintableChars(label), [label]);
|
||||
|
||||
if (loc === undefined) {
|
||||
return <NonClickableLocation msg={displayLabel} />;
|
||||
}
|
||||
|
||||
if (isStringLoc(loc)) {
|
||||
return <a href={loc}>{loc}</a>;
|
||||
if (loc.type === "string") {
|
||||
return <a href={loc.value}>{loc.value}</a>;
|
||||
}
|
||||
|
||||
if (databaseUri === undefined || resolvableLoc === undefined) {
|
||||
if (databaseUri === undefined) {
|
||||
return <NonClickableLocation msg={displayLabel} locationHint={title} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<ClickableLocation
|
||||
loc={resolvableLoc}
|
||||
loc={loc}
|
||||
label={displayLabel}
|
||||
databaseUri={databaseUri}
|
||||
onClick={onClick}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from "react";
|
||||
import * as Sarif from "sarif";
|
||||
import { isLineColumnLoc, isWholeFileLoc } from "../../../common/bqrs-utils";
|
||||
import { parseSarifLocation } from "../../../common/sarif-utils";
|
||||
import { basename } from "../../../common/path";
|
||||
import { useMemo } from "react";
|
||||
@@ -35,7 +34,7 @@ export function SarifLocation({
|
||||
return <Location label={text || "[no location]"} title={parsedLoc?.hint} />;
|
||||
}
|
||||
|
||||
if (isWholeFileLoc(parsedLoc)) {
|
||||
if (parsedLoc.type === "wholeFileLocation") {
|
||||
return (
|
||||
<Location
|
||||
loc={parsedLoc}
|
||||
@@ -47,7 +46,7 @@ export function SarifLocation({
|
||||
);
|
||||
}
|
||||
|
||||
if (isLineColumnLoc(parsedLoc)) {
|
||||
if (parsedLoc.type === "lineColumnLocation") {
|
||||
return (
|
||||
<Location
|
||||
loc={parsedLoc}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { ResolvableLocationValue } from "../../common/bqrs-cli-types";
|
||||
import {
|
||||
RawResultsSortState,
|
||||
QueryMetadata,
|
||||
SortDirection,
|
||||
RawResultsSortState,
|
||||
ResultSet,
|
||||
SortDirection,
|
||||
} from "../../common/interface-types";
|
||||
import { assertNever } from "../../common/helpers-pure";
|
||||
import { vscode } from "../vscode-api";
|
||||
import { UrlValueResolvable } from "../../common/raw-result-types";
|
||||
|
||||
export interface ResultTableProps {
|
||||
resultSet: ResultSet;
|
||||
@@ -40,7 +40,7 @@ const oddRowClassName = "vscode-codeql__result-table-row--odd";
|
||||
export const selectedRowClassName = "vscode-codeql__result-table-row--selected";
|
||||
|
||||
export function jumpToLocation(
|
||||
loc: ResolvableLocationValue,
|
||||
loc: UrlValueResolvable,
|
||||
databaseUri: string,
|
||||
): void {
|
||||
vscode.postMessage({
|
||||
|
||||
@@ -125,8 +125,7 @@ export const AnalyzedRepoItemContent = ({
|
||||
{rawResults && chosenResultFormat === ResultFormat.RawResults && (
|
||||
<RawResultsContainer>
|
||||
<RawResultsTable
|
||||
schema={rawResults.schema}
|
||||
results={rawResults.resultSet}
|
||||
resultSet={rawResults.resultSet}
|
||||
fileLinkPrefix={rawResults.fileLinkPrefix}
|
||||
sourceLocationPrefix={rawResults.sourceLocationPrefix}
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react";
|
||||
|
||||
import { CellValue } from "../../common/bqrs-cli-types";
|
||||
import { CellValue } from "../../common/raw-result-types";
|
||||
import { sendTelemetry } from "../common/telemetry";
|
||||
import { convertNonPrintableChars } from "../../common/text-utils";
|
||||
import { tryGetRemoteLocation } from "../../common/bqrs-utils";
|
||||
@@ -20,20 +20,20 @@ export const RawResultCell = ({
|
||||
fileLinkPrefix,
|
||||
sourceLocationPrefix,
|
||||
}: CellProps) => {
|
||||
switch (typeof value) {
|
||||
switch (value.type) {
|
||||
case "boolean":
|
||||
return <span>{value.toString()}</span>;
|
||||
return <span>{value.value.toString()}</span>;
|
||||
case "number":
|
||||
return <RawNumberValue value={value} />;
|
||||
return <RawNumberValue value={value.value} />;
|
||||
case "string":
|
||||
return <span>{convertNonPrintableChars(value.toString())}</span>;
|
||||
case "object": {
|
||||
return <span>{convertNonPrintableChars(value.value.toString())}</span>;
|
||||
case "entity": {
|
||||
const url = tryGetRemoteLocation(
|
||||
value.url,
|
||||
value.value.url,
|
||||
fileLinkPrefix,
|
||||
sourceLocationPrefix,
|
||||
);
|
||||
const safeLabel = convertNonPrintableChars(value.label);
|
||||
const safeLabel = convertNonPrintableChars(value.value.label);
|
||||
if (url) {
|
||||
return (
|
||||
<VSCodeLink onClick={sendRawResultsLinkTelemetry} href={url}>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
import { styled } from "styled-components";
|
||||
|
||||
import { CellValue } from "../../common/bqrs-cli-types";
|
||||
import { Row } from "../../common/raw-result-types";
|
||||
import { RawResultCell } from "./RawResultCell";
|
||||
|
||||
const StyledRow = styled.div`
|
||||
@@ -14,7 +14,7 @@ const StyledRow = styled.div`
|
||||
`;
|
||||
|
||||
type RowProps = {
|
||||
row: CellValue[];
|
||||
row: Row;
|
||||
fileLinkPrefix: string;
|
||||
sourceLocationPrefix: string;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as React from "react";
|
||||
import { useState } from "react";
|
||||
import { styled } from "styled-components";
|
||||
import { RawResultSet, ResultSetSchema } from "../../common/bqrs-cli-types";
|
||||
import TextButton from "../common/TextButton";
|
||||
import { useTelemetryOnChange } from "../common/telemetry";
|
||||
import { RawResultSet } from "../../common/raw-result-types";
|
||||
import { RawResultRow } from "./RawResultRow";
|
||||
|
||||
const numOfResultsInContractedMode = 5;
|
||||
@@ -26,8 +26,7 @@ const TableContainer = styled.div<TableContainerProps>`
|
||||
`;
|
||||
|
||||
type RawResultsTableProps = {
|
||||
schema: ResultSetSchema;
|
||||
results: RawResultSet;
|
||||
resultSet: RawResultSet;
|
||||
fileLinkPrefix: string;
|
||||
sourceLocationPrefix: string;
|
||||
};
|
||||
@@ -35,8 +34,7 @@ type RawResultsTableProps = {
|
||||
const filterTableExpandedTelemetry = (v: boolean) => v;
|
||||
|
||||
const RawResultsTable = ({
|
||||
schema,
|
||||
results,
|
||||
resultSet,
|
||||
fileLinkPrefix,
|
||||
sourceLocationPrefix,
|
||||
}: RawResultsTableProps) => {
|
||||
@@ -45,14 +43,14 @@ const RawResultsTable = ({
|
||||
filterTelemetryOnValue: filterTableExpandedTelemetry,
|
||||
});
|
||||
const numOfResultsToShow = tableExpanded
|
||||
? results.rows.length
|
||||
? resultSet.rows.length
|
||||
: numOfResultsInContractedMode;
|
||||
const showButton = results.rows.length > numOfResultsInContractedMode;
|
||||
const showButton = resultSet.rows.length > numOfResultsInContractedMode;
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableContainer $columnCount={schema.columns.length}>
|
||||
{results.rows.slice(0, numOfResultsToShow).map((row, rowIndex) => (
|
||||
<TableContainer $columnCount={resultSet.columns.length}>
|
||||
{resultSet.rows.slice(0, numOfResultsToShow).map((row, rowIndex) => (
|
||||
<RawResultRow
|
||||
key={rowIndex}
|
||||
row={row}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
AnalyzedRepoItemContentProps,
|
||||
} from "../AnalyzedRepoItemContent";
|
||||
import { ResultFormat } from "../../../variant-analysis/shared/variant-analysis-result-format";
|
||||
import { ColumnKind } from "../../../common/raw-result-types";
|
||||
|
||||
describe(AnalyzedRepoItemContent.name, () => {
|
||||
const render = (props: Partial<AnalyzedRepoItemContentProps> = {}) => {
|
||||
@@ -64,40 +65,49 @@ describe(AnalyzedRepoItemContent.name, () => {
|
||||
render({
|
||||
status: VariantAnalysisRepoStatus.Succeeded,
|
||||
rawResults: {
|
||||
schema: {
|
||||
resultSet: {
|
||||
name: "#select",
|
||||
rows: 1,
|
||||
totalRowCount: 2,
|
||||
columns: [
|
||||
{
|
||||
kind: "i",
|
||||
kind: ColumnKind.Integer,
|
||||
},
|
||||
{
|
||||
kind: "s",
|
||||
kind: ColumnKind.String,
|
||||
},
|
||||
{
|
||||
kind: "b",
|
||||
kind: ColumnKind.Boolean,
|
||||
},
|
||||
],
|
||||
},
|
||||
resultSet: {
|
||||
schema: {
|
||||
name: "#select",
|
||||
rows: 1,
|
||||
columns: [
|
||||
rows: [
|
||||
[
|
||||
{
|
||||
kind: "i",
|
||||
type: "number",
|
||||
value: 60688,
|
||||
},
|
||||
{
|
||||
kind: "s",
|
||||
type: "string",
|
||||
value: "foo",
|
||||
},
|
||||
{
|
||||
kind: "b",
|
||||
type: "boolean",
|
||||
value: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
type: "number",
|
||||
value: 5,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "bar",
|
||||
},
|
||||
{
|
||||
type: "boolean",
|
||||
value: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
rows: [
|
||||
[60688, "foo", true],
|
||||
[5, "bar", false],
|
||||
],
|
||||
},
|
||||
fileLinkPrefix:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[
|
||||
"v2.15.3",
|
||||
"v2.15.4",
|
||||
"v2.14.6",
|
||||
"v2.13.5",
|
||||
"v2.12.7",
|
||||
|
||||
@@ -1,27 +1,19 @@
|
||||
import { faker } from "@faker-js/faker";
|
||||
import {
|
||||
DbConfig,
|
||||
LocalDatabase,
|
||||
LocalList,
|
||||
RemoteRepositoryList,
|
||||
SelectedDbItem,
|
||||
DB_CONFIG_VERSION,
|
||||
} from "../../src/databases/config/db-config";
|
||||
import { DatabaseOrigin } from "../../src/databases/local-databases/database-origin";
|
||||
|
||||
export function createDbConfig({
|
||||
remoteLists = [],
|
||||
remoteOwners = [],
|
||||
remoteRepos = [],
|
||||
localLists = [],
|
||||
localDbs = [],
|
||||
selected = undefined,
|
||||
}: {
|
||||
remoteLists?: RemoteRepositoryList[];
|
||||
remoteOwners?: string[];
|
||||
remoteRepos?: string[];
|
||||
localLists?: LocalList[];
|
||||
localDbs?: LocalDatabase[];
|
||||
selected?: SelectedDbItem;
|
||||
} = {}): DbConfig {
|
||||
return {
|
||||
@@ -32,35 +24,7 @@ export function createDbConfig({
|
||||
owners: remoteOwners,
|
||||
repositories: remoteRepos,
|
||||
},
|
||||
local: {
|
||||
lists: localLists,
|
||||
databases: localDbs,
|
||||
},
|
||||
},
|
||||
selected,
|
||||
};
|
||||
}
|
||||
|
||||
export function createLocalDbConfigItem({
|
||||
name = `database${faker.number.int()}`,
|
||||
dateAdded = faker.date.past().getTime(),
|
||||
language = `language${faker.number.int()}`,
|
||||
storagePath = `storagePath${faker.number.int()}`,
|
||||
origin = {
|
||||
type: "folder",
|
||||
},
|
||||
}: {
|
||||
name?: string;
|
||||
dateAdded?: number;
|
||||
language?: string;
|
||||
storagePath?: string;
|
||||
origin?: DatabaseOrigin;
|
||||
} = {}): LocalDatabase {
|
||||
return {
|
||||
name,
|
||||
dateAdded,
|
||||
language,
|
||||
storagePath,
|
||||
origin,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
import { faker } from "@faker-js/faker";
|
||||
import {
|
||||
DbItemKind,
|
||||
LocalDatabaseDbItem,
|
||||
LocalDbItem,
|
||||
LocalListDbItem,
|
||||
RemoteDbItem,
|
||||
RemoteOwnerDbItem,
|
||||
RemoteRepoDbItem,
|
||||
RemoteSystemDefinedListDbItem,
|
||||
RemoteUserDefinedListDbItem,
|
||||
RootLocalDbItem,
|
||||
RootRemoteDbItem,
|
||||
} from "../../src/databases/db-item";
|
||||
import { DatabaseOrigin } from "../../src/databases/local-databases/database-origin";
|
||||
|
||||
// Root Remote Db Items
|
||||
export function createRootRemoteDbItem({
|
||||
@@ -103,66 +98,3 @@ export function createRemoteUserDefinedListDbItem({
|
||||
repos,
|
||||
};
|
||||
}
|
||||
|
||||
// Root Local Db Items
|
||||
export function createRootLocalDbItem({
|
||||
children = [],
|
||||
expanded = false,
|
||||
}: {
|
||||
children?: LocalDbItem[];
|
||||
expanded?: boolean;
|
||||
} = {}): RootLocalDbItem {
|
||||
return {
|
||||
kind: DbItemKind.RootLocal,
|
||||
children,
|
||||
expanded,
|
||||
};
|
||||
}
|
||||
|
||||
export function createLocalDatabaseDbItem({
|
||||
databaseName = `database${faker.number.int()}`,
|
||||
dateAdded = faker.date.past().getTime(),
|
||||
language = `language${faker.number.int()}`,
|
||||
storagePath = `storagePath${faker.number.int()}`,
|
||||
selected = false,
|
||||
origin = {
|
||||
type: "folder",
|
||||
},
|
||||
}: {
|
||||
databaseName?: string;
|
||||
dateAdded?: number;
|
||||
language?: string;
|
||||
storagePath?: string;
|
||||
selected?: boolean;
|
||||
origin?: DatabaseOrigin;
|
||||
} = {}): LocalDatabaseDbItem {
|
||||
return {
|
||||
kind: DbItemKind.LocalDatabase,
|
||||
selected,
|
||||
databaseName,
|
||||
dateAdded,
|
||||
language,
|
||||
storagePath,
|
||||
origin,
|
||||
};
|
||||
}
|
||||
|
||||
export function createLocalListDbItem({
|
||||
listName = `top_${faker.number.int()}`,
|
||||
selected = false,
|
||||
expanded = false,
|
||||
databases = [],
|
||||
}: {
|
||||
listName?: string;
|
||||
databases?: LocalDatabaseDbItem[];
|
||||
selected?: boolean;
|
||||
expanded?: boolean;
|
||||
} = {}): LocalListDbItem {
|
||||
return {
|
||||
kind: DbItemKind.LocalList,
|
||||
selected,
|
||||
expanded,
|
||||
databases,
|
||||
listName,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
Usage,
|
||||
Method,
|
||||
CallClassification,
|
||||
Method,
|
||||
Usage,
|
||||
} from "../../../src/model-editor/method";
|
||||
import { ResolvableLocationValue } from "../../../src/common/bqrs-cli-types";
|
||||
import { UrlValueResolvable } from "../../../src/common/raw-result-types";
|
||||
|
||||
export function createMethod(data: Partial<Method> = {}): Method {
|
||||
return {
|
||||
@@ -24,11 +24,13 @@ export function createMethod(data: Partial<Method> = {}): Method {
|
||||
export function createUsage({
|
||||
classification = CallClassification.Unknown,
|
||||
label = "test",
|
||||
url = {} as ResolvableLocationValue,
|
||||
url = {
|
||||
type: "wholeFileLocation",
|
||||
} as UrlValueResolvable,
|
||||
}: {
|
||||
classification?: CallClassification;
|
||||
label?: string;
|
||||
url?: ResolvableLocationValue;
|
||||
url?: UrlValueResolvable;
|
||||
} = {}): Usage {
|
||||
return {
|
||||
classification,
|
||||
|
||||
@@ -11,7 +11,6 @@ export function createMockModelEditorViewState(
|
||||
mode: Mode.Application,
|
||||
showGenerateButton: false,
|
||||
showLlmButton: false,
|
||||
showMultipleModels: false,
|
||||
showModeSwitchButton: true,
|
||||
extensionPack: createMockExtensionPack(),
|
||||
sourceArchiveAvailable: true,
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
import {
|
||||
bqrsToResultSet,
|
||||
mapUrlValue,
|
||||
} from "../../../src/common/bqrs-raw-results-mapper";
|
||||
import {
|
||||
BqrsResultSetSchema,
|
||||
DecodedBqrsChunk,
|
||||
} from "../../../src/common/bqrs-cli-types";
|
||||
import { ColumnKind } from "../../../src/common/raw-result-types";
|
||||
|
||||
describe("bqrsToResultSet", () => {
|
||||
const schema: BqrsResultSetSchema = {
|
||||
name: "#select",
|
||||
columns: [
|
||||
{
|
||||
name: "endpoint",
|
||||
kind: "e",
|
||||
},
|
||||
{
|
||||
kind: "s",
|
||||
},
|
||||
{
|
||||
kind: "s",
|
||||
},
|
||||
{
|
||||
kind: "s",
|
||||
},
|
||||
{
|
||||
kind: "s",
|
||||
},
|
||||
{
|
||||
name: "supported",
|
||||
kind: "b",
|
||||
},
|
||||
{
|
||||
kind: "s",
|
||||
},
|
||||
{
|
||||
name: "type",
|
||||
kind: "s",
|
||||
},
|
||||
],
|
||||
rows: 2,
|
||||
};
|
||||
|
||||
const chunk: DecodedBqrsChunk = {
|
||||
columns: [
|
||||
{ name: "endpoint", kind: "Entity" },
|
||||
{ kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ kind: "String" },
|
||||
{ name: "supported", kind: "Boolean" },
|
||||
{ kind: "String" },
|
||||
{ name: "type", kind: "String" },
|
||||
],
|
||||
tuples: [
|
||||
[
|
||||
{
|
||||
label: "connect",
|
||||
url: {
|
||||
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
|
||||
startLine: 22,
|
||||
startColumn: 19,
|
||||
endLine: 22,
|
||||
endColumn: 25,
|
||||
},
|
||||
},
|
||||
"org.example",
|
||||
"HelloController",
|
||||
"connect",
|
||||
"(String)",
|
||||
false,
|
||||
"example",
|
||||
"",
|
||||
],
|
||||
[
|
||||
{
|
||||
label: "index",
|
||||
url: {
|
||||
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
|
||||
startLine: 13,
|
||||
startColumn: 19,
|
||||
endLine: 13,
|
||||
endColumn: 23,
|
||||
},
|
||||
},
|
||||
"org.example",
|
||||
"HelloController",
|
||||
"index",
|
||||
"(String)",
|
||||
true,
|
||||
"example",
|
||||
"summary",
|
||||
],
|
||||
[
|
||||
{
|
||||
label: "index",
|
||||
url: "file://home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java:15:19:15:23",
|
||||
},
|
||||
"org.example",
|
||||
"HelloController",
|
||||
"index",
|
||||
"(String)",
|
||||
true,
|
||||
"example",
|
||||
"summary",
|
||||
],
|
||||
],
|
||||
};
|
||||
|
||||
it("creates a result set", () => {
|
||||
expect(bqrsToResultSet(schema, chunk)).toEqual({
|
||||
name: "#select",
|
||||
totalRowCount: 2,
|
||||
columns: [
|
||||
{
|
||||
name: "endpoint",
|
||||
kind: ColumnKind.Entity,
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.String,
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.String,
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.String,
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.String,
|
||||
},
|
||||
{
|
||||
name: "supported",
|
||||
kind: ColumnKind.Boolean,
|
||||
},
|
||||
{
|
||||
kind: ColumnKind.String,
|
||||
},
|
||||
{
|
||||
name: "type",
|
||||
kind: ColumnKind.String,
|
||||
},
|
||||
],
|
||||
rows: [
|
||||
[
|
||||
{
|
||||
type: "entity",
|
||||
value: {
|
||||
label: "connect",
|
||||
url: {
|
||||
type: "lineColumnLocation",
|
||||
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
|
||||
startLine: 22,
|
||||
startColumn: 19,
|
||||
endLine: 22,
|
||||
endColumn: 25,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "org.example",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "HelloController",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "connect",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "(String)",
|
||||
},
|
||||
{
|
||||
type: "boolean",
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "example",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
type: "entity",
|
||||
value: {
|
||||
label: "index",
|
||||
url: {
|
||||
type: "lineColumnLocation",
|
||||
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
|
||||
startLine: 13,
|
||||
startColumn: 19,
|
||||
endLine: 13,
|
||||
endColumn: 23,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "org.example",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "HelloController",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "index",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "(String)",
|
||||
},
|
||||
{
|
||||
type: "boolean",
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "example",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "summary",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
type: "entity",
|
||||
value: {
|
||||
label: "index",
|
||||
url: {
|
||||
type: "lineColumnLocation",
|
||||
uri: "home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
|
||||
startLine: 15,
|
||||
startColumn: 19,
|
||||
endLine: 15,
|
||||
endColumn: 23,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "org.example",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "HelloController",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "index",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "(String)",
|
||||
},
|
||||
{
|
||||
type: "boolean",
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "example",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
value: "summary",
|
||||
},
|
||||
],
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("mapUrlValue", () => {
|
||||
it("should detect Windows whole-file locations", () => {
|
||||
const loc = "file://C:/path/to/file.ext:0:0:0:0";
|
||||
const wholeFileLoc = mapUrlValue(loc);
|
||||
expect(wholeFileLoc).toEqual({
|
||||
type: "wholeFileLocation",
|
||||
uri: "C:/path/to/file.ext",
|
||||
});
|
||||
});
|
||||
it("should detect Unix whole-file locations", () => {
|
||||
const loc = "file:///path/to/file.ext:0:0:0:0";
|
||||
const wholeFileLoc = mapUrlValue(loc);
|
||||
expect(wholeFileLoc).toEqual({
|
||||
type: "wholeFileLocation",
|
||||
uri: "/path/to/file.ext",
|
||||
});
|
||||
});
|
||||
|
||||
it("should detect Unix 5-part locations", () => {
|
||||
const loc = "file:///path/to/file.ext:1:2:3:4";
|
||||
const wholeFileLoc = mapUrlValue(loc);
|
||||
expect(wholeFileLoc).toEqual({
|
||||
type: "lineColumnLocation",
|
||||
uri: "/path/to/file.ext",
|
||||
startLine: 1,
|
||||
startColumn: 2,
|
||||
endLine: 3,
|
||||
endColumn: 4,
|
||||
});
|
||||
});
|
||||
it("should set other string locations as strings", () => {
|
||||
for (const loc of ["file:///path/to/file.ext", "I am not a location"]) {
|
||||
const urlValue = mapUrlValue(loc);
|
||||
expect(urlValue).toEqual({
|
||||
type: "string",
|
||||
value: loc,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,42 +1,29 @@
|
||||
import { tryGetRemoteLocation } from "../../../src/common/bqrs-utils";
|
||||
import {
|
||||
tryGetRemoteLocation,
|
||||
tryGetResolvableLocation,
|
||||
} from "../../../src/common/bqrs-utils";
|
||||
UrlValue,
|
||||
UrlValueResolvable,
|
||||
} from "../../../src/common/raw-result-types";
|
||||
|
||||
describe("processing string locations", () => {
|
||||
it("should detect Windows whole-file locations", () => {
|
||||
const loc = "file://C:/path/to/file.ext:0:0:0:0";
|
||||
const wholeFileLoc = tryGetResolvableLocation(loc);
|
||||
expect(wholeFileLoc).toEqual({ uri: "C:/path/to/file.ext" });
|
||||
});
|
||||
it("should detect Unix whole-file locations", () => {
|
||||
const loc = "file:///path/to/file.ext:0:0:0:0";
|
||||
const wholeFileLoc = tryGetResolvableLocation(loc);
|
||||
expect(wholeFileLoc).toEqual({ uri: "/path/to/file.ext" });
|
||||
});
|
||||
|
||||
it("should detect Unix 5-part locations", () => {
|
||||
const loc = "file:///path/to/file.ext:1:2:3:4";
|
||||
const wholeFileLoc = tryGetResolvableLocation(loc);
|
||||
expect(wholeFileLoc).toEqual({
|
||||
uri: "/path/to/file.ext",
|
||||
startLine: 1,
|
||||
startColumn: 2,
|
||||
endLine: 3,
|
||||
endColumn: 4,
|
||||
});
|
||||
});
|
||||
it("should ignore other string locations", () => {
|
||||
for (const loc of ["file:///path/to/file.ext", "I am not a location"]) {
|
||||
const wholeFileLoc = tryGetResolvableLocation(loc);
|
||||
expect(wholeFileLoc).toBeUndefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("getting links to remote (GitHub) locations", () => {
|
||||
describe("tryGetRemoteLocation", () => {
|
||||
it("should return undefined if resolvableLocation is undefined", () => {
|
||||
const loc = "not a location";
|
||||
const loc = undefined;
|
||||
const fileLinkPrefix = "";
|
||||
const sourceLocationPrefix = "";
|
||||
|
||||
const link = tryGetRemoteLocation(
|
||||
loc,
|
||||
fileLinkPrefix,
|
||||
sourceLocationPrefix,
|
||||
);
|
||||
|
||||
expect(link).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should return undefined if resolvableLocation is string", () => {
|
||||
const loc: UrlValue = {
|
||||
type: "string",
|
||||
value: "not a location",
|
||||
};
|
||||
const fileLinkPrefix = "";
|
||||
const sourceLocationPrefix = "";
|
||||
|
||||
@@ -50,7 +37,8 @@ describe("getting links to remote (GitHub) locations", () => {
|
||||
});
|
||||
|
||||
it("should return undefined if resolvableLocation has the wrong format", () => {
|
||||
const loc = {
|
||||
const loc: UrlValueResolvable = {
|
||||
type: "lineColumnLocation",
|
||||
uri: "file:/path/to/file.ext",
|
||||
startLine: 194,
|
||||
startColumn: 18,
|
||||
@@ -70,7 +58,8 @@ describe("getting links to remote (GitHub) locations", () => {
|
||||
});
|
||||
|
||||
it("should return a remote file ref if the sourceLocationPrefix and resolvableLocation match up", () => {
|
||||
const loc = {
|
||||
const loc: UrlValueResolvable = {
|
||||
type: "lineColumnLocation",
|
||||
uri: "file:/home/foo/bar/path/to/file.ext",
|
||||
startLine: 194,
|
||||
startColumn: 18,
|
||||
@@ -92,7 +81,8 @@ describe("getting links to remote (GitHub) locations", () => {
|
||||
});
|
||||
|
||||
it("should return undefined if the sourceLocationPrefix is missing and resolvableLocation doesn't match the default format", () => {
|
||||
const loc = {
|
||||
const loc: UrlValueResolvable = {
|
||||
type: "lineColumnLocation",
|
||||
uri: "file:/home/foo/bar/path/to/file.ext",
|
||||
startLine: 194,
|
||||
startColumn: 18,
|
||||
@@ -112,7 +102,8 @@ describe("getting links to remote (GitHub) locations", () => {
|
||||
});
|
||||
|
||||
it("should return a remote file ref if the sourceLocationPrefix is missing, but the resolvableLocation matches the default format", () => {
|
||||
const loc = {
|
||||
const loc: UrlValueResolvable = {
|
||||
type: "lineColumnLocation",
|
||||
uri: "file:/home/runner/work/foo/bar/path/to/file.ext",
|
||||
startLine: 194,
|
||||
startColumn: 18,
|
||||
|
||||
@@ -117,6 +117,7 @@ describe("parsing sarif", () => {
|
||||
},
|
||||
};
|
||||
expect(parseSarifLocation(location, "prefix")).toEqual({
|
||||
type: "wholeFileLocation",
|
||||
uri: "file:/prefix/abc?x=test",
|
||||
userVisibleFile: "abc?x=test",
|
||||
});
|
||||
@@ -131,6 +132,7 @@ describe("parsing sarif", () => {
|
||||
},
|
||||
};
|
||||
expect(parseSarifLocation(location, "prefix")).toEqual({
|
||||
type: "wholeFileLocation",
|
||||
uri: "file:/abc%3Fx%3Dtest",
|
||||
userVisibleFile: "/abc?x=test",
|
||||
});
|
||||
@@ -151,6 +153,7 @@ describe("parsing sarif", () => {
|
||||
},
|
||||
};
|
||||
expect(parseSarifLocation(location, "prefix")).toEqual({
|
||||
type: "lineColumnLocation",
|
||||
uri: "file:abc%3Fx%3Dtest",
|
||||
userVisibleFile: "abc?x=test",
|
||||
startLine: 1,
|
||||
|
||||
@@ -12,319 +12,458 @@
|
||||
"artifactSizeInBytes": 3567,
|
||||
"interpretedResults": [],
|
||||
"rawResults": {
|
||||
"schema": {
|
||||
"resultSet": {
|
||||
"name": "#select",
|
||||
"rows": 22,
|
||||
"totalRowCount": 22,
|
||||
"columns": [
|
||||
{
|
||||
"name": "c",
|
||||
"kind": "e"
|
||||
"kind": "entity"
|
||||
},
|
||||
{
|
||||
"kind": "i"
|
||||
"kind": "integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
"resultSet": {
|
||||
"schema": {
|
||||
"name": "#select",
|
||||
"rows": 22,
|
||||
"columns": [
|
||||
{
|
||||
"name": "c",
|
||||
"kind": "e"
|
||||
},
|
||||
{
|
||||
"kind": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
"rows": [
|
||||
[
|
||||
{
|
||||
"label": "functio ... ght);\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/src/Expressions/examples/CompareIdenticalValues.js",
|
||||
"startLine": 8,
|
||||
"startColumn": 32,
|
||||
"endLine": 13,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... ght);\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/src/Expressions/examples/CompareIdenticalValues.js",
|
||||
"startLine": 8,
|
||||
"startColumn": 32,
|
||||
"endLine": 13,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
6
|
||||
{
|
||||
"type": "number",
|
||||
"value": 6
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... i-1);\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/src/LanguageFeatures/examples/ArgumentsCallerCallee.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 2,
|
||||
"endLine": 5,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... i-1);\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/src/LanguageFeatures/examples/ArgumentsCallerCallee.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 2,
|
||||
"endLine": 5,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
5
|
||||
{
|
||||
"type": "number",
|
||||
"value": 5
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... i-1);\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/src/LanguageFeatures/examples/ArgumentsCallerCalleeGood.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 2,
|
||||
"endLine": 5,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... i-1);\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/src/LanguageFeatures/examples/ArgumentsCallerCalleeGood.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 2,
|
||||
"endLine": 5,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
5
|
||||
{
|
||||
"type": "number",
|
||||
"value": 5
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... n -1;\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/src/Statements/examples/UselessComparisonTest.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endLine": 12,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... n -1;\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/src/Statements/examples/UselessComparisonTest.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endLine": 12,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
12
|
||||
{
|
||||
"type": "number",
|
||||
"value": 12
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... false\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/constants.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endLine": 8,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... false\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/constants.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endLine": 8,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
8
|
||||
{
|
||||
"type": "number",
|
||||
"value": 8
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... \\n }\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/loop.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endLine": 12,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... \\n }\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/loop.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endLine": 12,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
12
|
||||
{
|
||||
"type": "number",
|
||||
"value": 12
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... e\\n }\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/loop.js",
|
||||
"startLine": 14,
|
||||
"startColumn": 1,
|
||||
"endLine": 22,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... e\\n }\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/loop.js",
|
||||
"startLine": 14,
|
||||
"startColumn": 1,
|
||||
"endLine": 22,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
9
|
||||
{
|
||||
"type": "number",
|
||||
"value": 9
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... K\\n }\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/loop.js",
|
||||
"startLine": 24,
|
||||
"startColumn": 1,
|
||||
"endLine": 40,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... K\\n }\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/loop.js",
|
||||
"startLine": 24,
|
||||
"startColumn": 1,
|
||||
"endLine": 40,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
17
|
||||
{
|
||||
"type": "number",
|
||||
"value": 17
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... e\\n }\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/plus.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endLine": 17,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... e\\n }\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/plus.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endLine": 17,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
17
|
||||
{
|
||||
"type": "number",
|
||||
"value": 17
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... alse \\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/plus.js",
|
||||
"startLine": 19,
|
||||
"startColumn": 1,
|
||||
"endLine": 28,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... alse \\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/plus.js",
|
||||
"startLine": 19,
|
||||
"startColumn": 1,
|
||||
"endLine": 28,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
10
|
||||
{
|
||||
"type": "number",
|
||||
"value": 10
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... true\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/plus.js",
|
||||
"startLine": 30,
|
||||
"startColumn": 1,
|
||||
"endLine": 33,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... true\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/plus.js",
|
||||
"startLine": 30,
|
||||
"startColumn": 1,
|
||||
"endLine": 33,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
4
|
||||
{
|
||||
"type": "number",
|
||||
"value": 4
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... K\\n }\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/tst.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endLine": 15,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... K\\n }\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/tst.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endLine": 15,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
15
|
||||
{
|
||||
"type": "number",
|
||||
"value": 15
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... e\\n }\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/tst.js",
|
||||
"startLine": 17,
|
||||
"startColumn": 1,
|
||||
"endLine": 31,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... e\\n }\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/tst.js",
|
||||
"startLine": 17,
|
||||
"startColumn": 1,
|
||||
"endLine": 31,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
15
|
||||
{
|
||||
"type": "number",
|
||||
"value": 15
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... false\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/tst.js",
|
||||
"startLine": 33,
|
||||
"startColumn": 1,
|
||||
"endLine": 41,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... false\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/tst.js",
|
||||
"startLine": 33,
|
||||
"startColumn": 1,
|
||||
"endLine": 41,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
9
|
||||
{
|
||||
"type": "number",
|
||||
"value": 9
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... e\\n }\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/tst.js",
|
||||
"startLine": 43,
|
||||
"startColumn": 1,
|
||||
"endLine": 52,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... e\\n }\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/tst.js",
|
||||
"startLine": 43,
|
||||
"startColumn": 1,
|
||||
"endLine": 52,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
10
|
||||
{
|
||||
"type": "number",
|
||||
"value": 10
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... ght);\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Expressions/CompareIdenticalValues/tst.js",
|
||||
"startLine": 8,
|
||||
"startColumn": 32,
|
||||
"endLine": 13,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... ght);\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Expressions/CompareIdenticalValues/tst.js",
|
||||
"startLine": 8,
|
||||
"startColumn": 32,
|
||||
"endLine": 13,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
6
|
||||
{
|
||||
"type": "number",
|
||||
"value": 6
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... i-1);\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/LanguageFeatures/ArgumentsCallerCallee/tst.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 2,
|
||||
"endLine": 5,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... i-1);\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/LanguageFeatures/ArgumentsCallerCallee/tst.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 2,
|
||||
"endLine": 5,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
5
|
||||
{
|
||||
"type": "number",
|
||||
"value": 5
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... }\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Security/CWE-834/LoopBoundInjectionExitBad.js",
|
||||
"startLine": 17,
|
||||
"startColumn": 1,
|
||||
"endLine": 29,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... }\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Security/CWE-834/LoopBoundInjectionExitBad.js",
|
||||
"startLine": 17,
|
||||
"startColumn": 1,
|
||||
"endLine": 29,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
13
|
||||
{
|
||||
"type": "number",
|
||||
"value": 13
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... true\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Statements/UselessComparisonTest/constant.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endLine": 4,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... true\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Statements/UselessComparisonTest/constant.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endLine": 4,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
4
|
||||
{
|
||||
"type": "number",
|
||||
"value": 4
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... n -1;\\n}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Statements/UselessComparisonTest/example.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endLine": 12,
|
||||
"endColumn": 1
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... n -1;\\n}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Statements/UselessComparisonTest/example.js",
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endLine": 12,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
12
|
||||
{
|
||||
"type": "number",
|
||||
"value": 12
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... turn; }",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Statements/UselessComparisonTest/tst.js",
|
||||
"startLine": 8,
|
||||
"startColumn": 3,
|
||||
"endLine": 8,
|
||||
"endColumn": 43
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... turn; }",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Statements/UselessComparisonTest/tst.js",
|
||||
"startLine": 8,
|
||||
"startColumn": 3,
|
||||
"endLine": 8,
|
||||
"endColumn": 43
|
||||
}
|
||||
}
|
||||
},
|
||||
1
|
||||
{
|
||||
"type": "number",
|
||||
"value": 1
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "| functio ... i+1); |}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Statements/UselessComparisonTest/tst.js",
|
||||
"startLine": 9,
|
||||
"startColumn": 3,
|
||||
"endLine": 9,
|
||||
"endColumn": 52
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "| functio ... i+1); |}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Statements/UselessComparisonTest/tst.js",
|
||||
"startLine": 9,
|
||||
"startColumn": 3,
|
||||
"endLine": 9,
|
||||
"endColumn": 52
|
||||
}
|
||||
}
|
||||
},
|
||||
1
|
||||
{
|
||||
"type": "number",
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
@@ -345,59 +484,58 @@
|
||||
"artifactSizeInBytes": 1133,
|
||||
"interpretedResults": [],
|
||||
"rawResults": {
|
||||
"schema": {
|
||||
"resultSet": {
|
||||
"name": "#select",
|
||||
"rows": 2,
|
||||
"totalRowCount": 2,
|
||||
"columns": [
|
||||
{
|
||||
"name": "c",
|
||||
"kind": "e"
|
||||
"kind": "entity"
|
||||
},
|
||||
{
|
||||
"kind": "i"
|
||||
"kind": "integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
"resultSet": {
|
||||
"schema": {
|
||||
"name": "#select",
|
||||
"rows": 2,
|
||||
"columns": [
|
||||
{
|
||||
"name": "c",
|
||||
"kind": "e"
|
||||
},
|
||||
{
|
||||
"kind": "i"
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
"rows": [
|
||||
[
|
||||
{
|
||||
"label": "functio ... rn H|0}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/packages/logic-solver/minisat.js",
|
||||
"startLine": 7,
|
||||
"startColumn": 91430,
|
||||
"endLine": 7,
|
||||
"endColumn": 105027
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... rn H|0}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/packages/logic-solver/minisat.js",
|
||||
"startLine": 7,
|
||||
"startColumn": 91430,
|
||||
"endLine": 7,
|
||||
"endColumn": 105027
|
||||
}
|
||||
}
|
||||
},
|
||||
1
|
||||
{
|
||||
"type": "number",
|
||||
"value": 1
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"label": "functio ... ext;\\n\\t}",
|
||||
"url": {
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/packages/sha/sha256.js",
|
||||
"startLine": 94,
|
||||
"startColumn": 2,
|
||||
"endLine": 124,
|
||||
"endColumn": 2
|
||||
"type": "entity",
|
||||
"value": {
|
||||
"label": "functio ... ext;\\n\\t}",
|
||||
"url": {
|
||||
"type": "lineColumnLocation",
|
||||
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/packages/sha/sha256.js",
|
||||
"startLine": 94,
|
||||
"startColumn": 2,
|
||||
"endLine": 124,
|
||||
"endColumn": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
31
|
||||
{
|
||||
"type": "number",
|
||||
"value": 31
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
|
||||
@@ -7,13 +7,8 @@ import {
|
||||
SelectedDbItemKind,
|
||||
} from "../../../../src/databases/config/db-config";
|
||||
import { DbConfigStore } from "../../../../src/databases/config/db-config-store";
|
||||
import { createDbConfig } from "../../../factories/db-config-factories";
|
||||
import {
|
||||
createDbConfig,
|
||||
createLocalDbConfigItem,
|
||||
} from "../../../factories/db-config-factories";
|
||||
import {
|
||||
createLocalDatabaseDbItem,
|
||||
createLocalListDbItem,
|
||||
createRemoteOwnerDbItem,
|
||||
createRemoteRepoDbItem,
|
||||
createRemoteUserDefinedListDbItem,
|
||||
@@ -55,8 +50,6 @@ describe("db config store", () => {
|
||||
expect(config.databases.variantAnalysis.repositoryLists).toHaveLength(0);
|
||||
expect(config.databases.variantAnalysis.owners).toHaveLength(0);
|
||||
expect(config.databases.variantAnalysis.repositories).toHaveLength(0);
|
||||
expect(config.databases.local.lists).toHaveLength(0);
|
||||
expect(config.databases.local.databases).toHaveLength(0);
|
||||
expect(config.selected).toEqual({
|
||||
kind: SelectedDbItemKind.VariantAnalysisSystemDefinedList,
|
||||
listName: "top_10",
|
||||
@@ -302,26 +295,6 @@ describe("db config store", () => {
|
||||
configStore.dispose();
|
||||
});
|
||||
|
||||
it.skip("should add a local list", async () => {
|
||||
// Initial set up
|
||||
const dbConfig = createDbConfig();
|
||||
|
||||
const configStore = await initializeConfig(dbConfig, configPath, app);
|
||||
|
||||
// Add
|
||||
await configStore.addLocalList("list1");
|
||||
|
||||
// Read the config file
|
||||
const updatedDbConfig = (await readJSON(configPath)) as DbConfig;
|
||||
|
||||
// Check that the config file has been updated
|
||||
const updatedLocalDbs = updatedDbConfig.databases.local;
|
||||
expect(updatedLocalDbs.lists).toHaveLength(1);
|
||||
expect(updatedLocalDbs.lists[0].name).toEqual("list1");
|
||||
|
||||
configStore.dispose();
|
||||
});
|
||||
|
||||
it("should add a remote list", async () => {
|
||||
// Initial set up
|
||||
const dbConfig = createDbConfig();
|
||||
@@ -400,95 +373,6 @@ describe("db config store", () => {
|
||||
configStore.dispose();
|
||||
});
|
||||
|
||||
it.skip("should allow renaming a local list", async () => {
|
||||
// Initial set up
|
||||
const dbConfig = createDbConfig({
|
||||
localLists: [
|
||||
{
|
||||
name: "list1",
|
||||
databases: [
|
||||
createLocalDbConfigItem(),
|
||||
createLocalDbConfigItem(),
|
||||
createLocalDbConfigItem(),
|
||||
],
|
||||
},
|
||||
],
|
||||
selected: {
|
||||
kind: SelectedDbItemKind.LocalUserDefinedList,
|
||||
listName: "list1",
|
||||
},
|
||||
});
|
||||
|
||||
const configStore = await initializeConfig(dbConfig, configPath, app);
|
||||
|
||||
// Rename
|
||||
const currentDbItem = createLocalListDbItem({
|
||||
listName: "list1",
|
||||
});
|
||||
await configStore.renameLocalList(currentDbItem, "listRenamed");
|
||||
|
||||
// Read the config file
|
||||
const updatedDbConfig = (await readJSON(configPath)) as DbConfig;
|
||||
|
||||
// Check that the config file has been updated
|
||||
const updatedLocalDbs = updatedDbConfig.databases.local;
|
||||
expect(updatedLocalDbs.lists).toHaveLength(1);
|
||||
expect(updatedLocalDbs.lists[0].name).toEqual("listRenamed");
|
||||
|
||||
expect(updatedDbConfig.selected).toEqual({
|
||||
kind: SelectedDbItemKind.LocalUserDefinedList,
|
||||
listName: "listRenamed",
|
||||
});
|
||||
|
||||
configStore.dispose();
|
||||
});
|
||||
|
||||
it.skip("should allow renaming of a local db", async () => {
|
||||
// Initial set up
|
||||
const dbConfig = createDbConfig({
|
||||
localLists: [
|
||||
{
|
||||
name: "list1",
|
||||
databases: [
|
||||
createLocalDbConfigItem({ name: "db1" }),
|
||||
createLocalDbConfigItem({ name: "db2" }),
|
||||
createLocalDbConfigItem({ name: "db3" }),
|
||||
],
|
||||
},
|
||||
],
|
||||
selected: {
|
||||
kind: SelectedDbItemKind.LocalDatabase,
|
||||
databaseName: "db1",
|
||||
listName: "list1",
|
||||
},
|
||||
});
|
||||
|
||||
const configStore = await initializeConfig(dbConfig, configPath, app);
|
||||
|
||||
// Rename
|
||||
const currentDbItem = createLocalDatabaseDbItem({
|
||||
databaseName: "db1",
|
||||
});
|
||||
await configStore.renameLocalDb(currentDbItem, "dbRenamed", "list1");
|
||||
|
||||
// Read the config file
|
||||
const updatedDbConfig = (await readJSON(configPath)) as DbConfig;
|
||||
|
||||
// Check that the config file has been updated
|
||||
const updatedLocalDbs = updatedDbConfig.databases.local;
|
||||
expect(updatedLocalDbs.lists).toHaveLength(1);
|
||||
expect(updatedLocalDbs.lists[0].name).toEqual("list1");
|
||||
expect(updatedLocalDbs.lists[0].databases.length).toEqual(3);
|
||||
expect(updatedLocalDbs.lists[0].databases[0].name).toEqual("dbRenamed");
|
||||
expect(updatedDbConfig.selected).toEqual({
|
||||
kind: SelectedDbItemKind.LocalDatabase,
|
||||
databaseName: "dbRenamed",
|
||||
listName: "list1",
|
||||
});
|
||||
|
||||
configStore.dispose();
|
||||
});
|
||||
|
||||
it("should throw if the name of a list is taken", async () => {
|
||||
// Initial set up
|
||||
const dbConfig = createDbConfig({
|
||||
@@ -753,28 +637,6 @@ describe("db config store", () => {
|
||||
configStore.dispose();
|
||||
});
|
||||
|
||||
it.skip("should return true if a local db and local list exists", async () => {
|
||||
// Initial set up
|
||||
const dbConfig = createDbConfig({
|
||||
localLists: [
|
||||
{
|
||||
name: "list1",
|
||||
databases: [createLocalDbConfigItem({ name: "db1" })],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const configStore = await initializeConfig(dbConfig, configPath, app);
|
||||
|
||||
// Check
|
||||
const doesDbExist = configStore.doesLocalDbExist("db1", "list1");
|
||||
expect(doesDbExist).toEqual(true);
|
||||
const doesListExist = configStore.doesLocalListExist("list1");
|
||||
expect(doesListExist).toEqual(true);
|
||||
|
||||
configStore.dispose();
|
||||
});
|
||||
|
||||
it("should return false if items do not exist", async () => {
|
||||
// Initial set up
|
||||
const dbConfig = createDbConfig({});
|
||||
@@ -782,10 +644,6 @@ describe("db config store", () => {
|
||||
const configStore = await initializeConfig(dbConfig, configPath, app);
|
||||
|
||||
// Check
|
||||
const doesLocalDbExist = configStore.doesLocalDbExist("db1", "list1");
|
||||
expect(doesLocalDbExist).toEqual(false);
|
||||
const doesLocalListExist = configStore.doesLocalListExist("list1");
|
||||
expect(doesLocalListExist).toEqual(false);
|
||||
const doesRemoteDbExist = configStore.doesRemoteDbExist("db1", "list1");
|
||||
expect(doesRemoteDbExist).toEqual(false);
|
||||
const doesRemoteListExist = configStore.doesRemoteListExist("list1");
|
||||
@@ -802,10 +660,6 @@ describe("db config store", () => {
|
||||
configPath: string,
|
||||
app: App,
|
||||
): Promise<DbConfigStore> {
|
||||
if (dbConfig && dbConfig.databases && dbConfig.databases.local) {
|
||||
delete (dbConfig.databases as any).local;
|
||||
}
|
||||
// const config = clearLocalDbs(dbConfig);
|
||||
await writeJSON(configPath, dbConfig);
|
||||
const configStore = new DbConfigStore(app, false);
|
||||
await configStore.initialize();
|
||||
|
||||
@@ -5,10 +5,7 @@ import {
|
||||
} from "../../../../src/databases/config/db-config";
|
||||
import { DbConfigValidator } from "../../../../src/databases/config/db-config-validator";
|
||||
import { DbConfigValidationErrorKind } from "../../../../src/databases/db-validation-errors";
|
||||
import {
|
||||
createDbConfig,
|
||||
createLocalDbConfigItem,
|
||||
} from "../../../factories/db-config-factories";
|
||||
import { createDbConfig } from "../../../factories/db-config-factories";
|
||||
|
||||
describe("db config validation", () => {
|
||||
const extensionPath = join(__dirname, "../../../..");
|
||||
@@ -104,69 +101,4 @@ describe("db config validation", () => {
|
||||
"There are databases with the same name in the repoList1 list: owner1/repo2",
|
||||
});
|
||||
});
|
||||
|
||||
it("should return error when there are multiple local db lists with the same name", async () => {
|
||||
const dbConfig = createDbConfig({
|
||||
localLists: [
|
||||
{
|
||||
name: "dbList1",
|
||||
databases: [createLocalDbConfigItem()],
|
||||
},
|
||||
{
|
||||
name: "dbList1",
|
||||
databases: [createLocalDbConfigItem()],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const validationOutput = configValidator.validate(dbConfig);
|
||||
|
||||
expect(validationOutput).toHaveLength(1);
|
||||
expect(validationOutput[0]).toEqual({
|
||||
kind: DbConfigValidationErrorKind.DuplicateNames,
|
||||
message: "There are database lists with the same name: dbList1",
|
||||
});
|
||||
});
|
||||
|
||||
it("should return error when there are multiple local dbs with the same name", async () => {
|
||||
const dbConfig = createDbConfig({
|
||||
localDbs: [
|
||||
createLocalDbConfigItem({ name: "db1" }),
|
||||
createLocalDbConfigItem({ name: "db2" }),
|
||||
createLocalDbConfigItem({ name: "db1" }),
|
||||
],
|
||||
});
|
||||
|
||||
const validationOutput = configValidator.validate(dbConfig);
|
||||
|
||||
expect(validationOutput).toHaveLength(1);
|
||||
expect(validationOutput[0]).toEqual({
|
||||
kind: DbConfigValidationErrorKind.DuplicateNames,
|
||||
message: "There are databases with the same name: db1",
|
||||
});
|
||||
});
|
||||
|
||||
it("should return error when there are multiple local dbs with the same name in the same list", async () => {
|
||||
const dbConfig = createDbConfig({
|
||||
localLists: [
|
||||
{
|
||||
name: "dbList1",
|
||||
databases: [
|
||||
createLocalDbConfigItem({ name: "db1" }),
|
||||
createLocalDbConfigItem({ name: "db2" }),
|
||||
createLocalDbConfigItem({ name: "db1" }),
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const validationOutput = configValidator.validate(dbConfig);
|
||||
|
||||
expect(validationOutput).toHaveLength(1);
|
||||
expect(validationOutput[0]).toEqual({
|
||||
kind: DbConfigValidationErrorKind.DuplicateNames,
|
||||
message:
|
||||
"There are databases with the same name in the dbList1 list: db1",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,141 +1,14 @@
|
||||
import {
|
||||
LocalList,
|
||||
RemoteRepositoryList,
|
||||
removeLocalDb,
|
||||
removeLocalList,
|
||||
removeRemoteList,
|
||||
removeRemoteOwner,
|
||||
removeRemoteRepo,
|
||||
renameLocalDb,
|
||||
renameLocalList,
|
||||
renameRemoteList,
|
||||
SelectedDbItemKind,
|
||||
} from "../../../../src/databases/config/db-config";
|
||||
import {
|
||||
createDbConfig,
|
||||
createLocalDbConfigItem,
|
||||
} from "../../../factories/db-config-factories";
|
||||
import { createDbConfig } from "../../../factories/db-config-factories";
|
||||
|
||||
describe("db config", () => {
|
||||
describe("renameLocalList", () => {
|
||||
it("should rename a local list", () => {
|
||||
const originalConfig = createDbConfig({
|
||||
localLists: [
|
||||
{
|
||||
name: "list1",
|
||||
databases: [],
|
||||
},
|
||||
{
|
||||
name: "list2",
|
||||
databases: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const updatedConfig = renameLocalList(
|
||||
originalConfig,
|
||||
"list1",
|
||||
"listRenamed",
|
||||
);
|
||||
|
||||
expect(updatedConfig.databases.local.lists).toEqual([
|
||||
{
|
||||
name: "listRenamed",
|
||||
databases: [],
|
||||
},
|
||||
{
|
||||
name: "list2",
|
||||
databases: [],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("should rename a selected local list", () => {
|
||||
const originalConfig = createDbConfig({
|
||||
localLists: [
|
||||
{
|
||||
name: "list1",
|
||||
databases: [],
|
||||
},
|
||||
{
|
||||
name: "list2",
|
||||
databases: [],
|
||||
},
|
||||
],
|
||||
selected: {
|
||||
kind: SelectedDbItemKind.LocalUserDefinedList,
|
||||
listName: "list1",
|
||||
},
|
||||
});
|
||||
|
||||
const updatedConfig = renameLocalList(
|
||||
originalConfig,
|
||||
"list1",
|
||||
"listRenamed",
|
||||
);
|
||||
|
||||
expect(updatedConfig.databases.local.lists).toEqual([
|
||||
{
|
||||
name: "listRenamed",
|
||||
databases: [],
|
||||
},
|
||||
{
|
||||
name: "list2",
|
||||
databases: [],
|
||||
},
|
||||
]);
|
||||
|
||||
expect(updatedConfig.selected).toEqual({
|
||||
kind: SelectedDbItemKind.LocalUserDefinedList,
|
||||
listName: "listRenamed",
|
||||
});
|
||||
});
|
||||
|
||||
it("should rename a local list with a db that is selected", () => {
|
||||
const selectedLocalDb = createLocalDbConfigItem();
|
||||
const list1: LocalList = {
|
||||
name: "list1",
|
||||
databases: [
|
||||
createLocalDbConfigItem(),
|
||||
selectedLocalDb,
|
||||
createLocalDbConfigItem(),
|
||||
],
|
||||
};
|
||||
const list2: LocalList = {
|
||||
name: "list2",
|
||||
databases: [],
|
||||
};
|
||||
|
||||
const originalConfig = createDbConfig({
|
||||
localLists: [list1, list2],
|
||||
selected: {
|
||||
kind: SelectedDbItemKind.LocalDatabase,
|
||||
databaseName: selectedLocalDb.name,
|
||||
listName: list1.name,
|
||||
},
|
||||
});
|
||||
|
||||
const updatedConfig = renameLocalList(
|
||||
originalConfig,
|
||||
list1.name,
|
||||
"listRenamed",
|
||||
);
|
||||
|
||||
expect(updatedConfig.databases.local.lists.length).toEqual(2);
|
||||
expect(updatedConfig.databases.local.lists[0]).toEqual({
|
||||
...list1,
|
||||
name: "listRenamed",
|
||||
});
|
||||
expect(updatedConfig.databases.local.lists[1]).toEqual(list2);
|
||||
|
||||
expect(updatedConfig.selected).toEqual({
|
||||
kind: SelectedDbItemKind.LocalDatabase,
|
||||
databaseName: selectedLocalDb.name,
|
||||
listName: "listRenamed",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("renameRemoteList", () => {
|
||||
it("should rename a remote list", () => {
|
||||
const originalConfig = createDbConfig({
|
||||
@@ -255,205 +128,6 @@ describe("db config", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("renameLocalDb", () => {
|
||||
it("should rename a local db", () => {
|
||||
const db1 = createLocalDbConfigItem({ name: "db1" });
|
||||
const db2 = createLocalDbConfigItem({ name: "db2" });
|
||||
|
||||
const originalConfig = createDbConfig({
|
||||
localLists: [
|
||||
{
|
||||
name: "list1",
|
||||
databases: [
|
||||
createLocalDbConfigItem({ name: "db1" }),
|
||||
createLocalDbConfigItem({ name: "db2" }),
|
||||
],
|
||||
},
|
||||
],
|
||||
localDbs: [db1, db2],
|
||||
});
|
||||
|
||||
const updatedConfig = renameLocalDb(originalConfig, "db1", "dbRenamed");
|
||||
|
||||
const updatedLocalDbs = updatedConfig.databases.local;
|
||||
const originalLocalDbs = originalConfig.databases.local;
|
||||
|
||||
expect(updatedLocalDbs.lists).toEqual(originalLocalDbs.lists);
|
||||
expect(updatedLocalDbs.databases.length).toEqual(2);
|
||||
expect(updatedLocalDbs.databases[0]).toEqual({
|
||||
...db1,
|
||||
name: "dbRenamed",
|
||||
});
|
||||
expect(updatedLocalDbs.databases[1]).toEqual(db2);
|
||||
});
|
||||
|
||||
it("should rename a local db inside a list", () => {
|
||||
const db1List1 = createLocalDbConfigItem({ name: "db1" });
|
||||
const db2List1 = createLocalDbConfigItem({ name: "db2" });
|
||||
|
||||
const originalConfig = createDbConfig({
|
||||
localLists: [
|
||||
{
|
||||
name: "list1",
|
||||
databases: [db1List1, db2List1],
|
||||
},
|
||||
{
|
||||
name: "list2",
|
||||
databases: [
|
||||
createLocalDbConfigItem({ name: "db1" }),
|
||||
createLocalDbConfigItem({ name: "db2" }),
|
||||
],
|
||||
},
|
||||
],
|
||||
localDbs: [
|
||||
createLocalDbConfigItem({ name: "db1" }),
|
||||
createLocalDbConfigItem({ name: "db2" }),
|
||||
],
|
||||
});
|
||||
|
||||
const updatedConfig = renameLocalDb(
|
||||
originalConfig,
|
||||
db1List1.name,
|
||||
"dbRenamed",
|
||||
"list1",
|
||||
);
|
||||
|
||||
const updatedLocalDbs = updatedConfig.databases.local;
|
||||
const originalLocalDbs = originalConfig.databases.local;
|
||||
expect(updatedLocalDbs.databases).toEqual(originalLocalDbs.databases);
|
||||
expect(updatedLocalDbs.lists.length).toEqual(2);
|
||||
expect(updatedLocalDbs.lists[0].databases.length).toEqual(2);
|
||||
expect(updatedLocalDbs.lists[0].databases[0]).toEqual({
|
||||
...db1List1,
|
||||
name: "dbRenamed",
|
||||
});
|
||||
expect(updatedLocalDbs.lists[0].databases[1]).toEqual(db2List1);
|
||||
expect(updatedLocalDbs.lists[1]).toEqual(originalLocalDbs.lists[1]);
|
||||
});
|
||||
|
||||
it("should rename a local db that is selected", () => {
|
||||
const db1 = createLocalDbConfigItem({ name: "db1" });
|
||||
const db2 = createLocalDbConfigItem({ name: "db2" });
|
||||
|
||||
const originalConfig = createDbConfig({
|
||||
localLists: [
|
||||
{
|
||||
name: "list1",
|
||||
databases: [
|
||||
createLocalDbConfigItem({ name: "db1" }),
|
||||
createLocalDbConfigItem({ name: "db2" }),
|
||||
],
|
||||
},
|
||||
],
|
||||
localDbs: [db1, db2],
|
||||
selected: {
|
||||
kind: SelectedDbItemKind.LocalDatabase,
|
||||
databaseName: "db1",
|
||||
},
|
||||
});
|
||||
|
||||
const updatedConfig = renameLocalDb(originalConfig, "db1", "dbRenamed");
|
||||
|
||||
const updatedLocalDbs = updatedConfig.databases.local;
|
||||
const originalLocalDbs = originalConfig.databases.local;
|
||||
|
||||
expect(updatedLocalDbs.lists).toEqual(originalLocalDbs.lists);
|
||||
expect(updatedLocalDbs.databases.length).toEqual(2);
|
||||
expect(updatedLocalDbs.databases[0]).toEqual({
|
||||
...db1,
|
||||
name: "dbRenamed",
|
||||
});
|
||||
expect(updatedLocalDbs.databases[1]).toEqual(db2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("removeLocalList", () => {
|
||||
it("should remove a local list", () => {
|
||||
const originalConfig = createDbConfig({
|
||||
localLists: [
|
||||
{
|
||||
name: "list1",
|
||||
databases: [],
|
||||
},
|
||||
{
|
||||
name: "list2",
|
||||
databases: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const updatedConfig = removeLocalList(originalConfig, "list1");
|
||||
|
||||
expect(updatedConfig.databases.local.lists).toEqual([
|
||||
{
|
||||
name: "list2",
|
||||
databases: [],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("should remove a selected local list", () => {
|
||||
const originalConfig = createDbConfig({
|
||||
localLists: [
|
||||
{
|
||||
name: "list1",
|
||||
databases: [],
|
||||
},
|
||||
{
|
||||
name: "list2",
|
||||
databases: [],
|
||||
},
|
||||
],
|
||||
selected: {
|
||||
kind: SelectedDbItemKind.LocalUserDefinedList,
|
||||
listName: "list1",
|
||||
},
|
||||
});
|
||||
|
||||
const updatedConfig = removeLocalList(originalConfig, "list1");
|
||||
|
||||
expect(updatedConfig.databases.local.lists).toEqual([
|
||||
{
|
||||
name: "list2",
|
||||
databases: [],
|
||||
},
|
||||
]);
|
||||
|
||||
expect(updatedConfig.selected).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should remove a local list with a db that is selected", () => {
|
||||
const selectedLocalDb = createLocalDbConfigItem();
|
||||
const list1: LocalList = {
|
||||
name: "list1",
|
||||
databases: [
|
||||
createLocalDbConfigItem(),
|
||||
selectedLocalDb,
|
||||
createLocalDbConfigItem(),
|
||||
],
|
||||
};
|
||||
const list2: LocalList = {
|
||||
name: "list2",
|
||||
databases: [],
|
||||
};
|
||||
|
||||
const originalConfig = createDbConfig({
|
||||
localLists: [list1, list2],
|
||||
selected: {
|
||||
kind: SelectedDbItemKind.LocalDatabase,
|
||||
databaseName: selectedLocalDb.name,
|
||||
listName: list1.name,
|
||||
},
|
||||
});
|
||||
|
||||
const updatedConfig = removeLocalList(originalConfig, list1.name);
|
||||
|
||||
expect(updatedConfig.databases.local.lists.length).toEqual(1);
|
||||
expect(updatedConfig.databases.local.lists[0]).toEqual(list2);
|
||||
expect(updatedConfig.selected).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("removeRemoteList", () => {
|
||||
it("should remove a remote list", () => {
|
||||
const originalConfig = createDbConfig({
|
||||
@@ -541,109 +215,6 @@ describe("db config", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("removeLocalDb", () => {
|
||||
it("should remove a local db", () => {
|
||||
const db1 = createLocalDbConfigItem({ name: "db1" });
|
||||
const db2 = createLocalDbConfigItem({ name: "db2" });
|
||||
|
||||
const originalConfig = createDbConfig({
|
||||
localLists: [
|
||||
{
|
||||
name: "list1",
|
||||
databases: [
|
||||
createLocalDbConfigItem({ name: "db1" }),
|
||||
createLocalDbConfigItem({ name: "db2" }),
|
||||
],
|
||||
},
|
||||
],
|
||||
localDbs: [db1, db2],
|
||||
});
|
||||
|
||||
const updatedConfig = renameLocalDb(originalConfig, "db1", "dbRenamed");
|
||||
|
||||
const updatedLocalDbs = updatedConfig.databases.local;
|
||||
const originalLocalDbs = originalConfig.databases.local;
|
||||
|
||||
expect(updatedLocalDbs.lists).toEqual(originalLocalDbs.lists);
|
||||
expect(updatedLocalDbs.databases.length).toEqual(2);
|
||||
expect(updatedLocalDbs.databases[0]).toEqual({
|
||||
...db1,
|
||||
name: "dbRenamed",
|
||||
});
|
||||
expect(updatedLocalDbs.databases[1]).toEqual(db2);
|
||||
});
|
||||
|
||||
it("should remove a local db inside a list", () => {
|
||||
const db1List1 = createLocalDbConfigItem({ name: "db1" });
|
||||
const db2List1 = createLocalDbConfigItem({ name: "db2" });
|
||||
|
||||
const originalConfig = createDbConfig({
|
||||
localLists: [
|
||||
{
|
||||
name: "list1",
|
||||
databases: [db1List1, db2List1],
|
||||
},
|
||||
{
|
||||
name: "list2",
|
||||
databases: [
|
||||
createLocalDbConfigItem({ name: "db1" }),
|
||||
createLocalDbConfigItem({ name: "db2" }),
|
||||
],
|
||||
},
|
||||
],
|
||||
localDbs: [
|
||||
createLocalDbConfigItem({ name: "db1" }),
|
||||
createLocalDbConfigItem({ name: "db2" }),
|
||||
],
|
||||
});
|
||||
|
||||
const updatedConfig = removeLocalDb(
|
||||
originalConfig,
|
||||
db1List1.name,
|
||||
"list1",
|
||||
);
|
||||
|
||||
const updatedLocalDbs = updatedConfig.databases.local;
|
||||
const originalLocalDbs = originalConfig.databases.local;
|
||||
expect(updatedLocalDbs.databases).toEqual(originalLocalDbs.databases);
|
||||
expect(updatedLocalDbs.lists.length).toEqual(2);
|
||||
expect(updatedLocalDbs.lists[0].databases.length).toEqual(1);
|
||||
expect(updatedLocalDbs.lists[0].databases[0]).toEqual(db2List1);
|
||||
expect(updatedLocalDbs.lists[1]).toEqual(originalLocalDbs.lists[1]);
|
||||
});
|
||||
|
||||
it("should remove a local db that is selected", () => {
|
||||
const db1 = createLocalDbConfigItem({ name: "db1" });
|
||||
const db2 = createLocalDbConfigItem({ name: "db2" });
|
||||
|
||||
const originalConfig = createDbConfig({
|
||||
localLists: [
|
||||
{
|
||||
name: "list1",
|
||||
databases: [
|
||||
createLocalDbConfigItem({ name: "db1" }),
|
||||
createLocalDbConfigItem({ name: "db2" }),
|
||||
],
|
||||
},
|
||||
],
|
||||
localDbs: [db1, db2],
|
||||
selected: {
|
||||
kind: SelectedDbItemKind.LocalDatabase,
|
||||
databaseName: "db1",
|
||||
},
|
||||
});
|
||||
|
||||
const updatedConfig = removeLocalDb(originalConfig, "db1");
|
||||
|
||||
const updatedLocalDbs = updatedConfig.databases.local;
|
||||
const originalLocalDbs = originalConfig.databases.local;
|
||||
|
||||
expect(updatedLocalDbs.lists).toEqual(originalLocalDbs.lists);
|
||||
expect(updatedLocalDbs.databases.length).toEqual(1);
|
||||
expect(updatedLocalDbs.databases[0]).toEqual(db2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("removeRemoteRepo", () => {
|
||||
it("should remove a remote repo", () => {
|
||||
const repo1 = "owner1/repo1";
|
||||
|
||||
@@ -6,9 +6,7 @@ import {
|
||||
cleanNonExistentExpandedItems,
|
||||
} from "../../../src/databases/db-item-expansion";
|
||||
import {
|
||||
createLocalListDbItem,
|
||||
createRemoteUserDefinedListDbItem,
|
||||
createRootLocalDbItem,
|
||||
createRootRemoteDbItem,
|
||||
} from "../../factories/db-item-factories";
|
||||
|
||||
@@ -120,10 +118,6 @@ describe("db item expansion", () => {
|
||||
kind: ExpandedDbItemKind.RemoteUserDefinedList,
|
||||
listName: "list2",
|
||||
},
|
||||
{
|
||||
kind: ExpandedDbItemKind.LocalUserDefinedList,
|
||||
listName: "list1",
|
||||
},
|
||||
];
|
||||
|
||||
const currentDbItem = createRemoteUserDefinedListDbItem({
|
||||
@@ -153,10 +147,6 @@ describe("db item expansion", () => {
|
||||
kind: ExpandedDbItemKind.RemoteUserDefinedList,
|
||||
listName: "list2",
|
||||
},
|
||||
{
|
||||
kind: ExpandedDbItemKind.LocalUserDefinedList,
|
||||
listName: "list1",
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -175,10 +165,6 @@ describe("db item expansion", () => {
|
||||
kind: ExpandedDbItemKind.RemoteUserDefinedList,
|
||||
listName: "list2",
|
||||
},
|
||||
{
|
||||
kind: ExpandedDbItemKind.LocalUserDefinedList,
|
||||
listName: "list1",
|
||||
},
|
||||
];
|
||||
|
||||
const dbItems = [
|
||||
@@ -189,13 +175,6 @@ describe("db item expansion", () => {
|
||||
}),
|
||||
],
|
||||
}),
|
||||
createRootLocalDbItem({
|
||||
children: [
|
||||
createLocalListDbItem({
|
||||
listName: "list1",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
];
|
||||
|
||||
const newExpandedItems = cleanNonExistentExpandedItems(
|
||||
@@ -211,10 +190,6 @@ describe("db item expansion", () => {
|
||||
kind: ExpandedDbItemKind.RemoteUserDefinedList,
|
||||
listName: "list2",
|
||||
},
|
||||
{
|
||||
kind: ExpandedDbItemKind.LocalUserDefinedList,
|
||||
listName: "list1",
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,25 +1,14 @@
|
||||
import { getDbItemName } from "../../../src/databases/db-item-naming";
|
||||
import {
|
||||
createLocalDatabaseDbItem,
|
||||
createLocalListDbItem,
|
||||
createRemoteOwnerDbItem,
|
||||
createRemoteRepoDbItem,
|
||||
createRemoteSystemDefinedListDbItem,
|
||||
createRemoteUserDefinedListDbItem,
|
||||
createRootLocalDbItem,
|
||||
createRootRemoteDbItem,
|
||||
} from "../../factories/db-item-factories";
|
||||
|
||||
describe("db item naming", () => {
|
||||
describe("getDbItemName", () => {
|
||||
it("return undefined for root local db item", () => {
|
||||
const dbItem = createRootLocalDbItem();
|
||||
|
||||
const name = getDbItemName(dbItem);
|
||||
|
||||
expect(name).toBeUndefined();
|
||||
});
|
||||
|
||||
it("return undefined for root remote db item", () => {
|
||||
const dbItem = createRootRemoteDbItem();
|
||||
|
||||
@@ -28,14 +17,6 @@ describe("db item naming", () => {
|
||||
expect(name).toBeUndefined();
|
||||
});
|
||||
|
||||
it("return list name for local list db item", () => {
|
||||
const dbItem = createLocalListDbItem();
|
||||
|
||||
const name = getDbItemName(dbItem);
|
||||
|
||||
expect(name).toEqual(dbItem.listName);
|
||||
});
|
||||
|
||||
it("return list name for remote user defined list db item", () => {
|
||||
const dbItem = createRemoteUserDefinedListDbItem();
|
||||
|
||||
@@ -60,14 +41,6 @@ describe("db item naming", () => {
|
||||
expect(name).toEqual(dbItem.ownerName);
|
||||
});
|
||||
|
||||
it("return database name for local db item", () => {
|
||||
const dbItem = createLocalDatabaseDbItem();
|
||||
|
||||
const name = getDbItemName(dbItem);
|
||||
|
||||
expect(name).toEqual(dbItem.databaseName);
|
||||
});
|
||||
|
||||
it("return repo name for remote repo db item", () => {
|
||||
const dbItem = createRemoteRepoDbItem();
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user