Merge pull request #407 from jcreedcmu/jcreed/view-sarif
Allow viewing SARIF from query history view
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
- Better formatting and autoindentation when adding QLDoc comments to `.ql` and `.qll` files.
|
- Better formatting and autoindentation when adding QLDoc comments to `.ql` and `.qll` files.
|
||||||
- Allow for more flexibility when opening a database in the workspace. A user can now choose the actual database folder, or the nested `db-*` folder.
|
- Allow for more flexibility when opening a database in the workspace. A user can now choose the actual database folder, or the nested `db-*` folder.
|
||||||
|
- Add query history menu command for viewing corresponding SARIF file.
|
||||||
|
|
||||||
## 1.2.0 - 19 May 2020
|
## 1.2.0 - 19 May 2020
|
||||||
|
|
||||||
|
|||||||
@@ -284,6 +284,10 @@
|
|||||||
"command": "codeQLQueryHistory.showQueryText",
|
"command": "codeQLQueryHistory.showQueryText",
|
||||||
"title": "Show Query Text"
|
"title": "Show Query Text"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "codeQLQueryHistory.viewSarif",
|
||||||
|
"title": "View SARIF"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "codeQLQueryResults.nextPathStep",
|
"command": "codeQLQueryResults.nextPathStep",
|
||||||
"title": "CodeQL: Show Next Step on Path"
|
"title": "CodeQL: Show Next Step on Path"
|
||||||
@@ -388,6 +392,11 @@
|
|||||||
"group": "9_qlCommands",
|
"group": "9_qlCommands",
|
||||||
"when": "view == codeQLQueryHistory"
|
"when": "view == codeQLQueryHistory"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "codeQLQueryHistory.viewSarif",
|
||||||
|
"group": "9_qlCommands",
|
||||||
|
"when": "view == codeQLQueryHistory && viewItem == interpretedResultsItem"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "codeQLTests.showOutputDifferences",
|
"command": "codeQLTests.showOutputDifferences",
|
||||||
"group": "qltest@1",
|
"group": "qltest@1",
|
||||||
@@ -484,6 +493,10 @@
|
|||||||
"command": "codeQLQueryHistory.showQueryText",
|
"command": "codeQLQueryHistory.showQueryText",
|
||||||
"when": "false"
|
"when": "false"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "codeQLQueryHistory.viewSarif",
|
||||||
|
"when": "false"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "codeQLQueryHistory.setLabel",
|
"command": "codeQLQueryHistory.setLabel",
|
||||||
"when": "false"
|
"when": "false"
|
||||||
|
|||||||
@@ -402,7 +402,7 @@ export class InterfaceManager extends DisposableObject {
|
|||||||
const sarif = await interpretResults(
|
const sarif = await interpretResults(
|
||||||
this.cliServer,
|
this.cliServer,
|
||||||
metadata,
|
metadata,
|
||||||
resultsPaths.resultsPath,
|
resultsPaths,
|
||||||
sourceInfo
|
sourceInfo
|
||||||
);
|
);
|
||||||
// For performance reasons, limit the number of results we try
|
// For performance reasons, limit the number of results we try
|
||||||
@@ -440,7 +440,7 @@ export class InterfaceManager extends DisposableObject {
|
|||||||
): Promise<Interpretation | undefined> {
|
): Promise<Interpretation | undefined> {
|
||||||
let interpretation: Interpretation | undefined = undefined;
|
let interpretation: Interpretation | undefined = undefined;
|
||||||
if (
|
if (
|
||||||
(await query.hasInterpretedResults()) &&
|
(await query.canHaveInterpretedResults()) &&
|
||||||
query.quickEvalPosition === undefined // never do results interpretation if quickEval
|
query.quickEvalPosition === undefined // never do results interpretation if quickEval
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ class HistoryTreeDataProvider implements vscode.TreeDataProvider<CompletedQuery>
|
|||||||
constructor(private ctx: ExtensionContext) {
|
constructor(private ctx: ExtensionContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
getTreeItem(element: CompletedQuery): vscode.TreeItem {
|
async getTreeItem(element: CompletedQuery): Promise<vscode.TreeItem> {
|
||||||
const it = new vscode.TreeItem(element.toString());
|
const it = new vscode.TreeItem(element.toString());
|
||||||
|
|
||||||
it.command = {
|
it.command = {
|
||||||
@@ -83,6 +83,11 @@ class HistoryTreeDataProvider implements vscode.TreeDataProvider<CompletedQuery>
|
|||||||
arguments: [element],
|
arguments: [element],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Mark this query history item according to whether it has a
|
||||||
|
// SARIF file so that we can make context menu items conditionally
|
||||||
|
// available.
|
||||||
|
it.contextValue = await element.query.hasInterpretedResults() ? 'interpretedResultsItem' : 'rawResultsItem';
|
||||||
|
|
||||||
if (!element.didRunSuccessfully) {
|
if (!element.didRunSuccessfully) {
|
||||||
it.iconPath = path.join(this.ctx.extensionPath, FAILED_QUERY_HISTORY_ITEM_ICON);
|
it.iconPath = path.join(this.ctx.extensionPath, FAILED_QUERY_HISTORY_ITEM_ICON);
|
||||||
}
|
}
|
||||||
@@ -257,6 +262,22 @@ export class QueryHistoryManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleViewSarif(queryHistoryItem: CompletedQuery) {
|
||||||
|
try {
|
||||||
|
const hasInterpretedResults = await queryHistoryItem.query.canHaveInterpretedResults();
|
||||||
|
if (hasInterpretedResults) {
|
||||||
|
const textDocument = await vscode.workspace.openTextDocument(vscode.Uri.file(queryHistoryItem.query.resultsPaths.interpretedResultsPath));
|
||||||
|
await vscode.window.showTextDocument(textDocument, vscode.ViewColumn.One);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const label = queryHistoryItem.getLabel();
|
||||||
|
helpers.showAndLogInformationMessage(`Query ${label} has no interpreted results.`);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
helpers.showAndLogErrorMessage(e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async getQueryText(queryHistoryItem: CompletedQuery): Promise<string> {
|
async getQueryText(queryHistoryItem: CompletedQuery): Promise<string> {
|
||||||
if (queryHistoryItem.options.queryText) {
|
if (queryHistoryItem.options.queryText) {
|
||||||
return queryHistoryItem.options.queryText;
|
return queryHistoryItem.options.queryText;
|
||||||
@@ -296,6 +317,7 @@ export class QueryHistoryManager {
|
|||||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.setLabel', this.handleSetLabel.bind(this)));
|
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.setLabel', this.handleSetLabel.bind(this)));
|
||||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryLog', this.handleShowQueryLog.bind(this)));
|
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryLog', this.handleShowQueryLog.bind(this)));
|
||||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryText', this.handleShowQueryText.bind(this)));
|
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryText', this.handleShowQueryText.bind(this)));
|
||||||
|
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.viewSarif', this.handleViewSarif.bind(this)));
|
||||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.itemClicked', async (item) => {
|
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.itemClicked', async (item) => {
|
||||||
return this.handleItemClicked(item);
|
return this.handleItemClicked(item);
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import * as cli from './cli';
|
|||||||
import * as sarif from 'sarif';
|
import * as sarif from 'sarif';
|
||||||
import * as fs from 'fs-extra';
|
import * as fs from 'fs-extra';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { RawResultsSortState, SortedResultSetInfo, DatabaseInfo, QueryMetadata, InterpretedResultsSortState } from "./interface-types";
|
import { RawResultsSortState, SortedResultSetInfo, DatabaseInfo, QueryMetadata, InterpretedResultsSortState, ResultsPaths } from "./interface-types";
|
||||||
import { QueryHistoryConfig } from "./config";
|
import { QueryHistoryConfig } from "./config";
|
||||||
import { QueryHistoryItemOptions } from "./query-history";
|
import { QueryHistoryItemOptions } from "./query-history";
|
||||||
|
|
||||||
@@ -54,13 +54,6 @@ export class CompletedQuery implements QueryWithResults {
|
|||||||
return helpers.getQueryName(this.query);
|
return helpers.getQueryName(this.query);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if this query should produce interpreted results.
|
|
||||||
*/
|
|
||||||
canInterpretedResults(): Promise<boolean> {
|
|
||||||
return this.query.dbItem.hasMetadataFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
get statusString(): string {
|
get statusString(): string {
|
||||||
switch (this.result.resultType) {
|
switch (this.result.resultType) {
|
||||||
case messages.QueryResultType.CANCELLATION:
|
case messages.QueryResultType.CANCELLATION:
|
||||||
@@ -130,9 +123,8 @@ export class CompletedQuery implements QueryWithResults {
|
|||||||
/**
|
/**
|
||||||
* Call cli command to interpret results.
|
* Call cli command to interpret results.
|
||||||
*/
|
*/
|
||||||
export async function interpretResults(server: cli.CodeQLCliServer, metadata: QueryMetadata | undefined, resultsPath: string, sourceInfo?: cli.SourceInfo): Promise<sarif.Log> {
|
export async function interpretResults(server: cli.CodeQLCliServer, metadata: QueryMetadata | undefined, resultsPaths: ResultsPaths, sourceInfo?: cli.SourceInfo): Promise<sarif.Log> {
|
||||||
const interpretedResultsPath = resultsPath + ".interpreted.sarif";
|
const { resultsPath, interpretedResultsPath } = resultsPaths;
|
||||||
|
|
||||||
if (await fs.pathExists(interpretedResultsPath)) {
|
if (await fs.pathExists(interpretedResultsPath)) {
|
||||||
return JSON.parse(await fs.readFile(interpretedResultsPath, 'utf8'));
|
return JSON.parse(await fs.readFile(interpretedResultsPath, 'utf8'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,15 +157,22 @@ export class QueryInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this query should produce interpreted results.
|
* Holds if this query can in principle produce interpreted results.
|
||||||
*/
|
*/
|
||||||
async hasInterpretedResults(): Promise<boolean> {
|
async canHaveInterpretedResults(): Promise<boolean> {
|
||||||
const hasMetadataFile = await this.dbItem.hasMetadataFile();
|
const hasMetadataFile = await this.dbItem.hasMetadataFile();
|
||||||
if (!hasMetadataFile) {
|
if (!hasMetadataFile) {
|
||||||
logger.log("Cannot produce interpreted results since the database does not have a .dbinfo or codeql-database.yml file.");
|
logger.log("Cannot produce interpreted results since the database does not have a .dbinfo or codeql-database.yml file.");
|
||||||
}
|
}
|
||||||
return hasMetadataFile;
|
return hasMetadataFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this query actually has produced interpreted results.
|
||||||
|
*/
|
||||||
|
async hasInterpretedResults(): Promise<boolean> {
|
||||||
|
return fs.pathExists(this.resultsPaths.interpretedResultsPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QueryWithResults {
|
export interface QueryWithResults {
|
||||||
|
|||||||
Reference in New Issue
Block a user