@finos/legend-application-data-cube 0.2.7 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/builder/LegendDataCubeBuilder.d.ts.map +1 -1
- package/lib/components/builder/LegendDataCubeBuilder.js +4 -1
- package/lib/components/builder/LegendDataCubeBuilder.js.map +1 -1
- package/lib/components/builder/LegendDataCubeCreator.d.ts.map +1 -1
- package/lib/components/builder/LegendDataCubeCreator.js +12 -5
- package/lib/components/builder/LegendDataCubeCreator.js.map +1 -1
- package/lib/components/builder/LegendDataCubeLoader.js +6 -6
- package/lib/components/builder/LegendDataCubeLoader.js.map +1 -1
- package/lib/components/builder/source/AdhocQueryDataCubeSourceBuilder.d.ts.map +1 -1
- package/lib/components/builder/source/AdhocQueryDataCubeSourceBuilder.js +2 -3
- package/lib/components/builder/source/AdhocQueryDataCubeSourceBuilder.js.map +1 -1
- package/lib/components/builder/source/LegendQueryDataCubeSourceBuilder.js +6 -6
- package/lib/components/builder/source/LegendQueryDataCubeSourceBuilder.js.map +1 -1
- package/lib/components/builder/source/LocalFileDataCubeSourceBuilder.d.ts +22 -0
- package/lib/components/builder/source/LocalFileDataCubeSourceBuilder.d.ts.map +1 -0
- package/lib/components/builder/source/LocalFileDataCubeSourceBuilder.js +29 -0
- package/lib/components/builder/source/LocalFileDataCubeSourceBuilder.js.map +1 -0
- package/lib/index.css +1 -1
- package/lib/package.json +3 -3
- package/lib/stores/LegendDataCubeDataCubeEngine.d.ts +5 -3
- package/lib/stores/LegendDataCubeDataCubeEngine.d.ts.map +1 -1
- package/lib/stores/LegendDataCubeDataCubeEngine.js +297 -120
- package/lib/stores/LegendDataCubeDataCubeEngine.js.map +1 -1
- package/lib/stores/{LegendDataCubeCacheManager.d.ts → LegendDataCubeDuckDBEngine.d.ts} +18 -6
- package/lib/stores/LegendDataCubeDuckDBEngine.d.ts.map +1 -0
- package/lib/stores/{LegendDataCubeCacheManager.js → LegendDataCubeDuckDBEngine.js} +81 -39
- package/lib/stores/LegendDataCubeDuckDBEngine.js.map +1 -0
- package/lib/stores/builder/LegendDataCubeBuilderStore.js +2 -2
- package/lib/stores/builder/LegendDataCubeBuilderStore.js.map +1 -1
- package/lib/stores/builder/LegendDataCubeCreatorState.d.ts.map +1 -1
- package/lib/stores/builder/LegendDataCubeCreatorState.js +3 -0
- package/lib/stores/builder/LegendDataCubeCreatorState.js.map +1 -1
- package/lib/stores/builder/source/LegendDataCubeSourceBuilderState.d.ts +5 -1
- package/lib/stores/builder/source/LegendDataCubeSourceBuilderState.d.ts.map +1 -1
- package/lib/stores/builder/source/LegendDataCubeSourceBuilderState.js +4 -1
- package/lib/stores/builder/source/LegendDataCubeSourceBuilderState.js.map +1 -1
- package/lib/stores/builder/source/LegendQueryDataCubeSourceBuilderState.d.ts.map +1 -1
- package/lib/stores/builder/source/LegendQueryDataCubeSourceBuilderState.js +1 -1
- package/lib/stores/builder/source/LegendQueryDataCubeSourceBuilderState.js.map +1 -1
- package/lib/stores/builder/source/LocalFileDataCubeSourceBuilderState.d.ts +38 -0
- package/lib/stores/builder/source/LocalFileDataCubeSourceBuilderState.d.ts.map +1 -0
- package/lib/stores/builder/source/LocalFileDataCubeSourceBuilderState.js +122 -0
- package/lib/stores/builder/source/LocalFileDataCubeSourceBuilderState.js.map +1 -0
- package/lib/stores/model/LocalFileDataCubeSource.d.ts +44 -0
- package/lib/stores/model/LocalFileDataCubeSource.d.ts.map +1 -0
- package/lib/stores/model/LocalFileDataCubeSource.js +57 -0
- package/lib/stores/model/LocalFileDataCubeSource.js.map +1 -0
- package/package.json +13 -13
- package/src/components/builder/LegendDataCubeBuilder.tsx +6 -1
- package/src/components/builder/LegendDataCubeCreator.tsx +23 -6
- package/src/components/builder/LegendDataCubeLoader.tsx +7 -7
- package/src/components/builder/source/AdhocQueryDataCubeSourceBuilder.tsx +2 -3
- package/src/components/builder/source/LegendQueryDataCubeSourceBuilder.tsx +8 -8
- package/src/components/builder/source/LocalFileDataCubeSourceBuilder.tsx +59 -0
- package/src/stores/LegendDataCubeDataCubeEngine.ts +407 -182
- package/src/stores/{LegendDataCubeCacheManager.ts → LegendDataCubeDuckDBEngine.ts} +86 -42
- package/src/stores/builder/LegendDataCubeBuilderStore.tsx +2 -2
- package/src/stores/builder/LegendDataCubeCreatorState.tsx +6 -0
- package/src/stores/builder/source/LegendDataCubeSourceBuilderState.ts +4 -1
- package/src/stores/builder/source/LegendQueryDataCubeSourceBuilderState.ts +3 -1
- package/src/stores/builder/source/LocalFileDataCubeSourceBuilderState.ts +179 -0
- package/src/stores/model/LocalFileDataCubeSource.ts +67 -0
- package/tsconfig.json +4 -1
- package/lib/stores/LegendDataCubeCacheManager.d.ts.map +0 -1
- package/lib/stores/LegendDataCubeCacheManager.js.map +0 -1
|
@@ -27,17 +27,27 @@ import {
|
|
|
27
27
|
} from '@finos/legend-graph';
|
|
28
28
|
import {
|
|
29
29
|
assertNonNullable,
|
|
30
|
+
csvStringify,
|
|
30
31
|
guaranteeNonNullable,
|
|
31
32
|
UnsupportedOperationError,
|
|
32
33
|
} from '@finos/legend-shared';
|
|
33
34
|
import type { CachedDataCubeSource } from '@finos/legend-data-cube';
|
|
34
35
|
import { Type } from 'apache-arrow';
|
|
35
36
|
|
|
36
|
-
export class
|
|
37
|
+
export class LegendDataCubeDuckDBEngine {
|
|
37
38
|
private static readonly DUCKDB_DEFAULT_SCHEMA_NAME = 'main'; // See https://duckdb.org/docs/sql/statements/use.html
|
|
38
|
-
private static readonly
|
|
39
|
-
private static readonly
|
|
40
|
-
private static
|
|
39
|
+
private static readonly CACHE_TABLE_NAME_PREFIX = 'cache';
|
|
40
|
+
private static readonly INGEST_TABLE_NAME_PREFIX = 'ingest';
|
|
41
|
+
private static readonly CACHE_FILE_NAME = 'cacheData';
|
|
42
|
+
private static readonly INGEST_FILE_DATA_FILE_NAME = 'ingestData';
|
|
43
|
+
private static cacheTableCounter = 0;
|
|
44
|
+
private static ingestFileTableCounter = 0;
|
|
45
|
+
// https://duckdb.org/docs/guides/meta/describe.html
|
|
46
|
+
private static readonly COLUMN_NAME = 'column_name';
|
|
47
|
+
private static readonly COLUMN_TYPE = 'column_type';
|
|
48
|
+
// Options for creating csv using papa parser: https://www.papaparse.com/docs#config
|
|
49
|
+
private static readonly ESCAPE_CHAR = `'`;
|
|
50
|
+
private static readonly QUOTE_CHAR = `'`;
|
|
41
51
|
|
|
42
52
|
private _database?: duckdb.AsyncDuckDB | undefined;
|
|
43
53
|
|
|
@@ -84,50 +94,79 @@ export class LegendDataCubeDataCubeCacheManager {
|
|
|
84
94
|
}
|
|
85
95
|
|
|
86
96
|
async cache(result: TDSExecutionResult) {
|
|
87
|
-
const schema =
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
const csvFileName = `${LegendDataCubeDataCubeCacheManager.CSV_FILE_NAME}${LegendDataCubeDataCubeCacheManager.tableCounter}.csv`;
|
|
97
|
+
const schema = LegendDataCubeDuckDBEngine.DUCKDB_DEFAULT_SCHEMA_NAME;
|
|
98
|
+
LegendDataCubeDuckDBEngine.cacheTableCounter += 1;
|
|
99
|
+
const table = `${LegendDataCubeDuckDBEngine.CACHE_TABLE_NAME_PREFIX}${LegendDataCubeDuckDBEngine.cacheTableCounter}`;
|
|
100
|
+
const csvFileName = `${LegendDataCubeDuckDBEngine.CACHE_FILE_NAME}${LegendDataCubeDuckDBEngine.cacheTableCounter}.csv`;
|
|
92
101
|
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
const columnString = result.builder.columns
|
|
96
|
-
.map((col) => col.name)
|
|
97
|
-
.join(',');
|
|
102
|
+
const columnNames: string[] = [];
|
|
103
|
+
result.builder.columns.forEach((col) => columnNames.push(col.name));
|
|
98
104
|
|
|
99
|
-
const
|
|
105
|
+
const data = result.result.rows.map((row) => row.values);
|
|
100
106
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return `'${val.replaceAll(`'`, `''`)}'`;
|
|
105
|
-
} else if (val === null) {
|
|
106
|
-
return `NULL`;
|
|
107
|
-
}
|
|
108
|
-
return val;
|
|
109
|
-
});
|
|
110
|
-
dataString.push(`${updatedRows.join(',')}`);
|
|
107
|
+
const csvContent = csvStringify([columnNames, ...data], {
|
|
108
|
+
escapeChar: LegendDataCubeDuckDBEngine.ESCAPE_CHAR,
|
|
109
|
+
quoteChar: LegendDataCubeDuckDBEngine.QUOTE_CHAR,
|
|
111
110
|
});
|
|
111
|
+
await this.database.registerFileText(csvFileName, csvContent);
|
|
112
112
|
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
await this._database?.registerFileText(csvFileName, csvString);
|
|
116
|
-
|
|
113
|
+
const connection = await this.database.connect();
|
|
117
114
|
await connection.insertCSVFromPath(csvFileName, {
|
|
118
115
|
schema: schema,
|
|
119
116
|
name: table,
|
|
120
|
-
create:
|
|
117
|
+
create: true,
|
|
121
118
|
header: true,
|
|
122
119
|
detect: true,
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
120
|
+
dateFormat: 'YYYY-MM-DD',
|
|
121
|
+
timestampFormat: 'YYYY-MM-DD', // make sure Date is not auto-converted to timestamp
|
|
122
|
+
escape: LegendDataCubeDuckDBEngine.ESCAPE_CHAR,
|
|
123
|
+
quote: LegendDataCubeDuckDBEngine.QUOTE_CHAR,
|
|
126
124
|
});
|
|
125
|
+
await connection.close();
|
|
126
|
+
|
|
127
|
+
return { schema, table, rowCount: result.result.rows.length };
|
|
128
|
+
}
|
|
127
129
|
|
|
130
|
+
async ingestLocalFileData(data: string, format: string) {
|
|
131
|
+
const schema = LegendDataCubeDuckDBEngine.DUCKDB_DEFAULT_SCHEMA_NAME;
|
|
132
|
+
LegendDataCubeDuckDBEngine.ingestFileTableCounter += 1;
|
|
133
|
+
const table = `${LegendDataCubeDuckDBEngine.INGEST_TABLE_NAME_PREFIX}${LegendDataCubeDuckDBEngine.ingestFileTableCounter}`;
|
|
134
|
+
const fileName = `${LegendDataCubeDuckDBEngine.INGEST_FILE_DATA_FILE_NAME}${LegendDataCubeDuckDBEngine.ingestFileTableCounter}`;
|
|
135
|
+
|
|
136
|
+
await this._database?.registerFileText(fileName, data);
|
|
137
|
+
|
|
138
|
+
const connection = await this.database.connect();
|
|
139
|
+
|
|
140
|
+
switch (format.toLowerCase()) {
|
|
141
|
+
case 'csv': {
|
|
142
|
+
await connection.insertCSVFromPath(fileName, {
|
|
143
|
+
schema: schema,
|
|
144
|
+
name: table,
|
|
145
|
+
header: true,
|
|
146
|
+
detect: true,
|
|
147
|
+
dateFormat: 'YYYY-MM-DD',
|
|
148
|
+
timestampFormat: 'YYYY-MM-DD', // make sure Date is not auto-converted to timestamp
|
|
149
|
+
escape: LegendDataCubeDuckDBEngine.ESCAPE_CHAR,
|
|
150
|
+
quote: LegendDataCubeDuckDBEngine.QUOTE_CHAR,
|
|
151
|
+
});
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
default: {
|
|
155
|
+
throw new UnsupportedOperationError(
|
|
156
|
+
`Can't ingest local file data: unsupported format '${format}'`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const tableSpec = (await connection.query(`DESCRIBE ${schema}.${table}`))
|
|
162
|
+
.toArray()
|
|
163
|
+
.map((spec) => [
|
|
164
|
+
spec[LegendDataCubeDuckDBEngine.COLUMN_NAME],
|
|
165
|
+
spec[LegendDataCubeDuckDBEngine.COLUMN_TYPE],
|
|
166
|
+
]);
|
|
128
167
|
await connection.close();
|
|
129
168
|
|
|
130
|
-
return {
|
|
169
|
+
return { schema, table, tableSpec };
|
|
131
170
|
}
|
|
132
171
|
|
|
133
172
|
async runSQLQuery(sql: string) {
|
|
@@ -139,14 +178,18 @@ export class LegendDataCubeDataCubeCacheManager {
|
|
|
139
178
|
const columnNames = result.schema.fields.map((field) => field.name);
|
|
140
179
|
const rows = data.map((row) => {
|
|
141
180
|
const tdsRow = new TDSRow();
|
|
142
|
-
tdsRow.values = columnNames.map(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
181
|
+
tdsRow.values = columnNames.map((column) => {
|
|
182
|
+
const value = row[column] as unknown;
|
|
183
|
+
// NOTE: DuckDB WASM returns ArrayBuffer for numeric value, such as for count(*)
|
|
184
|
+
// so we need to convert it to number
|
|
185
|
+
if (ArrayBuffer.isView(value)) {
|
|
186
|
+
return row[column].valueOf() as number;
|
|
187
|
+
// BigInt is not supported by ag-grid, so we need to convert it to native number
|
|
188
|
+
} else if (typeof value === 'bigint') {
|
|
189
|
+
return Number(value);
|
|
190
|
+
}
|
|
191
|
+
return value as string | number | boolean | null;
|
|
192
|
+
});
|
|
150
193
|
return tdsRow;
|
|
151
194
|
});
|
|
152
195
|
const tdsExecutionResult = new TDSExecutionResult();
|
|
@@ -167,6 +210,7 @@ export class LegendDataCubeDataCubeCacheManager {
|
|
|
167
210
|
col.type = PRIMITIVE_TYPE.BOOLEAN;
|
|
168
211
|
break;
|
|
169
212
|
}
|
|
213
|
+
case Type.Timestamp:
|
|
170
214
|
case Type.Date:
|
|
171
215
|
case Type.DateDay:
|
|
172
216
|
case Type.DateMillisecond: {
|
|
@@ -231,7 +231,7 @@ export class LegendDataCubeBuilderStore {
|
|
|
231
231
|
|
|
232
232
|
this.initializeState.inProgress();
|
|
233
233
|
try {
|
|
234
|
-
await this.engine.
|
|
234
|
+
await this.engine.initialize();
|
|
235
235
|
this.initializeState.pass();
|
|
236
236
|
} catch (error) {
|
|
237
237
|
assertErrorThrown(error);
|
|
@@ -244,7 +244,7 @@ export class LegendDataCubeBuilderStore {
|
|
|
244
244
|
|
|
245
245
|
async cleanUp() {
|
|
246
246
|
try {
|
|
247
|
-
await this.engine.
|
|
247
|
+
await this.engine.dispose();
|
|
248
248
|
} catch (error) {
|
|
249
249
|
assertErrorThrown(error);
|
|
250
250
|
this.alertService.alertError(error, {
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
type LegendDataCubeBuilderStore,
|
|
42
42
|
} from './LegendDataCubeBuilderStore.js';
|
|
43
43
|
import { generateBuilderRoute } from '../../__lib__/LegendDataCubeNavigation.js';
|
|
44
|
+
import { LocalFileDataCubeSourceBuilderState } from './source/LocalFileDataCubeSourceBuilderState.js';
|
|
44
45
|
|
|
45
46
|
const DEFAULT_SOURCE_TYPE = LegendDataCubeSourceBuilderType.LEGEND_QUERY;
|
|
46
47
|
|
|
@@ -105,6 +106,11 @@ export class LegendDataCubeCreatorState {
|
|
|
105
106
|
this._application,
|
|
106
107
|
this._engine,
|
|
107
108
|
);
|
|
109
|
+
case LegendDataCubeSourceBuilderType.LOCAL_FILE:
|
|
110
|
+
return new LocalFileDataCubeSourceBuilderState(
|
|
111
|
+
this._application,
|
|
112
|
+
this._engine,
|
|
113
|
+
);
|
|
108
114
|
default:
|
|
109
115
|
throw new UnsupportedOperationError(
|
|
110
116
|
`Can't create source builder for unsupported type '${type}'`,
|
|
@@ -22,6 +22,7 @@ import type { DataCubeConfiguration } from '@finos/legend-data-cube';
|
|
|
22
22
|
export enum LegendDataCubeSourceBuilderType {
|
|
23
23
|
LEGEND_QUERY = 'Legend Query',
|
|
24
24
|
ADHOC_QUERY = 'Ad hoc Query',
|
|
25
|
+
LOCAL_FILE = 'Local File',
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
export abstract class LegendDataCubeSourceBuilderState {
|
|
@@ -40,7 +41,9 @@ export abstract class LegendDataCubeSourceBuilderState {
|
|
|
40
41
|
abstract get isValid(): boolean;
|
|
41
42
|
abstract generateSourceData(): Promise<PlainObject>;
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Modifies the configuration of the finalized DataCube based on the source builder.
|
|
46
|
+
*/
|
|
44
47
|
finalizeConfiguration(configuration: DataCubeConfiguration) {
|
|
45
48
|
// do nothing
|
|
46
49
|
}
|
|
@@ -137,7 +137,9 @@ export class LegendQueryDataCubeSourceBuilderState extends LegendDataCubeSourceB
|
|
|
137
137
|
|
|
138
138
|
override async generateSourceData() {
|
|
139
139
|
if (!this.query) {
|
|
140
|
-
throw new IllegalStateError(
|
|
140
|
+
throw new IllegalStateError(
|
|
141
|
+
`Can't generate source data: query is not set`,
|
|
142
|
+
);
|
|
141
143
|
}
|
|
142
144
|
const source = new RawLegendQueryDataCubeSource();
|
|
143
145
|
source.queryId = this.query.id;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2020-present, Goldman Sachs
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
ActionState,
|
|
19
|
+
csvStringify,
|
|
20
|
+
guaranteeType,
|
|
21
|
+
IllegalStateError,
|
|
22
|
+
parseCSVFile,
|
|
23
|
+
type PlainObject,
|
|
24
|
+
} from '@finos/legend-shared';
|
|
25
|
+
import {
|
|
26
|
+
LegendDataCubeSourceBuilderState,
|
|
27
|
+
LegendDataCubeSourceBuilderType,
|
|
28
|
+
} from './LegendDataCubeSourceBuilderState.js';
|
|
29
|
+
import type { LegendDataCubeApplicationStore } from '../../LegendDataCubeBaseStore.js';
|
|
30
|
+
import { action, makeObservable, observable } from 'mobx';
|
|
31
|
+
import type { LegendDataCubeDataCubeEngine } from '../../LegendDataCubeDataCubeEngine.js';
|
|
32
|
+
import {
|
|
33
|
+
LocalFileDataCubeSource,
|
|
34
|
+
LocalFileDataCubeSourceFormat,
|
|
35
|
+
RawLocalFileQueryDataCubeSource,
|
|
36
|
+
} from '../../model/LocalFileDataCubeSource.js';
|
|
37
|
+
|
|
38
|
+
export class LocalFileDataCubeSourceBuilderState extends LegendDataCubeSourceBuilderState {
|
|
39
|
+
readonly processState = ActionState.create();
|
|
40
|
+
|
|
41
|
+
fileName?: string | undefined;
|
|
42
|
+
fileFormat?: string | undefined;
|
|
43
|
+
// NOTE: type string is suitable for CSV/Excel, etc. but will not be appropriate
|
|
44
|
+
// for other format that we want to support, e.g. arrow/parquet
|
|
45
|
+
fileData?: string | undefined;
|
|
46
|
+
previewText?: string | undefined;
|
|
47
|
+
rowCount?: number | undefined;
|
|
48
|
+
|
|
49
|
+
constructor(
|
|
50
|
+
application: LegendDataCubeApplicationStore,
|
|
51
|
+
engine: LegendDataCubeDataCubeEngine,
|
|
52
|
+
) {
|
|
53
|
+
super(application, engine);
|
|
54
|
+
|
|
55
|
+
makeObservable(this, {
|
|
56
|
+
fileName: observable,
|
|
57
|
+
setFileName: action,
|
|
58
|
+
|
|
59
|
+
fileFormat: observable,
|
|
60
|
+
setFileFormat: action,
|
|
61
|
+
|
|
62
|
+
fileData: observable,
|
|
63
|
+
setFileData: action,
|
|
64
|
+
|
|
65
|
+
previewText: observable,
|
|
66
|
+
setPreviewText: action,
|
|
67
|
+
|
|
68
|
+
rowCount: observable,
|
|
69
|
+
setRowCount: action,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
setFileName(fileName: string | undefined) {
|
|
74
|
+
this.fileName = fileName;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
setFileFormat(format: string | undefined) {
|
|
78
|
+
this.fileFormat = format;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
setFileData(data: string | undefined) {
|
|
82
|
+
this.fileData = data;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
setRowCount(count: number | undefined) {
|
|
86
|
+
this.rowCount = count;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
setPreviewText(text: string | undefined) {
|
|
90
|
+
this.previewText = text;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
processFile(file: File | undefined) {
|
|
94
|
+
this.setFileName(undefined);
|
|
95
|
+
this.setFileFormat(undefined);
|
|
96
|
+
this.setFileData(undefined);
|
|
97
|
+
this.setRowCount(undefined);
|
|
98
|
+
this.setPreviewText(undefined);
|
|
99
|
+
|
|
100
|
+
if (!file) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this.processState.inProgress();
|
|
105
|
+
|
|
106
|
+
const fileName = file.name;
|
|
107
|
+
const fileFormat = fileName.split('.').pop();
|
|
108
|
+
|
|
109
|
+
switch (fileFormat?.toLowerCase()) {
|
|
110
|
+
case LocalFileDataCubeSourceFormat.CSV.toLowerCase(): {
|
|
111
|
+
parseCSVFile(file, {
|
|
112
|
+
complete: (result) => {
|
|
113
|
+
this.setFileData(
|
|
114
|
+
csvStringify(result.data, { escapeChar: `'`, quoteChar: `'` }),
|
|
115
|
+
);
|
|
116
|
+
this.setFileName(fileName);
|
|
117
|
+
this.setFileFormat(fileFormat);
|
|
118
|
+
this.setRowCount(result.data.length);
|
|
119
|
+
this.setPreviewText(
|
|
120
|
+
csvStringify(result.data.slice(0, 100), {
|
|
121
|
+
escapeChar: `'`,
|
|
122
|
+
quoteChar: `'`,
|
|
123
|
+
}),
|
|
124
|
+
);
|
|
125
|
+
},
|
|
126
|
+
header: true,
|
|
127
|
+
dynamicTyping: false,
|
|
128
|
+
skipEmptyLines: true,
|
|
129
|
+
});
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
default: {
|
|
133
|
+
this.processState.complete();
|
|
134
|
+
throw new IllegalStateError(
|
|
135
|
+
`Can't process file with format '${fileFormat}'`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
this.processState.complete();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
override get label(): LegendDataCubeSourceBuilderType {
|
|
144
|
+
return LegendDataCubeSourceBuilderType.LOCAL_FILE;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
override get isValid(): boolean {
|
|
148
|
+
return Boolean(this.fileData);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
override async generateSourceData(): Promise<PlainObject> {
|
|
152
|
+
if (
|
|
153
|
+
!this.fileData ||
|
|
154
|
+
!this.fileName ||
|
|
155
|
+
!this.fileFormat ||
|
|
156
|
+
this.rowCount === undefined
|
|
157
|
+
) {
|
|
158
|
+
throw new IllegalStateError(
|
|
159
|
+
`Can't generate source data: file data and information is not set`,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const source = guaranteeType(
|
|
164
|
+
await this._engine.ingestLocalFileData(this.fileData, this.fileFormat),
|
|
165
|
+
LocalFileDataCubeSource,
|
|
166
|
+
`Can't generate data source`,
|
|
167
|
+
);
|
|
168
|
+
const rawSource = new RawLocalFileQueryDataCubeSource();
|
|
169
|
+
rawSource.count = this.rowCount;
|
|
170
|
+
rawSource.fileName = this.fileName;
|
|
171
|
+
rawSource.db = source.db;
|
|
172
|
+
rawSource.model = source.model;
|
|
173
|
+
rawSource.schema = source.schema;
|
|
174
|
+
rawSource.table = source.table;
|
|
175
|
+
rawSource.runtime = source.runtime;
|
|
176
|
+
|
|
177
|
+
return RawLocalFileQueryDataCubeSource.serialization.toJson(rawSource);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2020-present, Goldman Sachs
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { DataCubeSource } from '@finos/legend-data-cube';
|
|
18
|
+
import { type V1_PureModelContextData } from '@finos/legend-graph';
|
|
19
|
+
import {
|
|
20
|
+
SerializationFactory,
|
|
21
|
+
usingConstantValueSchema,
|
|
22
|
+
type PlainObject,
|
|
23
|
+
} from '@finos/legend-shared';
|
|
24
|
+
import { createModelSchema, primitive, raw } from 'serializr';
|
|
25
|
+
|
|
26
|
+
export const LOCAL_FILE_QUERY_DATA_CUBE_SOURCE_TYPE = 'localFile';
|
|
27
|
+
|
|
28
|
+
export enum LocalFileDataCubeSourceFormat {
|
|
29
|
+
CSV = 'csv',
|
|
30
|
+
// TODO: arrow/parquet/excel, etc.
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class LocalFileDataCubeSource extends DataCubeSource {
|
|
34
|
+
model!: PlainObject<V1_PureModelContextData>;
|
|
35
|
+
runtime!: string;
|
|
36
|
+
db!: string;
|
|
37
|
+
schema!: string;
|
|
38
|
+
table!: string;
|
|
39
|
+
count!: number;
|
|
40
|
+
fileName!: string;
|
|
41
|
+
fileFormat!: LocalFileDataCubeSourceFormat;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export class RawLocalFileQueryDataCubeSource {
|
|
45
|
+
model!: PlainObject<V1_PureModelContextData>;
|
|
46
|
+
runtime!: string;
|
|
47
|
+
db!: string;
|
|
48
|
+
schema!: string;
|
|
49
|
+
table!: string;
|
|
50
|
+
count!: number;
|
|
51
|
+
fileName!: string;
|
|
52
|
+
fileFormat!: LocalFileDataCubeSourceFormat;
|
|
53
|
+
|
|
54
|
+
static readonly serialization = new SerializationFactory(
|
|
55
|
+
createModelSchema(RawLocalFileQueryDataCubeSource, {
|
|
56
|
+
_type: usingConstantValueSchema(LOCAL_FILE_QUERY_DATA_CUBE_SOURCE_TYPE),
|
|
57
|
+
count: primitive(),
|
|
58
|
+
db: primitive(),
|
|
59
|
+
fileFormat: primitive(),
|
|
60
|
+
fileName: primitive(),
|
|
61
|
+
model: raw(),
|
|
62
|
+
runtime: primitive(),
|
|
63
|
+
schema: primitive(),
|
|
64
|
+
table: primitive(),
|
|
65
|
+
}),
|
|
66
|
+
);
|
|
67
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -62,12 +62,14 @@
|
|
|
62
62
|
"./src/application/LegendDataCubePluginManager.ts",
|
|
63
63
|
"./src/stores/DuckDBWASM.d.ts",
|
|
64
64
|
"./src/stores/LegendDataCubeBaseStore.ts",
|
|
65
|
-
"./src/stores/LegendDataCubeCacheManager.ts",
|
|
66
65
|
"./src/stores/LegendDataCubeDataCubeEngine.ts",
|
|
66
|
+
"./src/stores/LegendDataCubeDuckDBEngine.ts",
|
|
67
67
|
"./src/stores/builder/source/AdhocQueryDataCubeSourceBuilderState.ts",
|
|
68
68
|
"./src/stores/builder/source/LegendDataCubeSourceBuilderState.ts",
|
|
69
69
|
"./src/stores/builder/source/LegendQueryDataCubeSourceBuilderState.ts",
|
|
70
|
+
"./src/stores/builder/source/LocalFileDataCubeSourceBuilderState.ts",
|
|
70
71
|
"./src/stores/model/LegendQueryDataCubeSource.ts",
|
|
72
|
+
"./src/stores/model/LocalFileDataCubeSource.ts",
|
|
71
73
|
"./src/application/LegendDataCube.tsx",
|
|
72
74
|
"./src/components/LegendDataCubeBlockingWindow.tsx",
|
|
73
75
|
"./src/components/LegendDataCubeFrameworkProvider.tsx",
|
|
@@ -81,6 +83,7 @@
|
|
|
81
83
|
"./src/components/builder/LegendDataCubeSourceViewer.tsx",
|
|
82
84
|
"./src/components/builder/source/AdhocQueryDataCubeSourceBuilder.tsx",
|
|
83
85
|
"./src/components/builder/source/LegendQueryDataCubeSourceBuilder.tsx",
|
|
86
|
+
"./src/components/builder/source/LocalFileDataCubeSourceBuilder.tsx",
|
|
84
87
|
"./src/stores/builder/LegendDataCubeBuilderStore.tsx",
|
|
85
88
|
"./src/stores/builder/LegendDataCubeCreatorState.tsx",
|
|
86
89
|
"./src/stores/builder/LegendDataCubeLoaderState.tsx"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LegendDataCubeCacheManager.d.ts","sourceRoot":"","sources":["../../src/stores/LegendDataCubeCacheManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,EAIL,kBAAkB,EAGnB,MAAM,qBAAqB,CAAC;AAM7B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAGpE,qBAAa,kCAAkC;IAC7C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,0BAA0B,CAAU;IAC5D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAW;IACpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAU;IAC/C,OAAO,CAAC,MAAM,CAAC,YAAY,CAAK;IAEhC,OAAO,CAAC,SAAS,CAAC,CAAiC;IAEnD,OAAO,KAAK,QAAQ,GAKnB;IAEK,UAAU;IAmCV,KAAK,CAAC,MAAM,EAAE,kBAAkB;;;;;IA+ChC,WAAW,CAAC,GAAG,EAAE,MAAM;IAmFvB,YAAY,CAAC,MAAM,EAAE,oBAAoB;IAOzC,OAAO;CAId"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LegendDataCubeCacheManager.js","sourceRoot":"","sources":["../../src/stores/LegendDataCubeCacheManager.ts"],"names":[],"mappings":"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"}
|