directus

Форк
0
/
merge-version-data.ts 
184 строки · 5.3 Кб
1
import type { Alterations, Item, SchemaOverview } from '@directus/types';
2
import { isObject } from '@directus/utils';
3
import Joi from 'joi';
4
import { cloneDeep } from 'lodash-es';
5

6
const alterationSchema = Joi.object({
7
	create: Joi.array().items(Joi.object().unknown()),
8
	update: Joi.array().items(Joi.object().unknown()),
9
	delete: Joi.array().items(Joi.string(), Joi.number()),
10
});
11

12
export function mergeVersionsRaw(item: Item, versionData: Partial<Item>[]) {
13
	const result = cloneDeep(item);
14

15
	for (const versionRecord of versionData) {
16
		for (const key of Object.keys(versionRecord)) {
17
			result[key] = versionRecord[key];
18
		}
19
	}
20

21
	return result;
22
}
23

24
export function mergeVersionsRecursive(
25
	item: Item,
26
	versionData: Item[],
27
	collection: string,
28
	schema: SchemaOverview,
29
): Item {
30
	if (versionData.length === 0) return item;
31

32
	return recursiveMerging(item, versionData, collection, schema) as Item;
33
}
34

35
function recursiveMerging(data: Item, versionData: unknown[], collection: string, schema: SchemaOverview): unknown {
36
	const result = cloneDeep(data);
37
	const relations = getRelations(collection, schema);
38

39
	for (const versionRecord of versionData) {
40
		if (!isObject(versionRecord)) {
41
			continue;
42
		}
43

44
		for (const key of Object.keys(data)) {
45
			if (key in versionRecord === false) {
46
				continue;
47
			}
48

49
			const currentValue: unknown = data[key];
50
			const newValue: unknown = versionRecord[key];
51

52
			if (typeof newValue !== 'object' || newValue === null) {
53
				// primitive type substitution, json and non relational array values are handled in the next check
54
				result[key] = newValue;
55
				continue;
56
			}
57

58
			if (key in relations === false) {
59
				// check for m2a exception
60
				if (isManyToAnyCollection(collection, schema) && key === 'item') {
61
					const item = addMissingKeys(isObject(currentValue) ? currentValue : {}, newValue);
62
					result[key] = recursiveMerging(item, [newValue], data['collection'], schema);
63
				} else {
64
					// item is not a relation
65
					result[key] = newValue;
66
				}
67

68
				continue;
69
			}
70

71
			const { error } = alterationSchema.validate(newValue);
72

73
			if (error) {
74
				if (typeof newValue === 'object' && key in relations) {
75
					const newItem = !currentValue || typeof currentValue !== 'object' ? newValue : currentValue;
76
					result[key] = recursiveMerging(newItem, [newValue], relations[key]!, schema);
77
				}
78

79
				continue;
80
			}
81

82
			const alterations = newValue as Alterations;
83
			const currentPrimaryKeyField = schema.collections[collection]!.primary;
84
			const relatedPrimaryKeyField = schema.collections[relations[key]!]!.primary;
85

86
			const mergedRelation: Item[] = [];
87

88
			if (Array.isArray(currentValue)) {
89
				if (alterations.delete.length > 0) {
90
					for (const currentItem of currentValue) {
91
						const currentId = typeof currentItem === 'object' ? currentItem[currentPrimaryKeyField] : currentItem;
92

93
						if (alterations.delete.includes(currentId) === false) {
94
							mergedRelation.push(currentItem);
95
						}
96
					}
97
				} else {
98
					mergedRelation.push(...currentValue);
99
				}
100

101
				if (alterations.update.length > 0) {
102
					for (const updatedItem of alterations.update) {
103
						// find existing item to update
104
						const itemIndex = mergedRelation.findIndex(
105
							(currentItem) => currentItem[relatedPrimaryKeyField] === updatedItem[currentPrimaryKeyField],
106
						);
107

108
						if (itemIndex === -1) {
109
							// check for raw primary keys
110
							const pkIndex = mergedRelation.findIndex(
111
								(currentItem) => currentItem === updatedItem[currentPrimaryKeyField],
112
							);
113

114
							if (pkIndex === -1) {
115
								// nothing to update so add the item as is
116
								mergedRelation.push(updatedItem);
117
							} else {
118
								mergedRelation[pkIndex] = updatedItem;
119
							}
120

121
							continue;
122
						}
123

124
						const item = addMissingKeys(mergedRelation[itemIndex]!, updatedItem);
125

126
						mergedRelation[itemIndex] = recursiveMerging(item, [updatedItem], relations[key]!, schema) as Item;
127
					}
128
				}
129
			}
130

131
			if (alterations.create.length > 0) {
132
				for (const createdItem of alterations.create) {
133
					const item = addMissingKeys({}, createdItem);
134
					mergedRelation.push(recursiveMerging(item, [createdItem], relations[key]!, schema) as Item);
135
				}
136
			}
137

138
			result[key] = mergedRelation;
139
		}
140
	}
141

142
	return result;
143
}
144

145
function addMissingKeys(item: Item, edits: Item) {
146
	const result: Item = { ...item };
147

148
	for (const key of Object.keys(edits)) {
149
		if (key in item === false) {
150
			result[key] = null;
151
		}
152
	}
153

154
	return result;
155
}
156

157
function isManyToAnyCollection(collection: string, schema: SchemaOverview) {
158
	const relation = schema.relations.find(
159
		(relation) => relation.collection === collection && relation.meta?.many_collection === collection,
160
	);
161

162
	if (!relation || !relation.meta?.one_field || !relation.related_collection) return false;
163

164
	return Boolean(
165
		schema.collections[relation.related_collection]?.fields[relation.meta.one_field]?.special.includes('m2a'),
166
	);
167
}
168

169
function getRelations(collection: string, schema: SchemaOverview) {
170
	return schema.relations.reduce(
171
		(result, relation) => {
172
			if (relation.related_collection === collection && relation.meta?.one_field) {
173
				result[relation.meta.one_field] = relation.collection;
174
			}
175

176
			if (relation.collection === collection && relation.related_collection) {
177
				result[relation.field] = relation.related_collection;
178
			}
179

180
			return result;
181
		},
182
		{} as Record<string, string>,
183
	);
184
}
185

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.