@finos/legend-extension-dsl-data-space 10.4.256 → 10.4.258

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 (86) hide show
  1. package/lib/components/DataSpaceDataAccess.d.ts +1 -1
  2. package/lib/components/DataSpaceDataAccess.d.ts.map +1 -1
  3. package/lib/components/DataSpaceDataAccess.js +7 -16
  4. package/lib/components/DataSpaceDataAccess.js.map +1 -1
  5. package/lib/components/DataSpaceDescription.d.ts.map +1 -1
  6. package/lib/components/DataSpaceDescription.js +2 -6
  7. package/lib/components/DataSpaceDescription.js.map +1 -1
  8. package/lib/components/DataSpaceExecutionContextViewer.d.ts +14 -0
  9. package/lib/components/DataSpaceExecutionContextViewer.d.ts.map +1 -1
  10. package/lib/components/DataSpaceExecutionContextViewer.js +23 -23
  11. package/lib/components/DataSpaceExecutionContextViewer.js.map +1 -1
  12. package/lib/components/DataSpaceQualityEmote.d.ts.map +1 -1
  13. package/lib/components/DataSpaceQualityEmote.js +6 -4
  14. package/lib/components/DataSpaceQualityEmote.js.map +1 -1
  15. package/lib/components/DataSpaceQuickStart.d.ts.map +1 -1
  16. package/lib/components/DataSpaceQuickStart.js +90 -22
  17. package/lib/components/DataSpaceQuickStart.js.map +1 -1
  18. package/lib/components/DataSpaceViewer.d.ts.map +1 -1
  19. package/lib/components/DataSpaceViewer.js +27 -9
  20. package/lib/components/DataSpaceViewer.js.map +1 -1
  21. package/lib/components/DataSpaceViewerActivityBar.d.ts.map +1 -1
  22. package/lib/components/DataSpaceViewerActivityBar.js +9 -5
  23. package/lib/components/DataSpaceViewerActivityBar.js.map +1 -1
  24. package/lib/components/__test-utils__/DataSpaceViewerTestUtils.d.ts +1 -1
  25. package/lib/components/__test-utils__/DataSpaceViewerTestUtils.d.ts.map +1 -1
  26. package/lib/components/__test-utils__/DataSpaceViewerTestUtils.js.map +1 -1
  27. package/lib/components/query-builder/DataSpaceQueryBuilder.d.ts.map +1 -1
  28. package/lib/components/query-builder/DataSpaceQueryBuilder.js +24 -15
  29. package/lib/components/query-builder/DataSpaceQueryBuilder.js.map +1 -1
  30. package/lib/graph-manager/action/analytics/DataSpaceAnalysis.d.ts +16 -0
  31. package/lib/graph-manager/action/analytics/DataSpaceAnalysis.d.ts.map +1 -1
  32. package/lib/graph-manager/action/analytics/DataSpaceAnalysis.js +16 -0
  33. package/lib/graph-manager/action/analytics/DataSpaceAnalysis.js.map +1 -1
  34. package/lib/graph-manager/protocol/pure/v1/V1_DSL_DataSpace_PureGraphManagerExtension.d.ts.map +1 -1
  35. package/lib/graph-manager/protocol/pure/v1/V1_DSL_DataSpace_PureGraphManagerExtension.js +31 -2
  36. package/lib/graph-manager/protocol/pure/v1/V1_DSL_DataSpace_PureGraphManagerExtension.js.map +1 -1
  37. package/lib/graph-manager/protocol/pure/v1/engine/analytics/V1_DataSpaceAnalysis.d.ts +18 -0
  38. package/lib/graph-manager/protocol/pure/v1/engine/analytics/V1_DataSpaceAnalysis.d.ts.map +1 -1
  39. package/lib/graph-manager/protocol/pure/v1/engine/analytics/V1_DataSpaceAnalysis.js +48 -0
  40. package/lib/graph-manager/protocol/pure/v1/engine/analytics/V1_DataSpaceAnalysis.js.map +1 -1
  41. package/lib/index.css +2 -2
  42. package/lib/index.css.map +1 -1
  43. package/lib/package.json +1 -1
  44. package/lib/stores/{DataSpaceMappingProviderAccessState.d.ts → DataSpaceDataProductAccessState.d.ts} +16 -10
  45. package/lib/stores/DataSpaceDataProductAccessState.d.ts.map +1 -0
  46. package/lib/stores/{DataSpaceMappingProviderAccessState.js → DataSpaceDataProductAccessState.js} +27 -16
  47. package/lib/stores/DataSpaceDataProductAccessState.js.map +1 -0
  48. package/lib/stores/DataSpaceLayoutState.d.ts +7 -34
  49. package/lib/stores/DataSpaceLayoutState.d.ts.map +1 -1
  50. package/lib/stores/DataSpaceLayoutState.js +22 -159
  51. package/lib/stores/DataSpaceLayoutState.js.map +1 -1
  52. package/lib/stores/DataSpaceViewerDiagramViewerState.js +1 -2
  53. package/lib/stores/DataSpaceViewerDiagramViewerState.js.map +1 -1
  54. package/lib/stores/DataSpaceViewerState.d.ts +54 -24
  55. package/lib/stores/DataSpaceViewerState.d.ts.map +1 -1
  56. package/lib/stores/DataSpaceViewerState.js +139 -55
  57. package/lib/stores/DataSpaceViewerState.js.map +1 -1
  58. package/lib/stores/query/DataSpaceAdvancedSearchState.d.ts.map +1 -1
  59. package/lib/stores/query/DataSpaceAdvancedSearchState.js +7 -1
  60. package/lib/stores/query/DataSpaceAdvancedSearchState.js.map +1 -1
  61. package/lib/stores/query-builder/DataSpaceQueryBuilderState.d.ts +2 -0
  62. package/lib/stores/query-builder/DataSpaceQueryBuilderState.d.ts.map +1 -1
  63. package/lib/stores/query-builder/DataSpaceQueryBuilderState.js +36 -9
  64. package/lib/stores/query-builder/DataSpaceQueryBuilderState.js.map +1 -1
  65. package/package.json +14 -14
  66. package/src/components/DataSpaceDataAccess.tsx +26 -68
  67. package/src/components/DataSpaceDescription.tsx +8 -41
  68. package/src/components/DataSpaceExecutionContextViewer.tsx +68 -69
  69. package/src/components/DataSpaceQualityEmote.tsx +6 -4
  70. package/src/components/DataSpaceQuickStart.tsx +284 -114
  71. package/src/components/DataSpaceViewer.tsx +83 -45
  72. package/src/components/DataSpaceViewerActivityBar.tsx +11 -7
  73. package/src/components/__test-utils__/DataSpaceViewerTestUtils.ts +1 -6
  74. package/src/components/query-builder/DataSpaceQueryBuilder.tsx +117 -101
  75. package/src/graph-manager/action/analytics/DataSpaceAnalysis.ts +19 -0
  76. package/src/graph-manager/protocol/pure/v1/V1_DSL_DataSpace_PureGraphManagerExtension.ts +37 -0
  77. package/src/graph-manager/protocol/pure/v1/engine/analytics/V1_DataSpaceAnalysis.ts +85 -0
  78. package/src/stores/{DataSpaceMappingProviderAccessState.ts → DataSpaceDataProductAccessState.ts} +40 -18
  79. package/src/stores/DataSpaceLayoutState.ts +25 -209
  80. package/src/stores/DataSpaceViewerDiagramViewerState.ts +1 -1
  81. package/src/stores/DataSpaceViewerState.ts +188 -88
  82. package/src/stores/query/DataSpaceAdvancedSearchState.ts +9 -2
  83. package/src/stores/query-builder/DataSpaceQueryBuilderState.ts +48 -12
  84. package/tsconfig.json +1 -1
  85. package/lib/stores/DataSpaceMappingProviderAccessState.d.ts.map +0 -1
  86. package/lib/stores/DataSpaceMappingProviderAccessState.js.map +0 -1
