directus 9.7.1 → 9.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache.js +2 -2
- package/dist/database/helpers/date/dialects/default.d.ts +3 -0
- package/dist/database/helpers/date/dialects/default.js +7 -0
- package/dist/database/helpers/date/dialects/sqlite.d.ts +0 -9
- package/dist/database/helpers/date/dialects/sqlite.js +0 -24
- package/dist/database/helpers/date/index.d.ts +6 -6
- package/dist/database/helpers/date/index.js +13 -13
- package/dist/database/helpers/date/types.d.ts +0 -9
- package/dist/database/helpers/{date → fn}/dialects/mssql.d.ts +3 -2
- package/dist/database/helpers/{date → fn}/dialects/mssql.js +14 -3
- package/dist/database/helpers/{date → fn}/dialects/mysql.d.ts +3 -2
- package/dist/database/helpers/{date → fn}/dialects/mysql.js +14 -3
- package/dist/database/helpers/{date → fn}/dialects/oracle.d.ts +3 -2
- package/dist/database/helpers/{date → fn}/dialects/oracle.js +14 -3
- package/dist/database/helpers/{date → fn}/dialects/postgres.d.ts +3 -2
- package/dist/database/helpers/{date → fn}/dialects/postgres.js +14 -3
- package/dist/database/helpers/fn/dialects/sqlite.d.ts +13 -0
- package/dist/database/helpers/fn/dialects/sqlite.js +42 -0
- package/dist/database/helpers/fn/index.d.ts +7 -0
- package/dist/database/helpers/fn/index.js +17 -0
- package/dist/database/helpers/fn/types.d.ts +18 -0
- package/dist/database/helpers/fn/types.js +27 -0
- package/dist/database/helpers/index.d.ts +4 -1
- package/dist/database/helpers/index.js +7 -1
- package/dist/database/migrations/20220308A-add-bookmark-icon-and-color.d.ts +3 -0
- package/dist/database/migrations/20220308A-add-bookmark-icon-and-color.js +17 -0
- package/dist/database/migrations/20220322A-rename-field-typecast-flags.js +6 -2
- package/dist/database/migrations/20220323A-add-field-validation.d.ts +3 -0
- package/dist/database/migrations/20220323A-add-field-validation.js +17 -0
- package/dist/database/migrations/20220325A-fix-typecast-flags.d.ts +3 -0
- package/dist/database/migrations/20220325A-fix-typecast-flags.js +49 -0
- package/dist/database/migrations/20220325B-add-default-language.d.ts +3 -0
- package/dist/database/migrations/20220325B-add-default-language.js +28 -0
- package/dist/database/run-ast.js +4 -2
- package/dist/database/system-data/fields/activity.yaml +4 -4
- package/dist/database/system-data/fields/fields.yaml +9 -0
- package/dist/database/system-data/fields/presets.yaml +14 -0
- package/dist/database/system-data/fields/settings.yaml +13 -1
- package/dist/logger.js +2 -1
- package/dist/services/activity.js +4 -1
- package/dist/services/authorization.d.ts +1 -1
- package/dist/services/authorization.js +128 -44
- package/dist/services/collections.js +222 -198
- package/dist/services/fields.js +184 -173
- package/dist/services/payload.js +8 -6
- package/dist/services/relations.js +93 -81
- package/dist/services/server.js +1 -0
- package/dist/services/shares.js +2 -1
- package/dist/services/users.js +3 -1
- package/dist/utils/apply-query.js +21 -3
- package/dist/utils/get-column.d.ts +6 -5
- package/dist/utils/get-column.js +16 -8
- package/dist/utils/get-schema.d.ts +1 -1
- package/dist/utils/get-schema.js +15 -10
- package/dist/utils/track.js +3 -2
- package/dist/utils/url.d.ts +1 -1
- package/dist/utils/url.js +1 -1
- package/dist/utils/validate-storage.js +3 -1
- package/package.json +12 -12
package/dist/services/fields.js
CHANGED
|
@@ -199,211 +199,222 @@ class FieldsService {
|
|
|
199
199
|
if (this.accountability && this.accountability.admin !== true) {
|
|
200
200
|
throw new exceptions_1.ForbiddenException();
|
|
201
201
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
false;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
202
|
+
try {
|
|
203
|
+
const exists = field.field in this.schema.collections[collection].fields ||
|
|
204
|
+
(0, lodash_1.isNil)(await this.knex.select('id').from('directus_fields').where({ collection, field: field.field }).first()) === false;
|
|
205
|
+
// Check if field already exists, either as a column, or as a row in directus_fields
|
|
206
|
+
if (exists) {
|
|
207
|
+
throw new exceptions_1.InvalidPayloadException(`Field "${field.field}" already exists in collection "${collection}"`);
|
|
208
|
+
}
|
|
209
|
+
await this.knex.transaction(async (trx) => {
|
|
210
|
+
const itemsService = new items_1.ItemsService('directus_fields', {
|
|
211
|
+
knex: trx,
|
|
212
|
+
accountability: this.accountability,
|
|
213
|
+
schema: this.schema,
|
|
214
|
+
});
|
|
215
|
+
const hookAdjustedField = await emitter_1.default.emitFilter(`fields.create`, field, {
|
|
216
|
+
collection: collection,
|
|
217
|
+
}, {
|
|
218
|
+
database: trx,
|
|
219
|
+
schema: this.schema,
|
|
220
|
+
accountability: this.accountability,
|
|
221
|
+
});
|
|
222
|
+
if (hookAdjustedField.type && constants_1.ALIAS_TYPES.includes(hookAdjustedField.type) === false) {
|
|
223
|
+
if (table) {
|
|
224
|
+
this.addColumnToTable(table, hookAdjustedField);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
await trx.schema.alterTable(collection, (table) => {
|
|
228
|
+
this.addColumnToTable(table, hookAdjustedField);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (hookAdjustedField.meta) {
|
|
233
|
+
await itemsService.createOne({
|
|
234
|
+
...hookAdjustedField.meta,
|
|
235
|
+
collection: collection,
|
|
236
|
+
field: hookAdjustedField.field,
|
|
237
|
+
}, { emitEvents: false });
|
|
238
|
+
}
|
|
239
|
+
emitter_1.default.emitAction(`fields.create`, {
|
|
240
|
+
payload: hookAdjustedField,
|
|
241
|
+
key: hookAdjustedField.field,
|
|
242
|
+
collection: collection,
|
|
243
|
+
}, {
|
|
244
|
+
database: (0, database_1.default)(),
|
|
245
|
+
schema: this.schema,
|
|
246
|
+
accountability: this.accountability,
|
|
247
|
+
});
|
|
214
248
|
});
|
|
215
|
-
|
|
249
|
+
}
|
|
250
|
+
finally {
|
|
251
|
+
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
252
|
+
await this.cache.clear();
|
|
253
|
+
}
|
|
254
|
+
await (0, cache_1.clearSystemCache)();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
async updateField(collection, field) {
|
|
258
|
+
if (this.accountability && this.accountability.admin !== true) {
|
|
259
|
+
throw new exceptions_1.ForbiddenException();
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
const hookAdjustedField = await emitter_1.default.emitFilter(`fields.update`, field, {
|
|
263
|
+
keys: [field.field],
|
|
216
264
|
collection: collection,
|
|
217
265
|
}, {
|
|
218
|
-
database:
|
|
266
|
+
database: this.knex,
|
|
219
267
|
schema: this.schema,
|
|
220
268
|
accountability: this.accountability,
|
|
221
269
|
});
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
270
|
+
const record = field.meta
|
|
271
|
+
? await this.knex.select('id').from('directus_fields').where({ collection, field: field.field }).first()
|
|
272
|
+
: null;
|
|
273
|
+
if (hookAdjustedField.schema) {
|
|
274
|
+
const existingColumn = await this.schemaInspector.columnInfo(collection, hookAdjustedField.field);
|
|
275
|
+
if (!(0, lodash_1.isEqual)(existingColumn, hookAdjustedField.schema)) {
|
|
276
|
+
try {
|
|
277
|
+
await this.knex.schema.alterTable(collection, (table) => {
|
|
278
|
+
if (!hookAdjustedField.schema)
|
|
279
|
+
return;
|
|
280
|
+
this.addColumnToTable(table, field, existingColumn);
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
throw await (0, translate_1.translateDatabaseError)(err);
|
|
285
|
+
}
|
|
230
286
|
}
|
|
231
287
|
}
|
|
232
288
|
if (hookAdjustedField.meta) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
289
|
+
if (record) {
|
|
290
|
+
await this.itemsService.updateOne(record.id, {
|
|
291
|
+
...hookAdjustedField.meta,
|
|
292
|
+
collection: collection,
|
|
293
|
+
field: hookAdjustedField.field,
|
|
294
|
+
}, { emitEvents: false });
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
await this.itemsService.createOne({
|
|
298
|
+
...hookAdjustedField.meta,
|
|
299
|
+
collection: collection,
|
|
300
|
+
field: hookAdjustedField.field,
|
|
301
|
+
}, { emitEvents: false });
|
|
302
|
+
}
|
|
238
303
|
}
|
|
239
|
-
emitter_1.default.emitAction(`fields.
|
|
304
|
+
emitter_1.default.emitAction(`fields.update`, {
|
|
240
305
|
payload: hookAdjustedField,
|
|
241
|
-
|
|
306
|
+
keys: [hookAdjustedField.field],
|
|
242
307
|
collection: collection,
|
|
243
308
|
}, {
|
|
244
309
|
database: (0, database_1.default)(),
|
|
245
310
|
schema: this.schema,
|
|
246
311
|
accountability: this.accountability,
|
|
247
312
|
});
|
|
248
|
-
|
|
249
|
-
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
250
|
-
await this.cache.clear();
|
|
313
|
+
return field.field;
|
|
251
314
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
if (this.accountability && this.accountability.admin !== true) {
|
|
256
|
-
throw new exceptions_1.ForbiddenException();
|
|
257
|
-
}
|
|
258
|
-
const hookAdjustedField = await emitter_1.default.emitFilter(`fields.update`, field, {
|
|
259
|
-
keys: [field.field],
|
|
260
|
-
collection: collection,
|
|
261
|
-
}, {
|
|
262
|
-
database: this.knex,
|
|
263
|
-
schema: this.schema,
|
|
264
|
-
accountability: this.accountability,
|
|
265
|
-
});
|
|
266
|
-
const record = field.meta
|
|
267
|
-
? await this.knex.select('id').from('directus_fields').where({ collection, field: field.field }).first()
|
|
268
|
-
: null;
|
|
269
|
-
if (hookAdjustedField.schema) {
|
|
270
|
-
const existingColumn = await this.schemaInspector.columnInfo(collection, hookAdjustedField.field);
|
|
271
|
-
if (!(0, lodash_1.isEqual)(existingColumn, hookAdjustedField.schema)) {
|
|
272
|
-
try {
|
|
273
|
-
await this.knex.schema.alterTable(collection, (table) => {
|
|
274
|
-
if (!hookAdjustedField.schema)
|
|
275
|
-
return;
|
|
276
|
-
this.addColumnToTable(table, field, existingColumn);
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
catch (err) {
|
|
280
|
-
throw await (0, translate_1.translateDatabaseError)(err);
|
|
281
|
-
}
|
|
315
|
+
finally {
|
|
316
|
+
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
317
|
+
await this.cache.clear();
|
|
282
318
|
}
|
|
319
|
+
await (0, cache_1.clearSystemCache)();
|
|
283
320
|
}
|
|
284
|
-
if (hookAdjustedField.meta) {
|
|
285
|
-
if (record) {
|
|
286
|
-
await this.itemsService.updateOne(record.id, {
|
|
287
|
-
...hookAdjustedField.meta,
|
|
288
|
-
collection: collection,
|
|
289
|
-
field: hookAdjustedField.field,
|
|
290
|
-
}, { emitEvents: false });
|
|
291
|
-
}
|
|
292
|
-
else {
|
|
293
|
-
await this.itemsService.createOne({
|
|
294
|
-
...hookAdjustedField.meta,
|
|
295
|
-
collection: collection,
|
|
296
|
-
field: hookAdjustedField.field,
|
|
297
|
-
}, { emitEvents: false });
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
301
|
-
await this.cache.clear();
|
|
302
|
-
}
|
|
303
|
-
await (0, cache_1.clearSystemCache)();
|
|
304
|
-
emitter_1.default.emitAction(`fields.update`, {
|
|
305
|
-
payload: hookAdjustedField,
|
|
306
|
-
keys: [hookAdjustedField.field],
|
|
307
|
-
collection: collection,
|
|
308
|
-
}, {
|
|
309
|
-
database: (0, database_1.default)(),
|
|
310
|
-
schema: this.schema,
|
|
311
|
-
accountability: this.accountability,
|
|
312
|
-
});
|
|
313
|
-
return field.field;
|
|
314
321
|
}
|
|
315
322
|
async deleteField(collection, field) {
|
|
316
323
|
if (this.accountability && this.accountability.admin !== true) {
|
|
317
324
|
throw new exceptions_1.ForbiddenException();
|
|
318
325
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
accountability: this.accountability,
|
|
325
|
-
});
|
|
326
|
-
await this.knex.transaction(async (trx) => {
|
|
327
|
-
var _a, _b;
|
|
328
|
-
if (this.schema.collections[collection] &&
|
|
329
|
-
field in this.schema.collections[collection].fields &&
|
|
330
|
-
this.schema.collections[collection].fields[field].alias === false) {
|
|
331
|
-
await trx.schema.table(collection, (table) => {
|
|
332
|
-
table.dropColumn(field);
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
const relations = this.schema.relations.filter((relation) => {
|
|
336
|
-
var _a;
|
|
337
|
-
return ((relation.collection === collection && relation.field === field) ||
|
|
338
|
-
(relation.related_collection === collection && ((_a = relation.meta) === null || _a === void 0 ? void 0 : _a.one_field) === field));
|
|
339
|
-
});
|
|
340
|
-
const relationsService = new relations_1.RelationsService({
|
|
341
|
-
knex: trx,
|
|
342
|
-
accountability: this.accountability,
|
|
326
|
+
try {
|
|
327
|
+
await emitter_1.default.emitFilter('fields.delete', [field], {
|
|
328
|
+
collection: collection,
|
|
329
|
+
}, {
|
|
330
|
+
database: this.knex,
|
|
343
331
|
schema: this.schema,
|
|
344
|
-
});
|
|
345
|
-
const fieldsService = new FieldsService({
|
|
346
|
-
knex: trx,
|
|
347
332
|
accountability: this.accountability,
|
|
348
|
-
schema: this.schema,
|
|
349
333
|
});
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
334
|
+
await this.knex.transaction(async (trx) => {
|
|
335
|
+
var _a, _b;
|
|
336
|
+
if (this.schema.collections[collection] &&
|
|
337
|
+
field in this.schema.collections[collection].fields &&
|
|
338
|
+
this.schema.collections[collection].fields[field].alias === false) {
|
|
339
|
+
await trx.schema.table(collection, (table) => {
|
|
340
|
+
table.dropColumn(field);
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
const relations = this.schema.relations.filter((relation) => {
|
|
344
|
+
var _a;
|
|
345
|
+
return ((relation.collection === collection && relation.field === field) ||
|
|
346
|
+
(relation.related_collection === collection && ((_a = relation.meta) === null || _a === void 0 ? void 0 : _a.one_field) === field));
|
|
347
|
+
});
|
|
348
|
+
const relationsService = new relations_1.RelationsService({
|
|
349
|
+
knex: trx,
|
|
350
|
+
accountability: this.accountability,
|
|
351
|
+
schema: this.schema,
|
|
352
|
+
});
|
|
353
|
+
const fieldsService = new FieldsService({
|
|
354
|
+
knex: trx,
|
|
355
|
+
accountability: this.accountability,
|
|
356
|
+
schema: this.schema,
|
|
357
|
+
});
|
|
358
|
+
for (const relation of relations) {
|
|
359
|
+
const isM2O = relation.collection === collection && relation.field === field;
|
|
360
|
+
// If the current field is a m2o, delete the related o2m if it exists and remove the relationship
|
|
361
|
+
if (isM2O) {
|
|
362
|
+
await relationsService.deleteOne(collection, field);
|
|
363
|
+
if (relation.related_collection && ((_a = relation.meta) === null || _a === void 0 ? void 0 : _a.one_field)) {
|
|
364
|
+
await fieldsService.deleteField(relation.related_collection, relation.meta.one_field);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// If the current field is a o2m, just delete the one field config from the relation
|
|
368
|
+
if (!isM2O && ((_b = relation.meta) === null || _b === void 0 ? void 0 : _b.one_field)) {
|
|
369
|
+
await trx('directus_relations')
|
|
370
|
+
.update({ one_field: null })
|
|
371
|
+
.where({ many_collection: relation.collection, many_field: relation.field });
|
|
357
372
|
}
|
|
358
373
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
374
|
+
const collectionMeta = await trx
|
|
375
|
+
.select('archive_field', 'sort_field')
|
|
376
|
+
.from('directus_collections')
|
|
377
|
+
.where({ collection })
|
|
378
|
+
.first();
|
|
379
|
+
const collectionMetaUpdates = {};
|
|
380
|
+
if ((collectionMeta === null || collectionMeta === void 0 ? void 0 : collectionMeta.archive_field) === field) {
|
|
381
|
+
collectionMetaUpdates.archive_field = null;
|
|
364
382
|
}
|
|
383
|
+
if ((collectionMeta === null || collectionMeta === void 0 ? void 0 : collectionMeta.sort_field) === field) {
|
|
384
|
+
collectionMetaUpdates.sort_field = null;
|
|
385
|
+
}
|
|
386
|
+
if (Object.keys(collectionMetaUpdates).length > 0) {
|
|
387
|
+
await trx('directus_collections').update(collectionMetaUpdates).where({ collection });
|
|
388
|
+
}
|
|
389
|
+
// Cleanup directus_fields
|
|
390
|
+
const metaRow = await trx
|
|
391
|
+
.select('collection', 'field')
|
|
392
|
+
.from('directus_fields')
|
|
393
|
+
.where({ collection, field })
|
|
394
|
+
.first();
|
|
395
|
+
if (metaRow) {
|
|
396
|
+
// Handle recursive FK constraints
|
|
397
|
+
await trx('directus_fields')
|
|
398
|
+
.update({ group: null })
|
|
399
|
+
.where({ group: metaRow.field, collection: metaRow.collection });
|
|
400
|
+
}
|
|
401
|
+
await trx('directus_fields').delete().where({ collection, field });
|
|
402
|
+
});
|
|
403
|
+
emitter_1.default.emitAction('fields.delete', {
|
|
404
|
+
payload: [field],
|
|
405
|
+
collection: collection,
|
|
406
|
+
}, {
|
|
407
|
+
database: this.knex,
|
|
408
|
+
schema: this.schema,
|
|
409
|
+
accountability: this.accountability,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
finally {
|
|
413
|
+
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
414
|
+
await this.cache.clear();
|
|
365
415
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
.from('directus_collections')
|
|
369
|
-
.where({ collection })
|
|
370
|
-
.first();
|
|
371
|
-
const collectionMetaUpdates = {};
|
|
372
|
-
if ((collectionMeta === null || collectionMeta === void 0 ? void 0 : collectionMeta.archive_field) === field) {
|
|
373
|
-
collectionMetaUpdates.archive_field = null;
|
|
374
|
-
}
|
|
375
|
-
if ((collectionMeta === null || collectionMeta === void 0 ? void 0 : collectionMeta.sort_field) === field) {
|
|
376
|
-
collectionMetaUpdates.sort_field = null;
|
|
377
|
-
}
|
|
378
|
-
if (Object.keys(collectionMetaUpdates).length > 0) {
|
|
379
|
-
await trx('directus_collections').update(collectionMetaUpdates).where({ collection });
|
|
380
|
-
}
|
|
381
|
-
// Cleanup directus_fields
|
|
382
|
-
const metaRow = await trx
|
|
383
|
-
.select('collection', 'field')
|
|
384
|
-
.from('directus_fields')
|
|
385
|
-
.where({ collection, field })
|
|
386
|
-
.first();
|
|
387
|
-
if (metaRow) {
|
|
388
|
-
// Handle recursive FK constraints
|
|
389
|
-
await trx('directus_fields')
|
|
390
|
-
.update({ group: null })
|
|
391
|
-
.where({ group: metaRow.field, collection: metaRow.collection });
|
|
392
|
-
}
|
|
393
|
-
await trx('directus_fields').delete().where({ collection, field });
|
|
394
|
-
});
|
|
395
|
-
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
396
|
-
await this.cache.clear();
|
|
397
|
-
}
|
|
398
|
-
await (0, cache_1.clearSystemCache)();
|
|
399
|
-
emitter_1.default.emitAction('fields.delete', {
|
|
400
|
-
payload: [field],
|
|
401
|
-
collection: collection,
|
|
402
|
-
}, {
|
|
403
|
-
database: this.knex,
|
|
404
|
-
schema: this.schema,
|
|
405
|
-
accountability: this.accountability,
|
|
406
|
-
});
|
|
416
|
+
await (0, cache_1.clearSystemCache)();
|
|
417
|
+
}
|
|
407
418
|
}
|
|
408
419
|
addColumnToTable(table, field, alter = null) {
|
|
409
420
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
package/dist/services/payload.js
CHANGED
|
@@ -247,7 +247,7 @@ class PayloadService {
|
|
|
247
247
|
payload[name] = newValue;
|
|
248
248
|
}
|
|
249
249
|
if (dateColumn.type === 'date') {
|
|
250
|
-
const [year, month, day] = value.toISOString().
|
|
250
|
+
const [year, month, day] = value.toISOString().slice(0, 10).split('-');
|
|
251
251
|
// Strip off the time / timezone information from a date-only value
|
|
252
252
|
const newValue = `${year}-${month}-${day}`;
|
|
253
253
|
payload[name] = newValue;
|
|
@@ -429,7 +429,7 @@ class PayloadService {
|
|
|
429
429
|
delete: joi_1.default.array().items(joi_1.default.string(), joi_1.default.number()),
|
|
430
430
|
});
|
|
431
431
|
for (const relation of relationsToProcess) {
|
|
432
|
-
if (!relation.meta
|
|
432
|
+
if (!relation.meta)
|
|
433
433
|
continue;
|
|
434
434
|
const currentPrimaryKeyField = this.schema.collections[relation.related_collection].primary;
|
|
435
435
|
const relatedPrimaryKeyField = this.schema.collections[relation.collection].primary;
|
|
@@ -441,9 +441,11 @@ class PayloadService {
|
|
|
441
441
|
const recordsToUpsert = [];
|
|
442
442
|
const savedPrimaryKeys = [];
|
|
443
443
|
// Nested array of individual items
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
444
|
+
const field = payload[relation.meta.one_field];
|
|
445
|
+
if (!field || Array.isArray(field)) {
|
|
446
|
+
const updates = field || []; // treat falsey values as removing all children
|
|
447
|
+
for (let i = 0; i < updates.length; i++) {
|
|
448
|
+
const relatedRecord = updates[i];
|
|
447
449
|
let record = (0, lodash_1.cloneDeep)(relatedRecord);
|
|
448
450
|
if (typeof relatedRecord === 'string' || typeof relatedRecord === 'number') {
|
|
449
451
|
const existingRecord = await this.knex
|
|
@@ -507,7 +509,7 @@ class PayloadService {
|
|
|
507
509
|
}
|
|
508
510
|
// "Updates" object w/ create/update/delete
|
|
509
511
|
else {
|
|
510
|
-
const alterations =
|
|
512
|
+
const alterations = field;
|
|
511
513
|
const { error } = nestedUpdateSchema.validate(alterations);
|
|
512
514
|
if (error)
|
|
513
515
|
throw new exceptions_1.InvalidPayloadException(`Invalid one-to-many update structure: ${error.message}`);
|
|
@@ -146,36 +146,40 @@ class RelationsService {
|
|
|
146
146
|
if (existingRelation) {
|
|
147
147
|
throw new exceptions_1.InvalidPayloadException(`Field "${relation.field}" in collection "${relation.collection}" already has an associated relationship`);
|
|
148
148
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
149
|
+
try {
|
|
150
|
+
const metaRow = {
|
|
151
|
+
...(relation.meta || {}),
|
|
152
|
+
many_collection: relation.collection,
|
|
153
|
+
many_field: relation.field,
|
|
154
|
+
one_collection: relation.related_collection || null,
|
|
155
|
+
};
|
|
156
|
+
await this.knex.transaction(async (trx) => {
|
|
157
|
+
if (relation.related_collection) {
|
|
158
|
+
await trx.schema.alterTable(relation.collection, async (table) => {
|
|
159
|
+
var _a;
|
|
160
|
+
this.alterType(table, relation);
|
|
161
|
+
const constraintName = (0, get_default_index_name_1.getDefaultIndexName)('foreign', relation.collection, relation.field);
|
|
162
|
+
const builder = table
|
|
163
|
+
.foreign(relation.field, constraintName)
|
|
164
|
+
.references(`${relation.related_collection}.${this.schema.collections[relation.related_collection].primary}`);
|
|
165
|
+
if ((_a = relation.schema) === null || _a === void 0 ? void 0 : _a.on_delete) {
|
|
166
|
+
builder.onDelete(relation.schema.on_delete);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
const relationsItemService = new items_1.ItemsService('directus_relations', {
|
|
171
|
+
knex: trx,
|
|
172
|
+
schema: this.schema,
|
|
173
|
+
// We don't set accountability here. If you have read access to certain fields, you are
|
|
174
|
+
// allowed to extract the relations regardless of permissions to directus_relations. This
|
|
175
|
+
// happens in `filterForbidden` down below
|
|
167
176
|
});
|
|
168
|
-
|
|
169
|
-
const relationsItemService = new items_1.ItemsService('directus_relations', {
|
|
170
|
-
knex: trx,
|
|
171
|
-
schema: this.schema,
|
|
172
|
-
// We don't set accountability here. If you have read access to certain fields, you are
|
|
173
|
-
// allowed to extract the relations regardless of permissions to directus_relations. This
|
|
174
|
-
// happens in `filterForbidden` down below
|
|
177
|
+
await relationsItemService.createOne(metaRow);
|
|
175
178
|
});
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
await (0, cache_1.clearSystemCache)();
|
|
182
|
+
}
|
|
179
183
|
}
|
|
180
184
|
/**
|
|
181
185
|
* Update an existing foreign key constraint
|
|
@@ -196,47 +200,51 @@ class RelationsService {
|
|
|
196
200
|
if (!existingRelation) {
|
|
197
201
|
throw new exceptions_1.InvalidPayloadException(`Field "${field}" in collection "${collection}" doesn't have a relationship.`);
|
|
198
202
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
203
|
+
try {
|
|
204
|
+
await this.knex.transaction(async (trx) => {
|
|
205
|
+
if (existingRelation.related_collection) {
|
|
206
|
+
await trx.schema.alterTable(collection, async (table) => {
|
|
207
|
+
var _a;
|
|
208
|
+
let constraintName = (0, get_default_index_name_1.getDefaultIndexName)('foreign', collection, field);
|
|
209
|
+
// If the FK already exists in the DB, drop it first
|
|
210
|
+
if (existingRelation === null || existingRelation === void 0 ? void 0 : existingRelation.schema) {
|
|
211
|
+
constraintName = existingRelation.schema.constraint_name || constraintName;
|
|
212
|
+
table.dropForeign(field, constraintName);
|
|
213
|
+
}
|
|
214
|
+
this.alterType(table, relation);
|
|
215
|
+
const builder = table
|
|
216
|
+
.foreign(field, constraintName || undefined)
|
|
217
|
+
.references(`${existingRelation.related_collection}.${this.schema.collections[existingRelation.related_collection].primary}`);
|
|
218
|
+
if ((_a = relation.schema) === null || _a === void 0 ? void 0 : _a.on_delete) {
|
|
219
|
+
builder.onDelete(relation.schema.on_delete);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
const relationsItemService = new items_1.ItemsService('directus_relations', {
|
|
224
|
+
knex: trx,
|
|
225
|
+
schema: this.schema,
|
|
226
|
+
// We don't set accountability here. If you have read access to certain fields, you are
|
|
227
|
+
// allowed to extract the relations regardless of permissions to directus_relations. This
|
|
228
|
+
// happens in `filterForbidden` down below
|
|
229
|
+
});
|
|
230
|
+
if (relation.meta) {
|
|
231
|
+
if (existingRelation === null || existingRelation === void 0 ? void 0 : existingRelation.meta) {
|
|
232
|
+
await relationsItemService.updateOne(existingRelation.meta.id, relation.meta);
|
|
208
233
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
234
|
+
else {
|
|
235
|
+
await relationsItemService.createOne({
|
|
236
|
+
...(relation.meta || {}),
|
|
237
|
+
many_collection: relation.collection,
|
|
238
|
+
many_field: relation.field,
|
|
239
|
+
one_collection: existingRelation.related_collection || null,
|
|
240
|
+
});
|
|
215
241
|
}
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
const relationsItemService = new items_1.ItemsService('directus_relations', {
|
|
219
|
-
knex: trx,
|
|
220
|
-
schema: this.schema,
|
|
221
|
-
// We don't set accountability here. If you have read access to certain fields, you are
|
|
222
|
-
// allowed to extract the relations regardless of permissions to directus_relations. This
|
|
223
|
-
// happens in `filterForbidden` down below
|
|
224
|
-
});
|
|
225
|
-
if (relation.meta) {
|
|
226
|
-
if (existingRelation === null || existingRelation === void 0 ? void 0 : existingRelation.meta) {
|
|
227
|
-
await relationsItemService.updateOne(existingRelation.meta.id, relation.meta);
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
await relationsItemService.createOne({
|
|
231
|
-
...(relation.meta || {}),
|
|
232
|
-
many_collection: relation.collection,
|
|
233
|
-
many_field: relation.field,
|
|
234
|
-
one_collection: existingRelation.related_collection || null,
|
|
235
|
-
});
|
|
236
242
|
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
finally {
|
|
246
|
+
await (0, cache_1.clearSystemCache)();
|
|
247
|
+
}
|
|
240
248
|
}
|
|
241
249
|
/**
|
|
242
250
|
* Delete an existing relationship
|
|
@@ -255,21 +263,25 @@ class RelationsService {
|
|
|
255
263
|
if (!existingRelation) {
|
|
256
264
|
throw new exceptions_1.InvalidPayloadException(`Field "${field}" in collection "${collection}" doesn't have a relationship.`);
|
|
257
265
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
266
|
+
try {
|
|
267
|
+
await this.knex.transaction(async (trx) => {
|
|
268
|
+
var _a;
|
|
269
|
+
const existingConstraints = await this.schemaInspector.foreignKeys();
|
|
270
|
+
const constraintNames = existingConstraints.map((key) => key.constraint_name);
|
|
271
|
+
if (((_a = existingRelation.schema) === null || _a === void 0 ? void 0 : _a.constraint_name) &&
|
|
272
|
+
constraintNames.includes(existingRelation.schema.constraint_name)) {
|
|
273
|
+
await trx.schema.alterTable(existingRelation.collection, (table) => {
|
|
274
|
+
table.dropForeign(existingRelation.field, existingRelation.schema.constraint_name);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
if (existingRelation.meta) {
|
|
278
|
+
await trx('directus_relations').delete().where({ many_collection: collection, many_field: field });
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
finally {
|
|
283
|
+
await (0, cache_1.clearSystemCache)();
|
|
284
|
+
}
|
|
273
285
|
}
|
|
274
286
|
/**
|
|
275
287
|
* Whether or not the current user has read access to relations
|