11import { CancellationToken , commands , ExtensionContext , Uri , window } from 'vscode' ;
2+ import { nanoid } from 'nanoid' ;
3+ import * as path from 'path' ;
4+ import * as fs from 'fs-extra' ;
5+
26import { Credentials } from '../authentication' ;
37import { CodeQLCliServer } from '../cli' ;
48import { ProgressCallback } from '../commandRunner' ;
5- import { showAndLogErrorMessage , showInformationMessageWithAction } from '../helpers' ;
9+ import { createTimestampFile , showAndLogErrorMessage , showInformationMessageWithAction } from '../helpers' ;
610import { Logger } from '../logging' ;
711import { runRemoteQuery } from './run-remote-query' ;
812import { RemoteQueriesInterfaceManager } from './remote-queries-interface' ;
@@ -13,6 +17,7 @@ import { RemoteQueryResultIndex } from './remote-query-result-index';
1317import { RemoteQueryResult } from './remote-query-result' ;
1418import { DownloadLink } from './download-link' ;
1519import { AnalysesResultsManager } from './analyses-results-manager' ;
20+ import { assertNever } from '../pure/helpers-pure' ;
1621
1722const autoDownloadMaxSize = 300 * 1024 ;
1823const autoDownloadMaxCount = 100 ;
@@ -25,7 +30,7 @@ export class RemoteQueriesManager {
2530 constructor (
2631 private readonly ctx : ExtensionContext ,
2732 private readonly cliServer : CodeQLCliServer ,
28- readonly storagePath : string ,
33+ private readonly storagePath : string ,
2934 logger : Logger ,
3035 ) {
3136 this . analysesResultsManager = new AnalysesResultsManager ( ctx , storagePath , logger ) ;
@@ -58,19 +63,24 @@ export class RemoteQueriesManager {
5863 ) : Promise < void > {
5964 const credentials = await Credentials . initialize ( this . ctx ) ;
6065
61- const queryResult = await this . remoteQueriesMonitor . monitorQuery ( query , cancellationToken ) ;
66+ const queryWorkflowResult = await this . remoteQueriesMonitor . monitorQuery ( query , cancellationToken ) ;
6267
6368 const executionEndTime = new Date ( ) ;
6469
65- if ( queryResult . status === 'CompletedSuccessfully' ) {
70+ if ( queryWorkflowResult . status === 'CompletedSuccessfully' ) {
6671 const resultIndex = await getRemoteQueryIndex ( credentials , query ) ;
6772 if ( ! resultIndex ) {
6873 void showAndLogErrorMessage ( `There was an issue retrieving the result for the query ${ query . queryName } ` ) ;
6974 return ;
7075 }
7176
72- const queryResult = this . mapQueryResult ( executionEndTime , resultIndex ) ;
73- await this . analysesResultsManager . prepareDownloadDirectory ( query . queryName ) ;
77+ const queryId = this . createQueryId ( query . queryName ) ;
78+ await this . prepareStorageDirectory ( queryId ) ;
79+ const queryResult = this . mapQueryResult ( executionEndTime , resultIndex , queryId ) ;
80+
81+ // Write the query result to the storage directory.
82+ const queryResultFilePath = path . join ( this . storagePath , queryId , 'query-result.json' ) ;
83+ await fs . writeFile ( queryResultFilePath , JSON . stringify ( queryResult , null , 2 ) , 'utf8' ) ;
7484
7585 // Kick off auto-download of results.
7686 void commands . executeCommand ( 'codeQL.autoDownloadRemoteQueryResults' , queryResult ) ;
@@ -81,13 +91,19 @@ export class RemoteQueriesManager {
8191 const shouldOpenView = await showInformationMessageWithAction ( message , 'View' ) ;
8292 if ( shouldOpenView ) {
8393 await this . interfaceManager . showResults ( query , queryResult ) ;
84-
8594 }
86- } else if ( queryResult . status === 'CompletedUnsuccessfully' ) {
87- await showAndLogErrorMessage ( `Remote query execution failed. Error: ${ queryResult . error } ` ) ;
88- return ;
89- } else if ( queryResult . status === 'Cancelled' ) {
95+ } else if ( queryWorkflowResult . status === 'CompletedUnsuccessfully' ) {
96+ await showAndLogErrorMessage ( `Remote query execution failed. Error: ${ queryWorkflowResult . error } ` ) ;
97+
98+ } else if ( queryWorkflowResult . status === 'Cancelled' ) {
9099 await showAndLogErrorMessage ( 'Remote query monitoring was cancelled' ) ;
100+
101+ } else if ( queryWorkflowResult . status === 'InProgress' ) {
102+ // Should not get here
103+ await showAndLogErrorMessage ( `Unexpected status: ${ queryWorkflowResult . status } ` ) ;
104+ } else {
105+ // Ensure all cases are covered
106+ assertNever ( queryWorkflowResult . status ) ;
91107 }
92108 }
93109
@@ -111,21 +127,44 @@ export class RemoteQueriesManager {
111127 results => this . interfaceManager . setAnalysisResults ( results ) ) ;
112128 }
113129
114- private mapQueryResult ( executionEndTime : Date , resultIndex : RemoteQueryResultIndex ) : RemoteQueryResult {
130+ private mapQueryResult ( executionEndTime : Date , resultIndex : RemoteQueryResultIndex , queryId : string ) : RemoteQueryResult {
115131 const analysisSummaries = resultIndex . items . map ( item => ( {
116132 nwo : item . nwo ,
117133 resultCount : item . resultCount ,
118134 fileSizeInBytes : item . sarifFileSize ? item . sarifFileSize : item . bqrsFileSize ,
119135 downloadLink : {
120136 id : item . artifactId . toString ( ) ,
121137 urlPath : `${ resultIndex . artifactsUrlPath } /${ item . artifactId } ` ,
122- innerFilePath : item . sarifFileSize ? 'results.sarif' : 'results.bqrs'
138+ innerFilePath : item . sarifFileSize ? 'results.sarif' : 'results.bqrs' ,
139+ queryId,
123140 } as DownloadLink
124141 } ) ) ;
125142
126143 return {
127144 executionEndTime,
128- analysisSummaries
145+ analysisSummaries,
129146 } ;
130147 }
148+
149+ /**
150+ * Generates a unique id for this query, suitable for determining the storage location for the downloaded query artifacts.
151+ * @param queryName
152+ * @returns
153+ */
154+ private createQueryId ( queryName : string ) : string {
155+ return `${ queryName } -${ nanoid ( ) } ` ;
156+
157+ }
158+
159+ /**
160+ * Prepares a directory for storing analysis results for a single query run.
161+ * This directory contains a timestamp file, which will be
162+ * used by the query history manager to determine when the directory
163+ * should be deleted.
164+ *
165+ * @param queryName The name of the query that was run.
166+ */
167+ private async prepareStorageDirectory ( queryId : string ) : Promise < void > {
168+ await createTimestampFile ( path . join ( this . storagePath , queryId ) ) ;
169+ }
131170}
0 commit comments