@finos/legend-graph
Version:
Legend graph and graph manager
267 lines (251 loc) • 10.1 kB
text/typescript
/**
* 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 {
list,
createModelSchema,
primitive,
custom,
optional,
serialize,
deserialize,
object,
} from 'serializr';
import {
type PlainObject,
usingConstantValueSchema,
UnsupportedOperationError,
assertErrorThrown,
usingModelSchema,
} from '@finos/legend-shared';
import { V1_PureModelContextData } from '../../model/context/V1_PureModelContextData.js';
import { V1_PureModelContextPointer } from '../../model/context/V1_PureModelContextPointer.js';
import type { V1_PackageableElement } from '../../model/packageableElements/V1_PackageableElement.js';
import {
V1_serializePackageableElement,
V1_deserializePackageableElement,
} from '../../transformation/pureProtocol/V1_PackageableElementSerialization.js';
import { V1_PureModelContextComposite } from '../../model/context/V1_PureModelContextComposite.js';
import { V1_Protocol } from '../../model/V1_Protocol.js';
import { V1_LegendSDLC } from '../../model/context/V1_SDLC.js';
import type { V1_PureModelContext } from '../../model/context/V1_PureModelContext.js';
import { V1_packageableElementPointerModelSchema } from '../../transformation/pureProtocol/serializationHelpers/V1_CoreSerializationHelper.js';
import type { PureProtocolProcessorPlugin } from '../../../PureProtocolProcessorPlugin.js';
import type { Entity } from '@finos/legend-storage';
import { GraphDataDeserializationError } from '../../../../../../graph-manager/GraphManagerUtils.js';
import { V1_PureModelContextText } from '../../model/context/V1_PureModelContextText.js';
import type {
ClassifierPathMappingMap,
SubtypeInfo,
} from '../../../../../action/protocol/ProtocolInfo.js';
enum V1_SDLCType {
ALLOY = 'alloy',
}
export enum V1_PureModelContextType {
DATA = 'data',
POINTER = 'pointer',
COMPOSITE = 'composite',
TEXT = 'text',
}
export const V1_entitiesToPureModelContextData = async (
entities: Entity[] | undefined,
graph: V1_PureModelContextData,
plugins: PureProtocolProcessorPlugin[],
subtypeInfo?: SubtypeInfo | undefined,
classifierPathMappingMap?: ClassifierPathMappingMap | undefined,
/**
* FIXME: to be deleted when most users have migrated to using full function signature as function name
* Currently, SDLC store many functions in legacy form (entity path does
* not contain full function signature). However, since they store function
* entity in text, when they parse the content to return JSON for entity
* content, the content is then updated to have proper `name` for function
* entities, this means that there's now a mismatch in the path constructed
* from entity content and the entity path, which is a contract that SDLC
* should maintain but currently not because of this change
* See https://github.com/finos/legend-sdlc/pull/515
*
* For that reason, during this migration, we want to respect entity path
* instead of the path constructed from entity content to properly
* reflect the renaming of function in local changes.
*/
TEMPORARY__entityPathIndex?: Map<string, string>,
): Promise<void> => {
try {
if (entities?.length) {
const entityToElement = (entity: Entity): V1_PackageableElement => {
const element = V1_deserializePackageableElement(
entity.content,
plugins,
subtypeInfo,
classifierPathMappingMap,
entity.classifierPath,
);
TEMPORARY__entityPathIndex?.set(element.path, entity.path);
return element;
};
graph.elements = await Promise.all<V1_PackageableElement>(
entities.map(
(e) =>
new Promise((resolve, reject) =>
setTimeout(() => {
try {
resolve(
// NOTE: here we skip the check for classifier path, so there could be cases
// where the classifier path is different from the actua element protocol path
// we might need to do validation here. This can happen when the classifier
// path is changed in the backend. If we are to check for this, we might consider
// not throwing error but quitely print out warnings about elements that would not
// be built.
entityToElement(e),
);
} catch (error) {
assertErrorThrown(error);
reject(error);
}
}, 0),
),
),
);
}
} catch (error) {
assertErrorThrown(error);
// wrap all de-serializer error so we can handle them downstream
throw new GraphDataDeserializationError(error);
}
};
export const V1_legendSDLCSerializationModelSchema = createModelSchema(
V1_LegendSDLC,
{
_type: usingConstantValueSchema(V1_SDLCType.ALLOY),
baseVersion: optional(primitive()),
version: primitive(),
groupId: primitive(),
artifactId: primitive(),
packageableElementPointers: list(
usingModelSchema(V1_packageableElementPointerModelSchema),
),
},
);
const V1_pureModelContextTextSchema = createModelSchema(
V1_PureModelContextText,
{
_type: usingConstantValueSchema(V1_PureModelContextType.TEXT),
serializer: usingModelSchema(V1_Protocol.serialization.schema),
code: optional(primitive()),
},
);
const V1_pureModelContextPointerModelSchema = createModelSchema(
V1_PureModelContextPointer,
{
_type: usingConstantValueSchema(V1_PureModelContextType.POINTER),
serializer: optional(usingModelSchema(V1_Protocol.serialization.schema)),
sdlcInfo: usingModelSchema(V1_legendSDLCSerializationModelSchema),
},
);
const V1_pureModelContextCompositeModelSchema = createModelSchema(
V1_PureModelContextComposite,
{
_type: usingConstantValueSchema(V1_PureModelContextType.COMPOSITE),
serializer: usingModelSchema(V1_Protocol.serialization.schema),
// TODO: use `V1_pureModelContextDataPropSchema`
data: object(V1_PureModelContextData),
pointer: usingModelSchema(V1_pureModelContextPointerModelSchema),
},
);
export const V1_setupPureModelContextDataSerialization = (
plugins: PureProtocolProcessorPlugin[],
subtypeInfo?: SubtypeInfo | undefined,
classifierPathMappingMap?: ClassifierPathMappingMap | undefined,
): void => {
createModelSchema(V1_PureModelContextData, {
_type: usingConstantValueSchema(V1_PureModelContextType.DATA),
origin: usingModelSchema(V1_pureModelContextPointerModelSchema),
elements: list(
custom(
(element: V1_PackageableElement) =>
V1_serializePackageableElement(element, plugins),
(element: PlainObject<V1_PackageableElement>) =>
V1_deserializePackageableElement(
element,
plugins,
subtypeInfo,
classifierPathMappingMap,
),
),
),
serializer: usingModelSchema(V1_Protocol.serialization.schema),
});
};
export const V1_deserializePureModelContextData = (
json: PlainObject<V1_PureModelContextData>,
): V1_PureModelContextData => deserialize(V1_PureModelContextData, json);
export const V1_serializePureModelContextData = (
protocol: V1_PureModelContextData,
): PlainObject<V1_PureModelContextData> =>
serialize(V1_PureModelContextData, protocol);
export const V1_serializePureModelContext = (
pureModelContext: V1_PureModelContext,
): PlainObject<V1_PureModelContext> => {
if (pureModelContext instanceof V1_PureModelContextPointer) {
return serialize(V1_pureModelContextPointerModelSchema, pureModelContext);
} else if (pureModelContext instanceof V1_PureModelContextData) {
const rawPMCD = V1_serializePureModelContextData(pureModelContext);
if (pureModelContext.INTERNAL__rawDependencyEntities?.length) {
rawPMCD.elements = [
...(rawPMCD as { elements: PlainObject[] }).elements,
...pureModelContext.INTERNAL__rawDependencyEntities.map(
(e) => e.content,
),
];
}
return rawPMCD;
} else if (pureModelContext instanceof V1_PureModelContextComposite) {
return serialize(V1_pureModelContextCompositeModelSchema, pureModelContext);
} else if (pureModelContext instanceof V1_PureModelContextText) {
return serialize(V1_pureModelContextTextSchema, pureModelContext);
}
throw new UnsupportedOperationError(
`Can't serialize Pure model context`,
pureModelContext,
);
};
export const V1_deserializePureModelContext = (
json: PlainObject<V1_PureModelContext>,
): V1_PureModelContext => {
switch (json._type) {
case V1_PureModelContextType.POINTER:
return deserialize(V1_pureModelContextPointerModelSchema, json);
case V1_PureModelContextType.DATA:
return V1_deserializePureModelContextData(json);
case V1_PureModelContextType.COMPOSITE:
return deserialize(V1_pureModelContextCompositeModelSchema, json);
case V1_PureModelContextType.TEXT:
return deserialize(V1_pureModelContextTextSchema, json);
default:
throw new UnsupportedOperationError(
`Can't deserialize Pure model context`,
json,
);
}
};
export const V1_pureModelContextDataPropSchema = custom(
(val: V1_PureModelContextData) => V1_serializePureModelContextData(val),
(val: PlainObject<V1_PureModelContextData>) =>
V1_deserializePureModelContextData(val),
);
export const V1_pureModelContextPropSchema = custom(
(val: V1_PureModelContext) => V1_serializePureModelContext(val),
(val) => V1_deserializePureModelContext(val),
);