@directus/api
Version:
Directus is a real-time API and App dashboard for managing SQL database content
138 lines (137 loc) • 7.99 kB
JavaScript
import deepDiff from 'deep-diff';
import { DiffKind } from '@directus/types';
import { sanitizeCollection, sanitizeField, sanitizeRelation, sanitizeSystemField } from './sanitize-schema.js';
export function getSnapshotDiff(current, after) {
const diffedSnapshot = {
collections: [
...current.collections.map((currentCollection) => {
const afterCollection = after.collections.find((afterCollection) => afterCollection.collection === currentCollection.collection);
const afterCollectionSanitized = afterCollection ? sanitizeCollection(afterCollection) : undefined;
return {
collection: currentCollection.collection,
diff: deepDiff.diff(sanitizeCollection(currentCollection), afterCollectionSanitized),
};
}),
...after.collections
.filter((afterCollection) => {
const currentCollection = current.collections.find((currentCollection) => currentCollection.collection === afterCollection.collection);
return !!currentCollection === false;
})
.map((afterCollection) => ({
collection: afterCollection.collection,
diff: deepDiff.diff(undefined, sanitizeCollection(afterCollection)),
})),
].filter((obj) => Array.isArray(obj.diff)),
fields: [
...current.fields.map((currentField) => {
const afterField = after.fields.find((afterField) => afterField.collection === currentField.collection && afterField.field === currentField.field);
const isAutoIncrementPrimaryKey = !!currentField.schema?.is_primary_key && !!currentField.schema?.has_auto_increment;
// Changing to/from alias fields should delete the current field
if (afterField &&
currentField.type !== afterField.type &&
(currentField.type === 'alias' || afterField.type === 'alias')) {
return {
collection: currentField.collection,
field: currentField.field,
diff: deepDiff.diff(sanitizeField(currentField, isAutoIncrementPrimaryKey), undefined),
};
}
const afterFieldSanitized = afterField ? sanitizeField(afterField, isAutoIncrementPrimaryKey) : undefined;
return {
collection: currentField.collection,
field: currentField.field,
diff: deepDiff.diff(sanitizeField(currentField, isAutoIncrementPrimaryKey), afterFieldSanitized),
};
}),
...after.fields
.filter((afterField) => {
let currentField = current.fields.find((currentField) => currentField.collection === afterField.collection && afterField.field === currentField.field);
// Changing to/from alias fields should create the new field
if (currentField &&
currentField.type !== afterField.type &&
(currentField.type === 'alias' || afterField.type === 'alias')) {
currentField = undefined;
}
return !!currentField === false;
})
.map((afterField) => ({
collection: afterField.collection,
field: afterField.field,
diff: deepDiff.diff(undefined, sanitizeField(afterField)),
})),
].filter((obj) => Array.isArray(obj.diff)),
systemFields: [
...(current.systemFields ?? []).map((currentSystemField) => {
const afterSystemField = (after.systemFields ?? []).find((afterSystemField) => afterSystemField.collection === currentSystemField.collection &&
afterSystemField.field === currentSystemField.field);
const afterSystemFieldSanitized = afterSystemField
? sanitizeSystemField(afterSystemField)
: invertIndexed(currentSystemField);
return {
collection: currentSystemField.collection,
field: currentSystemField.field,
diff: deepDiff.diff(sanitizeSystemField(currentSystemField), afterSystemFieldSanitized),
};
}),
...(after.systemFields ?? [])
.filter((afterSystemField) => {
if (!afterSystemField.schema.is_indexed)
return false;
const currentSystemField = (current.systemFields ?? []).find((currentSystemField) => currentSystemField.collection === afterSystemField.collection &&
afterSystemField.field === currentSystemField.field);
return Boolean(currentSystemField) === false;
})
.map((afterSystemField) => {
const currentSystemField = (current.systemFields ?? []).find((currentSystemField) => currentSystemField.collection === afterSystemField.collection &&
currentSystemField.field === afterSystemField.field);
const currentSystemFieldSanitized = currentSystemField
? sanitizeSystemField(currentSystemField)
: invertIndexed(afterSystemField);
return {
collection: afterSystemField.collection,
field: afterSystemField.field,
diff: deepDiff.diff(currentSystemFieldSanitized, sanitizeSystemField(afterSystemField)),
};
}),
].filter((obj) => Array.isArray(obj.diff)),
relations: [
...current.relations.map((currentRelation) => {
const afterRelation = after.relations.find((afterRelation) => afterRelation.collection === currentRelation.collection && afterRelation.field === currentRelation.field);
const afterRelationSanitized = afterRelation ? sanitizeRelation(afterRelation) : undefined;
return {
collection: currentRelation.collection,
field: currentRelation.field,
related_collection: currentRelation.related_collection,
diff: deepDiff.diff(sanitizeRelation(currentRelation), afterRelationSanitized),
};
}),
...after.relations
.filter((afterRelation) => {
const currentRelation = current.relations.find((currentRelation) => currentRelation.collection === afterRelation.collection && afterRelation.field === currentRelation.field);
return !!currentRelation === false;
})
.map((afterRelation) => ({
collection: afterRelation.collection,
field: afterRelation.field,
related_collection: afterRelation.related_collection,
diff: deepDiff.diff(undefined, sanitizeRelation(afterRelation)),
})),
].filter((obj) => Array.isArray(obj.diff)),
};
/**
* When you delete a collection, we don't have to individually drop all the fields/relations as well
*/
const deletedCollections = diffedSnapshot.collections
.filter((collection) => collection.diff?.[0]?.kind === DiffKind.DELETE)
.map(({ collection }) => collection);
diffedSnapshot.fields = diffedSnapshot.fields.filter((field) => deletedCollections.includes(field.collection) === false);
diffedSnapshot.relations = diffedSnapshot.relations.filter((relation) => deletedCollections.includes(relation.collection) === false);
return diffedSnapshot;
}
function invertIndexed(field) {
const newSchema = { ...field.schema };
if ('is_indexed' in field.schema) {
newSchema.is_indexed = !field.schema.is_indexed;
}
return { ...field, schema: newSchema };
}