@@ -24,18 +24,29 @@ import {
24
24
  ExpandMoreIcon,
25
25
  LegendLogo,
26
26
  MoreVerticalIcon,
27
- QuestionCircleIcon,
27
+ RefreshIcon,
28
28
  StatisticsIcon,
29
29
  clsx,
30
30
  } from '@finos/legend-art';
31
+ import { IconButton } from '@mui/material';
32
+ import {
33
+ CollapsibleChevron,
34
+ CollapsibleWikiSection,
35
+ DataProductAPGAccessRequestControl,
36
+ } from '@finos/legend-extension-dsl-data-product';
31
37
  import { type DataSpaceViewerState } from '../stores/DataSpaceViewerState.js';
32
38
  import { useApplicationStore } from '@finos/legend-application';
39
+ import { extractElementNameFromPath } from '@finos/legend-graph';
33
40
  import {
41
+ type DataSpaceExecutableAnalysisResult,
42
+ type DataSpaceExecutionContextAnalysisResult,
34
43
  DataSpaceExecutableTDSResult,
35
44
  DataSpaceServiceExecutableInfo,
36
45
  DataSpaceMultiExecutionServiceExecutableInfo,
46
+ LakehouseDataProductExecutableAccessorInfo,
37
47
  } from '../graph-manager/action/analytics/DataSpaceAnalysis.js';
