@gooddata/gooddata-js
Version:
GoodData JavaScript SDK
169 lines (144 loc) • 5.29 kB
text/typescript
// (C) 2007-2020 GoodData Corporation
import { VisualizationObject, IVisualizationWidget } from "@gooddata/typings";
import isEmpty from "lodash/isEmpty";
import omit from "lodash/omit";
import isArray from "lodash/isArray";
import isObject from "lodash/isObject";
import isString from "lodash/isString";
import stringify from "json-stable-stringify";
import { v4 as uuid } from "uuid";
import { IProperties } from "./interfaces";
import { isUri } from "./DataLayer/helpers/uri";
/*
* Helpers
*/
const getReferenceValue = (id: string, references: VisualizationObject.IReferenceItems) => references[id];
const getReferenceId = (value: string, references: VisualizationObject.IReferenceItems) =>
Object.keys(references).find(id => references[id] === value);
type IdGenerator = () => string;
const defaultIdGenerator: IdGenerator = () => uuid().replace(/-/g, "");
type StringTransformation = (value: string) => string;
/**
* Recursively traverses the object and tries to apply a conversion to every string value
*/
const traverse = (obj: any, convert: StringTransformation): any => {
if (isArray(obj)) {
return obj.map(a => traverse(a, convert));
} else if (isObject(obj)) {
const object = obj as Record<string, any>;
return Object.keys(object).reduce((result: any, key: string) => {
result[key] = traverse(object[key], convert);
return result;
}, {} as any);
} else if (isString(obj)) {
return convert(obj);
} else {
return obj;
}
};
interface IConversionResult {
convertedProperties: IProperties;
convertedReferences: VisualizationObject.IReferenceItems;
}
type ConversionFunction = (
originalProperties: IProperties,
originalReferences: VisualizationObject.IReferenceItems,
idGenerator: IdGenerator,
) => IConversionResult;
export type IObjectWithProperties = VisualizationObject.IVisualizationObject | IVisualizationWidget;
export type ReferenceConverter = (
mdObject: IObjectWithProperties,
idGenerator?: IdGenerator,
) => IObjectWithProperties;
const createConverter = (conversionFunction: ConversionFunction): ReferenceConverter => <
T extends IObjectWithProperties
>(
mdObject: T,
idGenerator: IdGenerator = defaultIdGenerator,
): T => {
const { content } = mdObject;
if (!content) {
return mdObject;
}
const { properties } = content;
if (!properties) {
return mdObject;
}
// prepare result objects
const originalProperties: IProperties = JSON.parse(properties);
const originalReferences = content.references || {};
const { convertedProperties, convertedReferences } = conversionFunction(
originalProperties,
originalReferences,
idGenerator,
);
// set the new properties and references
const referencesProp = isEmpty(convertedReferences) ? undefined : { references: convertedReferences };
const result: T = {
...mdObject,
content: {
...omit(mdObject.content, "references"),
properties: stringify(convertedProperties),
...referencesProp,
},
};
return result;
};
/*
* Conversion from References to URIs
*/
const convertReferenceToUri = (
references: VisualizationObject.IReferenceItems,
): StringTransformation => value => getReferenceValue(value, references) || value;
/**
* Converts reference based values to actual URIs
*
* @param mdObject The object to convert properties of
* @param [idGenerator=uuid] Function that returns unique ids
*/
export const convertReferencesToUris = createConverter((originalProperties, originalReferences) => {
const convertedProperties = traverse(originalProperties, convertReferenceToUri(originalReferences));
return {
convertedProperties,
convertedReferences: originalReferences,
};
});
/*
* Conversion from URIs to References
*/
const createUriToReferenceConverter = (
originalReferences: VisualizationObject.IReferenceItems,
idGenerator: IdGenerator,
) => {
const convertedReferences: VisualizationObject.IReferenceItems = {};
return {
convertedReferences,
conversion: (value: string) => {
if (!isUri(value)) {
return value;
}
const id =
getReferenceId(value, originalReferences) || // try to reuse original references
getReferenceId(value, convertedReferences) || // or use already converted new references
idGenerator(); // or get a completely new id
convertedReferences[id] = value;
return id;
},
};
};
/**
* Converts URIs to reference based values
*
* @param mdObject The object to convert properties of
* @param [idGenerator=uuid] Function that returns unique ids
*/
export const convertUrisToReferences = createConverter(
(originalProperties, originalReferences, idGenerator) => {
const converter = createUriToReferenceConverter(originalReferences, idGenerator);
const convertedProperties = traverse(originalProperties, converter.conversion);
return {
convertedProperties,
convertedReferences: converter.convertedReferences,
};
},
);