@finos/legend-application-studio 28.21.7 → 28.21.9
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/application/LegendStudioApplicationConfig.d.ts +4 -0
- package/lib/application/LegendStudioApplicationConfig.d.ts.map +1 -1
- package/lib/application/LegendStudioApplicationConfig.js +5 -0
- package/lib/application/LegendStudioApplicationConfig.js.map +1 -1
- package/lib/components/editor/editor-group/accessor/AccessorQueryBuilderHelper.js +1 -1
- package/lib/components/editor/editor-group/accessor/AccessorQueryBuilderHelper.js.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js +7 -20
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js.map +1 -1
- package/lib/components/editor/editor-group/service-editor/ServiceEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/service-editor/ServiceEditor.js +39 -6
- package/lib/components/editor/editor-group/service-editor/ServiceEditor.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/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js +3 -5
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js.map +1 -1
- package/lib/stores/extensions/DSL_Service_LegendStudioApplicationPlugin_Extension.d.ts +6 -1
- package/lib/stores/extensions/DSL_Service_LegendStudioApplicationPlugin_Extension.d.ts.map +1 -1
- package/package.json +11 -11
- package/src/application/LegendStudioApplicationConfig.ts +9 -0
- package/src/components/editor/editor-group/accessor/AccessorQueryBuilderHelper.tsx +1 -1
- package/src/components/editor/editor-group/dataProduct/DataProductEditor.tsx +8 -26
- package/src/components/editor/editor-group/service-editor/ServiceEditor.tsx +93 -7
- package/src/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.ts +10 -13
- package/src/stores/extensions/DSL_Service_LegendStudioApplicationPlugin_Extension.ts +10 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finos/legend-application-studio",
|
|
3
|
-
"version": "28.21.
|
|
3
|
+
"version": "28.21.9",
|
|
4
4
|
"description": "Legend Studio application core",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"legend",
|
|
@@ -47,17 +47,17 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@dagrejs/dagre": "1.1.4",
|
|
50
|
-
"@finos/legend-application": "16.0.
|
|
51
|
-
"@finos/legend-art": "7.1.
|
|
52
|
-
"@finos/legend-code-editor": "2.0.
|
|
53
|
-
"@finos/legend-data-cube": "0.3.
|
|
54
|
-
"@finos/legend-extension-dsl-data-product": "0.0.
|
|
55
|
-
"@finos/legend-extension-dsl-diagram": "8.1.
|
|
56
|
-
"@finos/legend-graph": "32.6.
|
|
57
|
-
"@finos/legend-lego": "2.0.
|
|
58
|
-
"@finos/legend-query-builder": "4.18.
|
|
50
|
+
"@finos/legend-application": "16.0.108",
|
|
51
|
+
"@finos/legend-art": "7.1.149",
|
|
52
|
+
"@finos/legend-code-editor": "2.0.174",
|
|
53
|
+
"@finos/legend-data-cube": "0.3.91",
|
|
54
|
+
"@finos/legend-extension-dsl-data-product": "0.0.84",
|
|
55
|
+
"@finos/legend-extension-dsl-diagram": "8.1.238",
|
|
56
|
+
"@finos/legend-graph": "32.6.6",
|
|
57
|
+
"@finos/legend-lego": "2.0.194",
|
|
58
|
+
"@finos/legend-query-builder": "4.18.13",
|
|
59
59
|
"@finos/legend-server-depot": "6.1.12",
|
|
60
|
-
"@finos/legend-server-lakehouse": "0.3.
|
|
60
|
+
"@finos/legend-server-lakehouse": "0.3.55",
|
|
61
61
|
"@finos/legend-server-sdlc": "5.4.3",
|
|
62
62
|
"@finos/legend-server-showcase": "0.2.66",
|
|
63
63
|
"@finos/legend-shared": "11.0.25",
|
|
@@ -191,6 +191,7 @@ export interface LegendStudioApplicationConfigurationData
|
|
|
191
191
|
query?: { url: string };
|
|
192
192
|
showcase?: { url: string };
|
|
193
193
|
pct?: { reportUrl: string };
|
|
194
|
+
legendAI?: { url: string };
|
|
194
195
|
}
|
|
195
196
|
|
|
196
197
|
export class LegendStudioApplicationConfig extends LegendApplicationConfig {
|
|
@@ -205,6 +206,7 @@ export class LegendStudioApplicationConfig extends LegendApplicationConfig {
|
|
|
205
206
|
readonly queryApplicationUrl?: string | undefined;
|
|
206
207
|
readonly showcaseServerUrl?: string | undefined;
|
|
207
208
|
readonly pctReportUrl?: string | undefined;
|
|
209
|
+
readonly legendAIUrl?: string | undefined;
|
|
208
210
|
|
|
209
211
|
constructor(
|
|
210
212
|
input: LegendApplicationConfigurationInput<LegendStudioApplicationConfigurationData>,
|
|
@@ -275,6 +277,13 @@ export class LegendStudioApplicationConfig extends LegendApplicationConfig {
|
|
|
275
277
|
);
|
|
276
278
|
}
|
|
277
279
|
|
|
280
|
+
// legend AI
|
|
281
|
+
if (input.configData.legendAI?.url) {
|
|
282
|
+
this.legendAIUrl = LegendApplicationConfig.resolveAbsoluteUrl(
|
|
283
|
+
input.configData.legendAI.url,
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
278
287
|
// options
|
|
279
288
|
this.options = LegendStudioApplicationCoreOptions.create(
|
|
280
289
|
input.configData.extensions?.core ?? {},
|
|
@@ -45,7 +45,7 @@ export const queryAccessorSource = async (
|
|
|
45
45
|
editorStore.applicationStore.config.options.queryBuilderConfig,
|
|
46
46
|
editorStore.editorMode.getSourceInfo(),
|
|
47
47
|
);
|
|
48
|
-
queryBuilderState.changeAccessorOwner(element);
|
|
48
|
+
await queryBuilderState.changeAccessorOwner(element);
|
|
49
49
|
const compatibleRuntimes = getCompatibleRuntimesFromAccessorOwner(
|
|
50
50
|
element,
|
|
51
51
|
editorStore.graphManagerState,
|
|
@@ -847,7 +847,7 @@ export const LakehouseDataProductAccessPointEditor = observer(
|
|
|
847
847
|
applicationStore.config.options.queryBuilderConfig,
|
|
848
848
|
editorStore.editorMode.getSourceInfo(),
|
|
849
849
|
);
|
|
850
|
-
queryBuilderState.changeAccessorOwner(ingestDefinition);
|
|
850
|
+
await queryBuilderState.changeAccessorOwner(ingestDefinition);
|
|
851
851
|
const compatibleRuntimes = getCompatibleRuntimesFromAccessorOwner(
|
|
852
852
|
ingestDefinition,
|
|
853
853
|
editorStore.graphManagerState,
|
|
@@ -2232,9 +2232,6 @@ const AccessPointGroupTab = observer(
|
|
|
2232
2232
|
const DataProductSidebar = observer(
|
|
2233
2233
|
(props: { dataProductEditorState: DataProductEditorState }) => {
|
|
2234
2234
|
const { dataProductEditorState } = props;
|
|
2235
|
-
const showTestingTab =
|
|
2236
|
-
dataProductEditorState.editorStore.applicationStore.config.options
|
|
2237
|
-
.NonProductionFeatureFlag;
|
|
2238
2235
|
const sidebarTabs = [
|
|
2239
2236
|
{
|
|
2240
2237
|
label: DATA_PRODUCT_TAB.HOME,
|
|
@@ -2250,15 +2247,11 @@ const DataProductSidebar = observer(
|
|
|
2250
2247
|
title: 'Operational Metadata',
|
|
2251
2248
|
icon: <GearSuggestIcon />,
|
|
2252
2249
|
},
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
icon: <FlaskIcon />,
|
|
2259
|
-
},
|
|
2260
|
-
]
|
|
2261
|
-
: []),
|
|
2250
|
+
{
|
|
2251
|
+
label: DATA_PRODUCT_TAB.TESTING,
|
|
2252
|
+
title: 'Testing',
|
|
2253
|
+
icon: <FlaskIcon />,
|
|
2254
|
+
},
|
|
2262
2255
|
{
|
|
2263
2256
|
label: DATA_PRODUCT_TAB.SUPPORT,
|
|
2264
2257
|
icon: <QuestionCircleIcon />,
|
|
@@ -3367,8 +3360,6 @@ export const DataProductEditor = observer(() => {
|
|
|
3367
3360
|
const [showPreview, setShowPreview] = useState(false);
|
|
3368
3361
|
const [dataProductViewerState, setDataProductViewerState] =
|
|
3369
3362
|
useState<DataProductViewerState>();
|
|
3370
|
-
const showTestingTab =
|
|
3371
|
-
editorStore.applicationStore.config.options.NonProductionFeatureFlag;
|
|
3372
3363
|
|
|
3373
3364
|
const selectedActivity = dataProductEditorState.selectedTab;
|
|
3374
3365
|
const renderActivivtyBarTab = (): React.ReactNode => {
|
|
@@ -3402,12 +3393,12 @@ export const DataProductEditor = observer(() => {
|
|
|
3402
3393
|
/>
|
|
3403
3394
|
);
|
|
3404
3395
|
case DATA_PRODUCT_TAB.TESTING:
|
|
3405
|
-
return
|
|
3396
|
+
return (
|
|
3406
3397
|
<DataProductTestableEditor
|
|
3407
3398
|
dataProductEditorState={dataProductEditorState}
|
|
3408
3399
|
isReadOnly={isReadOnly}
|
|
3409
3400
|
/>
|
|
3410
|
-
)
|
|
3401
|
+
);
|
|
3411
3402
|
default:
|
|
3412
3403
|
return null;
|
|
3413
3404
|
}
|
|
@@ -3423,15 +3414,6 @@ export const DataProductEditor = observer(() => {
|
|
|
3423
3414
|
);
|
|
3424
3415
|
}, [dataProductEditorState]);
|
|
3425
3416
|
|
|
3426
|
-
useEffect(() => {
|
|
3427
|
-
if (
|
|
3428
|
-
!showTestingTab &&
|
|
3429
|
-
dataProductEditorState.selectedTab === DATA_PRODUCT_TAB.TESTING
|
|
3430
|
-
) {
|
|
3431
|
-
dataProductEditorState.setSelectedTab(DATA_PRODUCT_TAB.HOME);
|
|
3432
|
-
}
|
|
3433
|
-
}, [dataProductEditorState, showTestingTab]);
|
|
3434
|
-
|
|
3435
3417
|
useEffect(
|
|
3436
3418
|
() =>
|
|
3437
3419
|
autorun(
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
CustomSelectorInput,
|
|
36
36
|
PanelFormValidatedTextField,
|
|
37
37
|
PanelContentLists,
|
|
38
|
+
SparkleIcon,
|
|
38
39
|
} from '@finos/legend-art';
|
|
39
40
|
import { debounce, prettyCONSTName } from '@finos/legend-shared';
|
|
40
41
|
import { ServiceExecutionEditor } from './ServiceExecutionEditor.js';
|
|
@@ -72,6 +73,7 @@ import { flowResult } from 'mobx';
|
|
|
72
73
|
import { LEGEND_STUDIO_DOCUMENTATION_KEY } from '../../../../__lib__/LegendStudioDocumentation.js';
|
|
73
74
|
import { DocumentationLink } from '@finos/legend-lego/application';
|
|
74
75
|
import { ServicePostValidationsEditor } from './ServicePostValidationEditor.js';
|
|
76
|
+
import type { DSL_Service_LegendStudioApplicationPlugin_Extension } from '../../../../stores/extensions/DSL_Service_LegendStudioApplicationPlugin_Extension.js';
|
|
75
77
|
|
|
76
78
|
type UserOption = { label: string; value: string };
|
|
77
79
|
|
|
@@ -242,6 +244,43 @@ const ServiceGeneralEditor = observer(() => {
|
|
|
242
244
|
}
|
|
243
245
|
};
|
|
244
246
|
|
|
247
|
+
// AI documentation suggestion
|
|
248
|
+
const legendAIUrl = editorStore.applicationStore.config.legendAIUrl;
|
|
249
|
+
const aiDocSuggester = legendAIUrl
|
|
250
|
+
? editorStore.pluginManager
|
|
251
|
+
.getApplicationPlugins()
|
|
252
|
+
.map((p) =>
|
|
253
|
+
(
|
|
254
|
+
p as DSL_Service_LegendStudioApplicationPlugin_Extension
|
|
255
|
+
).getExtraServiceDocumentationAISuggester?.(),
|
|
256
|
+
)
|
|
257
|
+
.find(Boolean)
|
|
258
|
+
: undefined;
|
|
259
|
+
const [isSuggestingWithAI, setIsSuggestingWithAI] = useState(false);
|
|
260
|
+
const [aiDocSuggestion, setAIDocSuggestion] = useState<string | undefined>(
|
|
261
|
+
undefined,
|
|
262
|
+
);
|
|
263
|
+
const suggestDocumentationWithAI = async (): Promise<void> => {
|
|
264
|
+
if (!aiDocSuggester || !legendAIUrl) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
setIsSuggestingWithAI(true);
|
|
268
|
+
setAIDocSuggestion(undefined);
|
|
269
|
+
try {
|
|
270
|
+
const suggestion = await aiDocSuggester(service, legendAIUrl);
|
|
271
|
+
setAIDocSuggestion(suggestion);
|
|
272
|
+
} finally {
|
|
273
|
+
setIsSuggestingWithAI(false);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
const applyAIDocSuggestion = (): void => {
|
|
277
|
+
if (!aiDocSuggestion) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
service_setDocumentation(service, aiDocSuggestion);
|
|
281
|
+
setAIDocSuggestion(undefined);
|
|
282
|
+
};
|
|
283
|
+
|
|
245
284
|
const changeTitle: React.ChangeEventHandler<HTMLInputElement> = (event) => {
|
|
246
285
|
if (!isReadOnly) {
|
|
247
286
|
service_setTitle(service, event.target.value);
|
|
@@ -392,29 +431,76 @@ const ServiceGeneralEditor = observer(() => {
|
|
|
392
431
|
<div className="panel__content__form__section">
|
|
393
432
|
<div className="panel__content__form__section__header__label">
|
|
394
433
|
Documentation
|
|
434
|
+
{aiDocSuggestion && (
|
|
435
|
+
<span className="service-editor__ai-suggestion-badge">
|
|
436
|
+
<SparkleIcon />
|
|
437
|
+
AI Suggestion
|
|
438
|
+
</span>
|
|
439
|
+
)}
|
|
440
|
+
{aiDocSuggester && !aiDocSuggestion && (
|
|
441
|
+
<button
|
|
442
|
+
className="service-editor__ai-suggest-btn"
|
|
443
|
+
onClick={(): void => {
|
|
444
|
+
suggestDocumentationWithAI().catch(
|
|
445
|
+
editorStore.applicationStore.alertUnhandledError,
|
|
446
|
+
);
|
|
447
|
+
}}
|
|
448
|
+
disabled={isSuggestingWithAI || isReadOnly}
|
|
449
|
+
title="Use AI to suggest documentation for this service"
|
|
450
|
+
>
|
|
451
|
+
<SparkleIcon />
|
|
452
|
+
<span>
|
|
453
|
+
{isSuggestingWithAI ? 'Suggesting...' : 'Suggest with AI'}
|
|
454
|
+
</span>
|
|
455
|
+
</button>
|
|
456
|
+
)}
|
|
395
457
|
</div>
|
|
396
458
|
<div
|
|
397
459
|
className={clsx('panel__content__form__section__header__prompt', {
|
|
398
|
-
'service-editor__general__warning-prompt':
|
|
460
|
+
'service-editor__general__warning-prompt':
|
|
461
|
+
!service.documentation && !aiDocSuggestion,
|
|
399
462
|
})}
|
|
400
463
|
>
|
|
401
|
-
{!service.documentation && (
|
|
464
|
+
{!service.documentation && !aiDocSuggestion && (
|
|
402
465
|
<ErrorWarnIcon style={{ fontSize: '1.2rem' }} />
|
|
403
466
|
)}
|
|
404
467
|
{`Provide a brief description of the service's functionalities and usage`}
|
|
405
468
|
</div>
|
|
406
469
|
<textarea
|
|
407
|
-
className=
|
|
470
|
+
className={clsx(
|
|
471
|
+
'panel__content__form__section__textarea service-editor__documentation__input',
|
|
472
|
+
{ 'textarea--ai-suggested': Boolean(aiDocSuggestion) },
|
|
473
|
+
)}
|
|
408
474
|
spellCheck={false}
|
|
409
475
|
disabled={isReadOnly}
|
|
410
|
-
|
|
476
|
+
readOnly={Boolean(aiDocSuggestion)}
|
|
477
|
+
value={aiDocSuggestion ?? service.documentation}
|
|
411
478
|
onChange={changeDocumentation}
|
|
412
479
|
style={{
|
|
413
|
-
borderColor:
|
|
414
|
-
|
|
415
|
-
|
|
480
|
+
borderColor:
|
|
481
|
+
!service.documentation && !aiDocSuggestion
|
|
482
|
+
? 'var(--color-red-300)'
|
|
483
|
+
: undefined,
|
|
416
484
|
}}
|
|
417
485
|
/>
|
|
486
|
+
{aiDocSuggestion && (
|
|
487
|
+
<div className="service-editor__ai-suggestion__actions">
|
|
488
|
+
<button
|
|
489
|
+
className="btn btn--dark service-editor__ai-suggestion__apply-btn"
|
|
490
|
+
onClick={applyAIDocSuggestion}
|
|
491
|
+
title="Apply AI suggestion to documentation"
|
|
492
|
+
>
|
|
493
|
+
Apply Suggestion
|
|
494
|
+
</button>
|
|
495
|
+
<button
|
|
496
|
+
className="btn service-editor__ai-suggestion__dismiss-btn"
|
|
497
|
+
onClick={(): void => setAIDocSuggestion(undefined)}
|
|
498
|
+
title="Dismiss AI suggestion"
|
|
499
|
+
>
|
|
500
|
+
Dismiss
|
|
501
|
+
</button>
|
|
502
|
+
</div>
|
|
503
|
+
)}
|
|
418
504
|
</div>
|
|
419
505
|
</PanelForm>
|
|
420
506
|
<PanelForm>
|
|
@@ -322,11 +322,12 @@ export class DataProductElementTestDataState {
|
|
|
322
322
|
let columns: string[] = [];
|
|
323
323
|
try {
|
|
324
324
|
if (element instanceof IngestDefinition) {
|
|
325
|
-
const accessor =
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
325
|
+
const accessor =
|
|
326
|
+
await graphManager.createAccessorFromPackageableElement(
|
|
327
|
+
element,
|
|
328
|
+
graph,
|
|
329
|
+
{ schemaName: undefined, tableName: item.id },
|
|
330
|
+
);
|
|
330
331
|
if (accessor) {
|
|
331
332
|
columns = accessor.relationType.columns.map((c) => c.name);
|
|
332
333
|
}
|
|
@@ -575,10 +576,8 @@ export class DataProductTestSuiteState extends TestableTestSuiteEditorState {
|
|
|
575
576
|
rawLambdaForTest,
|
|
576
577
|
this.editorStore.graphManagerState.graph,
|
|
577
578
|
)) as Accessor[];
|
|
578
|
-
resolvedAccessors = all.filter(
|
|
579
|
-
(accessor)
|
|
580
|
-
isIngestOrDataProductAccessor(accessor) &&
|
|
581
|
-
accessor.parentElement.path !== this.testableState.dataProduct.path,
|
|
579
|
+
resolvedAccessors = all.filter((accessor) =>
|
|
580
|
+
isIngestOrDataProductAccessor(accessor),
|
|
582
581
|
);
|
|
583
582
|
}
|
|
584
583
|
|
|
@@ -833,10 +832,8 @@ export class DataProductTestableState {
|
|
|
833
832
|
rawLambdaForSuite,
|
|
834
833
|
this.editorStore.graphManagerState.graph,
|
|
835
834
|
)) as Accessor[];
|
|
836
|
-
const externalAccessors = all.filter(
|
|
837
|
-
(accessor)
|
|
838
|
-
isIngestOrDataProductAccessor(accessor) &&
|
|
839
|
-
accessor.parentElement.path !== dp.path,
|
|
835
|
+
const externalAccessors = all.filter((accessor) =>
|
|
836
|
+
isIngestOrDataProductAccessor(accessor),
|
|
840
837
|
);
|
|
841
838
|
const byElement = new Map<string, Accessor[]>();
|
|
842
839
|
for (const acc of externalAccessors) {
|
|
@@ -23,6 +23,7 @@ import type {
|
|
|
23
23
|
ObserverContext,
|
|
24
24
|
PostDeploymentProperties,
|
|
25
25
|
HostedService,
|
|
26
|
+
Service,
|
|
26
27
|
} from '@finos/legend-graph';
|
|
27
28
|
|
|
28
29
|
export type ServiceTestRuntimeConnectionBuilder = (
|
|
@@ -48,6 +49,11 @@ export type PostDeploymentActionTypeGetter = (
|
|
|
48
49
|
metamodel: PostDeploymentProperties,
|
|
49
50
|
) => string | undefined;
|
|
50
51
|
|
|
52
|
+
export type ServiceDocumentationAISuggester = (
|
|
53
|
+
service: Service,
|
|
54
|
+
legendAIUrl: string,
|
|
55
|
+
) => Promise<string>;
|
|
56
|
+
|
|
51
57
|
export interface DSL_Service_LegendStudioApplicationPlugin_Extension
|
|
52
58
|
extends DSL_LegendStudioApplicationPlugin_Extension {
|
|
53
59
|
/**
|
|
@@ -70,4 +76,8 @@ export interface DSL_Service_LegendStudioApplicationPlugin_Extension
|
|
|
70
76
|
* Get the list of the Classifiers for Post Deployment Actions.
|
|
71
77
|
*/
|
|
72
78
|
getExtraPostDeploymentActionClassifierGetters?(): PostDeploymentActionTypeGetter[];
|
|
79
|
+
/**
|
|
80
|
+
* Get a suggester function that generates AI-suggested documentation for a service.
|
|
81
|
+
*/
|
|
82
|
+
getExtraServiceDocumentationAISuggester?(): ServiceDocumentationAISuggester;
|
|
73
83
|
}
|