@finos/legend-application-studio 26.1.4 → 26.1.6

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 (23) hide show
  1. package/lib/__lib__/LegendStudioEvent.d.ts +1 -0
  2. package/lib/__lib__/LegendStudioEvent.d.ts.map +1 -1
  3. package/lib/__lib__/LegendStudioEvent.js +1 -0
  4. package/lib/__lib__/LegendStudioEvent.js.map +1 -1
  5. package/lib/components/editor/ActivityBar.d.ts.map +1 -1
  6. package/lib/components/editor/ActivityBar.js +5 -1
  7. package/lib/components/editor/ActivityBar.js.map +1 -1
  8. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestsEditor.d.ts +14 -1
  9. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestsEditor.d.ts.map +1 -1
  10. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestsEditor.js +44 -16
  11. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestsEditor.js.map +1 -1
  12. package/lib/index.css +2 -2
  13. package/lib/index.css.map +1 -1
  14. package/lib/package.json +1 -1
  15. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestEditorState.d.ts +9 -1
  16. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestEditorState.d.ts.map +1 -1
  17. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestEditorState.js +60 -3
  18. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestEditorState.js.map +1 -1
  19. package/package.json +5 -5
  20. package/src/__lib__/LegendStudioEvent.ts +1 -0
  21. package/src/components/editor/ActivityBar.tsx +12 -1
  22. package/src/components/editor/editor-group/service-editor/testable/ServiceTestsEditor.tsx +212 -41
  23. package/src/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestEditorState.ts +116 -2
@@ -34,14 +34,26 @@ import {
34
34
  Dialog,
35
35
  RefreshIcon,
36
36
  TimesIcon,
37
+ FilledWindowMaximizeIcon,
38
+ Modal,
39
+ ModalHeader,
40
+ ModalFooter,
41
+ ModalBody,
42
+ ModalTitle,
37
43
  } from '@finos/legend-art';
38
44
  import {
45
+ type Binding,
39
46
  type ValueSpecification,
47
+ PrimitiveInstanceValue,
40
48
  PrimitiveType,
41
49
  PureMultiExecution,
42
50
  } from '@finos/legend-graph';
