@finos/legend-extension-dsl-data-space 10.4.258 → 10.4.259
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/DataSpaceDescription.d.ts +5 -0
- package/lib/components/DataSpaceDescription.d.ts.map +1 -1
- package/lib/components/DataSpaceDescription.js +39 -0
- package/lib/components/DataSpaceDescription.js.map +1 -1
- package/lib/components/DataSpaceExecutionContextViewer.d.ts +1 -6
- package/lib/components/DataSpaceExecutionContextViewer.d.ts.map +1 -1
- package/lib/components/DataSpaceExecutionContextViewer.js +12 -16
- package/lib/components/DataSpaceExecutionContextViewer.js.map +1 -1
- package/lib/components/DataSpaceLakehouseAccess.d.ts +22 -0
- package/lib/components/DataSpaceLakehouseAccess.d.ts.map +1 -0
- package/lib/components/DataSpaceLakehouseAccess.js +127 -0
- package/lib/components/DataSpaceLakehouseAccess.js.map +1 -0
- package/lib/components/DataSpaceQuickStart.d.ts.map +1 -1
- package/lib/components/DataSpaceQuickStart.js +3 -2
- package/lib/components/DataSpaceQuickStart.js.map +1 -1
- package/lib/components/DataSpaceViewer.d.ts.map +1 -1
- package/lib/components/DataSpaceViewer.js +7 -13
- package/lib/components/DataSpaceViewer.js.map +1 -1
- package/lib/components/DataSpaceWiki.d.ts.map +1 -1
- package/lib/components/DataSpaceWiki.js +3 -2
- package/lib/components/DataSpaceWiki.js.map +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/DataSpaceLayoutState.d.ts.map +1 -1
- package/lib/stores/DataSpaceLayoutState.js +3 -3
- package/lib/stores/DataSpaceLayoutState.js.map +1 -1
- package/lib/stores/DataSpaceViewerNavigation.d.ts +1 -0
- package/lib/stores/DataSpaceViewerNavigation.d.ts.map +1 -1
- package/lib/stores/DataSpaceViewerNavigation.js +1 -0
- package/lib/stores/DataSpaceViewerNavigation.js.map +1 -1
- package/lib/stores/DataSpaceViewerState.d.ts +3 -0
- package/lib/stores/DataSpaceViewerState.d.ts.map +1 -1
- package/lib/stores/DataSpaceViewerState.js +28 -2
- package/lib/stores/DataSpaceViewerState.js.map +1 -1
- package/package.json +8 -8
- package/src/components/DataSpaceDescription.tsx +86 -0
- package/src/components/DataSpaceExecutionContextViewer.tsx +41 -44
- package/src/components/DataSpaceLakehouseAccess.tsx +449 -0
- package/src/components/DataSpaceQuickStart.tsx +22 -11
- package/src/components/DataSpaceViewer.tsx +2 -55
- package/src/components/DataSpaceWiki.tsx +7 -1
- package/src/stores/DataSpaceLayoutState.ts +5 -6
- package/src/stores/DataSpaceViewerNavigation.ts +1 -0
- package/src/stores/DataSpaceViewerState.ts +36 -2
- package/tsconfig.json +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finos/legend-extension-dsl-data-space",
|
|
3
|
-
"version": "10.4.
|
|
3
|
+
"version": "10.4.259",
|
|
4
4
|
"description": "Legend extension for Data Space DSL",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"legend",
|
|
@@ -45,14 +45,14 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@finos/legend-application": "16.0.125",
|
|
47
47
|
"@finos/legend-art": "7.1.165",
|
|
48
|
-
"@finos/legend-code-editor": "2.0.
|
|
49
|
-
"@finos/legend-extension-dsl-data-product": "0.1.
|
|
50
|
-
"@finos/legend-extension-dsl-diagram": "8.1.
|
|
51
|
-
"@finos/legend-graph": "32.7.
|
|
52
|
-
"@finos/legend-lego": "2.0.
|
|
53
|
-
"@finos/legend-query-builder": "4.19.
|
|
48
|
+
"@finos/legend-code-editor": "2.0.206",
|
|
49
|
+
"@finos/legend-extension-dsl-data-product": "0.1.2",
|
|
50
|
+
"@finos/legend-extension-dsl-diagram": "8.1.270",
|
|
51
|
+
"@finos/legend-graph": "32.7.2",
|
|
52
|
+
"@finos/legend-lego": "2.0.226",
|
|
53
|
+
"@finos/legend-query-builder": "4.19.15",
|
|
54
54
|
"@finos/legend-server-depot": "6.1.22",
|
|
55
|
-
"@finos/legend-server-lakehouse": "0.3.
|
|
55
|
+
"@finos/legend-server-lakehouse": "0.3.86",
|
|
56
56
|
"@finos/legend-server-sdlc": "5.5.0",
|
|
57
57
|
"@finos/legend-shared": "11.0.30",
|
|
58
58
|
"@finos/legend-storage": "3.0.152",
|
|
@@ -20,10 +20,96 @@ import { observer } from 'mobx-react-lite';
|
|
|
20
20
|
import { DataSpaceWikiPlaceholder } from './DataSpacePlaceholder.js';
|
|
21
21
|
import { DataSpaceMarkdownTextViewer } from './DataSpaceMarkdownTextViewer.js';
|
|
22
22
|
import { useEffect, useRef } from 'react';
|
|
23
|
+
import { ExternalLinkIcon, VerifiedIcon, WrenchIcon } from '@finos/legend-art';
|
|
24
|
+
import { Tooltip } from '@mui/material';
|
|
23
25
|
import {
|
|
24
26
|
DATA_SPACE_VIEWER_ACTIVITY_MODE,
|
|
25
27
|
generateAnchorForActivity,
|
|
26
28
|
} from '../stores/DataSpaceViewerNavigation.js';
|
|
29
|
+
import type { DataSpaceInfoAnalysisResult } from '../graph-manager/action/analytics/DataSpaceAnalysis.js';
|
|
30
|
+
|
|
31
|
+
enum DATA_SPACE_WIKI_BADGE {
|
|
32
|
+
VERIFIED = 'VERIFIED',
|
|
33
|
+
IN_DEVELOPMENT = 'IN_DEVELOPMENT',
|
|
34
|
+
EXTERNAL = 'EXTERNAL',
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface DataSpaceWikiBadgeDescriptor {
|
|
38
|
+
classModifier: string;
|
|
39
|
+
icon: React.ReactNode;
|
|
40
|
+
label: string;
|
|
41
|
+
tooltipText?: string | undefined;
|
|
42
|
+
isActive: (info: DataSpaceInfoAnalysisResult | undefined) => boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const DATA_SPACE_WIKI_BADGE_DESCRIPTORS: Record<
|
|
46
|
+
DATA_SPACE_WIKI_BADGE,
|
|
47
|
+
DataSpaceWikiBadgeDescriptor
|
|
48
|
+
> = {
|
|
49
|
+
[DATA_SPACE_WIKI_BADGE.VERIFIED]: {
|
|
50
|
+
classModifier: 'verified',
|
|
51
|
+
icon: <VerifiedIcon />,
|
|
52
|
+
label: 'Verified',
|
|
53
|
+
isActive: (info) => info?.isVerified ?? false,
|
|
54
|
+
},
|
|
55
|
+
[DATA_SPACE_WIKI_BADGE.IN_DEVELOPMENT]: {
|
|
56
|
+
classModifier: 'in-development',
|
|
57
|
+
icon: <WrenchIcon />,
|
|
58
|
+
label: 'In Development',
|
|
59
|
+
tooltipText: 'This data space has not been finalized yet',
|
|
60
|
+
isActive: (info) => info?.isInDevelopment ?? false,
|
|
61
|
+
},
|
|
62
|
+
[DATA_SPACE_WIKI_BADGE.EXTERNAL]: {
|
|
63
|
+
classModifier: 'external',
|
|
64
|
+
icon: <ExternalLinkIcon />,
|
|
65
|
+
label: 'External',
|
|
66
|
+
tooltipText: 'This data originates from a third-party vendor',
|
|
67
|
+
isActive: (info) => info?.isExternal ?? false,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const DataSpaceWikiBadges = observer(
|
|
72
|
+
(props: { dataSpaceViewerState: DataSpaceViewerState }) => {
|
|
73
|
+
const { dataSpaceViewerState } = props;
|
|
74
|
+
const info = dataSpaceViewerState.dataSpaceAnalysisResult.info;
|
|
75
|
+
const activeBadges = Object.values(
|
|
76
|
+
DATA_SPACE_WIKI_BADGE_DESCRIPTORS,
|
|
77
|
+
).filter((badge) => badge.isActive(info));
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
activeBadges.length > 0 && (
|
|
81
|
+
<div
|
|
82
|
+
className={`data-space__viewer__wiki__badges data-space__viewer__wiki__badges--count-${activeBadges.length}`}
|
|
83
|
+
>
|
|
84
|
+
{activeBadges.map((badge) => {
|
|
85
|
+
const badgeContent = (
|
|
86
|
+
<div
|
|
87
|
+
key={badge.classModifier}
|
|
88
|
+
className={`data-space__viewer__wiki__badge data-space__viewer__wiki__badge--${badge.classModifier}`}
|
|
89
|
+
>
|
|
90
|
+
{badge.icon}
|
|
91
|
+
<span className="data-space__viewer__wiki__badge__label">
|
|
92
|
+
{badge.label}
|
|
93
|
+
</span>
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
return badge.tooltipText ? (
|
|
97
|
+
<Tooltip
|
|
98
|
+
key={badge.classModifier}
|
|
99
|
+
title={badge.tooltipText}
|
|
100
|
+
placement="bottom"
|
|
101
|
+
>
|
|
102
|
+
{badgeContent}
|
|
103
|
+
</Tooltip>
|
|
104
|
+
) : (
|
|
105
|
+
badgeContent
|
|
106
|
+
);
|
|
107
|
+
})}
|
|
108
|
+
</div>
|
|
109
|
+
)
|
|
110
|
+
);
|
|
111
|
+
},
|
|
112
|
+
);
|
|
27
113
|
|
|
28
114
|
export const DataSpaceDescription = observer(
|
|
29
115
|
(props: { dataSpaceViewerState: DataSpaceViewerState }) => {
|
|
@@ -92,42 +92,26 @@ const DataSpaceMappingProviderAccessControl = observer(
|
|
|
92
92
|
},
|
|
93
93
|
);
|
|
94
94
|
|
|
95
|
-
export const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
color="primary"
|
|
116
|
-
onClick={onOpenDataProduct}
|
|
117
|
-
sx={{
|
|
118
|
-
display:
|
|
119
|
-
!dataSpaceViewerState.viewDataProduct ||
|
|
120
|
-
deploymentId === undefined ||
|
|
121
|
-
isUnresolved
|
|
122
|
-
? 'none'
|
|
123
|
-
: undefined,
|
|
124
|
-
}}
|
|
125
|
-
>
|
|
126
|
-
Open DataProduct
|
|
127
|
-
</Button>
|
|
128
|
-
);
|
|
129
|
-
},
|
|
130
|
-
);
|
|
95
|
+
export const resolveOpenDataProductAction = (
|
|
96
|
+
dataSpaceViewerState: DataSpaceViewerState,
|
|
97
|
+
dataProductPath: string,
|
|
98
|
+
): (() => void) | undefined => {
|
|
99
|
+
const deploymentId =
|
|
100
|
+
dataSpaceViewerState.resolveDeploymentIdForDataProduct(dataProductPath);
|
|
101
|
+
const isUnresolved = Boolean(
|
|
102
|
+
dataSpaceViewerState.getDataProductAccessState(dataProductPath)
|
|
103
|
+
?.isDataProductUnresolved,
|
|
104
|
+
);
|
|
105
|
+
if (
|
|
106
|
+
!dataSpaceViewerState.viewDataProduct ||
|
|
107
|
+
deploymentId === undefined ||
|
|
108
|
+
isUnresolved
|
|
109
|
+
) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
const viewDataProduct = dataSpaceViewerState.viewDataProduct;
|
|
113
|
+
return (): void => viewDataProduct(dataProductPath, deploymentId);
|
|
114
|
+
};
|
|
131
115
|
|
|
132
116
|
export const DataSpaceMappingProviderEntry = observer(
|
|
133
117
|
(props: {
|
|
@@ -147,16 +131,33 @@ export const DataSpaceMappingProviderEntry = observer(
|
|
|
147
131
|
const tokenProvider =
|
|
148
132
|
dataSpaceViewerState.mappingProviderAccessConfig?.tokenProvider ??
|
|
149
133
|
((): undefined => undefined);
|
|
134
|
+
const onOpenDataProduct = resolveOpenDataProductAction(
|
|
135
|
+
dataSpaceViewerState,
|
|
136
|
+
mappingProvider.element,
|
|
137
|
+
);
|
|
138
|
+
const mappingProviderText = `${mappingProvider.element}${
|
|
139
|
+
mappingProvider.keys[0] ? `.${mappingProvider.keys[0]}` : ''
|
|
140
|
+
}`;
|
|
150
141
|
return (
|
|
151
142
|
<>
|
|
152
143
|
<div className="data-space__viewer__execution-context__entry__icon">
|
|
153
144
|
<PURE_DataProductIcon />
|
|
154
145
|
</div>
|
|
155
146
|
<div className="data-space__viewer__execution-context__entry__content data-space__viewer__execution-context__entry__content__text data-space__viewer__execution-context__mapping-provider__content">
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
147
|
+
{onOpenDataProduct ? (
|
|
148
|
+
<button
|
|
149
|
+
type="button"
|
|
150
|
+
className="data-space__viewer__execution-context__mapping-provider__label data-space__viewer__execution-context__mapping-provider__label--clickable"
|
|
151
|
+
title="Open Data Product"
|
|
152
|
+
onClick={onOpenDataProduct}
|
|
153
|
+
>
|
|
154
|
+
{mappingProviderText}
|
|
155
|
+
</button>
|
|
156
|
+
) : (
|
|
157
|
+
<span className="data-space__viewer__execution-context__mapping-provider__label">
|
|
158
|
+
{mappingProviderText}
|
|
159
|
+
</span>
|
|
160
|
+
)}
|
|
160
161
|
<div className="data-space__viewer__execution-context__mapping-provider__actions">
|
|
161
162
|
{mappingProviderAccessState && (
|
|
162
163
|
<DataSpaceMappingProviderAccessControl
|
|
@@ -164,10 +165,6 @@ export const DataSpaceMappingProviderEntry = observer(
|
|
|
164
165
|
tokenProvider={tokenProvider}
|
|
165
166
|
/>
|
|
166
167
|
)}
|
|
167
|
-
<DataSpaceOpenDataProductButton
|
|
168
|
-
dataSpaceViewerState={dataSpaceViewerState}
|
|
169
|
-
dataProductPath={mappingProvider.element}
|
|
170
|
-
/>
|
|
171
168
|
{mappingProviderAccessState && (
|
|
172
169
|
<IconButton
|
|
173
170
|
className="data-space__viewer__execution-context__refresh-btn"
|
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026-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 { useEffect, useMemo, useRef, useState } from 'react';
|
|
19
|
+
import {
|
|
20
|
+
Button,
|
|
21
|
+
ButtonGroup,
|
|
22
|
+
IconButton,
|
|
23
|
+
Menu,
|
|
24
|
+
MenuItem,
|
|
25
|
+
Tooltip,
|
|
26
|
+
} from '@mui/material';
|
|
27
|
+
import {
|
|
28
|
+
CaretDownIcon,
|
|
29
|
+
ExpandMoreIcon,
|
|
30
|
+
RefreshIcon,
|
|
31
|
+
clsx,
|
|
32
|
+
} from '@finos/legend-art';
|
|
33
|
+
import {
|
|
34
|
+
extractElementNameFromPath,
|
|
35
|
+
type V1_AccessPointGroup,
|
|
36
|
+
} from '@finos/legend-graph';
|
|
37
|
+
import {
|
|
38
|
+
AccessPointGroupAccess,
|
|
39
|
+
CollapsibleWikiSection,
|
|
40
|
+
DataProductAPGAccessRequestControl,
|
|
41
|
+
EntitlementsDataContractCreator,
|
|
42
|
+
type DataProductAPGState,
|
|
43
|
+
} from '@finos/legend-extension-dsl-data-product';
|
|
44
|
+
import { type DataSpaceViewerState } from '../stores/DataSpaceViewerState.js';
|
|
45
|
+
import type { DataSpaceDataProductAccessState } from '../stores/DataSpaceDataProductAccessState.js';
|
|
46
|
+
import { DataSpaceMarkdownTextViewer } from './DataSpaceMarkdownTextViewer.js';
|
|
47
|
+
import { resolveOpenDataProductAction } from './DataSpaceExecutionContextViewer.js';
|
|
48
|
+
import {
|
|
49
|
+
DATA_SPACE_VIEWER_ACTIVITY_MODE,
|
|
50
|
+
generateAnchorForActivity,
|
|
51
|
+
} from '../stores/DataSpaceViewerNavigation.js';
|
|
52
|
+
|
|
53
|
+
const isEntitled = (access: AccessPointGroupAccess): boolean =>
|
|
54
|
+
access === AccessPointGroupAccess.APPROVED ||
|
|
55
|
+
access === AccessPointGroupAccess.ENTERPRISE;
|
|
56
|
+
|
|
57
|
+
const getBulkRequestTargetApgStates = (
|
|
58
|
+
accessState: DataSpaceDataProductAccessState,
|
|
59
|
+
): DataProductAPGState[] => {
|
|
60
|
+
const apgStates = accessState.dataProductViewerState?.apgStates ?? [];
|
|
61
|
+
return apgStates.filter((state) => !isEntitled(state.access));
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const getContractableApgStates = (
|
|
65
|
+
accessState: DataSpaceDataProductAccessState,
|
|
66
|
+
): DataProductAPGState[] => {
|
|
67
|
+
const apgStates = accessState.dataProductViewerState?.apgStates ?? [];
|
|
68
|
+
return apgStates.filter(
|
|
69
|
+
(state) => state.access !== AccessPointGroupAccess.ENTERPRISE,
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const DataSpaceLakehouseAccessRequestButton = observer(
|
|
74
|
+
(props: {
|
|
75
|
+
accessState: DataSpaceDataProductAccessState;
|
|
76
|
+
onRequestForSelf: () => void;
|
|
77
|
+
onRequestForOthers: () => void;
|
|
78
|
+
}) => {
|
|
79
|
+
const { accessState, onRequestForSelf, onRequestForOthers } = props;
|
|
80
|
+
const buttonGroupRef = useRef<HTMLDivElement>(null);
|
|
81
|
+
const [isMenuOpen, setIsMenuOpen] = useState<boolean>(false);
|
|
82
|
+
const apgStates = accessState.dataProductViewerState?.apgStates;
|
|
83
|
+
if (!apgStates || apgStates.length === 0) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const total = apgStates.length;
|
|
87
|
+
const entitled = apgStates.filter((state) =>
|
|
88
|
+
isEntitled(state.access),
|
|
89
|
+
).length;
|
|
90
|
+
const pending = apgStates.filter(
|
|
91
|
+
(state) =>
|
|
92
|
+
state.access === AccessPointGroupAccess.SUBMITTED_FOR_APPROVALS ||
|
|
93
|
+
state.access === AccessPointGroupAccess.PENDING_MANAGER_APPROVAL ||
|
|
94
|
+
state.access === AccessPointGroupAccess.PENDING_DATA_OWNER_APPROVAL,
|
|
95
|
+
).length;
|
|
96
|
+
const hasAllAccess = entitled >= total;
|
|
97
|
+
const requestableTargets = getBulkRequestTargetApgStates(accessState);
|
|
98
|
+
const primaryDisabled = hasAllAccess || requestableTargets.length === 0;
|
|
99
|
+
const forOthersTargets = getContractableApgStates(accessState);
|
|
100
|
+
const showForOthers = forOthersTargets.length > 0;
|
|
101
|
+
const primaryTooltip = hasAllAccess
|
|
102
|
+
? `You have access to all ${total} Access Point Group${total === 1 ? '' : 's'} under this Data Product required to access this DataSpace.`
|
|
103
|
+
: `Request access to the Access Point Groups under this Data Product required to access this DataSpace. ${entitled} of ${total} granted${pending > 0 ? `, ${pending} pending approval` : ''}.`;
|
|
104
|
+
return (
|
|
105
|
+
<>
|
|
106
|
+
<ButtonGroup
|
|
107
|
+
variant="contained"
|
|
108
|
+
color={hasAllAccess ? 'success' : 'primary'}
|
|
109
|
+
ref={buttonGroupRef}
|
|
110
|
+
className="data-space__viewer__lakehouse-access__group__request-btn"
|
|
111
|
+
>
|
|
112
|
+
<Tooltip title={primaryTooltip} arrow={true}>
|
|
113
|
+
<span>
|
|
114
|
+
<Button
|
|
115
|
+
disabled={!hasAllAccess && primaryDisabled}
|
|
116
|
+
onClick={hasAllAccess ? undefined : onRequestForSelf}
|
|
117
|
+
sx={{
|
|
118
|
+
height: '100%',
|
|
119
|
+
cursor: hasAllAccess ? 'default' : undefined,
|
|
120
|
+
borderTopRightRadius: showForOthers ? 0 : undefined,
|
|
121
|
+
borderBottomRightRadius: showForOthers ? 0 : undefined,
|
|
122
|
+
}}
|
|
123
|
+
>
|
|
124
|
+
{hasAllAccess
|
|
125
|
+
? 'Access Granted'
|
|
126
|
+
: `Request Access (${entitled} / ${total})`}
|
|
127
|
+
</Button>
|
|
128
|
+
</span>
|
|
129
|
+
</Tooltip>
|
|
130
|
+
{showForOthers && (
|
|
131
|
+
<Button
|
|
132
|
+
size="small"
|
|
133
|
+
onClick={() => setIsMenuOpen((prev) => !prev)}
|
|
134
|
+
title="More options"
|
|
135
|
+
>
|
|
136
|
+
<CaretDownIcon />
|
|
137
|
+
</Button>
|
|
138
|
+
)}
|
|
139
|
+
</ButtonGroup>
|
|
140
|
+
{showForOthers && (
|
|
141
|
+
<Menu
|
|
142
|
+
anchorEl={buttonGroupRef.current}
|
|
143
|
+
open={isMenuOpen}
|
|
144
|
+
onClose={() => setIsMenuOpen(false)}
|
|
145
|
+
>
|
|
146
|
+
<MenuItem
|
|
147
|
+
onClick={() => {
|
|
148
|
+
onRequestForOthers();
|
|
149
|
+
setIsMenuOpen(false);
|
|
150
|
+
}}
|
|
151
|
+
>
|
|
152
|
+
Request Access for Others
|
|
153
|
+
</MenuItem>
|
|
154
|
+
</Menu>
|
|
155
|
+
)}
|
|
156
|
+
</>
|
|
157
|
+
);
|
|
158
|
+
},
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const DataSpaceLakehouseAccessGroup = observer(
|
|
162
|
+
(props: {
|
|
163
|
+
viewerState: DataSpaceViewerState;
|
|
164
|
+
dataProductPath: string;
|
|
165
|
+
initiallyCollapsed: boolean;
|
|
166
|
+
}) => {
|
|
167
|
+
const { viewerState, dataProductPath, initiallyCollapsed } = props;
|
|
168
|
+
const [isCollapsed, setIsCollapsed] = useState<boolean>(initiallyCollapsed);
|
|
169
|
+
const [bulkRequestMode, setBulkRequestMode] = useState<
|
|
170
|
+
'self' | 'others' | undefined
|
|
171
|
+
>(undefined);
|
|
172
|
+
const accessState = viewerState.getDataProductAccessState(dataProductPath);
|
|
173
|
+
const isReadOnly = accessState === undefined;
|
|
174
|
+
const readOnlyApgIds: string[] = useMemo(() => {
|
|
175
|
+
if (!isReadOnly) {
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
const dp =
|
|
179
|
+
viewerState.graphManagerState.graph.getOwnNullableDataProduct(
|
|
180
|
+
dataProductPath,
|
|
181
|
+
);
|
|
182
|
+
return dp?.accessPointGroups.map((group) => group.id) ?? [];
|
|
183
|
+
}, [dataProductPath, isReadOnly, viewerState.graphManagerState.graph]);
|
|
184
|
+
const tokenProvider =
|
|
185
|
+
viewerState.mappingProviderAccessConfig?.tokenProvider ??
|
|
186
|
+
((): undefined => undefined);
|
|
187
|
+
const apgStates = accessState?.dataProductViewerState?.apgStates ?? [];
|
|
188
|
+
const isLoading = accessState?.initializingState.isInProgress ?? false;
|
|
189
|
+
const hasError =
|
|
190
|
+
accessState?.errorMessage !== undefined && apgStates.length === 0;
|
|
191
|
+
const displayedCount = isReadOnly
|
|
192
|
+
? readOnlyApgIds.length
|
|
193
|
+
: apgStates.length;
|
|
194
|
+
|
|
195
|
+
const bulkTargetApgStates = accessState
|
|
196
|
+
? bulkRequestMode === 'others'
|
|
197
|
+
? getContractableApgStates(accessState)
|
|
198
|
+
: getBulkRequestTargetApgStates(accessState)
|
|
199
|
+
: [];
|
|
200
|
+
const bulkTargetApgs: V1_AccessPointGroup[] = bulkTargetApgStates.map(
|
|
201
|
+
(state) => state.apg,
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const representativeApgState: DataProductAPGState | undefined =
|
|
205
|
+
bulkTargetApgStates[0];
|
|
206
|
+
|
|
207
|
+
const onOpenDataProduct = resolveOpenDataProductAction(
|
|
208
|
+
viewerState,
|
|
209
|
+
dataProductPath,
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<div className="data-space__viewer__lakehouse-access__group">
|
|
214
|
+
<div className="data-space__viewer__lakehouse-access__group__header-row">
|
|
215
|
+
<button
|
|
216
|
+
className="data-space__viewer__lakehouse-access__group__caret-btn"
|
|
217
|
+
tabIndex={-1}
|
|
218
|
+
onClick={(): void => setIsCollapsed((prev) => !prev)}
|
|
219
|
+
title={isCollapsed ? 'Expand' : 'Collapse'}
|
|
220
|
+
>
|
|
221
|
+
<ExpandMoreIcon
|
|
222
|
+
className={clsx(
|
|
223
|
+
'data-space__viewer__lakehouse-access__group__caret',
|
|
224
|
+
{
|
|
225
|
+
'data-space__viewer__lakehouse-access__group__caret--collapsed':
|
|
226
|
+
isCollapsed,
|
|
227
|
+
},
|
|
228
|
+
)}
|
|
229
|
+
/>
|
|
230
|
+
</button>
|
|
231
|
+
<div className="data-space__viewer__lakehouse-access__group__header">
|
|
232
|
+
{onOpenDataProduct ? (
|
|
233
|
+
<button
|
|
234
|
+
type="button"
|
|
235
|
+
className="data-space__viewer__lakehouse-access__group__label data-space__viewer__lakehouse-access__group__label--clickable"
|
|
236
|
+
title={`Open Data Product: ${dataProductPath}`}
|
|
237
|
+
onClick={onOpenDataProduct}
|
|
238
|
+
>
|
|
239
|
+
{extractElementNameFromPath(dataProductPath)}
|
|
240
|
+
</button>
|
|
241
|
+
) : (
|
|
242
|
+
<span
|
|
243
|
+
className="data-space__viewer__lakehouse-access__group__label"
|
|
244
|
+
title={dataProductPath}
|
|
245
|
+
>
|
|
246
|
+
{extractElementNameFromPath(dataProductPath)}
|
|
247
|
+
</span>
|
|
248
|
+
)}
|
|
249
|
+
<span className="data-space__viewer__lakehouse-access__group__count">
|
|
250
|
+
{displayedCount}
|
|
251
|
+
</span>
|
|
252
|
+
</div>
|
|
253
|
+
{!isReadOnly && (
|
|
254
|
+
<div className="data-space__viewer__lakehouse-access__group__actions">
|
|
255
|
+
{!hasError && (
|
|
256
|
+
<DataSpaceLakehouseAccessRequestButton
|
|
257
|
+
accessState={accessState}
|
|
258
|
+
onRequestForSelf={(): void => setBulkRequestMode('self')}
|
|
259
|
+
onRequestForOthers={(): void => setBulkRequestMode('others')}
|
|
260
|
+
/>
|
|
261
|
+
)}
|
|
262
|
+
<IconButton
|
|
263
|
+
className="data-space__viewer__lakehouse-access__group__refresh-btn"
|
|
264
|
+
size="small"
|
|
265
|
+
color="primary"
|
|
266
|
+
title="Refresh Data Product access"
|
|
267
|
+
disabled={accessState.initializingState.isInProgress}
|
|
268
|
+
onClick={(): void =>
|
|
269
|
+
viewerState.refreshDataProductAccessState(dataProductPath)
|
|
270
|
+
}
|
|
271
|
+
>
|
|
272
|
+
<RefreshIcon />
|
|
273
|
+
</IconButton>
|
|
274
|
+
</div>
|
|
275
|
+
)}
|
|
276
|
+
</div>
|
|
277
|
+
{!isCollapsed && (
|
|
278
|
+
<div className="data-space__viewer__lakehouse-access__group__rows">
|
|
279
|
+
{isReadOnly ? (
|
|
280
|
+
readOnlyApgIds.length === 0 ? (
|
|
281
|
+
<div className="data-space__viewer__lakehouse-access__row">
|
|
282
|
+
<span className="data-space__viewer__lakehouse-access__row__unavailable">
|
|
283
|
+
Data Product is not available in the local project — Access
|
|
284
|
+
Point Groups cannot be listed in preview.
|
|
285
|
+
</span>
|
|
286
|
+
</div>
|
|
287
|
+
) : (
|
|
288
|
+
readOnlyApgIds.map((apgId) => (
|
|
289
|
+
<div
|
|
290
|
+
key={apgId}
|
|
291
|
+
className="data-space__viewer__lakehouse-access__row"
|
|
292
|
+
>
|
|
293
|
+
<span
|
|
294
|
+
className="data-space__viewer__lakehouse-access__row__apg"
|
|
295
|
+
title={apgId}
|
|
296
|
+
>
|
|
297
|
+
{apgId}
|
|
298
|
+
</span>
|
|
299
|
+
</div>
|
|
300
|
+
))
|
|
301
|
+
)
|
|
302
|
+
) : hasError ? (
|
|
303
|
+
<div
|
|
304
|
+
className="data-space__viewer__lakehouse-access__row"
|
|
305
|
+
title={accessState.errorMessage}
|
|
306
|
+
>
|
|
307
|
+
<span className="data-space__viewer__lakehouse-access__row__unavailable">
|
|
308
|
+
Unavailable
|
|
309
|
+
</span>
|
|
310
|
+
</div>
|
|
311
|
+
) : isLoading && apgStates.length === 0 ? (
|
|
312
|
+
<div className="data-space__viewer__lakehouse-access__row">
|
|
313
|
+
<span className="data-space__viewer__lakehouse-access__row__loading">
|
|
314
|
+
Loading...
|
|
315
|
+
</span>
|
|
316
|
+
</div>
|
|
317
|
+
) : (
|
|
318
|
+
apgStates.map((apgState) => (
|
|
319
|
+
<div
|
|
320
|
+
key={apgState.apg.id}
|
|
321
|
+
className="data-space__viewer__lakehouse-access__row"
|
|
322
|
+
>
|
|
323
|
+
<span
|
|
324
|
+
className="data-space__viewer__lakehouse-access__row__apg"
|
|
325
|
+
title={apgState.apg.id}
|
|
326
|
+
>
|
|
327
|
+
{apgState.apg.id}
|
|
328
|
+
</span>
|
|
329
|
+
<div className="data-space__viewer__lakehouse-access__row__actions">
|
|
330
|
+
{accessState.dataAccessState && (
|
|
331
|
+
<DataProductAPGAccessRequestControl
|
|
332
|
+
apgState={apgState}
|
|
333
|
+
dataAccessState={accessState.dataAccessState}
|
|
334
|
+
tokenProvider={tokenProvider}
|
|
335
|
+
/>
|
|
336
|
+
)}
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
))
|
|
340
|
+
)}
|
|
341
|
+
</div>
|
|
342
|
+
)}
|
|
343
|
+
{bulkRequestMode !== undefined &&
|
|
344
|
+
accessState?.dataAccessState &&
|
|
345
|
+
representativeApgState &&
|
|
346
|
+
bulkTargetApgs.length > 0 && (
|
|
347
|
+
<EntitlementsDataContractCreator
|
|
348
|
+
open={true}
|
|
349
|
+
onClose={(): void => setBulkRequestMode(undefined)}
|
|
350
|
+
tokenProvider={tokenProvider}
|
|
351
|
+
apgState={representativeApgState}
|
|
352
|
+
dataAccessState={accessState.dataAccessState}
|
|
353
|
+
overrideTargetApgs={bulkTargetApgs}
|
|
354
|
+
headerContent={
|
|
355
|
+
<>
|
|
356
|
+
Submit access request for{' '}
|
|
357
|
+
<span className="marketplace-lakehouse-text__emphasis">
|
|
358
|
+
{bulkTargetApgs.length}
|
|
359
|
+
</span>{' '}
|
|
360
|
+
Access Point Group
|
|
361
|
+
{bulkTargetApgs.length === 1 ? '' : 's'} in{' '}
|
|
362
|
+
<span className="marketplace-lakehouse-text__emphasis">
|
|
363
|
+
{accessState.dataAccessState.dataProductViewerState.product
|
|
364
|
+
.title ?? extractElementNameFromPath(dataProductPath)}
|
|
365
|
+
</span>{' '}
|
|
366
|
+
Data Product
|
|
367
|
+
</>
|
|
368
|
+
}
|
|
369
|
+
/>
|
|
370
|
+
)}
|
|
371
|
+
</div>
|
|
372
|
+
);
|
|
373
|
+
},
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
const DataSpaceLakehouseAccessPanel = observer(
|
|
377
|
+
(props: {
|
|
378
|
+
viewerState: DataSpaceViewerState;
|
|
379
|
+
dataProductPaths: string[];
|
|
380
|
+
}) => {
|
|
381
|
+
const { viewerState, dataProductPaths } = props;
|
|
382
|
+
return (
|
|
383
|
+
<>
|
|
384
|
+
<div className="data-space__viewer__lakehouse-access__description">
|
|
385
|
+
<DataSpaceMarkdownTextViewer
|
|
386
|
+
value={
|
|
387
|
+
"The following Data Products are required to access this DataSpace. Products you don't have access to can be requested below."
|
|
388
|
+
}
|
|
389
|
+
/>
|
|
390
|
+
</div>
|
|
391
|
+
<div className="data-space__viewer__lakehouse-access">
|
|
392
|
+
<div className="data-space__viewer__lakehouse-access__header">
|
|
393
|
+
Data Product Access
|
|
394
|
+
</div>
|
|
395
|
+
{dataProductPaths.map((dataProductPath) => (
|
|
396
|
+
<DataSpaceLakehouseAccessGroup
|
|
397
|
+
key={dataProductPath}
|
|
398
|
+
viewerState={viewerState}
|
|
399
|
+
dataProductPath={dataProductPath}
|
|
400
|
+
initiallyCollapsed={true}
|
|
401
|
+
/>
|
|
402
|
+
))}
|
|
403
|
+
</div>
|
|
404
|
+
</>
|
|
405
|
+
);
|
|
406
|
+
},
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
export const DataSpaceLakehouseAccess = observer(
|
|
410
|
+
(props: { dataSpaceViewerState: DataSpaceViewerState }) => {
|
|
411
|
+
const { dataSpaceViewerState } = props;
|
|
412
|
+
const sectionRef = useRef<HTMLDivElement>(null);
|
|
413
|
+
const anchor = generateAnchorForActivity(
|
|
414
|
+
DATA_SPACE_VIEWER_ACTIVITY_MODE.DATASPACE_LAKEHOUSE_ACCESS,
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
useEffect(() => {
|
|
418
|
+
if (sectionRef.current) {
|
|
419
|
+
dataSpaceViewerState.layoutState.setWikiPageAnchor(
|
|
420
|
+
anchor,
|
|
421
|
+
sectionRef.current,
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
return () => dataSpaceViewerState.layoutState.unsetWikiPageAnchor(anchor);
|
|
425
|
+
}, [dataSpaceViewerState, anchor]);
|
|
426
|
+
|
|
427
|
+
const dataProductPaths = dataSpaceViewerState.referencedDataProductPaths;
|
|
428
|
+
|
|
429
|
+
if (dataProductPaths.length === 0) {
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return (
|
|
434
|
+
<div ref={sectionRef} className="viewer__wiki__section">
|
|
435
|
+
<CollapsibleWikiSection
|
|
436
|
+
viewerState={dataSpaceViewerState}
|
|
437
|
+
section={DATA_SPACE_VIEWER_ACTIVITY_MODE.DATASPACE_LAKEHOUSE_ACCESS}
|
|
438
|
+
>
|
|
439
|
+
<div className="viewer__wiki__section__content">
|
|
440
|
+
<DataSpaceLakehouseAccessPanel
|
|
441
|
+
viewerState={dataSpaceViewerState}
|
|
442
|
+
dataProductPaths={dataProductPaths}
|
|
443
|
+
/>
|
|
444
|
+
</div>
|
|
445
|
+
</CollapsibleWikiSection>
|
|
446
|
+
</div>
|
|
447
|
+
);
|
|
448
|
+
},
|
|
449
|
+
);
|