Files
directus/api/src/utils/deep-map-response.ts
Nitwel ea31721914 Improve Content Versioning (#25437)
* initial testing

* bypass accountability and fix error

* continue implementing new content versioning

* fix versionRaw and tweak post nulling

* fix circular references

* improve deep-map and add testing

* add blackbox tests

* add load tests

* update loadtests

* update testsuite

* update load-tests

* undo package.json change

* add way to run everything in parallel

* add github actions

* fix gh action

* improve logging and add wait

* update runners and fmt

* cleanup

* add deadlock tests

* fix deadlock tests for oracle and mssql

* cleanup

* shorten transaction duration

* add web preview option to load tests

* fix lockfile

* fix import for QueryOptions

* format

* use admin for versioned writes

* Fix workflow permissions warning

* move loadtests to separate branch

* fix admin on read

* fix another bug

* update pnpm lock

* fix gql read one

* fix requesting version in gql

* rename bypassAccountability to skipTracking

* not filter default value fields

* update tests

* stupid mistake fix

* fix gql *_by_version

* Create great-experts-clap.md

* Revert "fix gql *_by_version"

This reverts commit 82bf7239e8.

* Update .changeset/great-experts-clap.md

Co-authored-by: daedalus <44623501+ComfortablyCoding@users.noreply.github.com>

* Update .changeset/great-experts-clap.md

Co-authored-by: daedalus <44623501+ComfortablyCoding@users.noreply.github.com>

* Update great-experts-clap.md

---------

Co-authored-by: daedalus <44623501+ComfortablyCoding@users.noreply.github.com>
Co-authored-by: ian <licitdev@gmail.com>
Co-authored-by: Alex Gaillard <alex@directus.io>
2025-08-27 13:25:21 -04:00

99 lines
2.9 KiB
TypeScript

import type { CollectionOverview, FieldOverview, Relation, SchemaOverview } from '@directus/types';
import { isPlainObject } from 'lodash-es';
import assert from 'node:assert';
import { getRelationInfo, type RelationInfo } from './get-relation-info.js';
import { InvalidQueryError } from '@directus/errors';
/**
* Allows to deep map the response from the ItemsService with collection, field and relation context for each entry.
* Bottom to Top depth first mapping of values.
*/
export function deepMapResponse(
object: Record<string, any>,
callback: (
entry: [key: string | number, value: unknown],
context: {
collection: CollectionOverview;
field: FieldOverview;
relation: Relation | null;
leaf: boolean;
relationType: RelationInfo['relationType'] | null;
},
) => [key: string | number, value: unknown],
context: {
schema: SchemaOverview;
collection: string;
relationInfo?: RelationInfo;
},
): any {
const collection = context.schema.collections[context.collection];
assert(
isPlainObject(object) && typeof object === 'object' && object !== null,
`DeepMapResponse only works on objects, received ${JSON.stringify(object)}`,
);
return Object.fromEntries(
Object.entries(object).map(([key, value]) => {
const field = collection?.fields[key];
if (!field) return [key, value];
const relationInfo = getRelationInfo(context.schema.relations, collection.collection, field.field);
let leaf = true;
if (relationInfo.relation && typeof value === 'object' && value !== null && isPlainObject(object)) {
switch (relationInfo.relationType) {
case 'm2o':
value = deepMapResponse(value, callback, {
schema: context.schema,
collection: relationInfo.relation.related_collection!,
relationInfo,
});
leaf = false;
break;
case 'o2m':
value = (value as any[]).map((childValue) => {
if (isPlainObject(childValue) && typeof childValue === 'object' && childValue !== null) {
leaf = false;
return deepMapResponse(childValue, callback, {
schema: context.schema,
collection: relationInfo!.relation!.collection,
relationInfo,
});
} else return childValue;
});
break;
case 'a2o': {
const related_collection = object[relationInfo.relation.meta!.one_collection_field!];
if (!related_collection) {
throw new InvalidQueryError({
reason: `When selecting '${collection.collection}.${field.field}', the field '${
collection.collection
}.${
relationInfo.relation.meta!.one_collection_field
}' has to be selected when using versioning and m2a relations `,
});
}
value = deepMapResponse(value, callback, {
schema: context.schema,
collection: related_collection,
relationInfo,
});
leaf = false;
break;
}
}
}
return callback([key, value], { collection, field, ...relationInfo, leaf });
}),
);
}