@finos/legend-application-data-cube 0.3.2 → 0.3.3

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.
Files changed (112) hide show
  1. package/lib/__lib__/LegendDataCubeNavigation.d.ts +2 -0
  2. package/lib/__lib__/LegendDataCubeNavigation.d.ts.map +1 -1
  3. package/lib/__lib__/LegendDataCubeNavigation.js +2 -0
  4. package/lib/__lib__/LegendDataCubeNavigation.js.map +1 -1
  5. package/lib/application/LegendDataCubeApplicationConfig.d.ts +4 -0
  6. package/lib/application/LegendDataCubeApplicationConfig.d.ts.map +1 -1
  7. package/lib/application/LegendDataCubeApplicationConfig.js +5 -0
  8. package/lib/application/LegendDataCubeApplicationConfig.js.map +1 -1
  9. package/lib/application/__test-utils__/LegendDataCubeApplicationTestUtils.d.ts +18 -0
  10. package/lib/application/__test-utils__/LegendDataCubeApplicationTestUtils.d.ts.map +1 -0
  11. package/lib/application/__test-utils__/LegendDataCubeApplicationTestUtils.js +48 -0
  12. package/lib/application/__test-utils__/LegendDataCubeApplicationTestUtils.js.map +1 -0
  13. package/lib/components/LegendDataCubeBlockingWindow.d.ts +2 -1
  14. package/lib/components/LegendDataCubeBlockingWindow.d.ts.map +1 -1
  15. package/lib/components/LegendDataCubeBlockingWindow.js +8 -3
  16. package/lib/components/LegendDataCubeBlockingWindow.js.map +1 -1
  17. package/lib/components/__test-utils__/LegendDataCubeStoreTestUtils.d.ts +42 -0
  18. package/lib/components/__test-utils__/LegendDataCubeStoreTestUtils.d.ts.map +1 -0
  19. package/lib/components/__test-utils__/LegendDataCubeStoreTestUtils.js +104 -0
  20. package/lib/components/__test-utils__/LegendDataCubeStoreTestUtils.js.map +1 -0
  21. package/lib/components/builder/LegendDataCubeBuilder.d.ts +5 -0
  22. package/lib/components/builder/LegendDataCubeBuilder.d.ts.map +1 -1
  23. package/lib/components/builder/LegendDataCubeBuilder.js +26 -5
  24. package/lib/components/builder/LegendDataCubeBuilder.js.map +1 -1
  25. package/lib/components/builder/LegendDataCubeBuilderStoreProvider.d.ts.map +1 -1
  26. package/lib/components/builder/LegendDataCubeBuilderStoreProvider.js +1 -1
  27. package/lib/components/builder/LegendDataCubeBuilderStoreProvider.js.map +1 -1
  28. package/lib/components/builder/LegendDataCubePartialSourceLoader.d.ts +19 -0
  29. package/lib/components/builder/LegendDataCubePartialSourceLoader.d.ts.map +1 -0
  30. package/lib/components/builder/LegendDataCubePartialSourceLoader.js +50 -0
  31. package/lib/components/builder/LegendDataCubePartialSourceLoader.js.map +1 -0
  32. package/lib/components/builder/LegendDataCubeSourceViewer.d.ts.map +1 -1
  33. package/lib/components/builder/LegendDataCubeSourceViewer.js +61 -1
  34. package/lib/components/builder/LegendDataCubeSourceViewer.js.map +1 -1
  35. package/lib/components/builder/source/LocalFileDataCubeSourceBuilder.d.ts.map +1 -1
  36. package/lib/components/builder/source/LocalFileDataCubeSourceBuilder.js +1 -2
  37. package/lib/components/builder/source/LocalFileDataCubeSourceBuilder.js.map +1 -1
  38. package/lib/components/builder/source/loader/LocalFileDataCubePartialSourceLoader.d.ts +22 -0
  39. package/lib/components/builder/source/loader/LocalFileDataCubePartialSourceLoader.d.ts.map +1 -0
  40. package/lib/components/builder/source/loader/LocalFileDataCubePartialSourceLoader.js +28 -0
  41. package/lib/components/builder/source/loader/LocalFileDataCubePartialSourceLoader.js.map +1 -0
  42. package/lib/index.css +2 -2
  43. package/lib/index.css.map +1 -1
  44. package/lib/package.json +3 -1
  45. package/lib/stores/LegendDataCubeBaseStore.d.ts +2 -1
  46. package/lib/stores/LegendDataCubeBaseStore.d.ts.map +1 -1
  47. package/lib/stores/LegendDataCubeBaseStore.js +6 -3
  48. package/lib/stores/LegendDataCubeBaseStore.js.map +1 -1
  49. package/lib/stores/LegendDataCubeDataCubeEngine.d.ts +6 -3
  50. package/lib/stores/LegendDataCubeDataCubeEngine.d.ts.map +1 -1
  51. package/lib/stores/LegendDataCubeDataCubeEngine.js +127 -106
  52. package/lib/stores/LegendDataCubeDataCubeEngine.js.map +1 -1
  53. package/lib/stores/LegendDataCubeDuckDBEngine.d.ts +11 -4
  54. package/lib/stores/LegendDataCubeDuckDBEngine.d.ts.map +1 -1
  55. package/lib/stores/LegendDataCubeDuckDBEngine.js +83 -16
  56. package/lib/stores/LegendDataCubeDuckDBEngine.js.map +1 -1
  57. package/lib/stores/builder/LegendDataCubeBuilderStore.d.ts +4 -0
  58. package/lib/stores/builder/LegendDataCubeBuilderStore.d.ts.map +1 -1
  59. package/lib/stores/builder/LegendDataCubeBuilderStore.js +58 -17
  60. package/lib/stores/builder/LegendDataCubeBuilderStore.js.map +1 -1
  61. package/lib/stores/builder/LegendDataCubeLoaderState.d.ts +11 -2
  62. package/lib/stores/builder/LegendDataCubeLoaderState.d.ts.map +1 -1
  63. package/lib/stores/builder/LegendDataCubeLoaderState.js +28 -1
  64. package/lib/stores/builder/LegendDataCubeLoaderState.js.map +1 -1
  65. package/lib/stores/builder/LegendDataCubeSourceLoaderState.d.ts +44 -0
  66. package/lib/stores/builder/LegendDataCubeSourceLoaderState.d.ts.map +1 -0
  67. package/lib/stores/builder/LegendDataCubeSourceLoaderState.js +101 -0
  68. package/lib/stores/builder/LegendDataCubeSourceLoaderState.js.map +1 -0
  69. package/lib/stores/builder/source/LocalFileDataCubeSourceBuilderState.d.ts +3 -2
  70. package/lib/stores/builder/source/LocalFileDataCubeSourceBuilderState.d.ts.map +1 -1
  71. package/lib/stores/builder/source/LocalFileDataCubeSourceBuilderState.js +7 -10
  72. package/lib/stores/builder/source/LocalFileDataCubeSourceBuilderState.js.map +1 -1
  73. package/lib/stores/builder/source/loader/LegendDataCubePartialSourceLoaderState.d.ts +29 -0
  74. package/lib/stores/builder/source/loader/LegendDataCubePartialSourceLoaderState.d.ts.map +1 -0
  75. package/lib/stores/builder/source/loader/LegendDataCubePartialSourceLoaderState.js +25 -0
  76. package/lib/stores/builder/source/loader/LegendDataCubePartialSourceLoaderState.js.map +1 -0
  77. package/lib/stores/builder/source/loader/LocalFileDataCubePartialSourceLoaderState.d.ts +43 -0
  78. package/lib/stores/builder/source/loader/LocalFileDataCubePartialSourceLoaderState.d.ts.map +1 -0
  79. package/lib/stores/builder/source/loader/LocalFileDataCubePartialSourceLoaderState.js +142 -0
  80. package/lib/stores/builder/source/loader/LocalFileDataCubePartialSourceLoaderState.js.map +1 -0
  81. package/lib/stores/model/LegendQueryDataCubeSource.d.ts +2 -1
  82. package/lib/stores/model/LegendQueryDataCubeSource.d.ts.map +1 -1
  83. package/lib/stores/model/LegendQueryDataCubeSource.js +1 -0
  84. package/lib/stores/model/LegendQueryDataCubeSource.js.map +1 -1
  85. package/lib/stores/model/LocalFileDataCubeSource.d.ts +3 -8
  86. package/lib/stores/model/LocalFileDataCubeSource.d.ts.map +1 -1
  87. package/lib/stores/model/LocalFileDataCubeSource.js +5 -15
  88. package/lib/stores/model/LocalFileDataCubeSource.js.map +1 -1
  89. package/package.json +13 -11
  90. package/src/__lib__/LegendDataCubeNavigation.ts +21 -0
  91. package/src/application/LegendDataCubeApplicationConfig.ts +10 -0
  92. package/src/application/__test-utils__/LegendDataCubeApplicationTestUtils.ts +52 -0
  93. package/src/components/LegendDataCubeBlockingWindow.tsx +8 -2
  94. package/src/components/__test-utils__/LegendDataCubeStoreTestUtils.tsx +231 -0
  95. package/src/components/builder/LegendDataCubeBuilder.tsx +51 -6
  96. package/src/components/builder/LegendDataCubeBuilderStoreProvider.tsx +3 -0
  97. package/src/components/builder/LegendDataCubePartialSourceLoader.tsx +108 -0
  98. package/src/components/builder/LegendDataCubeSourceViewer.tsx +171 -1
  99. package/src/components/builder/source/LocalFileDataCubeSourceBuilder.tsx +1 -2
  100. package/src/components/builder/source/loader/LocalFileDataCubePartialSourceLoader.tsx +60 -0
  101. package/src/stores/LegendDataCubeBaseStore.ts +13 -6
  102. package/src/stores/LegendDataCubeDataCubeEngine.ts +161 -120
  103. package/src/stores/LegendDataCubeDuckDBEngine.ts +101 -17
  104. package/src/stores/builder/LegendDataCubeBuilderStore.tsx +96 -24
  105. package/src/stores/builder/LegendDataCubeLoaderState.tsx +44 -1
  106. package/src/stores/builder/LegendDataCubeSourceLoaderState.tsx +145 -0
  107. package/src/stores/builder/source/LocalFileDataCubeSourceBuilderState.ts +9 -14
  108. package/src/stores/builder/source/loader/LegendDataCubePartialSourceLoaderState.ts +41 -0
  109. package/src/stores/builder/source/loader/LocalFileDataCubePartialSourceLoaderState.ts +217 -0
  110. package/src/stores/model/LegendQueryDataCubeSource.ts +2 -0
  111. package/src/stores/model/LocalFileDataCubeSource.ts +6 -15
  112. package/tsconfig.json +8 -1