43
- import { BasicValueSpecificationEditor } from '@finos/legend-query-builder';
44
51
  import {
52
+ BasicValueSpecificationEditor,
53
+ instanceValue_setValue,
54
+ } from '@finos/legend-query-builder';
55
+ import {
56
+ ContentType,
45
57
  filterByType,
46
58
  guaranteeNonNullable,
47
59
  prettyCONSTName,
@@ -71,6 +83,10 @@ import {
71
83
  TestAssertionEditor,
72
84
  TestAssertionItem,
73
85
  } from '../../testable/TestableSharedComponents.js';
86
+ import {
87
+ CODE_EDITOR_LANGUAGE,
88
+ CodeEditor,
89
+ } from '@finos/legend-lego/code-editor';
74
90
 
75
91
  export const NewParameterModal = observer(
76
92
  (props: { setupState: ServiceTestSetupState; isReadOnly: boolean }) => {
@@ -123,16 +139,106 @@ export const NewParameterModal = observer(
123
139
  },
124
140
  );
125
141
 
142
+ export const ExternalFormatParameterEditorModal = observer(
143
+ (props: {
144
+ paramState: ServiceValueSpecificationTestParameterState;
145
+ isReadOnly: boolean;
146
+ onClose: () => void;
147
+ updateParamValue: (val: string) => void;
148
+ bindingParamPair: {
149
+ binding: Binding;
150
+ param: string;
151
+ };
152
+ }) => {
153
+ const {
154
+ paramState,
155
+ isReadOnly,
156
+ onClose,
157
+ updateParamValue,
158
+ bindingParamPair,
159
+ } = props;
160
+ return (
161
+ <Dialog
162
+ open={true}
163
+ onClose={onClose}
164
+ classes={{ container: 'search-modal__container' }}
165
+ PaperProps={{ classes: { root: 'search-modal__inner-container' } }}
166
+ >
167
+ <Modal
168
+ darkMode={true}
169
+ className={clsx('editor-modal lambda-editor__popup__modal')}
170
+ >
171
+ <ModalHeader>
172
+ <ModalTitle title="Edit Parameter Value" />
173
+ </ModalHeader>
174
+ <ModalBody>
175
+ <div className="service-test-editor__setup__parameter__code-editor__container">
176
+ <div className="service-test-editor__setup__parameter__code-editor__container__content">
177
+ <CodeEditor
178
+ key={paramState.uuid}
179
+ inputValue={
180
+ (paramState.valueSpec as PrimitiveInstanceValue)
181
+ .values[0] as string
182
+ }
183
+ updateInput={updateParamValue}
184
+ isReadOnly={isReadOnly}
185
+ language={
186
+ bindingParamPair.binding.contentType ===
187
+ ContentType.APPLICATION_JSON.toString()
188
+ ? CODE_EDITOR_LANGUAGE.JSON
189
+ : CODE_EDITOR_LANGUAGE.TEXT
190
+ }
191
+ />
192
+ </div>
193
+ </div>
194
+ </ModalBody>
195
+ <ModalFooter>
196
+ <button className="btn btn--dark" onClick={onClose}>
197
+ Close
198
+ </button>
199
+ </ModalFooter>
200
+ </Modal>
201
+ </Dialog>
202
+ );
203
+ },
204
+ );
205
+
126
206
  const ServiceTestParameterEditor = observer(
127
207
  (props: {
128
208
  isReadOnly: boolean;
129
209
  paramState: ServiceValueSpecificationTestParameterState;
130
210
  serviceTestState: ServiceTestState;
211
+ bindingParamPair:
212
+ | {
213
+ binding: Binding;
214
+ param: string;
215
+ }
216
+ | undefined;
131
217
  }) => {
132
- const { serviceTestState, paramState, isReadOnly } = props;
218
+ const { serviceTestState, paramState, isReadOnly, bindingParamPair } =
219
+ props;
220
+ const [showPopUp, setShowPopUp] = useState(false);
133
221
  const setupState = serviceTestState.setupState;
134
222
  const paramIsRequired =
135
223
  paramState.varExpression.multiplicity.lowerBound > 0;
224
+ const type = bindingParamPair
225
+ ? bindingParamPair.binding.contentType
226
+ : paramState.varExpression.genericType?.value.rawType.name ?? 'unknown';
227
+
228
+ const openInPopUp = (): void => setShowPopUp(!showPopUp);
229
+ const closePopUp = (): void => setShowPopUp(false);
230
+ const updateParamValue = (val: string): void => {
231
+ if (paramState.valueSpec instanceof PrimitiveInstanceValue) {
232
+ instanceValue_setValue(
233
+ paramState.valueSpec,
234
+ val,
235
+ 0,
236
+ setupState.editorStore.changeDetectionState.observerContext,
237
+ );
238
+ paramState.updateValueSpecification(paramState.valueSpec);
239
+ }
240
+ };
241
+
136
242
  return (
137
243
  <div
138
244
  key={paramState.parameterValue.name}
@@ -143,48 +249,107 @@ const ServiceTestParameterEditor = observer(
143
249
  <button
144
250
  className={clsx('type-tree__node__type__label', {})}
145
251
  tabIndex={-1}
146
- title={
147
- paramState.varExpression.genericType?.value.rawType.name ?? ''
148
- }
252
+ title={type}
149
253
  >
150
- {paramState.varExpression.genericType?.value.rawType.name ??
151
- 'unknown'}
254
+ {type}
152
255
  </button>
153
256
  </div>
154
- <div className="service-test-editor__setup__parameter__value">
155
- <BasicValueSpecificationEditor
156
- valueSpecification={paramState.valueSpec}
157
- setValueSpecification={(val: ValueSpecification): void => {
158
- paramState.updateValueSpecification(val);
159
- }}
160
- graph={setupState.editorStore.graphManagerState.graph}
161
- obseverContext={
162
- setupState.editorStore.changeDetectionState.observerContext
163
- }
164
- typeCheckOption={{
165
- expectedType:
166
- paramState.varExpression.genericType?.value.rawType ??
167
- PrimitiveType.STRING,
168
- }}
169
- className="query-builder__parameters__value__editor"
170
- resetValue={(): void => {
171
- paramState.resetValueSpec();
172
- }}
173
- />
174
- <div className="service-test-editor__setup__parameter__value__actions">
175
- <button
176
- className="btn--icon btn--dark btn--sm"
177
- disabled={isReadOnly || paramIsRequired}
178
- onClick={(): void => setupState.removeParamValueState(paramState)}
179
- tabIndex={-1}
180
- title={
181
- paramIsRequired ? 'Parameter Required' : 'Remove Parameter'
182
- }
183
- >
184
- <TimesIcon />
185
- </button>
186
- </div>
187
- </div>
257
+ <>
258
+ {bindingParamPair ? (
259
+ <div className="service-test-editor__setup__parameter__code-editor">
260
+ <textarea
261
+ className="panel__content__form__section__textarea value-spec-editor__input"
262
+ spellCheck={false}
263
+ value={
264
+ (paramState.valueSpec as PrimitiveInstanceValue)
265
+ .values[0] as string
266
+ }
267
+ placeholder={
268
+ ((paramState.valueSpec as PrimitiveInstanceValue)
269
+ .values[0] as string) === ''
270
+ ? '(empty)'
271
+ : undefined
272
+ }
273
+ onChange={(event) => {
274
+ updateParamValue(event.target.value);
275
+ }}
276
+ />
277
+ {showPopUp && (
278
+ <ExternalFormatParameterEditorModal
279
+ paramState={paramState}
280
+ isReadOnly={isReadOnly}
281
+ onClose={closePopUp}
282
+ updateParamValue={updateParamValue}
283
+ bindingParamPair={bindingParamPair}
284
+ />
285
+ )}
286
+ <div className="service-test-editor__setup__parameter__value__actions">
287
+ <button
288
+ className={clsx(
289
+ 'service-test-editor__setup__parameter__code-editor__expand-btn',
290
+ )}
291
+ onClick={openInPopUp}
292
+ tabIndex={-1}
293
+ title="Open in a popup..."
294
+ >
295
+ <FilledWindowMaximizeIcon />
296
+ </button>
297
+ <button
298
+ className={clsx(
299
+ 'btn--icon btn--dark btn--sm service-test-editor__setup__parameter__code-editor__expand-btn',
300
+ )}
301
+ disabled={isReadOnly || paramIsRequired}
302
+ onClick={(): void =>
303
+ setupState.removeParamValueState(paramState)
304
+ }
305
+ tabIndex={-1}
306
+ title={
307
+ paramIsRequired ? 'Parameter Required' : 'Remove Parameter'
308
+ }
309
+ >
310
+ <TimesIcon />
311
+ </button>
312
+ </div>
313
+ </div>
314
+ ) : (
315
+ <div className="service-test-editor__setup__parameter__value">
316
+ <BasicValueSpecificationEditor
317
+ valueSpecification={paramState.valueSpec}
318
+ setValueSpecification={(val: ValueSpecification): void => {
319
+ paramState.updateValueSpecification(val);
320
+ }}
321
+ graph={setupState.editorStore.graphManagerState.graph}
322
+ obseverContext={
323
+ setupState.editorStore.changeDetectionState.observerContext
324
+ }
325
+ typeCheckOption={{
326
+ expectedType:
327
+ paramState.varExpression.genericType?.value.rawType ??
328
+ PrimitiveType.STRING,
329
+ }}
330
+ className="query-builder__parameters__value__editor"
331
+ resetValue={(): void => {
332
+ paramState.resetValueSpec();
333
+ }}
334
+ />
335
+ <div className="service-test-editor__setup__parameter__value__actions">
336
+ <button
337
+ className="btn--icon btn--dark btn--sm"
338
+ disabled={isReadOnly || paramIsRequired}
339
+ onClick={(): void =>
340
+ setupState.removeParamValueState(paramState)
341
+ }
342
+ tabIndex={-1}
343
+ title={
344
+ paramIsRequired ? 'Parameter Required' : 'Remove Parameter'
345
+ }
346
+ >
347
+ <TimesIcon />
348
+ </button>
349
+ </div>
350
+ </div>
351
+ )}
352
+ </>
188
353
  </div>
189
354
  );
190
355
  },
@@ -338,6 +503,12 @@ const ServiceTestSetupEditor = observer(
338
503
  isReadOnly={isReadOnly}
339
504
  paramState={paramState}
340
505
  serviceTestState={serviceTestState}
506
+ bindingParamPair={setupState
507
+ .getBindingWithParamFromQuery()
508
+ .find(
509
+ (pair) =>
510
+ pair.param === paramState.parameterValue.name,
511
+ )}
341
512
  />
342
513
  ))}
343
514
  </div>
@@ -15,6 +15,7 @@
15
15
  */
16
16
 
17
17
  import {
18
+ type Binding,
18
19
  type ServiceTest,
19
20
  type Service,
20
21
  type ValueSpecification,
@@ -23,6 +24,13 @@ import {
23
24
  buildLambdaVariableExpressions,
24
25
  VariableExpression,
25
26
  PureMultiExecution,
27
+ PackageableElementImplicitReference,
28
+ matchFunctionName,
29
+ isStubbed_RawLambda,
30
+ InstanceValue,
31
+ LambdaFunctionInstanceValue,
32
+ SimpleFunctionExpression,
33
+ CollectionInstanceValue,
26
34
  } from '@finos/legend-graph';
27
35
  import { action, flow, makeObservable, observable } from 'mobx';
28
36
  import { TestableTestEditorState } from '../../testable/TestableEditorState.js';
@@ -37,6 +45,7 @@ import {
37
45
  service_setSerializationFormat,
38
46
  } from '../../../../../graph-modifier/DSL_Service_GraphModifierHelper.js';
39
47
  import {
48
+ type PlainObject,
40
49
  assertErrorThrown,
41
50
  deleteEntry,
42
51
  filterByType,
@@ -44,10 +53,15 @@ import {
44
53
  isNonNullable,
45
54
  returnUndefOnError,
46
55
  uuid,
47
- type PlainObject,
56
+ getNullableFirstEntry,
57
+ LogEvent,
48
58
  } from '@finos/legend-shared';
49
59
  import type { EditorStore } from '../../../../EditorStore.js';
50
- import { generateVariableExpressionMockValue } from '@finos/legend-query-builder';
60
+ import {
61
+ QUERY_BUILDER_SUPPORTED_FUNCTIONS,
62
+ generateVariableExpressionMockValue,
63
+ } from '@finos/legend-query-builder';
64
+ import { LEGEND_STUDIO_APP_EVENT } from '../../../../../../__lib__/LegendStudioEvent.js';
51
65
 
52
66
  export enum SERIALIZATION_FORMAT {
53
67
  PURE = 'PURE',
@@ -180,6 +194,7 @@ export class ServiceTestSetupState {
180
194
  addServiceTestAssertKeys: action,
181
195
  syncWithQuery: action,
182
196
  removeParamValueState: action,
197
+ getBindingWithParamFromQuery: action,
183
198
  });
184
199
  this.parameterValueStates = this.buildParameterStates();
185
200
  }
@@ -221,6 +236,105 @@ export class ServiceTestSetupState {
221
236
  }));
222
237
  }