38
48
  import { DataSpaceMarkdownTextViewer } from './DataSpaceMarkdownTextViewer.js';
49
+ import { DataSpaceOpenDataProductButton } from './DataSpaceExecutionContextViewer.js';
39
50
  import type { DSL_DataSpace_LegendApplicationPlugin_Extension } from '../stores/DSL_DataSpace_LegendApplicationPlugin_Extension.js';
40
51
  import { useEffect, useRef, useState } from 'react';
41
52
  import { DataSpaceWikiPlaceholder } from './DataSpacePlaceholder.js';
@@ -118,6 +129,219 @@ const TDSColumnTypeCellRenderer = (
118
129
  );
119
130
  };
120
131
 
132
+ const DATA_PRODUCT_GROUP_AUTO_COLLAPSE_THRESHOLD = 3;
133
+
134
+ type DataSpaceDataProductAccessGroup = {
135
+ dataProductPath: string;
136
+ accessPointGroupIds: string[];
137
+ };
138
+
139
+ /**
140
+ * Resolves the `(dataProductPath, accessPointGroupId)` pairs an executable
141
+ * needs access to: either the Lakehouse Data Products its accessors reference,
142
+ * or — for an executable served by a mappingProvider execution context — the
143
+ * Data Product and access point groups that context resolves through.
144
+ */
145
+ const resolveDataProductAccessGroups = (
146
+ executableAnalysisResult: DataSpaceExecutableAnalysisResult,
147
+ executionContext: DataSpaceExecutionContextAnalysisResult | undefined,
148
+ ): DataSpaceDataProductAccessGroup[] => {
149
+ const groupsByDataProduct = new Map<string, string[]>();
150
+ const addGroup = (
151
+ dataProductPath: string,
152
+ accessPointGroupId: string,
153
+ ): void => {
154
+ const accessPointGroupIds = groupsByDataProduct.get(dataProductPath) ?? [];
155
+ if (!accessPointGroupIds.includes(accessPointGroupId)) {
156
+ accessPointGroupIds.push(accessPointGroupId);
157
+ }
158
+ groupsByDataProduct.set(dataProductPath, accessPointGroupIds);
159
+ };
160
+ for (const accessor of executableAnalysisResult.executableAccessorInfo) {
161
+ if (accessor instanceof LakehouseDataProductExecutableAccessorInfo) {
162
+ addGroup(accessor.dataProductPath, accessor.accessPointGroupId);
163
+ }
164
+ }
165
+ if (groupsByDataProduct.size === 0) {
166
+ const mappingProvider = executionContext?.mappingProvider;
167
+ mappingProvider?.keys.forEach((key) =>
168
+ addGroup(mappingProvider.element, key),
169
+ );
170
+ }
171
+ return Array.from(groupsByDataProduct.entries()).map(
172
+ ([dataProductPath, accessPointGroupIds]) => ({
173
+ dataProductPath,
174
+ accessPointGroupIds,
175
+ }),
176
+ );
177
+ };
178
+
179
+ /**
180
+ * Renders the "Request Access" button + status for each
181
+ * `(dataProductPath, accessPointGroupId)` pair, grouped by Data Product path.
182
+ * Access states are shared across executables + APGs of the same DP via
183
+ * `DataSpaceViewerState.dataProductAccessStates`.
184
+ */
185
+ const DataSpaceDataProductAccessPanel = observer(
186
+ (props: {
187
+ viewerState: DataSpaceViewerState;
188
+ groups: DataSpaceDataProductAccessGroup[];
189
+ }) => {
190
+ const { viewerState, groups } = props;
191
+ const tokenProvider =
192
+ viewerState.mappingProviderAccessConfig?.tokenProvider ??
193
+ ((): undefined => undefined);
194
+ const [collapsedDataProducts, setCollapsedDataProducts] = useState<
195
+ Set<string>
196
+ >(
197
+ () =>
198
+ new Set(
199
+ groups.length >= DATA_PRODUCT_GROUP_AUTO_COLLAPSE_THRESHOLD
200
+ ? groups.map((group) => group.dataProductPath)
201
+ : [],
202
+ ),
203
+ );
204
+ if (groups.length === 0) {
205
+ return null;
206
+ }
207
+ const toggleGroupCollapse = (dataProductPath: string): void => {
208
+ setCollapsedDataProducts((prev) => {
209
+ const next = new Set(prev);
210
+ if (next.has(dataProductPath)) {
211
+ next.delete(dataProductPath);
212
+ } else {
213
+ next.add(dataProductPath);
214
+ }
215
+ return next;
216
+ });
217
+ };
218
+ return (
219
+ <div className="data-space__viewer__quickstart__tds__apg-access">
220
+ <div className="data-space__viewer__quickstart__tds__apg-access__header">
221
+ Data Product Access
222
+ </div>
223
+ {groups.map((group) => {
224
+ const isGroupCollapsed = collapsedDataProducts.has(
225
+ group.dataProductPath,
226
+ );
227
+ const accessState = viewerState.getDataProductAccessState(
228
+ group.dataProductPath,
229
+ );
230
+ return (
231
+ <div
232
+ key={group.dataProductPath}
233
+ className="data-space__viewer__quickstart__tds__apg-access__group"
234
+ >
235
+ <div className="data-space__viewer__quickstart__tds__apg-access__group__header-row">
236
+ <button
237
+ className="data-space__viewer__quickstart__tds__apg-access__group__caret-btn"
238
+ tabIndex={-1}
239
+ onClick={() => toggleGroupCollapse(group.dataProductPath)}
240
+ title={isGroupCollapsed ? 'Expand' : 'Collapse'}
241
+ >
242
+ <ExpandMoreIcon
243
+ className={clsx(
244
+ 'data-space__viewer__quickstart__tds__apg-access__group__caret',
245
+ {
246
+ 'data-space__viewer__quickstart__tds__apg-access__group__caret--collapsed':
247
+ isGroupCollapsed,
248
+ },
249
+ )}
250
+ />
251
+ </button>
252
+ <div className="data-space__viewer__quickstart__tds__apg-access__group__header">
253
+ <span
254
+ className="data-space__viewer__quickstart__tds__apg-access__group__label"
255
+ title={group.dataProductPath}
256
+ >
257
+ {extractElementNameFromPath(group.dataProductPath)}
258
+ </span>
259
+ <span className="data-space__viewer__quickstart__tds__apg-access__group__count">
260
+ {group.accessPointGroupIds.length}
261
+ </span>
262
+ </div>
263
+ <div className="data-space__viewer__quickstart__tds__apg-access__group__actions">
264
+ <DataSpaceOpenDataProductButton
265
+ dataSpaceViewerState={viewerState}
266
+ dataProductPath={group.dataProductPath}
267
+ />
268
+ {accessState && (
269
+ <IconButton
270
+ className="data-space__viewer__quickstart__tds__apg-access__group__refresh-btn"
271
+ size="small"
272
+ color="primary"
273
+ title="Refresh Data Product access"
274
+ disabled={accessState.initializingState.isInProgress}
275
+ onClick={(): void =>
276
+ viewerState.refreshDataProductAccessState(
277
+ group.dataProductPath,
278
+ )
279
+ }
280
+ >
281
+ <RefreshIcon />
282
+ </IconButton>
283
+ )}
284
+ </div>
285
+ </div>
286
+ {!isGroupCollapsed && (
287
+ <div className="data-space__viewer__quickstart__tds__apg-access__group__rows">
288
+ {group.accessPointGroupIds.map((accessPointGroupId) => {
289
+ const apgState =
290
+ accessState?.dataProductViewerState?.apgStates.find(
291
+ (state) => state.apg.id === accessPointGroupId,
292
+ );
293
+ return (
294
+ <div
295
+ key={accessPointGroupId}
296
+ className="data-space__viewer__quickstart__tds__apg-access__row"
297
+ >
298
+ <span
299
+ className="data-space__viewer__quickstart__tds__apg-access__row__apg"
300
+ title={accessPointGroupId}
301
+ >
302
+ {accessPointGroupId}
303
+ </span>
304
+ <div className="data-space__viewer__quickstart__tds__apg-access__row__actions">
305
+ {apgState && accessState?.dataAccessState ? (
306
+ <DataProductAPGAccessRequestControl
307
+ apgState={apgState}
308
+ dataAccessState={accessState.dataAccessState}
309
+ tokenProvider={tokenProvider}
310
+ />
311
+ ) : accessState?.errorMessage ? (
312
+ <span title={accessState.errorMessage}>
313
+ Unavailable
314
+ </span>
315
+ ) : accessState?.initializingState.isInProgress ? (
316
+ <span>Loading...</span>
317
+ ) : null}
318
+ </div>
319
+ </div>
320
+ );
321
+ })}
322
+ </div>
323
+ )}
324
+ </div>
325
+ );
326
+ })}
327
+ </div>
328
+ );
329
+ },
330
+ );
331
+
332
+ const resolveExecutionContextForExecutable = (
333
+ dataSpaceViewerState: DataSpaceViewerState,
334
+ executableAnalysisResult: DataSpaceExecutableAnalysisResult,
335
+ ): DataSpaceExecutionContextAnalysisResult | undefined => {
336
+ const analysisResult = dataSpaceViewerState.dataSpaceAnalysisResult;
337
+ const executionContextKey =
338
+ executableAnalysisResult.info?.executionContextKey;
339
+ return executionContextKey
340
+ ? (analysisResult.executionContextsIndex.get(executionContextKey) ??
341
+ analysisResult.defaultExecutionContext)
342
+ : analysisResult.defaultExecutionContext;
343
+ };
344
+
121
345
  const DataSpaceExecutableResultView = observer(
122
346
  (props: {
123
347
  executableState: DataSpaceViewerExecutableState;
@@ -137,6 +361,13 @@ const DataSpaceExecutableResultView = observer(
137
361
  dataSpaceViewerState.quickStartState.dataAccessStateIndex.get(
138
362
  executableAnalysisResult,
139
363
  );
364
+ const dataProductAccessGroups = resolveDataProductAccessGroups(
365
+ executableAnalysisResult,
366
+ resolveExecutionContextForExecutable(
367
+ dataSpaceViewerState,
368
+ executableAnalysisResult,
369
+ ),
370
+ );
140
371
  const isTDSResult =
141
372
  resultState instanceof DataSpaceExecutableTDSResultState;
142
373
 
@@ -397,19 +628,26 @@ const DataSpaceExecutableResultView = observer(
397
628
  </div>
398
629
  </div>
399
630
  )}
400
- {selectedTab === EXECUTABLE_ACTION_TAB.DATA_ACCESS &&
401
- (dataAccessState ? (
402
- <DataAccessOverview
403
- dataAccessState={dataAccessState}
404
- compact={true}
631
+ {selectedTab === EXECUTABLE_ACTION_TAB.DATA_ACCESS && (
632
+ <>
633
+ <DataSpaceDataProductAccessPanel
634
+ viewerState={dataSpaceViewerState}
635
+ groups={dataProductAccessGroups}
405
636
  />
406
- ) : (
407
- <div className="data-space__viewer__quickstart__tds__placeholder-panel">
408
- <BlankPanelContent>
409
- No data access information available
410
- </BlankPanelContent>
411
- </div>
412
- ))}
637
+ {dataAccessState ? (
638
+ <DataAccessOverview
639
+ dataAccessState={dataAccessState}
640
+ compact={true}
641
+ />
642
+ ) : dataProductAccessGroups.length === 0 ? (
643
+ <div className="data-space__viewer__quickstart__tds__placeholder-panel">
644
+ <BlankPanelContent>
645
+ No data access information available
646
+ </BlankPanelContent>
647
+ </div>
648
+ ) : null}
649
+ </>
650
+ )}
413
651
  {selectedTab === EXECUTABLE_ACTION_TAB.USAGE_STATS && (
414
652
  <div className="data-space__viewer__quickstart__tds__placeholder-panel">
415
653
  <BlankPanelContent>
@@ -471,14 +709,6 @@ const DataSpaceExecutableAnalysisResultView = observer(
471
709
  const executableAnalysisResult = executableState.value;
472
710
  const quickStartRef = useRef<HTMLDivElement>(null);
473
711
  const anchor = generateAnchorForQuickStart(executableAnalysisResult);
474
- const isCollapsed =
475
- dataSpaceViewerState.layoutState.sectionCollapseState.isSectionCollapsed(
476
- anchor,
477
- );
478
- const toggleCollapse = (): void =>
479
- dataSpaceViewerState.layoutState.sectionCollapseState.toggleSectionCollapse(
480
- anchor,
481
- );
482
712
 
483
713
  useEffect(() => {
484
714
  if (quickStartRef.current) {
@@ -510,37 +740,30 @@ const DataSpaceExecutableAnalysisResultView = observer(
510
740
  return (
511
741
  <div ref={quickStartRef} className="data-space__viewer__quickstart__item">
512
742
  <div className="data-space__viewer__quickstart__item__header">
513
- <button
514
- className="data-space__viewer__quickstart__item__header__caret-btn"
515
- tabIndex={-1}
516
- onClick={toggleCollapse}
517
- title={isCollapsed ? 'Expand' : 'Collapse'}
518
- >
519
- <ExpandMoreIcon
520
- className={clsx(
521
- 'data-space__viewer__quickstart__item__header__caret',
522
- {
523
- 'data-space__viewer__quickstart__item__header__caret--collapsed':
524
- isCollapsed,
525
- },
526
- )}
527
- />
528
- </button>
529
743
  <div className="data-space__viewer__quickstart__item__header__title">
530
744
  {executableAnalysisResult.title}
531
745
  </div>
532
746
  <div className="data-space__viewer__quickstart__item__header__type">
533
747
  {executableTypeLabel}
534
748
  </div>
749
+ <CollapsibleChevron
750
+ viewerState={dataSpaceViewerState}
751
+ anchor={anchor}
752
+ />
535
753
  <button
536
754
  className="data-space__viewer__quickstart__item__header__anchor"
537
755
  tabIndex={-1}
538
- onClick={() => dataSpaceViewerState.changeZone(anchor, true)}
756
+ onClick={() => {
757
+ dataSpaceViewerState.changeZone(anchor, true);
758
+ dataSpaceViewerState.copyLinkToClipboard(anchor);
759
+ }}
539
760
  >
540
761
  <AnchorLinkIcon />
541
762
  </button>
542
763
  </div>
543
- {!isCollapsed && (
764
+ {!dataSpaceViewerState.layoutState.sectionCollapseState.isSectionCollapsed(
765
+ anchor,
766
+ ) && (
544
767
  <>
545
768
  {executableAnalysisResult.description !== undefined && (
546
769
  <div className="data-space__viewer__quickstart__item__description">
@@ -568,24 +791,11 @@ const DataSpaceExecutableAnalysisResultView = observer(
568
791
  export const DataSpaceQuickStart = observer(
569
792
  (props: { dataSpaceViewerState: DataSpaceViewerState }) => {
570
793
  const { dataSpaceViewerState } = props;
571
- const applicationStore = useApplicationStore();
572
794
  const analysisResult = dataSpaceViewerState.dataSpaceAnalysisResult;
573
- const documentationUrl = analysisResult.supportInfo?.documentationUrl;
574
795
  const sectionRef = useRef<HTMLDivElement>(null);
575
796
  const anchor = generateAnchorForActivity(
576
797
  DATA_SPACE_VIEWER_ACTIVITY_MODE.QUICK_START,
577
798
  );
578
- const executableAnchors = dataSpaceViewerState.executableStates.map(
579
- (executableState) => generateAnchorForQuickStart(executableState.value),
580
- );
581
- const isCollapsed =
582
- dataSpaceViewerState.layoutState.sectionCollapseState.areAllSectionsCollapsed(
583
- executableAnchors,
584
- );
585
- const toggleCollapse = (): void =>
586
- dataSpaceViewerState.layoutState.sectionCollapseState.toggleAllSectionsCollapse(
587
- executableAnchors,
588
- );
589
799
 
590
800
  useEffect(() => {
591
801
  if (sectionRef.current) {
@@ -597,71 +807,31 @@ export const DataSpaceQuickStart = observer(
597
807
  return () => dataSpaceViewerState.layoutState.unsetWikiPageAnchor(anchor);
598
808
  }, [dataSpaceViewerState, anchor]);
599
809
 
600
- const seeDocumentation = (): void => {
601
- if (documentationUrl) {
602
- applicationStore.navigationService.navigator.visitAddress(
603
- documentationUrl,
604
- );
605
- }
606
- };
607
-
608
810
  return (
609
- <div ref={sectionRef} className="data-space__viewer__wiki__section">
610
- <div className="data-space__viewer__wiki__section__header">
611
- <div className="data-space__viewer__wiki__section__header__label">
612
- {executableAnchors.length > 0 && (
613
- <button
614
- className="data-space__viewer__wiki__section__header__caret-btn"
615
- tabIndex={-1}
616
- onClick={toggleCollapse}
617
- title={isCollapsed ? 'Expand All' : 'Collapse All'}
618
- >
619
- <ExpandMoreIcon
620
- className={clsx(
621
- 'data-space__viewer__wiki__section__header__caret',
622
- {
623
- 'data-space__viewer__wiki__section__header__caret--collapsed':
624
- isCollapsed,
625
- },
626
- )}
627
- />
628
- </button>
811
+ <div ref={sectionRef} className="viewer__wiki__section">
812
+ <CollapsibleWikiSection
813
+ viewerState={dataSpaceViewerState}
814
+ section={DATA_SPACE_VIEWER_ACTIVITY_MODE.QUICK_START}
815
+ showDocumentation={true}
816
+ >
817
+ <div className="viewer__wiki__section__content">
818
+ {dataSpaceViewerState.executableStates.length !== 0 && (
819
+ <div className="data-space__viewer__quickstart">
820
+ {dataSpaceViewerState.executableStates.map(
821
+ (executableState) => (
822
+ <DataSpaceExecutableAnalysisResultView
823
+ key={executableState.uuid}
824
+ executableState={executableState}
825
+ />
826
+ ),
827
+ )}
828
+ </div>
829
+ )}
830
+ {analysisResult.executables.length === 0 && (
831
+ <DataSpaceWikiPlaceholder message="(not specified)" />
629
832
  )}
630
- Quick Start
631
- <button
632
- className="data-space__viewer__wiki__section__header__anchor"
633
- tabIndex={-1}
634
- onClick={() => dataSpaceViewerState.changeZone(anchor, true)}
635
- >
636
- <AnchorLinkIcon />
637
- </button>
638
833
  </div>
639
- {Boolean(documentationUrl) && (
640
- <button
641
- className="data-space__viewer__wiki__section__header__documentation"
642
- tabIndex={-1}
643
- onClick={seeDocumentation}
644
- title="See Documentation"
645
- >
646
- <QuestionCircleIcon />
647
- </button>
648
- )}
649
- </div>
650
- <div className="data-space__viewer__wiki__section__content">
651
- {dataSpaceViewerState.executableStates.length !== 0 && (
652
- <div className="data-space__viewer__quickstart">
653
- {dataSpaceViewerState.executableStates.map((executableState) => (
654
- <DataSpaceExecutableAnalysisResultView
655
- key={executableState.uuid}
656
- executableState={executableState}
657
- />
658
- ))}
659
- </div>
660
- )}
661
- {analysisResult.executables.length === 0 && (
662
- <DataSpaceWikiPlaceholder message="(not specified)" />
663
- )}
664
- </div>
834
+ </CollapsibleWikiSection>
665
835
  </div>
666
836
  );
667
837
  },
@@ -56,6 +56,11 @@ const DataSpaceHeader = observer(
56
56
  const applicationStore = useApplicationStore();
57
57
  const headerRef = useRef<HTMLDivElement>(null);
58
58
  const analysisResult = dataSpaceViewerState.dataSpaceAnalysisResult;
59
+ const hasMultipleExecutionContexts =
60
+ analysisResult.executionContextsIndex.size > 1;
61
+ const runQueryContext =
62
+ analysisResult.defaultExecutionContext ??
63
+ Array.from(analysisResult.executionContextsIndex.values())[0];
59
64
 
60
65
  useEffect(() => {
61
66
  if (headerRef.current) {
@@ -97,61 +102,94 @@ const DataSpaceHeader = observer(
97
102
  qualityState={dataSpaceViewerState.qualityState}
98
103
  />
99
104
  <div className="data-space__viewer__header__actions">
100
- {dataSpaceViewerState.currentExecutionContext && (
101
- <ControlledDropdownMenu
102
- className="data-space__viewer__header__execution-context-selector"
103
- menuProps={{
104
- anchorOrigin: { vertical: 'bottom', horizontal: 'center' },
105
- transformOrigin: { vertical: 'top', horizontal: 'center' },
106
- elevation: 7,
107
- }}
108
- title={`Current Execution Context: ${dataSpaceViewerState.currentExecutionContext.name}\nClick to switch`}
109
- content={
110
- <MenuContent>
111
- {Array.from(
112
- dataSpaceViewerState.dataSpaceAnalysisResult.executionContextsIndex.values(),
113
- ).map((context) => (
114
- <MenuContentItem
115
- key={context.name}
116
- className={clsx(
117
- 'data-space__viewer__header__execution-context-selector__option',
118
- {
119
- 'data-space__viewer__header__execution-context-selector__option--active':
120
- context ===
121
- dataSpaceViewerState.currentExecutionContext,
122
- },
123
- )}
124
- onClick={() =>
125
- dataSpaceViewerState.setCurrentExecutionContext(
126
- context,
127
- )
128
- }
129
- >
130
- {context.name}
131
- </MenuContentItem>
132
- ))}
133
- </MenuContent>
134
- }
135
- >
105
+ {runQueryContext && (
106
+ <div className="data-space__viewer__header__execution-context-selector">
136
107
  <div className="data-space__viewer__header__execution-context-selector__trigger">
137
- <div className="data-space__viewer__header__execution-context-selector__trigger__icon">
108
+ <div
109
+ className="data-space__viewer__header__execution-context-selector__trigger__icon"
110
+ title="Run Query"
111
+ onClick={() =>
112
+ dataSpaceViewerState.queryDataSpace(
113
+ runQueryContext.name,
114
+ )
115
+ }
116
+ >
138
117
  <PlayIcon />
139
118
  </div>
140
- <div className="data-space__viewer__header__execution-context-selector__trigger__label">
141
- {dataSpaceViewerState.currentExecutionContext.name}
142
- </div>
143
- <div className="data-space__viewer__header__execution-context-selector__trigger__dropdown-icon">
144
- <CaretDownIcon />
145
- </div>
119
+ {hasMultipleExecutionContexts ? (
120
+ <ControlledDropdownMenu
121
+ className="data-space__viewer__header__execution-context-selector__trigger__switcher"
122
+ menuProps={{
123
+ anchorOrigin: {
124
+ vertical: 'bottom',
125
+ horizontal: 'center',
126
+ },
127
+ transformOrigin: {
128
+ vertical: 'top',
129
+ horizontal: 'center',
130
+ },
131
+ elevation: 0,
132
+ MenuListProps: {
133
+ className:
134
+ 'data-space__viewer__header__execution-context-selector__menu',
135
+ },
136
+ }}
137
+ title={`Run Query in another execution context\nDefault: ${runQueryContext.name}`}
138
+ content={
139
+ <MenuContent>
140
+ {Array.from(
141
+ dataSpaceViewerState.dataSpaceAnalysisResult.executionContextsIndex.values(),
142
+ ).map((context) => (
143
+ <MenuContentItem
144
+ key={context.name}
145
+ className={clsx(
146
+ 'data-space__viewer__header__execution-context-selector__option',
147
+ {
148
+ 'data-space__viewer__header__execution-context-selector__option--active':
149
+ context ===
150
+ dataSpaceViewerState.currentExecutionContext,
151
+ },
152
+ )}
153
+ onClick={() => {
154
+ dataSpaceViewerState.setCurrentExecutionContext(
155
+ context,
156
+ );
157
+ dataSpaceViewerState.queryDataSpace(
158
+ context.name,
159
+ );
160
+ }}
161
+ >
162
+ {context.name}
163
+ </MenuContentItem>
164
+ ))}
165
+ </MenuContent>
166
+ }
167
+ >
168
+ <div className="data-space__viewer__header__execution-context-selector__trigger__label">
169
+ Run Query
170
+ </div>
171
+ <div className="data-space__viewer__header__execution-context-selector__trigger__dropdown-icon">
172
+ <CaretDownIcon />
173
+ </div>
174
+ </ControlledDropdownMenu>
175
+ ) : (
176
+ <div className="data-space__viewer__header__execution-context-selector__trigger__label">
177
+ Run Query
178
+ </div>
179
+ )}
146
180
  </div>
147
- </ControlledDropdownMenu>
181
+ </div>
148
182
  )}
149
183
  <ControlledDropdownMenu
150
184
  className="data-space__viewer__header__actions-selector"
151
185
  menuProps={{
152
186
  anchorOrigin: { vertical: 'bottom', horizontal: 'right' },
153
187
  transformOrigin: { vertical: 'top', horizontal: 'right' },
154
- elevation: 7,
188
+ elevation: 0,
189
+ MenuListProps: {
190
+ className:
191
+ 'data-space__viewer__header__actions-selector__menu',
192
+ },
155
193
  }}
156
194
  title="More Actions..."
157
195
  content={
@@ -126,13 +126,17 @@ export const DataSpaceViewerActivityBar = observer(
126
126
  <LaunchIcon className="data-space__viewer__activity-bar__icon--launch" />
127
127
  ),
128
128
  },
129
- {
130
- mode: DATA_SPACE_VIEWER_ACTIVITY_MODE.DATA_ACCESS,
131
- title: 'Data Access',
132
- icon: (
133
- <DataAccessIcon className="data-space__viewer__activity-bar__icon--access" />
134
- ),
135
- },
129
+ ...(dataSpaceViewerState.isDataAccessAvailable
130
+ ? [
131
+ {
132
+ mode: DATA_SPACE_VIEWER_ACTIVITY_MODE.DATA_ACCESS,
133
+ title: 'Data Access',
134
+ icon: (
135
+ <DataAccessIcon className="data-space__viewer__activity-bar__icon--access" />
136
+ ),
137
+ },
138
+ ]
139
+ : []),
136
140
  ];
137
141
 
138
142
  const activities: ActivityBarItemConfig[] = [
@@ -107,12 +107,7 @@ export const TEST_DATA_SPACE_VERSION_ID = '0.0.0';
107
107
 
108
108
  export type TEST__DataSpaceViewerActionOverrides = {
109
109
  viewDataProduct?:
110
- | ((
111
- groupId: string,
112
- artifactId: string,
113
- versionId: string,
114
- dataProductPath: string,
115
- ) => void)
110
+ | ((dataProductPath: string, deploymentId: number) => void)
116
111
  | undefined;
117
112
  fetchDataSpaceQuality?: (() => Promise<DataSpaceQualityResult>) | undefined;
118
113
  };