UNPKG

@finos/legend-extension-dsl-data-space

Version:
1,123 lines (1,075 loc) 42.1 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 { type QueryInfo, type AbstractPureGraphManager, type PureProtocolProcessorPlugin, type MappingModelCoverageAnalysisResult, type GraphManagerOperationReport, type FunctionAnalysisInfo, PureModel, V1_PureGraphManager, PureClientVersion, CoreModel, SystemModel, V1_Mapping, resolvePackagePathAndElementName, V1_PureModelContextType, V1_PackageableRuntime, V1_EngineRuntime, V1_Class, GRAPH_MANAGER_EVENT, V1_buildDatasetSpecification, V1_buildModelCoverageAnalysisResult, V1_deserializePackageableElement, QueryDataSpaceExecutionContextInfo, V1_RemoteEngine, LegendSDLC, PackageableElementPointerType, V1_PackageableElementPointer, V1_ConcreteFunctionDefinition, V1_buildFunctionInfoAnalysis, GraphBuilderError, V1_MappingAnalysisCoveragePartition, V1_deserializeDataspaceCoveragePartition, V1_MappingModelCoveragePartition, V1_getGenericTypeFullPath, V1_buildFullPath, } from '@finos/legend-graph'; import type { Entity, ProjectGAVCoordinates, StoredFileGeneration, } from '@finos/legend-storage'; import { type PlainObject, ActionState, assertErrorThrown, filterByType, guaranteeNonEmptyString, guaranteeNonNullable, guaranteeType, isNonNullable, LogEvent, uniq, } from '@finos/legend-shared'; import { DataSpaceSupportCombinedInfo, DataSpaceSupportEmail, } from '../../../../graph/metamodel/pure/model/packageableElements/dataSpace/DSL_DataSpace_DataSpace.js'; import { V1_DataSpace, V1_DataSpaceExecutionContext, V1_DataSpaceSupportCombinedInfo, V1_DataSpaceSupportEmail, V1_DataSpaceTemplateExecutable, } from '../../../../graph-manager/protocol/pure/v1/model/packageableElements/dataSpace/V1_DSL_DataSpace_DataSpace.js'; import { DataSpaceAnalysisResult, DataSpaceDiagramAnalysisResult, NormalizedDataSpaceDocumentationEntry, DataSpaceExecutableAnalysisResult, DataSpaceExecutableTDSResultColumn, DataSpaceExecutableTDSResult, DataSpaceExecutionContextAnalysisResult, DataSpaceServiceExecutableInfo, DataSpaceStereotypeInfo, DataSpaceTaggedValueInfo, DataSpaceClassDocumentationEntry, DataSpacePropertyDocumentationEntry, DataSpaceEnumerationDocumentationEntry, DataSpaceBasicDocumentationEntry, DataSpaceAssociationDocumentationEntry, DataSpaceMultiExecutionServiceExecutableInfo, DataSpaceMultiExecutionServiceKeyedExecutableInfo, DataSpaceTemplateExecutableInfo, DataSpaceFunctionPointerExecutableInfo, DataSpaceExecutionContextRuntimeMetadata, } from '../../../action/analytics/DataSpaceAnalysis.js'; import { DSL_DataSpace_PureGraphManagerExtension } from '../DSL_DataSpace_PureGraphManagerExtension.js'; import { type V1_DataSpaceAnalysisResult, V1_DataSpaceAssociationDocumentationEntry, V1_DataSpaceClassDocumentationEntry, V1_DataSpaceEnumerationDocumentationEntry, V1_DataSpaceExecutableTDSResult, V1_DataSpaceServiceExecutableInfo, V1_deserializeDataSpaceAnalysisResult, V1_DataSpaceMultiExecutionServiceExecutableInfo, V1_DataSpaceTemplateExecutableInfo, V1_DataSpaceFunctionPointerExecutableInfo, } from './engine/analytics/V1_DataSpaceAnalysis.js'; import { getDiagram } from '@finos/legend-extension-dsl-diagram/graph'; import { resolveVersion } from '@finos/legend-server-depot'; import { DATASPACE_ANALYTICS_FILE_NAME } from '../../../action/analytics/DataSpaceAnalysisHelper.js'; import { buildDataSpaceElements } from '../../../DSL_DataSpaceAnalyticsHelper.js'; import { DSL_DATASPACE_EVENT } from '../../../../__lib__/DSL_DataSpace_Event.js'; const ANALYZE_DATA_SPACE_TRACE = 'analyze data product'; const TEMPORARY__TDS_SAMPLE_VALUES__DELIMITER = '-- e.g.'; export class V1_DSL_DataSpace_PureGraphManagerExtension extends DSL_DataSpace_PureGraphManagerExtension { declare graphManager: V1_PureGraphManager; constructor(graphManager: AbstractPureGraphManager) { super(graphManager); this.graphManager = guaranteeType(graphManager, V1_PureGraphManager); } getSupportedProtocolVersion(): string { return PureClientVersion.V1_0_0; } getDataSpaceProtocolFromEntity(dataSpaceEntity: Entity): V1_DataSpace { return guaranteeType( V1_deserializePackageableElement( dataSpaceEntity.content, this.graphManager.pluginManager.getPureProtocolProcessorPlugins(), ), V1_DataSpace, ); } IsTemplateQueryIdValid(dataSpaceEntity: Entity, id: string): boolean { const dataSpaceProtocol = this.getDataSpaceProtocolFromEntity(dataSpaceEntity); return !dataSpaceProtocol.executables ?.filter(filterByType(V1_DataSpaceTemplateExecutable)) .map((et) => et.id) .includes(id); } async addNewExecutableToDataSpaceEntity( dataSpaceEntity: Entity, currentQuery: QueryInfo, executable: { id: string; title: string; description?: string; }, ): Promise<Entity> { const content = currentQuery.content; const rawLambda = await this.graphManager.engine.transformCodeToLambda(content); const dataSpaceProtocol = this.getDataSpaceProtocolFromEntity(dataSpaceEntity); const dataSpaceTemplateExecutable = new V1_DataSpaceTemplateExecutable(); dataSpaceTemplateExecutable.id = executable.id; dataSpaceTemplateExecutable.title = executable.title; dataSpaceTemplateExecutable.description = executable.description; const execContext = currentQuery.executionContext; if (execContext instanceof QueryDataSpaceExecutionContextInfo) { dataSpaceTemplateExecutable.executionContextKey = execContext.executionKey; } else if (currentQuery.mapping && currentQuery.runtime) { dataSpaceTemplateExecutable.executionContextKey = guaranteeNonNullable( dataSpaceProtocol.executionContexts.filter( (ec) => ec.mapping.path === currentQuery.mapping && ec.defaultRuntime.path === currentQuery.runtime, )[0]?.name, 'can`t find a corresponding executatin key based on query`s mapping and runtime in data product', ); } dataSpaceTemplateExecutable.query = rawLambda; dataSpaceProtocol.executables = dataSpaceProtocol.executables ? [...dataSpaceProtocol.executables, dataSpaceTemplateExecutable] : [dataSpaceTemplateExecutable]; return this.graphManager.elementProtocolToEntity(dataSpaceProtocol); } async analyzeDataSpace( dataSpacePath: string, entitiesRetriever: () => Promise<Entity[]>, cacheRetriever?: () => Promise<PlainObject<DataSpaceAnalysisResult>>, actionState?: ActionState, ): Promise<DataSpaceAnalysisResult> { const cacheResult = cacheRetriever ? await this.fetchDataSpaceAnalysisFromCache(cacheRetriever, actionState) : undefined; // TODO: improve abstraction so that we do not need to access the engine server client directly const engineClient = guaranteeType( this.graphManager.engine, V1_RemoteEngine, 'analyzeDataSpace is only supported by remote engine', ).getEngineServerClient(); let analysisResult: PlainObject<V1_DataSpaceAnalysisResult>; if (cacheResult) { analysisResult = cacheResult; } else { actionState?.setMessage('Fetching project entities and dependencies...'); const entities = await entitiesRetriever(); actionState?.setMessage('Analyzing data product...'); analysisResult = await engineClient.postWithTracing< PlainObject<V1_DataSpaceAnalysisResult> >( engineClient.getTraceData(ANALYZE_DATA_SPACE_TRACE), `${engineClient._pure()}/analytics/dataSpace/render`, { clientVersion: V1_PureGraphManager.DEV_PROTOCOL_VERSION, dataSpace: dataSpacePath, model: { _type: V1_PureModelContextType.DATA, elements: entities.map((entity) => entity.content), }, }, {}, undefined, undefined, { enableCompression: true }, ); } return this.buildDataSpaceAnalytics( analysisResult, this.graphManager.pluginManager.getPureProtocolProcessorPlugins(), ); } retrieveDataSpaceAnalysisFromFileGeneration = ( fileGeneration: StoredFileGeneration[], ): PlainObject<V1_DataSpaceAnalysisResult> | undefined => { const fileContent = fileGeneration.find((fileGen) => fileGen.file.path.includes(DATASPACE_ANALYTICS_FILE_NAME), )?.file.content; return fileContent ? JSON.parse(fileContent) : undefined; }; retrieveExecutionContextFromTemplateQueryId( dataSpaceAnalysisResult: PlainObject<V1_DataSpaceAnalysisResult>, templateQueryId: string, plugins: PureProtocolProcessorPlugin[], ): string | undefined { const analysisResult = V1_deserializeDataSpaceAnalysisResult( dataSpaceAnalysisResult, plugins, ); let execContext = undefined; const info = analysisResult.executables.find( (ex) => ex.info?.id === templateQueryId, )?.info; if (info) { execContext = info.executionContextKey ?? analysisResult.defaultExecutionContext; } return execContext; } async analyzeDataSpaceCoverage( dataSpacePath: string, entitiesWithClassifierRetriever: () => Promise< [PlainObject<Entity>[], PlainObject<Entity>[]] >, cacheRetriever?: () => Promise<PlainObject<StoredFileGeneration>[]>, actionState?: ActionState, graphReport?: GraphManagerOperationReport | undefined, pureGraph?: PureModel | undefined, executionContext?: string | undefined, mappingPath?: string | undefined, projectInfo?: ProjectGAVCoordinates, templateQueryId?: string, ): Promise<DataSpaceAnalysisResult> { // reterieve data product artifacts (more than one file) from cache const dataspaceArtifacts = cacheRetriever ? ( await this.fetchDataSpaceArtifactsFromCache( cacheRetriever, actionState, ) ).filter((artifact) => artifact.path === dataSpacePath) : undefined; let analysisResult: PlainObject<V1_DataSpaceAnalysisResult>; let cachedAnalysisResult; let mappingAnalysisCoveragePartition: V1_MappingAnalysisCoveragePartition[] = []; let mappingModelCoveragePartition: V1_MappingModelCoveragePartition[] = []; // build the data product analytics result from the file generation const result = this.retrieveDataSpaceAnalysisFromFileGeneration( dataspaceArtifacts as unknown as StoredFileGeneration[], ); if (result) { cachedAnalysisResult = V1_deserializeDataSpaceAnalysisResult( result, this.graphManager.pluginManager.getPureProtocolProcessorPlugins(), ); } // retrieve mapping analysis coverage partition and mapping model coverage partition const dataspaceCoveragePartition = dataspaceArtifacts?.map((artifact) => V1_deserializeDataspaceCoveragePartition(artifact.file as PlainObject), ); if (dataspaceCoveragePartition) { mappingAnalysisCoveragePartition = dataspaceCoveragePartition.filter( (partition) => partition instanceof V1_MappingAnalysisCoveragePartition, ); mappingModelCoveragePartition = dataspaceCoveragePartition.filter( (partition) => partition instanceof V1_MappingModelCoveragePartition, ); } // when trying to build a minimal graph, we need to make sure that there is associated mapping model coverage // for every mapping in the data product analytics result otherwise we will have to build the full graph if ( result && cachedAnalysisResult?.executionContexts.every( (e) => mappingModelCoveragePartition.find((p) => p.mapping === e.mapping) ?.model !== undefined, ) ) { analysisResult = result; } else { throw new GraphBuilderError( 'Fail to get a valid data product analytics json from metadata, start building full graph', ); } const plugins = this.graphManager.pluginManager.getPureProtocolProcessorPlugins(); return this.buildDataSpaceAnalytics( analysisResult, plugins, true, mappingAnalysisCoveragePartition, mappingModelCoveragePartition, graphReport, pureGraph, executionContext ? executionContext : templateQueryId ? this.retrieveExecutionContextFromTemplateQueryId( analysisResult, templateQueryId, plugins, ) : undefined, mappingPath, projectInfo, entitiesWithClassifierRetriever, ); } async retrieveDataSpaceAnalysisFromCache( cacheRetriever: () => Promise<PlainObject<DataSpaceAnalysisResult>>, actionState?: ActionState, ): Promise<DataSpaceAnalysisResult | undefined> { const cacheResult = await this.fetchDataSpaceAnalysisFromCache( cacheRetriever, actionState, ); return cacheResult ? this.buildDataSpaceAnalytics( cacheResult, this.graphManager.pluginManager.getPureProtocolProcessorPlugins(), ) : undefined; } private async fetchDataSpaceAnalysisFromCache( cacheRetriever: () => Promise<PlainObject<DataSpaceAnalysisResult>>, actionState?: ActionState, ): Promise<PlainObject<V1_DataSpaceAnalysisResult> | undefined> { let cacheResult: PlainObject<V1_DataSpaceAnalysisResult> | undefined; try { actionState?.setMessage( 'Fetching data product analysis result from cache...', ); cacheResult = await cacheRetriever(); } catch (error) { assertErrorThrown(error); this.graphManager.logService.warn( LogEvent.create(GRAPH_MANAGER_EVENT.CACHE_MANAGER_FAILURE), `Can't fetch data product analysis result cache: ${error.message}`, ); } return cacheResult; } private async fetchDataSpaceArtifactsFromCache( cacheRetriever: () => Promise<PlainObject<StoredFileGeneration>[]>, actionState?: ActionState, ): Promise<PlainObject<StoredFileGeneration>[]> { let cacheResult: PlainObject<StoredFileGeneration>[] = []; try { actionState?.setMessage('Fetching data product artifacts from cache...'); cacheResult = await cacheRetriever(); } catch (error) { assertErrorThrown(error); this.graphManager.logService.warn( LogEvent.create(GRAPH_MANAGER_EVENT.CACHE_MANAGER_FAILURE), `Can't fetch data product artifacts cache: ${error.message}`, ); } return cacheResult; } // build function analysis info by fetching functions within this project from metadata when building minimal graph async processFunctionForMinimalGraph( entitiesWithClassifierRetriever: () => Promise< [PlainObject<Entity>[], PlainObject<Entity>[]] >, graph: PureModel, dataSpaceAnalysisResult: DataSpaceAnalysisResult, plugins: PureProtocolProcessorPlugin[], ): Promise<void> { const [functionEntities, dependencyFunctionEntities]: [ PlainObject<Entity>[], PlainObject<Entity>[], ] = await entitiesWithClassifierRetriever(); const functionProtocols = functionEntities.map((func) => guaranteeType( V1_deserializePackageableElement( (func.entity as Entity).content, plugins, ), V1_ConcreteFunctionDefinition, ), ); const dependencyFunctionProtocols = dependencyFunctionEntities.map((func) => guaranteeType( V1_deserializePackageableElement( (func.entity as Entity).content, plugins, ), V1_ConcreteFunctionDefinition, ), ); const functionInfos = V1_buildFunctionInfoAnalysis( functionProtocols, graph, ); const dependencyFunctionInfos = V1_buildFunctionInfoAnalysis( dependencyFunctionProtocols, graph, ); if (functionInfos.length > 0) { const functionInfoMap = new Map<string, FunctionAnalysisInfo>(); functionInfos.forEach((funcInfo) => { functionInfoMap.set(funcInfo.functionPath, funcInfo); }); dataSpaceAnalysisResult.functionInfos = functionInfoMap; } if (dependencyFunctionInfos.length > 0) { const dependencyFunctionInfoMap = new Map<string, FunctionAnalysisInfo>(); dependencyFunctionInfos.forEach((funcInfo) => { dependencyFunctionInfoMap.set(funcInfo.functionPath, funcInfo); }); dataSpaceAnalysisResult.dependencyFunctionInfos = dependencyFunctionInfoMap; } } async buildDataSpaceAnalytics( analytics: PlainObject<V1_DataSpaceAnalysisResult>, plugins: PureProtocolProcessorPlugin[], buildMinimalGraph = false, mappingAnalysisCoveragePartition: V1_MappingAnalysisCoveragePartition[] = [], mappingModelCoveragePartition: V1_MappingModelCoveragePartition[] = [], graphReport?: GraphManagerOperationReport | undefined, pureGraph?: PureModel | undefined, executionContext?: string | undefined, mappingPath?: string | undefined, projectInfo?: ProjectGAVCoordinates, entitiesWithClassifierRetriever?: () => Promise< [PlainObject<Entity>[], PlainObject<Entity>[]] >, ): Promise<DataSpaceAnalysisResult> { const analysisResult = V1_deserializeDataSpaceAnalysisResult( analytics, plugins, ); // build map to quick access mapping analysis coverage partition and mapping model coverage partition const mappingAnalysisCoveragePartitionMap = new Map< string, V1_MappingAnalysisCoveragePartition >(); mappingAnalysisCoveragePartition.forEach((partition) => { mappingAnalysisCoveragePartitionMap.set(partition.mapping, partition); }); const mappingModelCoveragePartitionMap = new Map< string, V1_MappingModelCoveragePartition >(); mappingModelCoveragePartition.forEach((partition) => { mappingModelCoveragePartitionMap.set(partition.mapping, partition); }); const result = new DataSpaceAnalysisResult(); result.name = analysisResult.name; result.package = analysisResult.package; result.path = analysisResult.path; result.title = analysisResult.title; result.description = analysisResult.description; result.taggedValues = analysisResult.taggedValues.map((taggedValue) => { const info = new DataSpaceTaggedValueInfo(); info.profile = taggedValue.profile; info.tag = taggedValue.tag; info.value = taggedValue.value; return info; }); result.stereotypes = analysisResult.stereotypes.map((stereotype) => { const info = new DataSpaceStereotypeInfo(); info.profile = stereotype.profile; info.value = stereotype.value; return info; }); result.elements = analysisResult.elements; if (analysisResult.supportInfo) { if (analysisResult.supportInfo instanceof V1_DataSpaceSupportEmail) { const supportEmail = new DataSpaceSupportEmail(); supportEmail.documentationUrl = analysisResult.supportInfo.documentationUrl; supportEmail.address = guaranteeNonEmptyString( analysisResult.supportInfo.address, `Data product support email 'address' field is missing or empty`, ); result.supportInfo = supportEmail; } else if ( analysisResult.supportInfo instanceof V1_DataSpaceSupportCombinedInfo ) { const combinedInfo = new DataSpaceSupportCombinedInfo(); combinedInfo.documentationUrl = analysisResult.supportInfo.documentationUrl; combinedInfo.website = analysisResult.supportInfo.website; combinedInfo.faqUrl = analysisResult.supportInfo.faqUrl; combinedInfo.supportUrl = analysisResult.supportInfo.supportUrl; combinedInfo.emails = analysisResult.supportInfo.emails; result.supportInfo = combinedInfo; } // NOTE: we will relax the check and not throw here for unknown support info type } // indicate if the minimal graph building is successful let isMiniGraphSuccess = false; let graphEntities; let graph: PureModel; if (pureGraph) { graph = pureGraph; } else { // create an empty graph const extensionElementClasses = this.graphManager.pluginManager.getPureGraphPlugins(); const systemModel = new SystemModel(extensionElementClasses); const coreModel = new CoreModel(extensionElementClasses); await this.graphManager.buildSystem( coreModel, systemModel, ActionState.create(), {}, ); systemModel.initializeAutoImports(); graph = new PureModel( coreModel, systemModel, this.graphManager.pluginManager.getPureGraphPlugins(), ); } // Create dummy mappings and runtimes // TODO?: these stubbed mappings and runtimes are not really useful that useful, so either we should // simplify the model here or potentially refactor the backend analytics endpoint to return these as model const mappingModels = uniq( analysisResult.executionContexts.map((context) => context.mapping), ).map((path) => { const mapping = new V1_Mapping(); const [packagePath, name] = resolvePackagePathAndElementName(path); mapping.package = packagePath; mapping.name = name; return mapping; }); const runtimeModels = uniq( analysisResult.executionContexts .map((context) => context.defaultRuntime) .concat( analysisResult.executionContexts.flatMap( (val) => val.compatibleRuntimes, ), ), ).map((path) => { const runtime = new V1_PackageableRuntime(); const [packagePath, name] = resolvePackagePathAndElementName(path); runtime.package = packagePath; runtime.name = name; runtime.runtimeValue = new V1_EngineRuntime(); return runtime; }); // The DataSpace entity is excluded from AnalyticsResult.Json to reduce the JSON size // because all its information can be found in V1_DataSpaceAnalysisResult. // Therefore, we are building a simple v1_DataSpace entity based on V1_DataSpaceAnalysisResult. const dataspaceEntity = new V1_DataSpace(); dataspaceEntity.name = analysisResult.name; dataspaceEntity.package = analysisResult.package; dataspaceEntity.supportInfo = analysisResult.supportInfo; dataspaceEntity.executionContexts = analysisResult.executionContexts.map( (execContext) => { const contextProtocol = new V1_DataSpaceExecutionContext(); contextProtocol.name = execContext.name; contextProtocol.title = execContext.title; contextProtocol.description = execContext.description; contextProtocol.mapping = new V1_PackageableElementPointer( PackageableElementPointerType.MAPPING, execContext.mapping, ); contextProtocol.defaultRuntime = new V1_PackageableElementPointer( PackageableElementPointerType.RUNTIME, execContext.defaultRuntime, ); return contextProtocol; }, ); dataspaceEntity.defaultExecutionContext = analysisResult.defaultExecutionContext; dataspaceEntity.title = analysisResult.title; dataspaceEntity.description = analysisResult.description; // fetch the PMCD based on the resolved mapping path const resolvedMappingPath = mappingPath ?? analysisResult.executionContexts.find( (value) => value.name === (executionContext ?? analysisResult.defaultExecutionContext), )?.mapping; let pmcd; if (resolvedMappingPath) { const mappingModelCoverageAnalysisResult = mappingModelCoveragePartitionMap.get(resolvedMappingPath); pmcd = mappingModelCoverageAnalysisResult?.model; } // if there is a PMCD saved in metadata, we can build a minimal graph if (pmcd && projectInfo) { graphEntities = pmcd.elements .concat(mappingModels) .concat(runtimeModels) .concat(dataspaceEntity) // NOTE: if an element could be found in the graph already it means it comes from system // so we could rid of it .filter((el) => !graph.getNullableElement(el.path, false)) .map((el) => this.graphManager.elementProtocolToEntity(el)); await this.graphManager.buildGraph( graph, graphEntities, ActionState.create(), { origin: new LegendSDLC( projectInfo.groupId, projectInfo.artifactId, resolveVersion(projectInfo.versionId), ), }, graphReport, ); try { buildDataSpaceElements( graph, V1_buildFullPath(analysisResult.package, analysisResult.name), analysisResult.elements, ); } catch (error) { assertErrorThrown(error); this.graphManager.logService.warn( LogEvent.create(DSL_DATASPACE_EVENT.BUILD_ANALYTICS_RESULT), `Error building dataspace elements: ${error.message}`, ); } // build extra function analysis info if (entitiesWithClassifierRetriever) { try { await this.processFunctionForMinimalGraph( entitiesWithClassifierRetriever, graph, result, plugins, ); } catch { //do nothing } } isMiniGraphSuccess = true; } else { const elements = analysisResult.model.elements // NOTE: this is a temporary hack to fix a problem with data product analytics // where the classes for properties are not properly surveyed // We need to wait for the actual fix in backend to be merged and released // See https://github.com/finos/legend-engine/pull/836 .concat( uniq( analysisResult.model.elements.flatMap((element) => { if (element instanceof V1_Class) { return element.derivedProperties .map((prop) => V1_getGenericTypeFullPath(prop.returnGenericType), ) .concat( element.properties.map((prop) => V1_getGenericTypeFullPath(prop.genericType), ), ); } return []; }), ) // make sure to not include types already returned by the analysis .filter( (path) => !analysisResult.model.elements .map((el) => el.path) .includes(path), ) .map((path) => { const [pkgPath, name] = resolvePackagePathAndElementName(path); if (!pkgPath) { // exclude package-less elements (i.e. primitive types) return undefined; } const _class = new V1_Class(); _class.name = name; _class.package = pkgPath; return _class; }) .filter(isNonNullable), ) .concat(mappingModels) .concat(runtimeModels); const alreadyContainsDataspace = elements.find( (e) => e.path === dataspaceEntity.path, ); let allElements = elements; if (!alreadyContainsDataspace) { allElements = elements.concat(dataspaceEntity); } // prepare the model context data graphEntities = allElements // NOTE: if an element could be found in the graph already it means it comes from system // so we could rid of it .filter((el) => !graph.getNullableElement(el.path, false)) .map((el) => this.graphManager.elementProtocolToEntity(el)); await this.graphManager.buildGraph( graph, graphEntities, ActionState.create(), ); } const mappingToMappingCoverageResult = new Map< string, MappingModelCoverageAnalysisResult >(); if (analysisResult.mappingToMappingCoverageResult) { Array.from(analysisResult.mappingToMappingCoverageResult).forEach( ([key, value]) => { mappingToMappingCoverageResult.set( key, V1_buildModelCoverageAnalysisResult( value, this.graphManager, graph.getMapping(key), mappingModelCoveragePartitionMap.get(key)?.model, ), ); }, ); } mappingAnalysisCoveragePartition.forEach((partition) => { mappingToMappingCoverageResult.set( partition.mapping, V1_buildModelCoverageAnalysisResult( partition.analysisResult, this.graphManager, graph.getMapping(partition.mapping), mappingModelCoveragePartitionMap.get(partition.mapping)?.model, ), ); }); // execution context result.executionContextsIndex = new Map< string, DataSpaceExecutionContextAnalysisResult >(); analysisResult.executionContexts.forEach((context) => { const contextAnalysisResult = new DataSpaceExecutionContextAnalysisResult(); contextAnalysisResult.name = context.name; contextAnalysisResult.title = context.title; contextAnalysisResult.description = context.description; contextAnalysisResult.mapping = graph.getMapping(context.mapping); contextAnalysisResult.defaultRuntime = graph.getRuntime( context.defaultRuntime, ); if (context.runtimeMetadata) { const metadata = new DataSpaceExecutionContextRuntimeMetadata(); if (context.runtimeMetadata.storePath) { metadata.storePath = context.runtimeMetadata.storePath; } if (context.runtimeMetadata.connectionPath) { metadata.connectionPath = context.runtimeMetadata.connectionPath; } if (context.runtimeMetadata.connectionType) { metadata.connectionType = context.runtimeMetadata.connectionType; } contextAnalysisResult.runtimeMetadata = metadata; } // for handling deprecated mappingModelCoverageAnalysisResult if (context.mappingModelCoverageAnalysisResult) { mappingToMappingCoverageResult.set( context.mapping, V1_buildModelCoverageAnalysisResult( context.mappingModelCoverageAnalysisResult, this.graphManager, contextAnalysisResult.mapping, mappingModelCoveragePartitionMap.get(context.mapping)?.model, ), ); } contextAnalysisResult.compatibleRuntimes = context.compatibleRuntimes.map( (runtime) => graph.getRuntime(runtime), ); contextAnalysisResult.datasets = context.datasets.map((dataset) => V1_buildDatasetSpecification(dataset, plugins), ); result.executionContextsIndex.set( contextAnalysisResult.name, contextAnalysisResult, ); }); result.defaultExecutionContext = guaranteeNonNullable( result.executionContextsIndex.get(analysisResult.defaultExecutionContext), ); // elements documentation result.elementDocs = analysisResult.elementDocs.flatMap((docEntry) => { const entries: NormalizedDataSpaceDocumentationEntry[] = []; if (docEntry instanceof V1_DataSpaceClassDocumentationEntry) { const classData = new DataSpaceClassDocumentationEntry(); classData.name = docEntry.name; classData.docs = docEntry.docs; classData.path = docEntry.path; classData.milestoning = docEntry.milestoning; entries.push( new NormalizedDataSpaceDocumentationEntry( docEntry.name, docEntry.docs.join('\n').trim(), classData, classData, ), ); docEntry.properties.forEach((property) => { const propertyData = new DataSpacePropertyDocumentationEntry(); propertyData.name = property.name; propertyData.docs = property.docs; propertyData.type = property.type; propertyData.milestoning = property.milestoning; propertyData.multiplicity = property.multiplicity ? graph.getMultiplicity( property.multiplicity.lowerBound, property.multiplicity.upperBound, ) : undefined; classData.properties.push(propertyData); entries.push( new NormalizedDataSpaceDocumentationEntry( property.name, property.docs.join('\n').trim(), classData, propertyData, ), ); }); } else if ( docEntry instanceof V1_DataSpaceEnumerationDocumentationEntry ) { const enumerationData = new DataSpaceEnumerationDocumentationEntry(); enumerationData.name = docEntry.name; enumerationData.docs = docEntry.docs; enumerationData.path = docEntry.path; entries.push( new NormalizedDataSpaceDocumentationEntry( docEntry.name, docEntry.docs.join('\n').trim(), enumerationData, enumerationData, ), ); docEntry.enumValues.forEach((enumValue) => { const enumData = new DataSpaceBasicDocumentationEntry(); enumData.name = enumValue.name; enumData.docs = enumValue.docs; enumerationData.enumValues.push(enumData); entries.push( new NormalizedDataSpaceDocumentationEntry( enumValue.name, enumValue.docs.join('\n').trim(), enumerationData, enumData, ), ); }); } else if ( docEntry instanceof V1_DataSpaceAssociationDocumentationEntry ) { const associationData = new DataSpaceAssociationDocumentationEntry(); associationData.name = docEntry.name; associationData.docs = docEntry.docs; associationData.path = docEntry.path; entries.push( new NormalizedDataSpaceDocumentationEntry( docEntry.name, docEntry.docs.join('\n').trim(), associationData, associationData, ), ); docEntry.properties.forEach((property) => { const propertyData = new DataSpacePropertyDocumentationEntry(); propertyData.name = property.name; propertyData.docs = property.docs; propertyData.type = property.type; propertyData.milestoning = property.milestoning; propertyData.multiplicity = property.multiplicity ? graph.getMultiplicity( property.multiplicity.lowerBound, property.multiplicity.upperBound, ) : undefined; associationData.properties.push(propertyData); entries.push( new NormalizedDataSpaceDocumentationEntry( property.name, property.docs.join('\n'), associationData, propertyData, ), ); }); } return entries; }); // diagrams result.diagrams = ( buildMinimalGraph && isMiniGraphSuccess ? [] : analysisResult.diagrams ).map((diagramProtocol) => { const diagram = new DataSpaceDiagramAnalysisResult(); diagram.title = diagramProtocol.title; diagram.description = diagramProtocol.description; diagram.diagram = getDiagram(diagramProtocol.diagram, graph); return diagram; }); // executables result.executables = analysisResult.executables.map( (executableProtocol) => { const executable = new DataSpaceExecutableAnalysisResult(); executable.title = executableProtocol.title; executable.description = executableProtocol.description; if (executableProtocol.executable) { executable.executable = executableProtocol.executable; } if ( executableProtocol.info instanceof V1_DataSpaceTemplateExecutableInfo ) { const templateExecutableInfo = new DataSpaceTemplateExecutableInfo(); if (executableProtocol.info.id) { templateExecutableInfo.id = executableProtocol.info.id; } if (executableProtocol.info.executionContextKey) { templateExecutableInfo.executionContextKey = executableProtocol.info.executionContextKey; } templateExecutableInfo.query = executableProtocol.info.query; executable.info = templateExecutableInfo; } else if ( executableProtocol.info instanceof V1_DataSpaceFunctionPointerExecutableInfo ) { const templateExecutableInfo = new DataSpaceFunctionPointerExecutableInfo(); if (executableProtocol.info.id) { templateExecutableInfo.id = executableProtocol.info.id; } if (executableProtocol.info.executionContextKey) { templateExecutableInfo.executionContextKey = executableProtocol.info.executionContextKey; } templateExecutableInfo.function = executableProtocol.info.function; templateExecutableInfo.query = executableProtocol.info.query; executable.info = templateExecutableInfo; } else if ( executableProtocol.info instanceof V1_DataSpaceServiceExecutableInfo ) { const serviceExecutableInfo = new DataSpaceServiceExecutableInfo(); serviceExecutableInfo.query = executableProtocol.info.query; if (executableProtocol.info.id) { serviceExecutableInfo.id = executableProtocol.info.id; } if (executableProtocol.info.executionContextKey) { serviceExecutableInfo.executionContextKey = executableProtocol.info.executionContextKey; } serviceExecutableInfo.pattern = executableProtocol.info.pattern; serviceExecutableInfo.mapping = executableProtocol.info.mapping; serviceExecutableInfo.runtime = executableProtocol.info.runtime; executable.info = serviceExecutableInfo; serviceExecutableInfo.datasets = serviceExecutableInfo.datasets.map( (dataset) => V1_buildDatasetSpecification(dataset, plugins), ); } else if ( executableProtocol.info instanceof V1_DataSpaceMultiExecutionServiceExecutableInfo ) { const multiExecutionServiceExecutableInfo = new DataSpaceMultiExecutionServiceExecutableInfo(); multiExecutionServiceExecutableInfo.keyedExecutableInfos = executableProtocol.info.keyedExecutableInfos.map((info) => { const keyedExecutableInfo = new DataSpaceMultiExecutionServiceKeyedExecutableInfo(); keyedExecutableInfo.key = info.key; keyedExecutableInfo.mapping = info.mapping; keyedExecutableInfo.runtime = info.runtime; keyedExecutableInfo.datasets = info.datasets.map((dataset) => V1_buildDatasetSpecification(dataset, plugins), ); return keyedExecutableInfo; }); multiExecutionServiceExecutableInfo.query = executableProtocol.info.query; multiExecutionServiceExecutableInfo.pattern = executableProtocol.info.pattern; if (executableProtocol.info.id) { multiExecutionServiceExecutableInfo.id = executableProtocol.info.id; } if (executableProtocol.info.executionContextKey) { multiExecutionServiceExecutableInfo.executionContextKey = executableProtocol.info.executionContextKey; } executable.info = multiExecutionServiceExecutableInfo; } if ( executableProtocol.result instanceof V1_DataSpaceExecutableTDSResult ) { const tdsResult = new DataSpaceExecutableTDSResult(); tdsResult.columns = executableProtocol.result.columns.map( (tdsColumn) => { const column = new DataSpaceExecutableTDSResultColumn(); column.name = tdsColumn.name; column.type = tdsColumn.type; column.relationalType = tdsColumn.relationalType; column.documentation = tdsColumn.documentation; if ( tdsColumn.documentation?.includes( TEMPORARY__TDS_SAMPLE_VALUES__DELIMITER, ) ) { column.documentation = tdsColumn.documentation .substring( 0, tdsColumn.documentation.indexOf( TEMPORARY__TDS_SAMPLE_VALUES__DELIMITER, ), ) .trim(); column.sampleValues = tdsColumn.documentation .substring( tdsColumn.documentation.indexOf( TEMPORARY__TDS_SAMPLE_VALUES__DELIMITER, ) + TEMPORARY__TDS_SAMPLE_VALUES__DELIMITER.length, ) .trim(); } return column; }, ); executable.result = tdsResult; } return executable; }, ); result.mappingToMappingCoverageResult = mappingToMappingCoverageResult; return result; } }