Merge remote-tracking branch 'origin/main' into koesie10/npm-upgrade

This commit is contained in:
Koen Vlaswinkel
2023-12-13 14:40:30 +01:00
115 changed files with 1698 additions and 3550 deletions

View File

@@ -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)

View File

@@ -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",

View File

@@ -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"
}
]
},

View File

@@ -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",

View File

@@ -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[];
}

View 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"
);
}

View File

@@ -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,
);
}

View File

@@ -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[];
};
/**

View 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;
};

View File

@@ -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,

View File

@@ -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);

View File

@@ -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,
) {

View File

@@ -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.");
}

View File

@@ -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>();
}

View File

@@ -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`);
}
}
}

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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:

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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:

View File

@@ -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,

View File

@@ -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,
};
}

View File

@@ -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,
) {

View File

@@ -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,
);
}
}

View File

@@ -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`,

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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
);
}

View File

@@ -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,

View File

@@ -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
});
}

View File

@@ -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,
},
];

View File

@@ -48,7 +48,6 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
t: "setMethodModelingPanelViewState",
viewState: {
language: this.language,
showMultipleModels: this.modelConfig.showMultipleModels,
},
});
}

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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,

View File

@@ -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,

View File

@@ -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];
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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,
},
},
},
],

View File

@@ -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",

View File

@@ -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,
};

View File

@@ -216,7 +216,6 @@ LibraryRow.args = {
viewState: createMockModelEditorViewState({
showGenerateButton: true,
showLlmButton: true,
showMultipleModels: true,
}),
hideModeledMethods: false,
};

View File

@@ -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({});

View File

@@ -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,

View File

@@ -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: {

View File

@@ -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",

View File

@@ -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,

View File

@@ -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",

View File

@@ -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 };
}

View File

@@ -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;

View File

@@ -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:

View File

@@ -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;

View File

@@ -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;

View File

@@ -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}

View File

@@ -110,7 +110,6 @@ export function MethodModelingView({ initialViewState }: Props): JSX.Element {
method={method}
modeledMethods={modeledMethods}
isModelingInProgress={isModelingInProgress}
showMultipleModels={viewState?.showMultipleModels}
onChange={onChange}
/>
);

View File

@@ -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}
/>
);
};

View File

@@ -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();
});
});
});

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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>
);

View File

@@ -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(

View File

@@ -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);

View File

@@ -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}`;

View File

@@ -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>

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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);
}
}

View File

@@ -174,11 +174,6 @@ export function ResultsApp() {
resultSet: {
t: "InterpretedResultSet",
name: tableName,
schema: {
name: tableName,
rows: 1,
columns: [],
},
interpretation: msg.interpretation,
},
selectedTable: tableName,

View File

@@ -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"],

View File

@@ -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;

View File

@@ -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}

View File

@@ -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}

View File

@@ -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({

View File

@@ -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}
/>

View File

@@ -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}>

View File

@@ -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;
};

View File

@@ -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}

View File

@@ -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:

View File

@@ -1,5 +1,5 @@
[
"v2.15.3",
"v2.15.4",
"v2.14.6",
"v2.13.5",
"v2.12.7",

View File

@@ -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,
};
}

View File

@@ -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,
};
}

View File

@@ -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,

View File

@@ -11,7 +11,6 @@ export function createMockModelEditorViewState(
mode: Mode.Application,
showGenerateButton: false,
showLlmButton: false,
showMultipleModels: false,
showModeSwitchButton: true,
extensionPack: createMockExtensionPack(),
sourceArchiveAvailable: true,

View File

@@ -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,
});
}
});
});

View File

@@ -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,

View File

@@ -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,

View File

@@ -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
}
]
]
},

View File

@@ -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();

View File

@@ -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",
});
});
});

View File

@@ -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";

View File

@@ -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",
},
]);
});
});

View File

@@ -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