-
Notifications
You must be signed in to change notification settings - Fork 226
Expand file tree
/
Copy pathquery-serialization.ts
More file actions
116 lines (106 loc) · 4.77 KB
/
query-serialization.ts
File metadata and controls
116 lines (106 loc) · 4.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import * as fs from 'fs-extra';
import * as path from 'path';
import { showAndLogErrorMessage } from './helpers';
import { asyncFilter, getErrorMessage, getErrorStack } from './pure/helpers-pure';
import { CompletedQueryInfo, LocalQueryInfo } from './query-results';
import { QueryHistoryInfo } from './query-history-info';
import { QueryStatus } from './query-status';
import { QueryEvaluationInfo } from './run-queries-shared';
import { QueryResultType } from './pure/legacy-messages';
export async function slurpQueryHistory(fsPath: string): Promise<QueryHistoryInfo[]> {
try {
if (!(await fs.pathExists(fsPath))) {
return [];
}
const data = await fs.readFile(fsPath, 'utf8');
const obj = JSON.parse(data);
if (![1, 2].includes(obj.version)) {
void showAndLogErrorMessage(`Can't parse query history. Unsupported query history format: v${obj.version}. `);
return [];
}
const queries = obj.queries;
const parsedQueries = queries.map((q: QueryHistoryInfo) => {
// Need to explicitly set prototype since reading in from JSON will not
// do this automatically. Note that we can't call the constructor here since
// the constructor invokes extra logic that we don't want to do.
if (q.t === 'local') {
Object.setPrototypeOf(q, LocalQueryInfo.prototype);
// Date instances are serialized as strings. Need to
// convert them back to Date instances.
(q.initialInfo as any).start = new Date(q.initialInfo.start);
if (q.completedQuery) {
// Again, need to explicitly set prototypes.
Object.setPrototypeOf(q.completedQuery, CompletedQueryInfo.prototype);
Object.setPrototypeOf(q.completedQuery.query, QueryEvaluationInfo.prototype);
// slurped queries do not need to be disposed
q.completedQuery.dispose = () => { /**/ };
// Previously, there was a typo in the completedQuery type. There was a field
// `sucessful` and it was renamed to `successful`. We need to handle this case.
if ('sucessful' in q.completedQuery) {
(q.completedQuery as any).successful = (q.completedQuery as any).sucessful;
delete (q.completedQuery as any).sucessful;
}
if (!('successful' in q.completedQuery)) {
(q.completedQuery as any).successful = q.completedQuery.result?.resultType === QueryResultType.SUCCESS;
}
}
} else if (q.t === 'remote') {
// A bug was introduced that didn't set the completed flag in query history
// items. The following code makes sure that the flag is set in order to
// "patch" older query history items.
if (q.status === QueryStatus.Completed) {
q.completed = true;
}
}
return q;
});
// filter out queries that have been deleted on disk
// most likely another workspace has deleted them because the
// queries aged out.
return asyncFilter(parsedQueries, async (q) => {
if (q.t === 'remote' || q.t === 'variant-analysis') {
// the slurper doesn't know where the remote queries are stored
// so we need to assume here that they exist. Later, we check to
// see if they exist on disk.
return true;
}
const resultsPath = q.completedQuery?.query.resultsPaths.resultsPath;
return !!resultsPath && await fs.pathExists(resultsPath);
});
} catch (e) {
void showAndLogErrorMessage('Error loading query history.', {
fullMessage: ['Error loading query history.', getErrorStack(e)].join('\n'),
});
// since the query history is invalid, it should be deleted so this error does not happen on next startup.
await fs.remove(fsPath);
return [];
}
}
/**
* Save the query history to disk. It is not necessary that the parent directory
* exists, but if it does, it must be writable. An existing file will be overwritten.
*
* Any errors will be rethrown.
*
* @param queries the list of queries to save.
* @param fsPath the path to save the queries to.
*/
export async function splatQueryHistory(queries: QueryHistoryInfo[], fsPath: string): Promise<void> {
try {
if (!(await fs.pathExists(fsPath))) {
await fs.mkdir(path.dirname(fsPath), { recursive: true });
}
// remove incomplete local queries since they cannot be recreated on restart
const filteredQueries = queries.filter(q => q.t === 'local' ? q.completedQuery !== undefined : true);
const data = JSON.stringify({
// version 2:
// - adds the `variant-analysis` type
// - ensures a `successful` property exists on completedQuery
version: 2,
queries: filteredQueries
}, null, 2);
await fs.writeFile(fsPath, data);
} catch (e) {
throw new Error(`Error saving query history to ${fsPath}: ${getErrorMessage(e)}`);
}
}