UNPKG

@finos/legend-application-studio

Version:
319 lines 14.7 kB
/** * Copyright (c) 2020-present, Goldman Sachs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { ExternalFormatData, RelationalCSVData, ConnectionTestData, EqualToJson, DEFAULT_TEST_ASSERTION_PREFIX, RelationalCSVDataTable, getAllIdentifiedConnectionsFromRuntime, ModelStoreData, ModelEmbeddedData, PackageableElementExplicitReference, TestExecuted, TestExecutionStatus, isStubbed_RawLambda, SimpleFunctionExpression, InstanceValue, CollectionInstanceValue, matchFunctionName, PackageableElementImplicitReference, VariableExpression, PrimitiveInstanceValue, PrimitiveType, LambdaFunctionInstanceValue, DataElement, RelationElementsData, RelationElement, RelationRowTestData, } from '@finos/legend-graph'; import { assertTrue, ContentType, generateEnumerableNameFromToken, guaranteeNonEmptyString, guaranteeType, isNonNullable, LogEvent, returnUndefOnError, UnsupportedOperationError, uuid, } from '@finos/legend-shared'; import { EmbeddedDataType } from '../editor-state/ExternalFormatState.js'; import { createMockDataForMappingElementSource } from './MockDataUtils.js'; import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '@finos/legend-query-builder'; import { LEGEND_STUDIO_APP_EVENT } from '../../../__lib__/LegendStudioEvent.js'; export const DEFAULT_TEST_ASSERTION_ID = 'assertion_1'; export const DEFAULT_TEST_ID = 'test_1'; export const validateTestableId = (id, possibleIds) => { // undefined in this case means user has yet to write anything so we shouldnt show any error message if (id === undefined) { return undefined; } if (!id) { return 'ID is required'; } else if (id.includes(' ')) { return `ID can't contain spaces`; } else if (possibleIds?.includes(id)) { return `ID '${id}' already exists`; } return undefined; }; export const createBareExternalFormat = (contentType, content) => { const data = new ExternalFormatData(); data.contentType = contentType ?? ContentType.APPLICATION_JSON; data.data = content ?? ''; return data; }; export const createDefaultEqualToJSONTestAssertion = (id) => { const xt = createBareExternalFormat(undefined, '{}'); const assertion = new EqualToJson(); assertion.expected = xt; assertion.id = id ?? uuid(); return assertion; }; export const createEmbeddedDataFromClass = (_class, editorStore) => { const _json = createMockDataForMappingElementSource(_class, editorStore); const data = createBareExternalFormat(); data.data = _json; return data; }; export const createBareModelStoreData = (_class, editorStore) => { const embeddedData = createEmbeddedDataFromClass(_class, editorStore); const modelStoreData = new ModelStoreData(); const modelData = new ModelEmbeddedData(); modelData.data = embeddedData; modelData.model = PackageableElementExplicitReference.create(_class); modelStoreData.modelData = [modelData]; return modelStoreData; }; export class EmbeddedDataCreatorFromEmbeddedData { editorStore; constructor(editorStore) { this.editorStore = editorStore; } visit_EmbeddedData(data) { const extraEmbeddedDataCloners = this.editorStore.pluginManager .getApplicationPlugins() .flatMap((plugin) => plugin.getExtraEmbeddedDataCloners?.() ?? []); for (const creator of extraEmbeddedDataCloners) { const embeddedData = creator(data); if (embeddedData) { return embeddedData; } } throw new UnsupportedOperationError(`Unsupported embedded data${data.toString()}`); } visit_INTERNAL__UnknownEmbeddedData(data) { throw new Error('Method not implemented.'); } visit_ExternalFormatData(data) { const val = new ExternalFormatData(); val.contentType = data.contentType; val.data = data.data; return val; } visit_RelationElementsData(data) { const val = new RelationElementsData(); val.relationElements = data.relationElements.map((relationElement) => { const v = new RelationElement(); v.columns = relationElement.columns; v.paths = relationElement.paths; v.rows = relationElement.rows.map((row) => { const r = new RelationRowTestData(); r.values = row.values; return r; }); return v; }); return val; } visit_ModelStoreData(data) { const val = new ModelStoreData(); val.modelData = data.modelData ?.map((e) => { if (e instanceof ModelEmbeddedData) { const v = new ModelEmbeddedData(); v.model = PackageableElementExplicitReference.create(e.model.value); v.data = e.data.accept_EmbeddedDataVisitor(new EmbeddedDataCreatorFromEmbeddedData(this.editorStore)); return v; } return undefined; }) .filter(isNonNullable); return val; } visit_DataElementReference(data) { const datElement = guaranteeType(data.dataElement.value, DataElement); return datElement.data.accept_EmbeddedDataVisitor(new EmbeddedDataCreatorFromEmbeddedData(this.editorStore)); } visit_RelationalCSVData(data) { const val = new RelationalCSVData(); return val; } } // NOTE: this will all move to `engine` once engine support generating test data for all connections // Throws if unable to generate test data export class TEMPORARY__EmbeddedDataConnectionVisitor { editorStore; constructor(editorStore) { this.editorStore = editorStore; } visit_Connection(connection) { throw new UnsupportedOperationError(); } visit_INTERNAL__UnknownConnection(connection) { throw new UnsupportedOperationError(); } visit_ConnectionPointer(connection) { const packageableConnection = connection.packageableConnection.value.connectionValue; return packageableConnection.accept_ConnectionVisitor(this); } visit_ModelChainConnection(connection) { throw new UnsupportedOperationError(); } visit_JsonModelConnection(connection) { return createEmbeddedDataFromClass(connection.class.value, this.editorStore); } visit_XmlModelConnection(connection) { throw new UnsupportedOperationError(); } visit_FlatDataConnection(connection) { throw new UnsupportedOperationError(); } visit_RelationalDatabaseConnection(connection) { throw new UnsupportedOperationError(); } } export class EmbeddedDataConnectionTypeVisitor { editorStore; constructor(editorStore) { this.editorStore = editorStore; } visit_Connection(connection) { const extraEmbeddedDataCreator = this.editorStore.pluginManager .getApplicationPlugins() .flatMap((plugin) => plugin.getExtraEmbeddedDataTypeFromConnectionMatchers?.() ?? []); for (const creator of extraEmbeddedDataCreator) { const embeddedData = creator(connection); if (embeddedData) { return embeddedData; } } return EmbeddedDataType.EXTERNAL_FORMAT_DATA; } visit_INTERNAL__UnknownConnection(connection) { return EmbeddedDataType.EXTERNAL_FORMAT_DATA; } visit_ConnectionPointer(connection) { const packageableConnection = connection.packageableConnection.value.connectionValue; return packageableConnection.accept_ConnectionVisitor(this); } visit_ModelChainConnection(connection) { return EmbeddedDataType.EXTERNAL_FORMAT_DATA; } visit_JsonModelConnection(connection) { return EmbeddedDataType.EXTERNAL_FORMAT_DATA; } visit_XmlModelConnection(connection) { return EmbeddedDataType.EXTERNAL_FORMAT_DATA; } visit_FlatDataConnection(connection) { return EmbeddedDataType.EXTERNAL_FORMAT_DATA; } visit_RelationalDatabaseConnection(connection) { return EmbeddedDataType.RELATIONAL_CSV; } } export const initializeConnectionDataFromRuntime = (runtime, editorStore) => { const identifiedConnections = getAllIdentifiedConnectionsFromRuntime(runtime); return identifiedConnections .map((identifiedConnection) => { const connection = identifiedConnection.connection; const embeddedData = returnUndefOnError(() => connection.accept_ConnectionVisitor(new TEMPORARY__EmbeddedDataConnectionVisitor(editorStore))); if (embeddedData) { const connectionTestData = new ConnectionTestData(); connectionTestData.connectionId = identifiedConnection.id; connectionTestData.testData = embeddedData; return connectionTestData; } return undefined; }) .filter(isNonNullable); }; export const createEmptyEqualToJsonAssertion = (test) => { const assert = new EqualToJson(); assert.id = generateEnumerableNameFromToken(test.assertions.map((a) => a.id), DEFAULT_TEST_ASSERTION_PREFIX); assert.expected = createBareExternalFormat(); assert.parentTest = test; return assert; }; // Temproary as engine should return an embedded data type export const TEMPORARY__createRelationalDataFromCSV = (val) => { const NEW_LINE = '\n'; const data = new RelationalCSVData(); const separator = `${NEW_LINE}-----${NEW_LINE}`; const lineBreak = /\r?\n/; const tables = val .split(separator) .filter((e) => !(e === NEW_LINE || e === '\r' || e === '')); tables.forEach((tableData) => { const tableInfo = tableData.split(lineBreak); assertTrue(tableInfo.length >= 2, 'Table and Schema Name required from test data'); const table = new RelationalCSVDataTable(); table.schema = guaranteeNonEmptyString(tableInfo.shift()); table.table = guaranteeNonEmptyString(tableInfo.shift()); table.values = tableInfo.join(NEW_LINE) + NEW_LINE; data.tables.push(table); }); return data; }; // test result export const isTestPassing = (testResult) => testResult instanceof TestExecuted && testResult.testExecutionStatus === TestExecutionStatus.PASS; export const getContentTypeWithParamRecursively = (expression) => { let currentExpression = expression; const res = []; // use if statement to safely scan service query without breaking the app while (currentExpression instanceof SimpleFunctionExpression) { if (matchFunctionName(currentExpression.functionName, QUERY_BUILDER_SUPPORTED_FUNCTIONS.INTERNALIZE) || matchFunctionName(currentExpression.functionName, QUERY_BUILDER_SUPPORTED_FUNCTIONS.GET_RUNTIME_WITH_MODEL_QUERY_CONNECTION)) { if (currentExpression.parametersValues[1] instanceof InstanceValue) { if (currentExpression.parametersValues[1].values[0] instanceof PackageableElementImplicitReference && currentExpression.parametersValues[2] instanceof VariableExpression) { res.push({ contentType: currentExpression.parametersValues[1].values[0].value.contentType, param: currentExpression.parametersValues[2].name, }); } else if (matchFunctionName(currentExpression.functionName, QUERY_BUILDER_SUPPORTED_FUNCTIONS.GET_RUNTIME_WITH_MODEL_QUERY_CONNECTION) && currentExpression.parametersValues[1] instanceof PrimitiveInstanceValue && currentExpression.parametersValues[1].genericType.value.rawType === PrimitiveType.STRING && currentExpression.parametersValues[2] instanceof VariableExpression) { res.push({ contentType: currentExpression.parametersValues[1] .values[0], param: currentExpression.parametersValues[2].name, }); } } currentExpression = currentExpression.parametersValues[1]; } else if (matchFunctionName(currentExpression.functionName, QUERY_BUILDER_SUPPORTED_FUNCTIONS.FROM)) { currentExpression = currentExpression.parametersValues[2]; } else if (matchFunctionName(currentExpression.functionName, QUERY_BUILDER_SUPPORTED_FUNCTIONS.MERGERUNTIMES)) { const collection = currentExpression.parametersValues[0]; if (collection instanceof CollectionInstanceValue) { collection.values .map((v) => getContentTypeWithParamRecursively(v)) .flat() .map((p) => res.push(p)); } currentExpression = collection; } else { currentExpression = currentExpression.parametersValues[0]; } } return res; }; export const getContentTypeWithParamFromQuery = (query, editorStore) => { if (query && !isStubbed_RawLambda(query)) { // safely pass unsupported funtions when building ValueSpecification from Rawlambda try { const valueSpec = editorStore.graphManagerState.graphManager.buildValueSpecification(editorStore.graphManagerState.graphManager.serializeRawValueSpecification(query), editorStore.graphManagerState.graph); if (valueSpec instanceof LambdaFunctionInstanceValue) { return getContentTypeWithParamRecursively(valueSpec.values[0]?.expressionSequence.find((exp) => exp instanceof SimpleFunctionExpression && (matchFunctionName(exp.functionName, QUERY_BUILDER_SUPPORTED_FUNCTIONS.SERIALIZE) || matchFunctionName(exp.functionName, QUERY_BUILDER_SUPPORTED_FUNCTIONS.EXTERNALIZE)))); } } catch (error) { editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.SERVICE_TEST_SETUP_FAILURE), error); } } return []; }; //# sourceMappingURL=TestableUtils.js.map