UNPKG

@finos/legend-shared

Version:
142 lines 6.85 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 { pruneNullValues } from '../CommonUtils.js'; import { custom, SKIP, deserialize, serialize, } from 'serializr'; export class SerializationFactory { schema; deserializeNullAsUndefined; constructor(schema, options) { this.schema = schema; this.deserializeNullAsUndefined = Boolean(options?.deserializeNullAsUndefined); } toJson(val) { return serialize(this.schema, val); } fromJson(val) { return deserialize(this.schema, this.deserializeNullAsUndefined ? pruneNullValues(val) : val); } } export const usingModelSchema = (schema) => custom((value) => (value === undefined ? SKIP : serialize(schema, value)), (value) => deserialize(schema, value)); // NOTE: we need these methods because `map()` of `serializr` tries to smartly // determines if it should produce object or ES6 Map but we always want ES6 Map, // so we would use this function export const deserializeMap = (val, itemDeserializer) => { const result = new Map(); Object.keys(val).forEach((key) => result.set(key, itemDeserializer(val[key]))); return result; }; export const serializeMap = (val, itemSerializer) => { const result = {}; val.forEach((v, key) => { result[key] = itemSerializer(v); }); return result; }; export const usingConstantValueSchema = (value) => custom(() => value, () => value); /** * This is the idiomatic usage pattern for `optional(custom(...))`. * * `optional` only affects serialization so we must make sure to check * if the value is `undefined` or not, if yes, serialize, else, return `undefined` * which will be processed by `optional(...)` as `SKIP`. * * `optional` does not affect deserialization, however, as `undefined` values * are automatically skipped * See https://github.com/mobxjs/serializr/issues/73#issuecomment-535641545 */ export const optionalCustom = ( // eslint-disable-next-line @typescript-eslint/no-explicit-any serializer, // eslint-disable-next-line @typescript-eslint/no-explicit-any deserializer, additionalArgs) => custom((val) => (val ? serializer(val) : SKIP), (val) => (val ? deserializer(val) : SKIP), additionalArgs); export const optionalCustomUsingModelSchema = (schema) => custom((val) => (val ? serialize(schema, val) : SKIP), (val) => (val ? deserialize(schema, val) : SKIP)); export const deserializeArray = (values, itemDeserializer, options) => { if (Array.isArray(values)) { return values.map(itemDeserializer); } return options?.skipIfEmpty ? SKIP : []; }; export const serializeArray = (values, itemSerializer, options) => { let forceReturnEmptyInTest = false; // NOTE: this block is meant for test, `webpack` will tree-shake it // so we never reach inside, else we would get error about `process is not defined` as we're // in browser environment // eslint-disable-next-line no-process-env if (process.env.NODE_ENV === 'test') { forceReturnEmptyInTest = Boolean(options?.INTERNAL__forceReturnEmptyInTest) && // TODO: when we distribute engine-roundtrip tests to different test groups, we should // remove this condition and clean up test data accordingly. // eslint-disable-next-line no-process-env process.env.TEST_GROUP === 'engine-roundtrip'; } if (Array.isArray(values)) { return values.length ? values.map((value) => itemSerializer(value)) : forceReturnEmptyInTest ? [] : options?.skipIfEmpty ? SKIP : []; } return forceReturnEmptyInTest ? [] : SKIP; }; /** * This is the idiomatic usage pattern for serialization of optional list of objects. * * Notice our particular usage of `serializeArray` and `deserializeArray` that is deisnged * for testing and accounting for logic mismatches between servers and studio */ export const customListWithSchema = (schema, options) => custom((values) => serializeArray(values, (value) => serialize(schema, value), { skipIfEmpty: true, INTERNAL__forceReturnEmptyInTest: options?.INTERNAL__forceReturnEmptyInTest, }), (values) => deserializeArray(values, (value) => deserialize(schema, value), { skipIfEmpty: false, })); export const customList = (serializer, deserializer, options) => custom((values) => serializeArray(values, (value) => serializer(value), { skipIfEmpty: true, INTERNAL__forceReturnEmptyInTest: options?.INTERNAL__forceReturnEmptyInTest, }), (values) => deserializeArray(values, (value) => deserializer(value), { skipIfEmpty: false, })); export const customEquivalentList = (options) => customList((value) => value, (value) => value, options); export const optionalCustomListWithSchema = (schema, options) => optionalCustom((values) => serializeArray(values, (value) => serialize(schema, value), { skipIfEmpty: true, INTERNAL__forceReturnEmptyInTest: options?.INTERNAL__forceReturnEmptyInTest, }), (values) => deserializeArray(values, (value) => deserialize(schema, value), { skipIfEmpty: false, })); export const optionalCustomList = (serializer, deserializer, options) => optionalCustom((values) => serializeArray(values, (value) => serializer(value), { skipIfEmpty: true, INTERNAL__forceReturnEmptyInTest: options?.INTERNAL__forceReturnEmptyInTest, }), (values) => deserializeArray(values, (value) => deserializer(value), { skipIfEmpty: false, })); /** * NOTE: this is a workaround for `serializr` to avoid the magic extension mechanism provided * by `createModelSchema`, where depending on the order schemas are defined, if the schema of the * super class is specified first, when we serialize subclasses, we would get fields of the order * of fields from the super classes first, followed by fields from the subclasses, not the order * specified in the subclass's schema. * * See https://github.com/mobxjs/serializr/issues/179 */ export const TEMPORARY__disableModelSchemaExtensionMechanism = (schema) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment schema.extends = undefined; return schema; }; //# sourceMappingURL=SerializationUtils.js.map