@@ -27,6 +27,9 @@ import { useRef } from 'react';
27
27
  export const LegendDataCubeBlockingWindow = observer(
28
28
  (props: { windowState: LegendDataCubeBlockingWindowState }) => {
29
29
  const { windowState } = props;
30
+ const closeModal = !windowState.onClose
31
+ ? (): void => windowState.close()
32
+ : windowState.onClose;
30
33
  const ref = useRef<HTMLDivElement>(null);
31
34
 
32
35
  // set the width and height of the dialog to make sure content overflow works properly
@@ -45,7 +48,7 @@ export const LegendDataCubeBlockingWindow = observer(
45
48
  return (
46
49
  <Dialog
47
50
  open={windowState.isOpen}
48
- onClose={() => windowState.close()}
51
+ onClose={closeModal}
49
52
  slotProps={{
50
53
  transition: {
51
54
  onEnter: handleEnter,
@@ -72,7 +75,7 @@ export const LegendDataCubeBlockingWindow = observer(
72
75
  <div className="px-2">{windowState.configuration.title}</div>
73
76
  <button
74
77
  className="flex h-[23px] w-6 items-center justify-center hover:bg-red-500 hover:text-white"
75
- onClick={() => windowState.close()}
78
+ onClick={closeModal}
76
79
  >
77
80
  <DataCubeIcon.X />
78
81
  </button>
@@ -91,11 +94,13 @@ export const LegendDataCubeBlockingWindow = observer(
91
94
  export class LegendDataCubeBlockingWindowState {
92
95
  isOpen = false;
93
96
  readonly configuration: LayoutConfiguration;
97
+ readonly onClose: (() => void) | undefined;
94
98
 
95
99
  constructor(
96
100
  title: string,
97
101
  contentRenderer: (config: LayoutConfiguration) => React.ReactNode,
98
102
  windowConfiguration?: WindowConfiguration | undefined,
103
+ closeModal?: (() => void) | undefined,
99
104
  ) {
100
105
  makeObservable(this, {
101
106
  isOpen: observable,
@@ -108,6 +113,7 @@ export class LegendDataCubeBlockingWindowState {
108
113
  if (windowConfiguration) {
109
114
  this.configuration.window = windowConfiguration;
110
115
  }
116
+ this.onClose = closeModal;
111
117
  }
112
118
 
113
119
  open() {
@@ -0,0 +1,231 @@
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 { type RenderResult, render, waitFor } from '@testing-library/react';
18
+ import {
19
+ type AbstractPlugin,
20
+ type AbstractPreset,
21
+ type PlainObject,
22
+ } from '@finos/legend-shared';
23
+ import { createMock, createSpy } from '@finos/legend-shared/test';
24
+ import {
25
+ ApplicationStore,
26
+ ApplicationStoreProvider,
27
+ } from '@finos/legend-application';
28
+ import { LegendDataCubePluginManager } from '../../application/LegendDataCubePluginManager.js';
29
+ import { Core_LegendDataCubeApplicationPlugin } from '../../application/Core_LegendDataCubeApplicationPlugin.js';
30
+ import { TEST__getTestLegendDataCubeApplicationConfig } from '../../application/__test-utils__/LegendDataCubeApplicationTestUtils.js';
31
+ import {
32
+ type LegendDataCubeBuilderState,
33
+ LegendDataCubeBuilderStore,
34
+ } from '../../stores/builder/LegendDataCubeBuilderStore.js';
35
+ import {
36
+ type LegendDataCubeApplicationStore,
37
+ LegendDataCubeBaseStore,
38
+ } from '../../stores/LegendDataCubeBaseStore.js';
39
+ import { TEST__BrowserEnvironmentProvider } from '@finos/legend-application/test';
40
+ import { LegendDataCubeFrameworkProvider } from '../LegendDataCubeFrameworkProvider.js';
41
+ import { Route, Routes } from '@finos/legend-application/browser';
42
+ import { LEGEND_DATA_CUBE_ROUTE_PATTERN } from '../../__lib__/LegendDataCubeNavigation.js';
43
+ import { LegendDataCubeBuilder } from '../builder/LegendDataCubeBuilder.js';
44
+ import { LEGEND_DATACUBE_TEST_ID } from '@finos/legend-data-cube';
45
+ import { Core_LegendDataCube_LegendApplicationPlugin } from '../../application/Core_LegendDataCube_LegendApplicationPlugin.js';
46
+ import {
47
+ type PersistentDataCube,
48
+ type V1_LambdaReturnTypeInput,
49
+ type V1_Query,
50
+ type V1_RawLambda,
51
+ type V1_ValueSpecification,
52
+ V1_entitiesToPureModelContextData,
53
+ V1_ExecuteInput,
54
+ V1_PureModelContextData,
55
+ V1_serializePureModelContext,
56
+ } from '@finos/legend-graph';
57
+ import { DSL_DataSpace_GraphManagerPreset } from '@finos/legend-extension-dsl-data-space/graph';
58
+ import {
59
+ ENGINE_TEST_SUPPORT__execute,
60
+ ENGINE_TEST_SUPPORT__getLambdaRelationType,
61
+ ENGINE_TEST_SUPPORT__grammarToJSON_lambda,
62
+ ENGINE_TEST_SUPPORT__grammarToJSON_valueSpecification,
63
+ ENGINE_TEST_SUPPORT__JSONToGrammar_valueSpecification,
64
+ } from '@finos/legend-graph/test';
65
+ import type { Entity } from '@finos/legend-storage';
66
+
67
+ export const TEST__provideMockedLegendDataCubeBaseStore =
68
+ async (customization?: {
69
+ mock?: LegendDataCubeBaseStore | undefined;
70
+ applicationStore?: LegendDataCubeApplicationStore | undefined;
71
+ pluginManager?: LegendDataCubePluginManager | undefined;
72
+ extraPlugins?: AbstractPlugin[] | undefined;
73
+ extraPresets?: AbstractPreset[] | undefined;
74
+ }): Promise<LegendDataCubeBaseStore> => {
75
+ const pluginManager =
76
+ customization?.pluginManager ?? LegendDataCubePluginManager.create();
77
+ pluginManager
78
+ .usePlugins([
79
+ new Core_LegendDataCube_LegendApplicationPlugin(),
80
+ new Core_LegendDataCubeApplicationPlugin(),
81
+ ...(customization?.extraPlugins ?? []),
82
+ ])
83
+ .usePresets([
84
+ new DSL_DataSpace_GraphManagerPreset(),
85
+ ...(customization?.extraPresets ?? []),
86
+ ])
87
+ .install();
88
+ const applicationStore =
89
+ customization?.applicationStore ??
90
+ new ApplicationStore(
91
+ TEST__getTestLegendDataCubeApplicationConfig(),
92
+ pluginManager,
93
+ );
94
+ const value =
95
+ customization?.mock ?? new LegendDataCubeBaseStore(applicationStore);
96
+ await value.initialize();
97
+ const MOCK__LegendDataCubeBaseStoreProvider = require('../LegendDataCubeFrameworkProvider.js'); // eslint-disable-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-require-imports
98
+ MOCK__LegendDataCubeBaseStoreProvider.useLegendDataCubeBaseStore =
99
+ createMock();
100
+ MOCK__LegendDataCubeBaseStoreProvider.useLegendDataCubeBaseStore.mockReturnValue(
101
+ value,
102
+ );
103
+ return value;
104
+ };
105
+
106
+ export const TEST__provideMockedLegendDataCubeBuilderStore =
107
+ async (customization?: {
108
+ mock?: LegendDataCubeBuilderStore;
109
+ mockBaseStore?: LegendDataCubeBaseStore;
110
+ applicationStore?: LegendDataCubeApplicationStore;
111
+ pluginManager?: LegendDataCubePluginManager;
112
+ extraPlugins?: AbstractPlugin[];
113
+ extraPresets?: AbstractPreset[];
114
+ }): Promise<LegendDataCubeBuilderStore> => {
115
+ const value =
116
+ customization?.mock ??
117
+ new LegendDataCubeBuilderStore(
118
+ await TEST__provideMockedLegendDataCubeBaseStore({
119
+ mock: customization?.mockBaseStore,
120
+ applicationStore: customization?.applicationStore,
121
+ pluginManager: customization?.pluginManager,
122
+ extraPlugins: customization?.extraPlugins,
123
+ extraPresets: customization?.extraPresets,
124
+ }),
125
+ );
126
+ const MOCK__LegendDataCubeBuilderStoreProvider = require('../builder/LegendDataCubeBuilderStoreProvider.js'); // eslint-disable-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-require-imports
127
+ MOCK__LegendDataCubeBuilderStoreProvider.useLegendDataCubeBuilderStore =
128
+ createMock();
129
+ MOCK__LegendDataCubeBuilderStoreProvider.useLegendDataCubeBuilderStore.mockReturnValue(
130
+ value,
131
+ );
132
+ return value;
133
+ };
134
+
135
+ export const TEST__setUpDataCubeBuilder = async (
136
+ MOCK__builderStore: LegendDataCubeBuilderStore,
137
+ mockDataCube?: PersistentDataCube,
138
+ mockQuery?: V1_Query,
139
+ mockEntities?: PlainObject<Entity>[],
140
+ ): Promise<{
141
+ renderResult: RenderResult;
142
+ legendDataCubeBuilderState: LegendDataCubeBuilderState | undefined;
143
+ }> => {
144
+ if (mockDataCube) {
145
+ createSpy(MOCK__builderStore.graphManager, 'getDataCube').mockResolvedValue(
146
+ mockDataCube,
147
+ );
148
+ }
149
+ if (mockQuery) {
150
+ createSpy(
151
+ MOCK__builderStore.graphManager.engine,
152
+ 'getQuery',
153
+ ).mockResolvedValue(mockQuery);
154
+ }
155
+ if (mockEntities) {
156
+ const pmcd = new V1_PureModelContextData();
157
+ await V1_entitiesToPureModelContextData(
158
+ mockEntities as unknown as Entity[],
159
+ pmcd,
160
+ MOCK__builderStore.application.pluginManager.getPureProtocolProcessorPlugins(),
161
+ undefined,
162
+ undefined,
163
+ );
164
+ createSpy(
165
+ MOCK__builderStore.depotServerClient,
166
+ 'getVersionEntities',
167
+ ).mockResolvedValue(mockEntities);
168
+ createSpy(
169
+ MOCK__builderStore.engineServerClient,
170
+ 'lambdaRelationType',
171
+ ).mockImplementation(
172
+ async (input: PlainObject<V1_LambdaReturnTypeInput>) => {
173
+ return ENGINE_TEST_SUPPORT__getLambdaRelationType(
174
+ input.lambda as PlainObject<V1_RawLambda>,
175
+ V1_serializePureModelContext(pmcd),
176
+ );
177
+ },
178
+ );
179
+ createSpy(
180
+ MOCK__builderStore.engineServerClient,
181
+ 'runQuery',
182
+ ).mockImplementation(async (input: PlainObject<V1_ExecuteInput>) => {
183
+ const executeInput = V1_ExecuteInput.serialization.fromJson(input);
184
+ executeInput.model = pmcd;
185
+ return ENGINE_TEST_SUPPORT__execute(executeInput);
186
+ });
187
+ }
188
+ createSpy(
189
+ MOCK__builderStore.engineServerClient,
190
+ 'grammarToJSON_lambda',
191
+ ).mockImplementation(async (input: string) =>
192
+ ENGINE_TEST_SUPPORT__grammarToJSON_lambda(input),
193
+ );
194
+ createSpy(
195
+ MOCK__builderStore.engineServerClient,
196
+ 'grammarToJSON_valueSpecification',
197
+ ).mockImplementation(async (input: string) =>
198
+ ENGINE_TEST_SUPPORT__grammarToJSON_valueSpecification(input),
199
+ );
200
+ createSpy(
201
+ MOCK__builderStore.engineServerClient,
202
+ 'JSONToGrammar_valueSpecification',
203
+ ).mockImplementation(async (input: PlainObject<V1_ValueSpecification>) =>
204
+ ENGINE_TEST_SUPPORT__JSONToGrammar_valueSpecification(input),
205
+ );
206
+
207
+ const renderResult = render(
208
+ <ApplicationStoreProvider store={MOCK__builderStore.application}>
209
+ <TEST__BrowserEnvironmentProvider
210
+ initialEntries={[mockDataCube?.id ? `/${mockDataCube.id}` : '/']}
211
+ >
212
+ <LegendDataCubeFrameworkProvider>
213
+ <Routes>
214
+ <Route
215
+ path={LEGEND_DATA_CUBE_ROUTE_PATTERN.BUILDER}
216
+ element={<LegendDataCubeBuilder />}
217
+ />
218
+ </Routes>
219
+ </LegendDataCubeFrameworkProvider>
220
+ </TEST__BrowserEnvironmentProvider>
221
+ </ApplicationStoreProvider>,
222
+ );
223
+ await waitFor(() =>
224
+ renderResult.getByTestId(LEGEND_DATACUBE_TEST_ID.PLACEHOLDER),
225
+ );
226
+
227
+ return {
228
+ renderResult,
229
+ legendDataCubeBuilderState: MOCK__builderStore.builder,
230
+ };
231
+ };
@@ -38,7 +38,7 @@ import {
38
38
  import { useEffect } from 'react';
39
39
  import { LegendDataCubeSettingStorageKey } from '../../__lib__/LegendDataCubeSetting.js';
40
40
  import type { LegendDataCubeBuilderStore } from '../../stores/builder/LegendDataCubeBuilderStore.js';
41
- import { LocalFileDataCubeSource } from '../../stores/model/LocalFileDataCubeSource.js';
41
+ import { ReleaseViewer } from '@finos/legend-application';
42
42
 
43
43
  const LegendDataCubeBuilderHeader = observer(() => {
44
44
  const store = useLegendDataCubeBuilderStore();
@@ -58,11 +58,7 @@ const LegendDataCubeBuilderHeader = observer(() => {
58
58
  <FormButton
59
59
  compact={true}
60
60
  className="ml-1.5"
61
- disabled={
62
- !store.builder?.dataCube ||
63
- /* TODO: @gs-gunjan we should allow saving DataCube using CSV file source */
64
- store.builder.source instanceof LocalFileDataCubeSource
65
- }
61
+ disabled={!store.builder?.dataCube}
66
62
  onClick={() => store.saverDisplay.open()}
67
63
  >
68
64
  Save DataCube
@@ -71,8 +67,37 @@ const LegendDataCubeBuilderHeader = observer(() => {
71
67
  );
72
68
  });
73
69
 
70
+ export const LegendDataCubeReleaseLogManager = observer(
71
+ (props: { showOnlyLatestNotes: boolean }) => {
72
+ const { showOnlyLatestNotes } = props;
73
+ const store = useLegendDataCubeBuilderStore();
74
+ const applicationStore = store.application;
75
+ const releaseService = applicationStore.releaseNotesService;
76
+ const releaseNotes =
77
+ (showOnlyLatestNotes
78
+ ? releaseService.showableVersions()
79
+ : releaseService.releaseNotes) ?? [];
80
+
81
+ applicationStore.releaseNotesService.updateViewedVersion();
82
+
83
+ return (
84
+ <div className="legend-datacube-release-notes h-full items-center p-3">
85
+ <div className="my-0.5 flex font-mono">
86
+ New features, enhancements and bug fixes that were released
87
+ </div>
88
+ <div className="p-2">
89
+ {releaseNotes.map((e) => (
90
+ <ReleaseViewer key={e.version} releaseNotes={e} />
91
+ ))}
92
+ </div>
93
+ </div>
94
+ );
95
+ },
96
+ );
97
+
74
98
  export const LegendDataCubeAbout = observer(() => {
75
99
  const store = useLegendDataCubeBuilderStore();
100
+ const releaseService = store.application.releaseNotesService;
76
101
  const config = store.application.config;
77
102
 
78
103
  return (
@@ -93,6 +118,14 @@ export const LegendDataCubeAbout = observer(() => {
93
118
  <div>Build Time:</div>
94
119
  <div className="ml-1 font-bold">{config.appVersionBuildTime}</div>
95
120
  </div>
121
+ {releaseService.isConfigured && (
122
+ <div
123
+ onClick={() => store.releaseLogDisplay.open()}
124
+ className="my-0.5 flex cursor-pointer font-bold text-sky-500 underline"
125
+ >
126
+ <div>Details of Released Versions</div>
127
+ </div>
128
+ )}
96
129
  <div className="mt-3 rounded-sm bg-white px-4 py-2">
97
130
  <div className="my-0.5 flex font-mono">
98
131
  <div>Engine Server:</div>
@@ -255,6 +288,18 @@ export const LegendDataCubeBuilder = withLegendDataCubeBuilderStore(
255
288
  .catch((error) => store.alertService.alertUnhandledError(error));
256
289
  }, [store, dataCubeId]);
257
290
 
291
+ useEffect(() => {
292
+ const releaseService = application.releaseNotesService;
293
+ const releaseNotes = releaseService.showableVersions();
294
+ const isOpen = releaseService.showCurrentReleaseModal;
295
+
296
+ if (releaseService.isConfigured && isOpen && releaseNotes?.length) {
297
+ store.releaseNotesDisplay.open();
298
+ } else {
299
+ releaseService.updateViewedVersion();
300
+ }
301
+ }, [application, store.releaseNotesDisplay]);
302
+
258
303
  if (!store.initializeState.hasSucceeded) {
259
304
  return (
260
305
  <DataCubePlaceholder
@@ -39,6 +39,9 @@ const LegendDataCubeBuilderStoreProvider = (props: {
39
39
  <LegendDataCubeBlockingWindow
40
40
  windowState={store.deleteConfirmationDisplay}
41
41
  />
42
+ <LegendDataCubeBlockingWindow
43
+ windowState={store.loader.sourceLoader.display}
44
+ />
42
45
  </LegendDataCubeBuilderStoreContext.Provider>
43
46
  );
44
47
  };
@@ -0,0 +1,108 @@
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 { observer } from 'mobx-react-lite';
18
+ import { FormButton } from '@finos/legend-data-cube';
19
+ import { useLegendDataCubeBuilderStore } from './LegendDataCubeBuilderStoreProvider.js';
20
+ import { LocalFileDataCubePartialSourceLoaderState } from '../../stores/builder/source/loader/LocalFileDataCubePartialSourceLoaderState.js';
21
+ import { LocalFileDataCubePartialSourceLoader } from './source/loader/LocalFileDataCubePartialSourceLoader.js';
22
+ import { useEffect } from 'react';
23
+ import { DataCubeIcon } from '@finos/legend-art';
24
+ import { formatDistanceToNow } from '@finos/legend-shared';
25
+
26
+ export const LegendDataCubePartialSourceLoader = observer(() => {
27
+ const store = useLegendDataCubeBuilderStore();
28
+ const state = store.loader.sourceLoader;
29
+ const sourceLoader = state.partialSourceLoader;
30
+
31
+ useEffect(() => {
32
+ sourceLoader.initialize();
33
+ }, [sourceLoader]);
34
+
35
+ return (
36
+ <>
37
+ <div className="h-[calc(100%_-_40px)] w-full px-2 pt-2">
38
+ <div className="h-full w-full border border-neutral-300 bg-white">
39
+ <div className="h-full w-full select-none p-2">
40
+ <div className="relative mb-0.5 flex h-[42px] w-full border border-neutral-200 bg-neutral-100">
41
+ <div className="w-full">
42
+ <div className="h-6 w-4/5 overflow-hidden text-ellipsis whitespace-nowrap px-1.5 leading-6">
43
+ {state.persistentDataCube?.name}
44
+ </div>
45
+ <div className="flex h-[18px] items-start justify-between px-1.5">
46
+ <div className="flex">
47
+ <DataCubeIcon.ClockEdit className="text-sm text-neutral-500" />
48
+ <div className="ml-1 text-sm text-neutral-500">
49
+ {state.persistentDataCube?.lastUpdatedAt
50
+ ? formatDistanceToNow(
51
+ new Date(state.persistentDataCube.lastUpdatedAt),
52
+ {
53
+ includeSeconds: true,
54
+ addSuffix: true,
55
+ },
56
+ )
57
+ : '(unknown)'}
58
+ </div>
59
+ </div>
60
+ <div className="flex">
61
+ <DataCubeIcon.User className="text-sm text-neutral-500" />
62
+ <div className="ml-1 text-sm text-neutral-500">
63
+ {state.persistentDataCube?.owner}
64
+ </div>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ <div className="flex h-10 w-full items-center">
70
+ <div className="flex h-full w-32 flex-shrink-0 items-center text-sm">
71
+ Source Type:
72
+ <div className="pl-3">{sourceLoader.label}</div>
73
+ </div>
74
+ </div>
75
+ <div className="h-[calc(100%_-_41px)] w-full overflow-auto">
76
+ {sourceLoader instanceof
77
+ LocalFileDataCubePartialSourceLoaderState && (
78
+ <LocalFileDataCubePartialSourceLoader
79
+ partialSourceLoader={sourceLoader}
80
+ />
81
+ )}
82
+ </div>
83
+ </div>
84
+ </div>
85
+ </div>
86
+ <div className="flex h-10 items-center justify-end px-2">
87
+ <FormButton onClick={state.display.onClose}>Cancel</FormButton>
88
+ <FormButton
89
+ className="ml-2"
90
+ disabled={!sourceLoader.isValid || state.finalizeState.isInProgress}
91
+ onClick={() => {
92
+ state
93
+ .finalize()
94
+ .then(() => store.loadPartialSourceDataCube())
95
+ .catch((error) => {
96
+ store.alertService.alertUnhandledError(error);
97
+ })
98
+ .finally(() => {
99
+ state.display.close();
100
+ });
101
+ }}
102
+ >
103
+ OK
104
+ </FormButton>
105
+ </div>
106
+ </>
107
+ );
108
+ });
@@ -18,8 +18,175 @@ import { observer } from 'mobx-react-lite';
18
18
  import { useLegendDataCubeBuilderStore } from './LegendDataCubeBuilderStoreProvider.js';
19
19
  import { LegendQueryDataCubeSource } from '../../stores/model/LegendQueryDataCubeSource.js';
20
20
  import { useLegendDataCubeApplicationStore } from '../LegendDataCubeFrameworkProvider.js';
21
- import { EXTERNAL_APPLICATION_NAVIGATION__generateQueryViewUrl } from '../../__lib__/LegendDataCubeNavigation.js';
21
+ import {
22
+ EXTERNAL_APPLICATION_NAVIGATION__generateQueryViewUrl,
23
+ EXTERNAL_APPLICATION_NAVIGATION__generateStudioSDLCViewUrl,
24
+ EXTERNAL_APPLICATION_NAVIGATION__generateStudioViewUrl,
25
+ } from '../../__lib__/LegendDataCubeNavigation.js';
22
26
  import { DataCubeIcon } from '@finos/legend-art';
27
+ import { UserDefinedFunctionDataCubeSource } from '@finos/legend-data-cube';
28
+ import { useCallback, useEffect, useState } from 'react';
29
+ import {
30
+ type DepotServerClient,
31
+ DepotScope,
32
+ StoreProjectData,
33
+ } from '@finos/legend-server-depot';
34
+ import { returnUndefOnError } from '@finos/legend-shared';
35
+ import {
36
+ V1_deserializePureModelContext,
37
+ V1_LegendSDLC,
38
+ V1_PureModelContextPointer,
39
+ type V1_PureModelContext,
40
+ } from '@finos/legend-graph';
41
+
42
+ const handleFetchProject = (
43
+ depotServerClient: DepotServerClient,
44
+ model: V1_PureModelContext,
45
+ handleProjectChange: (val: StoreProjectData | undefined) => void,
46
+ ) => {
47
+ if (
48
+ model instanceof V1_PureModelContextPointer &&
49
+ model.sdlcInfo instanceof V1_LegendSDLC
50
+ ) {
51
+ depotServerClient
52
+ .getProject(model.sdlcInfo.groupId, model.sdlcInfo.artifactId)
53
+ .then((e) => {
54
+ handleProjectChange(
55
+ returnUndefOnError(() => StoreProjectData.serialization.fromJson(e)),
56
+ );
57
+ })
58
+ .catch((e) => {
59
+ // ignore
60
+ });
61
+ }
62
+ };
63
+
64
+ const generateStudioViewLink = (
65
+ studio: string,
66
+ project: StoreProjectData,
67
+ version: string,
68
+ element: string | undefined,
69
+ ): string => {
70
+ if (version.endsWith(DepotScope.SNAPSHOT)) {
71
+ return EXTERNAL_APPLICATION_NAVIGATION__generateStudioViewUrl(
72
+ studio,
73
+ project.groupId,
74
+ project.artifactId,
75
+ version,
76
+ element,
77
+ );
78
+ } else {
79
+ return EXTERNAL_APPLICATION_NAVIGATION__generateStudioSDLCViewUrl(
80
+ studio,
81
+ project.projectId,
82
+ version,
83
+ element,
84
+ );
85
+ }
86
+ };
87
+
88
+ const UserDefinedFunctionSourceViewer = observer(
89
+ (props: { source: UserDefinedFunctionDataCubeSource }) => {
90
+ const { source } = props;
91
+ const sourceModel = returnUndefOnError(() =>
92
+ V1_deserializePureModelContext(source.model),
93
+ );
94
+ const version =
95
+ sourceModel instanceof V1_PureModelContextPointer &&
96
+ sourceModel.sdlcInfo instanceof V1_LegendSDLC
97
+ ? sourceModel.sdlcInfo.version
98
+ : undefined;
99
+ const store = useLegendDataCubeBuilderStore();
100
+ const application = useLegendDataCubeApplicationStore();
101
+ const [project, setProject] = useState<StoreProjectData | undefined>(
102
+ undefined,
103
+ );
104
+ const link =
105
+ project?.projectId && application.config.studioApplicationUrl && version
106
+ ? generateStudioViewLink(
107
+ application.config.studioApplicationUrl,
108
+ project,
109
+ version,
110
+ source.functionPath,
111
+ )
112
+ : undefined;
113
+ const _handleFetchProject = useCallback(() => {
114
+ if (sourceModel) {
115
+ handleFetchProject(store.depotServerClient, sourceModel, setProject);
116
+ }
117
+ }, [sourceModel, store.depotServerClient]);
118
+
119
+ useEffect(() => {
120
+ _handleFetchProject();
121
+ }, [_handleFetchProject]);
122
+
123
+ return (
124
+ <div className="h-full w-full px-2 pt-2">
125
+ <div className="h-[calc(100%_-_8px)] w-full border border-neutral-300 bg-white">
126
+ <div className="h-full w-full select-none p-2">
127
+ <div className="flex h-6">
128
+ <div className="flex h-6 items-center text-xl font-medium">
129
+ <DataCubeIcon.Table />
130
+ </div>
131
+ <div className="ml-1 flex h-6 items-center text-xl font-medium">
132
+ User Defined Function
133
+ </div>
134
+ </div>
135
+ {link && (
136
+ <div className="mt-2 flex h-6 w-full">
137
+ <div className="flex h-full w-[calc(100%_-_20px)] items-center border border-r-0 border-neutral-400 px-1.5 font-bold text-sky-500 underline">
138
+ <a
139
+ href={link}
140
+ target="_blank"
141
+ rel="noopener noreferrer"
142
+ className="overflow-hidden overflow-ellipsis whitespace-nowrap"
143
+ >
144
+ {source.functionPath}
145
+ </a>
146
+ </div>
147
+ <button
148
+ className="flex aspect-square h-full w-6 items-center justify-center border border-neutral-400 bg-neutral-300 hover:brightness-95"
149
+ onClick={() => {
150
+ store.application.clipboardService
151
+ .copyTextToClipboard(link)
152
+ .catch((error) =>
153
+ store.alertService.alertUnhandledError(error),
154
+ );
155
+ }}
156
+ title="Copy Link"
157
+ >
158
+ <DataCubeIcon.Clipboard />
159
+ </button>
160
+ </div>
161
+ )}
162
+ {!link && (
163
+ <div className="mt-2 flex h-6 w-full">
164
+ <div className="flex h-full w-[calc(100%_-_20px)] items-center border border-r-0 border-neutral-400 bg-neutral-200 px-1.5">
165
+ <div className="overflow-hidden overflow-ellipsis whitespace-nowrap">
166
+ {source.functionPath}
167
+ </div>
168
+ </div>
169
+ <button
170
+ className="flex aspect-square h-full w-6 items-center justify-center border border-neutral-400 bg-neutral-300 hover:brightness-95"
171
+ onClick={() => {
172
+ application.clipboardService
173
+ .copyTextToClipboard(source.functionPath)
174
+ .catch((error) =>
175
+ store.alertService.alertUnhandledError(error),
176
+ );
177
+ }}
178
+ title="Copy ID"
179
+ >
180
+ <DataCubeIcon.Clipboard />
181
+ </button>
182
+ </div>
183
+ )}
184
+ </div>
185
+ </div>
186
+ </div>
187
+ );
188
+ },
189
+ );
23
190
 
24
191
  export const LegendDataCubeSourceViewer = observer(() => {
25
192
  const store = useLegendDataCubeBuilderStore();
@@ -36,6 +203,7 @@ export const LegendDataCubeSourceViewer = observer(() => {
36
203
  source.info.id,
37
204
  )
38
205
  : undefined;
206
+
39
207
  return (
40
208
  <div className="h-full w-full px-2 pt-2">
41
209
  <div className="h-[calc(100%_-_8px)] w-full border border-neutral-300 bg-white">
@@ -101,6 +269,8 @@ export const LegendDataCubeSourceViewer = observer(() => {
101
269
  </div>
102
270
  </div>
103
271
  );
272
+ } else if (source instanceof UserDefinedFunctionDataCubeSource) {
273
+ return <UserDefinedFunctionSourceViewer source={source} />;
104
274
  }
105
275
  return (
106
276
  <div className="h-full w-full px-2 pt-2">{`Can't display source`}</div>