223
238
 
239
+ getBindingWithParamFromQuery(): {
240
+ binding: Binding;
241
+ param: string;
242
+ }[] {
243
+ const query =
244
+ this.testState.suiteState.testableState.serviceEditorState.serviceQuery;
245
+ if (query && !isStubbed_RawLambda(query)) {
246
+ // safely pass unsupported funtions when building ValueSpecification from Rawlambda
247
+ try {
248
+ const valueSpec =
249
+ this.editorStore.graphManagerState.graphManager.buildValueSpecification(
250
+ this.editorStore.graphManagerState.graphManager.serializeRawValueSpecification(
251
+ query,
252
+ ),
253
+ this.editorStore.graphManagerState.graph,
254
+ );
255
+
256
+ if (valueSpec instanceof LambdaFunctionInstanceValue) {
257
+ return this.getBindingWithParamRecursively(
258
+ valueSpec.values[0]?.expressionSequence[0],
259
+ );
260
+ }
261
+ } catch (error) {
262
+ this.editorStore.applicationStore.logService.error(
263
+ LogEvent.create(
264
+ LEGEND_STUDIO_APP_EVENT.TEST_DATA_GENERATION__SETUP__FAILURE,
265
+ ),
266
+ error,
267
+ );
268
+ }
269
+ }
270
+ return [];
271
+ }
272
+
273
+ getBindingWithParamRecursively(expression: ValueSpecification | undefined): {
274
+ binding: Binding;
275
+ param: string;
276
+ }[] {
277
+ let currentExpression = expression;
278
+ const res: {
279
+ binding: Binding;
280
+ param: string;
281
+ }[] = [];
282
+ // use if statement to safely scan service query without breaking the app
283
+ while (currentExpression instanceof SimpleFunctionExpression) {
284
+ if (
285
+ matchFunctionName(
286
+ currentExpression.functionName,
287
+ QUERY_BUILDER_SUPPORTED_FUNCTIONS.INTERNALIZE,
288
+ ) ||
289
+ matchFunctionName(
290
+ currentExpression.functionName,
291
+ QUERY_BUILDER_SUPPORTED_FUNCTIONS.GET_RUNTIME_WITH_MODEL_QUERY_CONNECTION,
292
+ )
293
+ ) {
294
+ if (currentExpression.parametersValues[1] instanceof InstanceValue) {
295
+ if (
296
+ currentExpression.parametersValues[1].values[0] instanceof
297
+ PackageableElementImplicitReference<Binding> &&
298
+ currentExpression.parametersValues[2] instanceof VariableExpression
299
+ ) {
300
+ res.push({
301
+ binding: currentExpression.parametersValues[1].values[0]
302
+ .value as Binding,
303
+ param: currentExpression.parametersValues[2].name,
304
+ });
305
+ }
306
+ }
307
+ currentExpression = currentExpression.parametersValues[1];
308
+ } else if (
309
+ matchFunctionName(
310
+ currentExpression.functionName,
311
+ QUERY_BUILDER_SUPPORTED_FUNCTIONS.FROM,
312
+ )
313
+ ) {
314
+ currentExpression = currentExpression.parametersValues[2];
315
+ } else if (
316
+ matchFunctionName(
317
+ currentExpression.functionName,
318
+ QUERY_BUILDER_SUPPORTED_FUNCTIONS.MERGERUNTIMES,
319
+ )
320
+ ) {
321
+ const collection = currentExpression.parametersValues[0];
322
+ if (collection instanceof CollectionInstanceValue) {
323
+ collection.values
324
+ .map((v) => this.getBindingWithParamRecursively(v))
325
+ .flat()
326
+ .map((p) => res.push(p));
327
+ }
328
+ currentExpression = collection;
329
+ } else {
330
+ currentExpression = getNullableFirstEntry(
331
+ currentExpression.parametersValues,
332
+ );
333
+ }
334
+ }
335
+ return res;
336
+ }
337
+
224
338
  addServiceTestAssertKeys(val: string[]): void {
225
339
  service_addAssertKeyForTest(this.testState.test, val);
226
340
  }