@firecms/core
Version:
Awesome Firebase/Firestore-based headless open-source CMS
182 lines (166 loc) • 7.3 kB
text/typescript
import {
EntityCollection,
MapProperty,
ModifyCollectionProps,
PropertiesOrBuilders,
Property,
PropertyOrBuilder
} from "../types";
import { mergeDeep } from "./objects";
import { sortProperties } from "./collections";
import { isPropertyBuilder } from "./entities";
function applyModifyFunction(modifyCollection: ((props: ModifyCollectionProps) => (EntityCollection | void)) | undefined,
collection: EntityCollection,
parentPaths: string[]) {
if (modifyCollection) {
const modified = modifyCollection({
collection,
parentPaths
});
const resCollection = modified ?? collection;
if (resCollection.subcollections) {
resCollection.subcollections = resCollection.subcollections.map((subcollection) => {
return applyModifyFunction(modifyCollection, subcollection, [...parentPaths, collection.path]);
});
}
return resCollection;
} else {
return collection;
}
}
/**
*
*/
export function joinCollectionLists(targetCollections: EntityCollection[],
sourceCollections: EntityCollection[] | undefined,
parentPaths: string[] = [],
modifyCollection?: (props: ModifyCollectionProps) => EntityCollection | void): EntityCollection[] {
// merge collections that are in both lists
const updatedCollections = (sourceCollections ?? [])
.map((sourceCol) => {
const targetCol = targetCollections?.find((collection) => {
return collection.id === sourceCol.id;
// return collection.path === codedCollection.path || collection.id && codedCollection.id;
});
if (!targetCol) {
return applyModifyFunction(modifyCollection, sourceCol, parentPaths);
} else {
return mergeCollection(targetCol, sourceCol, parentPaths, modifyCollection);
}
});
const sourceCollectionIds = updatedCollections.map(c => c.id);
// fetched collections that are not in the source collections
const resultStoredCollections = targetCollections
.filter((col) => {
return !sourceCollectionIds.includes(col.id);
})
.map((col) => {
if (modifyCollection) {
return applyModifyFunction(modifyCollection, col, parentPaths);
} else {
return col;
}
});
return [...updatedCollections, ...resultStoredCollections];
}
/**
*
*/
export function mergeCollection(target: EntityCollection,
source: EntityCollection,
parentPaths: string[] = [],
modifyCollection?: (props: ModifyCollectionProps) => EntityCollection | void
): EntityCollection {
const subcollectionsMerged = joinCollectionLists(
target?.subcollections ?? [],
source?.subcollections ?? [],
[...parentPaths, target.path],
modifyCollection
);
const propertiesMerged: PropertiesOrBuilders = { ...target.properties } as PropertiesOrBuilders;
Object.keys(source.properties).forEach((key) => {
const property = target.properties[key] as Property;
if (property)
propertiesMerged[key] = mergePropertyOrBuilder(property, source.properties[key] as PropertyOrBuilder);
else
propertiesMerged[key] = source.properties[key] as PropertyOrBuilder;
});
const mergedCollection = mergeDeep(target, source, true);
const targetPropertiesOrder = getCollectionKeys(target);
const sourcePropertiesOrder = getCollectionKeys(source);
const mergedPropertiesOrder = [...new Set([...sourcePropertiesOrder, ...targetPropertiesOrder])];
const mergedEntityViews = [...new Set([...(target.entityViews ?? []), ...(source.entityViews ?? [])])];
const mergedEntityActions = [...new Set([...(target.entityActions ?? []), ...(source.entityActions ?? [])])];
let resultCollection: EntityCollection = {
...mergedCollection,
subcollections: subcollectionsMerged,
properties: sortProperties(propertiesMerged, mergedPropertiesOrder),
propertiesOrder: mergedPropertiesOrder,
entityViews: mergedEntityViews,
entityActions: mergedEntityActions,
};
if (modifyCollection) {
const modifiedCollection = modifyCollection({
collection: resultCollection,
parentPaths
});
if (modifiedCollection)
resultCollection = modifiedCollection;
}
// @ts-ignore
resultCollection["merged"] = true;
return resultCollection
}
function mergePropertyOrBuilder(target: PropertyOrBuilder, source: PropertyOrBuilder): PropertyOrBuilder {
if (!source) return target;
if (!target) return source;
if (isPropertyBuilder(source)) {
return source;
} else if (isPropertyBuilder(target)) {
return target;
} else {
const mergedProperty = mergeDeep(target, source);
const targetEditable = target.editable === undefined ? true : Boolean(target.editable);
const sourceEditable = source.editable === undefined ? true : Boolean(source.editable);
if (source.dataType === "map" && source.properties) {
const targetProperties = ("properties" in target ? target.properties : {}) as PropertiesOrBuilders;
const sourceProperties = ("properties" in source ? source.properties : {}) as PropertiesOrBuilders;
const targetPropertiesOrder = "propertiesOrder" in target && target.propertiesOrder ? target.propertiesOrder : Object.keys(targetProperties);
const sourcePropertiesOrder = "propertiesOrder" in source && source.propertiesOrder ? source.propertiesOrder : ("properties" in source ? Object.keys(source.properties) : []);
const mergedPropertiesOrder = [...new Set([...targetPropertiesOrder, ...sourcePropertiesOrder])];
const mergedProperties: PropertiesOrBuilders = { ...targetProperties } as PropertiesOrBuilders;
Object.keys(source.properties).forEach((key) => {
const property = targetProperties[key] as Property;
if (property)
mergedProperties[key] = mergePropertyOrBuilder(property, sourceProperties[key] as PropertyOrBuilder);
});
return {
...mergedProperty,
editable: targetEditable && sourceEditable,
properties: mergedProperties,
propertiesOrder: mergedPropertiesOrder
} as MapProperty;
}
return {
...mergedProperty,
editable: targetEditable && sourceEditable
};
}
}
function getCollectionKeys(collection: EntityCollection) {
if (collection.propertiesOrder && collection.propertiesOrder.length > 0) {
const propertiesOrder = collection.propertiesOrder;
if (collection.additionalFields) {
collection.additionalFields.forEach((field) => {
if (!propertiesOrder.includes(field.key)) {
propertiesOrder.push(field.key);
}
});
}
return propertiesOrder;
}
return [
...Object.keys(collection.properties),
...(collection.additionalFields ?? [])?.map(f => f.key)
];
}