Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix JSON.stringify issue with circular ref objects #16166

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions api/src/services/payload.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
import { format, parseISO, isValid } from 'date-fns';
import { parseJSON, toArray } from '@directus/shared/utils';
import { parseJSON, toArray, JSONstringifyWithCircularRefs } from '@directus/shared/utils';
import { unflatten } from 'flat';
import Joi from 'joi';
import { Knex } from 'knex';
Expand Down Expand Up @@ -176,7 +176,7 @@ export class PayloadService {
for (const [key, value] of Object.entries(record)) {
if (Array.isArray(value) || (typeof value === 'object' && !(value instanceof Date) && value !== null)) {
if (!value.isRawInstance) {
record[key] = JSON.stringify(value);
record[key] = JSONstringifyWithCircularRefs(value);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/shared/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export * from './move-in-array';
export * from './parse-filter';
export * from './parse-json';
export * from './pluralize';
export * from './stringify-json';
export * from './to-array';
export * from './validate-extension-manifest';
export * from './validate-payload';
64 changes: 64 additions & 0 deletions packages/shared/src/utils/stringify-json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// https://stackoverflow.com/a/61962964/7185314
// This falls under the license CC BY-SA 4.0
// Read the license: https://creativecommons.org/licenses/by-sa/4.0/

export const JSONstringifyWithCircularRefs = (() => {
const refs = new Map();
const parents: any[] = [];
const path = ['this'];

function clear() {
refs.clear();
parents.length = 0;
path.length = 1;
}

function updateParents(key: string, value: any) {
let idx = parents.length - 1;
let prev = parents[idx];
if (prev[key] === value || idx === 0) {
path.push(key);
parents.push(value);
} else {
while (idx-- >= 0) {
prev = parents[idx];
if (prev[key] === value) {
idx += 2;
parents.length = idx;
path.length = idx;
--idx;
parents[idx] = value;
path[idx] = key;
break;
}
}
}
}

function checkCircular(key: string, value: any) {
if (value != null) {
if (typeof value === 'object') {
if (key) {
updateParents(key, value);
}

const other = refs.get(value);
if (other) {
return '[Circular Reference]' + other;
} else {
refs.set(value, path.join('.'));
}
}
}
return value;
}

return function stringifyWithCircularRefs(obj: any, space?: string | number) {
try {
parents.push(obj);
return JSON.stringify(obj, checkCircular, space);
} finally {
clear();
}
};
})();