@datalayer/core 1.0.19 → 1.0.21
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/README.md +27 -0
- package/lib/api/DatalayerApi.d.ts +5 -1
- package/lib/api/DatalayerApi.js +55 -8
- package/lib/api/iam/oauth2.d.ts +3 -3
- package/lib/api/iam/oauth2.js +2 -2
- package/lib/api/iam/profile.d.ts +9 -1
- package/lib/api/iam/profile.js +15 -0
- package/lib/api/runtimes/checkpoints.js +1 -1
- package/lib/api/runtimes/snapshots.d.ts +7 -7
- package/lib/api/runtimes/snapshots.js +9 -9
- package/lib/client/auth/storage.js +103 -12
- package/lib/client/index.d.ts +7 -7
- package/lib/client/index.js +1 -1
- package/lib/client/mixins/RuntimesMixin.d.ts +6 -6
- package/lib/client/mixins/RuntimesMixin.js +5 -5
- package/lib/client/mixins/SpacerMixin.js +22 -1
- package/lib/collaboration/DatalayerCollaborationProvider.d.ts +4 -3
- package/lib/components/avatars/DLAvatar.d.ts +24 -0
- package/lib/components/avatars/DLAvatar.js +26 -0
- package/lib/components/avatars/UserProfileAvatar.js +4 -4
- package/lib/components/avatars/index.d.ts +1 -0
- package/lib/components/avatars/index.js +1 -0
- package/lib/components/checkout/StripeCheckout.d.ts +24 -3
- package/lib/components/checkout/StripeCheckout.js +1116 -55
- package/lib/components/display/DatalayerBox.js +6 -1
- package/lib/components/runtimes/RuntimeLauncherDialog.d.ts +2 -2
- package/lib/components/snapshots/{RuntimeSnapshotMenu.d.ts → SandboxSnapshotMenu.d.ts} +4 -4
- package/lib/components/snapshots/{RuntimeSnapshotMenu.js → SandboxSnapshotMenu.js} +33 -33
- package/lib/components/snapshots/index.d.ts +1 -1
- package/lib/components/snapshots/index.js +1 -1
- package/lib/components/storage/ContentsBrowser.js +2 -2
- package/lib/components/tokens/SpaceVariantToken.d.ts +1 -1
- package/lib/components/tokens/SpaceVariantToken.js +7 -4
- package/lib/components/users/PeerIndicator.js +5 -4
- package/lib/hooks/useCache.d.ts +125 -18
- package/lib/hooks/useCache.js +893 -120
- package/lib/hooks/useDatalayer.js +31 -3
- package/lib/hooks/useIAM.js +1 -8
- package/lib/hooks/useProjects.d.ts +44 -0
- package/lib/hooks/useProjects.js +26 -2
- package/lib/index.d.ts +2 -2
- package/lib/index.js +1 -1
- package/lib/mocks/components/FlashMock.js +8 -2
- package/lib/models/{RuntimeSnapshot.d.ts → CodeSandboxSnapshot.d.ts} +5 -5
- package/lib/models/{RuntimeSnapshot.js → CodeSandboxSnapshot.js} +1 -1
- package/lib/models/{RuntimeSnapshotDTO.d.ts → CodeSandboxSnapshotDTO.d.ts} +30 -30
- package/lib/models/{RuntimeSnapshotDTO.js → CodeSandboxSnapshotDTO.js} +6 -6
- package/lib/models/Environment.d.ts +4 -0
- package/lib/models/IAM.d.ts +66 -0
- package/lib/models/Organization.d.ts +1 -0
- package/lib/models/Organization.js +1 -0
- package/lib/models/Page.d.ts +2 -2
- package/lib/models/Page.js +2 -2
- package/lib/models/Profile.d.ts +1 -1
- package/lib/models/RuntimeDTO.d.ts +2 -2
- package/lib/models/Space.d.ts +1 -0
- package/lib/models/Space.js +29 -5
- package/lib/models/SpaceDTO.d.ts +4 -0
- package/lib/models/SpaceDTO.js +5 -0
- package/lib/models/Usage.d.ts +15 -0
- package/lib/models/Usage.js +9 -0
- package/lib/models/User.d.ts +21 -0
- package/lib/models/User.js +6 -2
- package/lib/models/index.d.ts +2 -2
- package/lib/models/index.js +2 -2
- package/lib/otel/auth.js +0 -4
- package/lib/services/index.d.ts +1 -1
- package/lib/services/index.js +1 -1
- package/lib/state/substates/IAMState.js +0 -7
- package/lib/state/substates/RuntimesState.d.ts +9 -9
- package/lib/state/substates/RuntimesState.js +3 -3
- package/lib/stateful/runtimes/actions.d.ts +17 -17
- package/lib/stateful/runtimes/actions.js +35 -55
- package/lib/stateful/runtimes/apis.d.ts +3 -3
- package/lib/stateful/runtimes/snapshots.d.ts +2 -2
- package/lib/stateful/runtimes/snapshots.js +7 -7
- package/lib/utils/Format.d.ts +6 -0
- package/lib/utils/Format.js +17 -0
- package/lib/utils/Snapshot.d.ts +1 -1
- package/lib/utils/Snapshot.js +1 -1
- package/lib/views/datasources/DatasourceDetail.d.ts +7 -1
- package/lib/views/datasources/DatasourceDetail.js +61 -23
- package/lib/views/datasources/DatasourceNew.d.ts +8 -1
- package/lib/views/datasources/DatasourceNew.js +23 -10
- package/lib/views/datasources/Datasources.d.ts +5 -1
- package/lib/views/datasources/Datasources.js +38 -30
- package/lib/views/iam/SignInSimple.d.ts +1 -1
- package/lib/views/iam/SignInSimple.js +52 -28
- package/lib/views/iam-tokens/IAMTokenNew.d.ts +3 -1
- package/lib/views/iam-tokens/IAMTokenNew.js +2 -2
- package/lib/views/iam-tokens/IAMTokens.d.ts +3 -1
- package/lib/views/iam-tokens/IAMTokens.js +2 -2
- package/lib/views/otel/OtelHeader.d.ts +5 -0
- package/lib/views/otel/OtelHeader.js +3 -3
- package/lib/views/profile/UserBadge.d.ts +21 -0
- package/lib/views/profile/UserBadge.js +100 -8
- package/lib/views/secrets/Secrets.js +21 -21
- package/package.json +8 -7
|
@@ -9,9 +9,8 @@ import { URLExt } from '@jupyterlab/coreutils';
|
|
|
9
9
|
import { PromiseDelegate } from '@lumino/coreutils';
|
|
10
10
|
import { Upload } from 'tus-js-client';
|
|
11
11
|
import { requestDatalayerAPI, } from '../../api/index.js';
|
|
12
|
-
import {
|
|
12
|
+
import { asCodeSandboxSnapshot } from '../../models/index.js';
|
|
13
13
|
import { iamStore, runtimesStore } from '../../state/index.js';
|
|
14
|
-
import { sleep } from '../../utils/index.js';
|
|
15
14
|
/**
|
|
16
15
|
* Get available Environments.
|
|
17
16
|
*/
|
|
@@ -100,7 +99,7 @@ export async function deleteRuntime(options) {
|
|
|
100
99
|
*/
|
|
101
100
|
export async function snapshotRuntime(options) {
|
|
102
101
|
const data = await requestDatalayerAPI({
|
|
103
|
-
url: URLExt.join(runtimesStore.getState().runtimesRunUrl, 'api/runtimes/v1/
|
|
102
|
+
url: URLExt.join(runtimesStore.getState().runtimesRunUrl, 'api/runtimes/v1/sandbox-snapshots'),
|
|
104
103
|
method: 'POST',
|
|
105
104
|
body: {
|
|
106
105
|
pod_name: options.id,
|
|
@@ -110,29 +109,34 @@ export async function snapshotRuntime(options) {
|
|
|
110
109
|
},
|
|
111
110
|
token: iamStore.getState().token,
|
|
112
111
|
});
|
|
113
|
-
if (!data.success
|
|
114
|
-
throw new Error(`Failed to take the
|
|
112
|
+
if (!data.success) {
|
|
113
|
+
throw new Error(`Failed to take the code sandbox snapshot ${options.id} - ${data}`);
|
|
114
|
+
}
|
|
115
|
+
// Runtimes service can return 202 Accepted without inline snapshot payload
|
|
116
|
+
// while the snapshot lifecycle completes asynchronously via pub-sub events.
|
|
117
|
+
if (!data.snapshot) {
|
|
118
|
+
return undefined;
|
|
115
119
|
}
|
|
116
|
-
return
|
|
120
|
+
return asCodeSandboxSnapshot(data.snapshot);
|
|
117
121
|
}
|
|
118
122
|
/**
|
|
119
|
-
* Get
|
|
123
|
+
* Get Code Sandbox Snapshots.
|
|
120
124
|
*/
|
|
121
|
-
export async function
|
|
125
|
+
export async function getSandboxSnapshots() {
|
|
122
126
|
const data = await requestDatalayerAPI({
|
|
123
|
-
url: URLExt.join(runtimesStore.getState().runtimesRunUrl, 'api/runtimes/v1/
|
|
127
|
+
url: URLExt.join(runtimesStore.getState().runtimesRunUrl, 'api/runtimes/v1/sandbox-snapshots'),
|
|
124
128
|
token: iamStore.getState().token,
|
|
125
129
|
});
|
|
126
130
|
if (!data.success) {
|
|
127
|
-
console.error('Failed to fetch
|
|
131
|
+
console.error('Failed to fetch code sandbox snapshots.', data);
|
|
128
132
|
return [];
|
|
129
133
|
}
|
|
130
|
-
return (data.snapshots ?? []).map(
|
|
134
|
+
return (data.snapshots ?? []).map(asCodeSandboxSnapshot);
|
|
131
135
|
}
|
|
132
136
|
/**
|
|
133
|
-
* Load a
|
|
137
|
+
* Load a Code Sandbox Snapshot within a Runtime.
|
|
134
138
|
*/
|
|
135
|
-
export async function
|
|
139
|
+
export async function loadSandboxSnapshot(options) {
|
|
136
140
|
const data = await requestDatalayerAPI({
|
|
137
141
|
url: URLExt.join(runtimesStore.getState().runtimesRunUrl, 'api/runtimes/v1/runtimes', options.id),
|
|
138
142
|
method: 'PUT',
|
|
@@ -142,29 +146,29 @@ export async function loadRuntimeSnapshot(options) {
|
|
|
142
146
|
token: iamStore.getState().token,
|
|
143
147
|
});
|
|
144
148
|
if (!data.success) {
|
|
145
|
-
throw new Error(`Failed to load the
|
|
149
|
+
throw new Error(`Failed to load the code sandbox snapshot; ${data.message}`);
|
|
146
150
|
}
|
|
147
151
|
}
|
|
148
152
|
/**
|
|
149
|
-
* Returns the
|
|
153
|
+
* Returns the Code Sandbox Snapshot download URL.
|
|
150
154
|
*
|
|
151
155
|
* @param id Snapshot UID to download
|
|
152
156
|
* @returns The download URL
|
|
153
157
|
*/
|
|
154
|
-
export function
|
|
155
|
-
return (URLExt.join(runtimesStore.getState().runtimesRunUrl, `api/runtimes/v1/
|
|
158
|
+
export function createSandboxSnapshotDownloadURL(id) {
|
|
159
|
+
return (URLExt.join(runtimesStore.getState().runtimesRunUrl, `api/runtimes/v1/sandbox-snapshots/${id}`) +
|
|
156
160
|
URLExt.objectToQueryString({
|
|
157
161
|
download: '1',
|
|
158
162
|
token: iamStore.getState().token ?? '',
|
|
159
163
|
}));
|
|
160
164
|
}
|
|
161
165
|
/**
|
|
162
|
-
* Export a
|
|
166
|
+
* Export a Code Sandbox Snapshot.
|
|
163
167
|
*
|
|
164
|
-
* @param id
|
|
168
|
+
* @param id Code Sandbox snapshot UID to download
|
|
165
169
|
*/
|
|
166
|
-
export function
|
|
167
|
-
const url =
|
|
170
|
+
export function exportCodeSandboxSnapshot(id) {
|
|
171
|
+
const url = createSandboxSnapshotDownloadURL(id);
|
|
168
172
|
const element = document.createElement('a');
|
|
169
173
|
element.href = url;
|
|
170
174
|
element.download = '';
|
|
@@ -173,46 +177,22 @@ export function exportRuntimeSnapshot(id) {
|
|
|
173
177
|
document.body.removeChild(element);
|
|
174
178
|
}
|
|
175
179
|
/**
|
|
176
|
-
* Delete a
|
|
180
|
+
* Delete a Code Sandbox Snapshot.
|
|
177
181
|
*/
|
|
178
|
-
export async function
|
|
182
|
+
export async function deleteCodeSandboxSnapshot(id) {
|
|
179
183
|
await requestDatalayerAPI({
|
|
180
|
-
url: URLExt.join(runtimesStore.getState().runtimesRunUrl, `api/runtimes/v1/
|
|
184
|
+
url: URLExt.join(runtimesStore.getState().runtimesRunUrl, `api/runtimes/v1/sandbox-snapshots/${id}`),
|
|
181
185
|
method: 'DELETE',
|
|
182
186
|
token: iamStore.getState().token,
|
|
183
187
|
});
|
|
184
|
-
// Poll Runtime Snapshot state up-to its deletion.
|
|
185
|
-
try {
|
|
186
|
-
let sleepTimeout = 1000;
|
|
187
|
-
while (true) {
|
|
188
|
-
await sleep(sleepTimeout);
|
|
189
|
-
sleepTimeout *= 2;
|
|
190
|
-
const response = await requestDatalayerAPI({
|
|
191
|
-
url: URLExt.join(runtimesStore.getState().runtimesRunUrl, `api/runtimes/v1/runtime-snapshots/${id}`),
|
|
192
|
-
token: iamStore.getState().token,
|
|
193
|
-
});
|
|
194
|
-
if (response.success === false) {
|
|
195
|
-
throw new Error(response.message);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
catch (error) {
|
|
200
|
-
if (error.name === 'RunResponseError' &&
|
|
201
|
-
error.response.status === 404) {
|
|
202
|
-
// Expected not found
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
throw error;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
188
|
}
|
|
209
189
|
/**
|
|
210
|
-
* Update
|
|
190
|
+
* Update Code Sandbox Snapshot metadata.
|
|
211
191
|
*/
|
|
212
|
-
export async function
|
|
192
|
+
export async function updateCodeSandboxSnapshot(id, metadata) {
|
|
213
193
|
if (metadata.name || metadata.description) {
|
|
214
194
|
await requestDatalayerAPI({
|
|
215
|
-
url: URLExt.join(runtimesStore.getState().runtimesRunUrl, `api/runtimes/v1/
|
|
195
|
+
url: URLExt.join(runtimesStore.getState().runtimesRunUrl, `api/runtimes/v1/sandbox-snapshots/${id}`),
|
|
216
196
|
method: 'PATCH',
|
|
217
197
|
body: { ...metadata },
|
|
218
198
|
token: iamStore.getState().token,
|
|
@@ -220,19 +200,19 @@ export async function updateRuntimeSnapshot(id, metadata) {
|
|
|
220
200
|
}
|
|
221
201
|
}
|
|
222
202
|
/**
|
|
223
|
-
* Upload a
|
|
203
|
+
* Upload a Code Sandbox Snapshot.
|
|
224
204
|
*
|
|
225
205
|
* Note: The promise will be rejected if the runtime state is empty.
|
|
226
206
|
*/
|
|
227
|
-
export async function
|
|
207
|
+
export async function uploadCodeSandboxSnapshot(options) {
|
|
228
208
|
if (options.file.size === 0) {
|
|
229
|
-
return Promise.reject('Empty
|
|
209
|
+
return Promise.reject('Empty Code Sandbox Snapshot.');
|
|
230
210
|
}
|
|
231
211
|
const tracker = new PromiseDelegate();
|
|
232
212
|
// Create a new tus upload.
|
|
233
213
|
const upload = new Upload(options.file, {
|
|
234
214
|
// Endpoint is the upload creation URL from your tus server.
|
|
235
|
-
endpoint: `${runtimesStore.getState().runtimesRunUrl}/api/runtimes/v1/
|
|
215
|
+
endpoint: `${runtimesStore.getState().runtimesRunUrl}/api/runtimes/v1/sandbox-snapshots/upload`,
|
|
236
216
|
headers: { Authorization: `Bearer ${iamStore.getState().token}` },
|
|
237
217
|
// Retry delays will enable tus-js-client to automatically retry on errors.
|
|
238
218
|
// retryDelays: [0, 3000, 5000, 10000, 20000],
|
|
@@ -5,7 +5,7 @@ import { ISessionContext } from '@jupyterlab/apputils';
|
|
|
5
5
|
import { ServiceManager, Kernel, ServerConnection } from '@jupyterlab/services';
|
|
6
6
|
import { IDisposable } from '@lumino/disposable';
|
|
7
7
|
import { ISignal } from '@lumino/signaling';
|
|
8
|
-
import type {
|
|
8
|
+
import type { ICodeSandboxSnapshot, IRuntimeCapabilities, IRuntimeModel, IDatalayerEnvironment, IRuntimeType, IRuntimeLocation } from '../../models';
|
|
9
9
|
/**
|
|
10
10
|
* Abstract interface for the Datalayer session context.
|
|
11
11
|
* It extends the JupyterLab session context
|
|
@@ -188,13 +188,13 @@ export interface IRemoteRuntimesManager extends IDisposable {
|
|
|
188
188
|
* Whether to stop the kernel after the snapshot completion or not.
|
|
189
189
|
*/
|
|
190
190
|
stop?: boolean;
|
|
191
|
-
}): Promise<
|
|
191
|
+
}): Promise<ICodeSandboxSnapshot | undefined>;
|
|
192
192
|
/**
|
|
193
193
|
* Load a snapshot within a runtim
|
|
194
194
|
*
|
|
195
195
|
* The runtime may be given by its `id` or `podName`.
|
|
196
196
|
*/
|
|
197
|
-
|
|
197
|
+
loadSandboxSnapshot(options: {
|
|
198
198
|
/**
|
|
199
199
|
* The kernel id
|
|
200
200
|
*/
|
|
@@ -12,13 +12,13 @@ type Props = {
|
|
|
12
12
|
*
|
|
13
13
|
* Note: You should use this only for browser runtimes.
|
|
14
14
|
*/
|
|
15
|
-
export declare function
|
|
15
|
+
export declare function createSandboxSnapshot(props: Props): Promise<void>;
|
|
16
16
|
/**
|
|
17
17
|
* Load a snapshot within a browser kernel.
|
|
18
18
|
*
|
|
19
19
|
* Note: You should use this only for browser kernels.
|
|
20
20
|
*/
|
|
21
|
-
export declare function
|
|
21
|
+
export declare function loadBrowserSandboxSnapshot({ connection, id, }: {
|
|
22
22
|
connection: Kernel.IKernelConnection;
|
|
23
23
|
id: string;
|
|
24
24
|
}): Promise<void>;
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
import { KernelExecutor } from '@datalayer/jupyter-react';
|
|
6
|
-
import {
|
|
6
|
+
import { createSandboxSnapshotDownloadURL, uploadCodeSandboxSnapshot } from './index.js';
|
|
7
7
|
/**
|
|
8
8
|
* Snapshot a runtime through the frontend and upload it to the cloud.
|
|
9
9
|
*
|
|
10
10
|
* Note: You should use this only for browser runtimes.
|
|
11
11
|
*/
|
|
12
|
-
export async function
|
|
12
|
+
export async function createSandboxSnapshot(props) {
|
|
13
13
|
const { connection, metadata, onUploadProgress } = props;
|
|
14
14
|
const dump = await new KernelExecutor({ connection }).execute(GET_RUNTIME_SNAPSHOT_SNIPPET, {
|
|
15
15
|
storeHistory: false,
|
|
@@ -19,7 +19,7 @@ export async function createRuntimeSnapshot(props) {
|
|
|
19
19
|
// Convert the data to blob.
|
|
20
20
|
const bytes = base64ToBytes(serializedData);
|
|
21
21
|
const file = new Blob([bytes.buffer]);
|
|
22
|
-
return
|
|
22
|
+
return uploadCodeSandboxSnapshot({
|
|
23
23
|
file,
|
|
24
24
|
metadata,
|
|
25
25
|
onProgress: onUploadProgress,
|
|
@@ -36,14 +36,14 @@ function base64ToBytes(base64) {
|
|
|
36
36
|
*
|
|
37
37
|
* Note: You should use this only for browser kernels.
|
|
38
38
|
*/
|
|
39
|
-
export async function
|
|
40
|
-
const downloadURL =
|
|
39
|
+
export async function loadBrowserSandboxSnapshot({ connection, id, }) {
|
|
40
|
+
const downloadURL = createSandboxSnapshotDownloadURL(id);
|
|
41
41
|
const response = await fetch(downloadURL);
|
|
42
42
|
const buffer = await response.arrayBuffer();
|
|
43
43
|
const base64 = bytesToBase64(new Uint8Array(buffer));
|
|
44
44
|
await new KernelExecutor({
|
|
45
45
|
connection,
|
|
46
|
-
}).execute(
|
|
46
|
+
}).execute(getLoadSandboxSnapshotSnippet(base64), {
|
|
47
47
|
storeHistory: false,
|
|
48
48
|
silent: true,
|
|
49
49
|
});
|
|
@@ -104,7 +104,7 @@ const GET_RUNTIME_SNAPSHOT_SNIPPET = `def _create_snapshot():
|
|
|
104
104
|
_create_snapshot()
|
|
105
105
|
del _create_snapshot
|
|
106
106
|
`;
|
|
107
|
-
function
|
|
107
|
+
function getLoadSandboxSnapshotSnippet(content) {
|
|
108
108
|
return `async def _load_snapshot():
|
|
109
109
|
import os
|
|
110
110
|
import logging
|
package/lib/utils/Format.d.ts
CHANGED
|
@@ -9,6 +9,12 @@ export declare const MEMORY_UNIT_LIMITS: {
|
|
|
9
9
|
readonly [U in MemoryUnit]: number;
|
|
10
10
|
};
|
|
11
11
|
export declare function formatForDisplay(numBytes: number | undefined, units?: MemoryUnit | undefined): string;
|
|
12
|
+
/**
|
|
13
|
+
* Format a byte size value using a human-friendly unit.
|
|
14
|
+
*
|
|
15
|
+
* Examples: 0 B, 532 B, 1.2 KB, 24 MB, 1.45 GB
|
|
16
|
+
*/
|
|
17
|
+
export declare function formatByteSize(numBytes: number | string | null | undefined): string;
|
|
12
18
|
/**
|
|
13
19
|
* Given a number of bytes, convert to the most human-readable
|
|
14
20
|
* format, (GB, TB, etc).
|
package/lib/utils/Format.js
CHANGED
|
@@ -17,6 +17,23 @@ export function formatForDisplay(numBytes, units) {
|
|
|
17
17
|
const lu = convertToLargestUnit(numBytes, units);
|
|
18
18
|
return lu[0].toFixed(2) + ' ' + lu[1];
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Format a byte size value using a human-friendly unit.
|
|
22
|
+
*
|
|
23
|
+
* Examples: 0 B, 532 B, 1.2 KB, 24 MB, 1.45 GB
|
|
24
|
+
*/
|
|
25
|
+
export function formatByteSize(numBytes) {
|
|
26
|
+
const normalizedNumBytes = typeof numBytes === 'string' ? Number(numBytes) : numBytes;
|
|
27
|
+
if (!Number.isFinite(normalizedNumBytes) || !normalizedNumBytes || normalizedNumBytes < 0) {
|
|
28
|
+
return '0 B';
|
|
29
|
+
}
|
|
30
|
+
const [value, unit] = convertToLargestUnit(normalizedNumBytes);
|
|
31
|
+
const decimals = value >= 100 ? 0 : value >= 10 ? 1 : 2;
|
|
32
|
+
const formattedValue = value
|
|
33
|
+
.toFixed(decimals)
|
|
34
|
+
.replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1');
|
|
35
|
+
return `${formattedValue} ${unit}`;
|
|
36
|
+
}
|
|
20
37
|
/**
|
|
21
38
|
* Given a number of bytes, convert to the most human-readable
|
|
22
39
|
* format, (GB, TB, etc).
|
package/lib/utils/Snapshot.d.ts
CHANGED
package/lib/utils/Snapshot.js
CHANGED
|
@@ -16,6 +16,6 @@ const KERNEL_SNAPSHOT_DESCRIPTION_CONFIGURATION = {
|
|
|
16
16
|
* @param suffix Name prefix
|
|
17
17
|
* @returns The kernel snapshot name
|
|
18
18
|
*/
|
|
19
|
-
export function
|
|
19
|
+
export function createSandboxSnapshotName(suffix) {
|
|
20
20
|
return `${uniqueNamesGenerator(KERNEL_SNAPSHOT_DESCRIPTION_CONFIGURATION)}-${suffix}`;
|
|
21
21
|
}
|
|
@@ -1,2 +1,8 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type DatasourceDetailProps = {
|
|
2
|
+
/** Optional principal uid used to scope datasource reads/updates. */
|
|
3
|
+
principalUid?: string;
|
|
4
|
+
/** Optional principal kind used to scope datasource reads/updates. */
|
|
5
|
+
principalKind?: 'user' | 'organization' | 'team';
|
|
6
|
+
};
|
|
7
|
+
export declare const DatasourceDetail: ({ principalUid, principalKind, }?: DatasourceDetailProps) => import("react/jsx-runtime").JSX.Element;
|
|
2
8
|
export default DatasourceDetail;
|
|
@@ -1,27 +1,33 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/*
|
|
3
3
|
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
4
4
|
* Distributed under the terms of the Modified BSD License.
|
|
5
5
|
*/
|
|
6
6
|
import { useState, useEffect } from 'react';
|
|
7
7
|
import { useParams } from 'react-router-dom';
|
|
8
|
-
import {
|
|
8
|
+
import { PageLayout, Text, Button, TextInput, FormControl, Textarea, Label, Spinner, Heading, } from '@primer/react';
|
|
9
9
|
import { Box } from '@datalayer/primer-addons';
|
|
10
10
|
import { EyeIcon, EyeClosedIcon } from '@primer/octicons-react';
|
|
11
11
|
import { BoringAvatar } from '../../components/avatars/index.js';
|
|
12
12
|
import { useCache, useToast } from '../../hooks/index.js';
|
|
13
13
|
import { useRunStore } from '../../state/index.js';
|
|
14
|
-
export const DatasourceDetail = () => {
|
|
14
|
+
export const DatasourceDetail = ({ principalUid, principalKind, } = {}) => {
|
|
15
15
|
const { datasourceId } = useParams();
|
|
16
16
|
const runStore = useRunStore();
|
|
17
17
|
const { enqueueToast } = useToast();
|
|
18
18
|
const { useUpdateDatasource, useDatasource } = useCache();
|
|
19
|
-
const updateDatasourceMutation = useUpdateDatasource(
|
|
20
|
-
|
|
19
|
+
const updateDatasourceMutation = useUpdateDatasource({
|
|
20
|
+
principalUid,
|
|
21
|
+
principalKind,
|
|
22
|
+
});
|
|
23
|
+
const datasourceQuery = useDatasource(datasourceId ?? '', {
|
|
24
|
+
principalUid,
|
|
25
|
+
principalKind,
|
|
26
|
+
});
|
|
21
27
|
const [datasource, setDatasource] = useState();
|
|
22
28
|
const [formValues, setFormValues] = useState({
|
|
23
|
-
name:
|
|
24
|
-
description:
|
|
29
|
+
name: '',
|
|
30
|
+
description: '',
|
|
25
31
|
});
|
|
26
32
|
const [validationResult, setValidationResult] = useState({
|
|
27
33
|
name: undefined,
|
|
@@ -32,7 +38,10 @@ export const DatasourceDetail = () => {
|
|
|
32
38
|
if (datasourceQuery.data) {
|
|
33
39
|
const datasource = datasourceQuery.data;
|
|
34
40
|
setDatasource(datasource);
|
|
35
|
-
setFormValues({
|
|
41
|
+
setFormValues({
|
|
42
|
+
name: datasource.name || '',
|
|
43
|
+
description: datasource.description || '',
|
|
44
|
+
});
|
|
36
45
|
}
|
|
37
46
|
}, [datasourceQuery.data]);
|
|
38
47
|
const nameNameChange = (event) => {
|
|
@@ -48,8 +57,8 @@ export const DatasourceDetail = () => {
|
|
|
48
57
|
}));
|
|
49
58
|
};
|
|
50
59
|
useEffect(() => {
|
|
51
|
-
setValidationResult({
|
|
52
|
-
...
|
|
60
|
+
setValidationResult(prev => ({
|
|
61
|
+
...prev,
|
|
53
62
|
name: formValues.name === undefined
|
|
54
63
|
? undefined
|
|
55
64
|
: formValues.name.length > 2
|
|
@@ -60,19 +69,28 @@ export const DatasourceDetail = () => {
|
|
|
60
69
|
: formValues.description.length > 2
|
|
61
70
|
? true
|
|
62
71
|
: false,
|
|
63
|
-
});
|
|
72
|
+
}));
|
|
64
73
|
}, [formValues]);
|
|
65
74
|
const nameSubmit = async () => {
|
|
75
|
+
if (!datasource) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
66
78
|
runStore.layout().showBackdrop();
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
79
|
+
const updatedDatasource = {
|
|
80
|
+
...datasource,
|
|
81
|
+
name: formValues.name,
|
|
82
|
+
description: formValues.description,
|
|
83
|
+
};
|
|
84
|
+
updateDatasourceMutation.mutate(updatedDatasource, {
|
|
70
85
|
onSuccess: (resp) => {
|
|
71
|
-
if (resp
|
|
86
|
+
if (typeof resp === 'object' &&
|
|
87
|
+
resp !== null &&
|
|
88
|
+
'success' in resp &&
|
|
89
|
+
resp.success) {
|
|
72
90
|
enqueueToast('The datasource is successfully updated.', {
|
|
73
91
|
variant: 'success',
|
|
74
92
|
});
|
|
75
|
-
setDatasource(
|
|
93
|
+
setDatasource(updatedDatasource);
|
|
76
94
|
}
|
|
77
95
|
},
|
|
78
96
|
onSettled: () => {
|
|
@@ -80,12 +98,32 @@ export const DatasourceDetail = () => {
|
|
|
80
98
|
},
|
|
81
99
|
});
|
|
82
100
|
};
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
101
|
+
if (!datasourceId) {
|
|
102
|
+
return _jsx(_Fragment, {});
|
|
103
|
+
}
|
|
104
|
+
if (datasourceQuery.isLoading) {
|
|
105
|
+
return (_jsx(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: _jsx(PageLayout.Content, { children: _jsx(Box, { sx: { maxWidth: 960, mx: 'auto', width: '100%' }, children: _jsx(Box, { sx: {
|
|
106
|
+
display: 'flex',
|
|
107
|
+
justifyContent: 'center',
|
|
108
|
+
alignItems: 'center',
|
|
109
|
+
py: 6,
|
|
110
|
+
}, children: _jsx(Spinner, { size: "large" }) }) }) }) }));
|
|
111
|
+
}
|
|
112
|
+
if (datasourceQuery.isError || (!datasourceQuery.isLoading && !datasource)) {
|
|
113
|
+
return (_jsx(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: _jsx(PageLayout.Content, { children: _jsx(Box, { sx: { maxWidth: 960, mx: 'auto', width: '100%' }, children: _jsx(Box, { sx: { p: 4 }, children: _jsx(Text, { sx: { color: 'danger.fg' }, children: "Datasource not found or failed to load." }) }) }) }) }));
|
|
114
|
+
}
|
|
115
|
+
return (_jsx(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: _jsx(PageLayout.Content, { children: _jsxs(Box, { sx: { maxWidth: 960, mx: 'auto', width: '100%' }, children: [_jsxs(Box, { sx: { mb: 4 }, children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Datasource" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Update datasource connection metadata and review configured credentials." })] }), _jsx(Box, { sx: {
|
|
116
|
+
border: '1px solid',
|
|
117
|
+
borderColor: 'border.default',
|
|
118
|
+
borderRadius: 2,
|
|
119
|
+
bg: 'canvas.default',
|
|
120
|
+
p: 3,
|
|
121
|
+
}, children: _jsxs(Box, { sx: { display: 'grid', gridTemplateColumns: ['1fr', '180px 1fr'], gap: 4 }, children: [_jsxs(Box, { children: [_jsx(BoringAvatar, { displayName: datasource?.name, size: 100, style: { paddingRight: 10 } }), _jsx(Box, { mt: 3, children: _jsx(Label, { size: "large", children: datasource?.variant }) })] }), _jsx(Box, { children: _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: formValues.name, onChange: nameNameChange }), validationResult.name === false && (_jsx(FormControl.Validation, { variant: "error", children: "Name must have more than 2 characters." }))] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Description" }), _jsx(Textarea, { block: true, value: formValues.description, onChange: nameDescriptionChange, rows: 5 }), validationResult.description === false && (_jsx(FormControl.Validation, { variant: "error", children: "Description must have more than 2 characters." }))] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Database" }), _jsx(TextInput, { placeholder: "Database", monospace: true, size: "large", contrast: !passwordVisibility, disabled: !passwordVisibility, type: passwordVisibility ? 'text' : 'password', value: datasource?.database || '', readOnly: true, trailingAction: _jsx(TextInput.Action, { onClick: () => {
|
|
122
|
+
setPasswordVisibility(!passwordVisibility);
|
|
123
|
+
}, icon: passwordVisibility ? EyeClosedIcon : EyeIcon, "aria-label": passwordVisibility ? 'Hide database' : 'Reveal database', sx: { color: 'var(--fgColor-muted)' } }), sx: { overflow: 'visible' } })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Output bucket" }), _jsx(TextInput, { placeholder: "Output bucket", monospace: true, size: "large", contrast: !passwordVisibility, disabled: !passwordVisibility, type: passwordVisibility ? 'text' : 'password', value: datasource?.outputBucket || '', readOnly: true, trailingAction: _jsx(TextInput.Action, { onClick: () => {
|
|
124
|
+
setPasswordVisibility(!passwordVisibility);
|
|
125
|
+
}, icon: passwordVisibility ? EyeClosedIcon : EyeIcon, "aria-label": passwordVisibility
|
|
126
|
+
? 'Hide output bucket'
|
|
127
|
+
: 'Reveal output bucket', sx: { color: 'var(--fgColor-muted)' } }), sx: { overflow: 'visible' } })] }), _jsx(Box, { sx: { marginTop: 3 }, children: _jsx(Button, { variant: "primary", disabled: !validationResult.name || !validationResult.description, onClick: nameSubmit, children: "Update datasource" }) })] }) })] }) })] }) }) }));
|
|
90
128
|
};
|
|
91
129
|
export default DatasourceDetail;
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
1
2
|
export type DatasourceNewProps = {
|
|
2
3
|
/** Route to navigate after creating a datasource. Defaults to '/settings/integrations/datasources'. */
|
|
3
4
|
datasourcesListRoute?: string;
|
|
4
5
|
/** Route to navigate to the secrets page. Defaults to '/settings/iam/secrets'. */
|
|
5
6
|
secretsRoute?: string;
|
|
7
|
+
/** Optional principal uid used to scope datasource creation. */
|
|
8
|
+
principalUid?: string;
|
|
9
|
+
/** Optional principal kind used to scope datasource creation. */
|
|
10
|
+
principalKind?: 'user' | 'organization' | 'team';
|
|
11
|
+
/** Optional contextual principal summary rendered below the page intro. */
|
|
12
|
+
accountPrincipal?: ReactNode;
|
|
6
13
|
};
|
|
7
|
-
export declare const DatasourceNew: ({ datasourcesListRoute, secretsRoute, }?: DatasourceNewProps) => import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export declare const DatasourceNew: ({ datasourcesListRoute, secretsRoute, principalUid, principalKind, accountPrincipal, }?: DatasourceNewProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
15
|
export default DatasourceNew;
|
|
@@ -4,14 +4,17 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
4
4
|
* Distributed under the terms of the Modified BSD License.
|
|
5
5
|
*/
|
|
6
6
|
import { useEffect, useState } from 'react';
|
|
7
|
-
import {
|
|
7
|
+
import { PageLayout, FormControl, Button, TextInput, Text, Heading, Textarea, Select, Flash, Link, } from '@primer/react';
|
|
8
8
|
import { Box } from '@datalayer/primer-addons';
|
|
9
9
|
import { useCache, useNavigate, useToast } from '../../hooks/index.js';
|
|
10
10
|
import { useRunStore } from '../../state/index.js';
|
|
11
|
-
export const DatasourceNew = ({ datasourcesListRoute = '/settings/integrations/datasources', secretsRoute = '/settings/iam/secrets', } = {}) => {
|
|
11
|
+
export const DatasourceNew = ({ datasourcesListRoute = '/settings/integrations/datasources', secretsRoute = '/settings/iam/secrets', principalUid, principalKind, accountPrincipal, } = {}) => {
|
|
12
12
|
const runStore = useRunStore();
|
|
13
13
|
const { useCreateDatasource } = useCache();
|
|
14
|
-
const createDatasourceMutation = useCreateDatasource(
|
|
14
|
+
const createDatasourceMutation = useCreateDatasource({
|
|
15
|
+
principalUid,
|
|
16
|
+
principalKind,
|
|
17
|
+
});
|
|
15
18
|
const navigate = useNavigate();
|
|
16
19
|
const { enqueueToast } = useToast();
|
|
17
20
|
const [formValues, setFormValues] = useState({
|
|
@@ -59,8 +62,8 @@ export const DatasourceNew = ({ datasourcesListRoute = '/settings/integrations/d
|
|
|
59
62
|
}));
|
|
60
63
|
};
|
|
61
64
|
useEffect(() => {
|
|
62
|
-
setValidationResult({
|
|
63
|
-
...
|
|
65
|
+
setValidationResult(prev => ({
|
|
66
|
+
...prev,
|
|
64
67
|
name: formValues.name === undefined
|
|
65
68
|
? undefined
|
|
66
69
|
: formValues.name.length > 2
|
|
@@ -85,9 +88,12 @@ export const DatasourceNew = ({ datasourcesListRoute = '/settings/integrations/d
|
|
|
85
88
|
: formValues.outputBucket.length > 0
|
|
86
89
|
? true
|
|
87
90
|
: false,
|
|
88
|
-
});
|
|
91
|
+
}));
|
|
89
92
|
}, [formValues]);
|
|
90
93
|
const submitCreate = () => {
|
|
94
|
+
if (!formValues.name || !formValues.description) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
91
97
|
runStore.layout().showBackdrop('Creating an datasource...');
|
|
92
98
|
createDatasourceMutation.mutate({
|
|
93
99
|
name: formValues.name,
|
|
@@ -97,8 +103,15 @@ export const DatasourceNew = ({ datasourcesListRoute = '/settings/integrations/d
|
|
|
97
103
|
description: formValues.description,
|
|
98
104
|
}, {
|
|
99
105
|
onSuccess: (resp) => {
|
|
100
|
-
if (resp
|
|
101
|
-
|
|
106
|
+
if (typeof resp === 'object' &&
|
|
107
|
+
resp !== null &&
|
|
108
|
+
'success' in resp &&
|
|
109
|
+
resp.success) {
|
|
110
|
+
const message = 'message' in resp &&
|
|
111
|
+
typeof resp.message === 'string'
|
|
112
|
+
? resp.message
|
|
113
|
+
: 'Datasource created.';
|
|
114
|
+
enqueueToast(message, { variant: 'success' });
|
|
102
115
|
navigate(datasourcesListRoute);
|
|
103
116
|
}
|
|
104
117
|
},
|
|
@@ -107,12 +120,12 @@ export const DatasourceNew = ({ datasourcesListRoute = '/settings/integrations/d
|
|
|
107
120
|
},
|
|
108
121
|
});
|
|
109
122
|
};
|
|
110
|
-
return (_jsxs(Box, { children: [
|
|
123
|
+
return (_jsx(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: _jsx(PageLayout.Content, { children: _jsxs(Box, { sx: { maxWidth: 960, mx: 'auto', width: '100%' }, children: [_jsxs(Box, { sx: { mb: 4 }, children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "New Datasource" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Create a datasource and configure required secrets for the selected provider." }), accountPrincipal && _jsx(Box, { sx: { mt: 2 }, children: accountPrincipal })] }), _jsxs(Flash, { variant: "warning", sx: { mb: 3 }, children: [formValues.variant === 'athena' && (_jsxs(Text, { children: ["For", ' ', _jsx(Link, { href: "https://aws.amazon.com/athena", children: "Amazon Athena" }), ",", ' ', "ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate(secretsRoute, e), children: "Secrets" }), ' ', "are available:", ' ', _jsx(Text, { as: "code", children: "AWS_SECRET_ACCESS_KEY" }), ' ', _jsx(Text, { as: "code", children: "AWS_ACCESS_KEY_ID" }), ' ', _jsx(Text, { as: "code", children: "AWS_DEFAULT_REGION" })] })), formValues.variant === 'bigquery' && (_jsxs(Text, { children: ["For", ' ', _jsx(Link, { href: "https://cloud.google.com/bigquery", children: "Google Big Query" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate(secretsRoute, e), children: "Secret" }), ' ', "is available:", ' ', _jsx(Text, { as: "code", children: "GOOGLE_APPLICATION_CREDENTIALS" })] })), formValues.variant === 'mssentinel' && (_jsxs(Text, { children: ["For", ' ', _jsx(Link, { href: "https://learn.microsoft.com/en-us/azure/sentinel/overview?tabs=defender-portaly", children: "Microsoft Sentinel" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate(secretsRoute, e), children: "Secret" }), ' ', "is available:", ' ', _jsx(Text, { as: "code", children: "AZURE_TENANT_ID" }), ` `, _jsx(Text, { as: "code", children: "AZURE_CLIENT_ID" }), ` `, _jsx(Text, { as: "code", children: "AZURE_CLIENT_SECRET" }), ` `, _jsx(Text, { as: "code", children: "AZURE_SUBSCRIPTION_ID" }), ` `, _jsx(Text, { as: "code", children: "AZURE_RESOURCE_GROUP" }), ` `, _jsx(Text, { as: "code", children: "MSSENTINEL_WORKSPACE_ID" }), ` `, _jsx(Text, { as: "code", children: "MSSENTINEL_WORKSPACE_NAME" })] })), formValues.variant === 'splunk' && (_jsxs(Text, { children: ["For ", _jsx(Link, { href: "https://www.splunk.com/", children: "Splunk" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate(secretsRoute, e), children: "Secret" }), ' ', "is available:", ' ', _jsx(Text, { as: "code", children: "SPLUNK_HOST" }), ` `, _jsx(Text, { as: "code", children: "SPLUNK_PORT" }), ` `, _jsx(Text, { as: "code", children: "SPLUNK_USERNAME" }), ` `, _jsx(Text, { as: "code", children: "SPLUNK_PASSWORD" })] }))] }), _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Datasource type" }), _jsxs(Select, { name: "type", value: formValues.variant, onChange: valueVariantChange, children: [_jsx(Select.Option, { value: "athena", children: "Amazon Athena" }), _jsx(Select.Option, { value: "bigquery", children: "Google BigQuery" }), _jsx(Select.Option, { value: "mssentinel", children: "Microsoft Sentinel" }), _jsx(Select.Option, { value: "splunk", children: "Splunk" })] }), _jsx(FormControl.Caption, { children: "Pick the most appropriate datasource type." })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: formValues.name, onChange: valueNameChange, autoFocus: true }), _jsx(FormControl.Caption, { children: "Hint: The datasource name is a short name that identifies in a unique way your datasource." }), validationResult.name === false && (_jsx(FormControl.Validation, { variant: "error", children: "Name length must be between 2 and 32 characters." }))] }), formValues.variant === 'athena' && (_jsxs(_Fragment, { children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Database" }), _jsx(TextInput, { block: true, value: formValues.database, onChange: valueDatabaseChange }), validationResult.database === false && (_jsx(FormControl.Validation, { variant: "error", children: "Database must have more than 1." }))] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Output Bucket" }), _jsx(TextInput, { block: true, value: formValues.outputBucket, onChange: valueOutputBucketChange }), validationResult.database === false && (_jsx(FormControl.Validation, { variant: "error", children: "Output bucket must have more than 1." }))] })] })), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Description" }), _jsx(Textarea, { block: true, value: formValues.description, onChange: valueDescriptionChange }), validationResult.description === false && (_jsx(FormControl.Validation, { variant: "error", children: "Description must have more than 2 characters." }))] }), _jsx(Button, { variant: "primary", disabled: !validationResult.database ||
|
|
111
124
|
!validationResult.outputBucket ||
|
|
112
125
|
!validationResult.name ||
|
|
113
126
|
!validationResult.description, sx: { marginTop: 2 }, onClick: e => {
|
|
114
127
|
e.preventDefault();
|
|
115
128
|
submitCreate();
|
|
116
|
-
}, children: "Create a datasource" })] }) }) })
|
|
129
|
+
}, children: "Create a datasource" })] })] }) }) }));
|
|
117
130
|
};
|
|
118
131
|
export default DatasourceNew;
|
|
@@ -3,6 +3,10 @@ export type DatasourcesProps = {
|
|
|
3
3
|
newDatasourceRoute?: string;
|
|
4
4
|
/** Base route for the datasources list (used for edit navigation). Defaults to current relative path. */
|
|
5
5
|
datasourcesListRoute?: string;
|
|
6
|
+
/** Optional principal uid used to scope datasource reads. */
|
|
7
|
+
principalUid?: string;
|
|
8
|
+
/** Optional principal kind used to scope datasource reads. */
|
|
9
|
+
principalKind?: 'user' | 'organization' | 'team';
|
|
6
10
|
};
|
|
7
|
-
export declare const Datasources: ({ newDatasourceRoute, datasourcesListRoute, }?: DatasourcesProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare const Datasources: ({ newDatasourceRoute, datasourcesListRoute, principalUid, principalKind, }?: DatasourcesProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
12
|
export default Datasources;
|