Skip to content

Commit

Permalink
Fix SO export sorting algorithm (elastic#142078)
Browse files Browse the repository at this point in the history
* Fix SO export sorting algorithm

* improve var name

* adapt another unit  test

Co-authored-by: Kibana Machine <[email protected]>
(cherry picked from commit 4cdd74d)
  • Loading branch information
pgayvallet committed Sep 29, 2022
1 parent d9fc073 commit 2dbc144
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -146,16 +146,18 @@ describe('getSortedObjectsForExport()', () => {
attributes = {},
sort = [],
type = 'index-pattern',
idPrefix = '',
}: {
attributes?: Record<string, unknown>;
sort?: string[];
type?: string;
idPrefix?: string;
} = {}
) {
const hits = [];
for (let i = 1; i <= hitCount; i++) {
hits.push({
id: `${i}`,
id: `${idPrefix}${i}`,
type,
attributes,
sort,
Expand Down Expand Up @@ -247,7 +249,7 @@ describe('getSortedObjectsForExport()', () => {

describe('>1k hits', () => {
const firstMockHits = generateHits(1000, { sort: ['a', 'b'] });
const secondMockHits = generateHits(500);
const secondMockHits = generateHits(500, { idPrefix: 'second-hit-' });

test('requests multiple pages', async () => {
savedObjectsClient.find.mockResolvedValueOnce({
Expand Down
37 changes: 37 additions & 0 deletions src/core/server/saved_objects/export/sort_objects.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
* Side Public License, v 1.
*/

import { range } from 'lodash';
import { sortObjects } from './sort_objects';
import type { SavedObject } from '@kbn/core-saved-objects-common';

describe('sortObjects()', () => {
test('should return on empty array', () => {
Expand Down Expand Up @@ -309,6 +311,7 @@ describe('sortObjects()', () => {
]
`);
});

test('should not fail on complex circular dependencies', () => {
const docs = [
{
Expand Down Expand Up @@ -424,4 +427,38 @@ describe('sortObjects()', () => {
]
`);
});

test('should not fail on large graph of objects', () => {
// create an object that references all objects with a higher `index` up to `depth`.
const createComplexNode = (index: number, depth: number): SavedObject => {
return {
type: 'test',
id: `${index}`,
attributes: {},
references: range(index + 1, depth).map((refIndex) => ({
type: 'test',
id: `${refIndex}`,
name: `test-${refIndex}`,
})),
};
};

const createComplexGraph = (depth: number): SavedObject[] => {
const nodes: SavedObject[] = [];
for (let i = 0; i < depth; i++) {
nodes.push(createComplexNode(i, depth));
}
return nodes;
};

const depth = 100;
const graph = createComplexGraph(depth);
const sorted = sortObjects(graph);

expect(sorted.map(({ type, id }) => `${type}:${id}`)).toEqual(
range(depth)
.reverse()
.map((index) => `test:${index}`)
);
});
});
21 changes: 12 additions & 9 deletions src/core/server/saved_objects/export/sort_objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,36 @@

import { SavedObject } from '../types';

const getId = (object: { type: string; id: string }) => `${object.type}:${object.id}`;

export function sortObjects(savedObjects: SavedObject[]): SavedObject[] {
const path = new Set<SavedObject>();
const traversed = new Set<string>();
const sorted = new Set<SavedObject>();
const objectsByTypeId = new Map(
savedObjects.map((object) => [`${object.type}:${object.id}`, object] as [string, SavedObject])
savedObjects.map((object) => [getId(object), object] as [string, SavedObject])
);

function includeObjects(objects: SavedObject[]) {
for (const object of objects) {
if (path.has(object)) {
const objectId = getId(object);
if (traversed.has(objectId)) {
continue;
}

const refdObjects = object.references
.map((ref) => objectsByTypeId.get(`${ref.type}:${ref.id}`))
const objectRefs = object.references
.map((ref) => objectsByTypeId.get(getId(ref)))
.filter((ref): ref is SavedObject => !!ref);

if (refdObjects.length) {
path.add(object);
includeObjects(refdObjects);
path.delete(object);
traversed.add(objectId);
if (objectRefs.length) {
includeObjects(objectRefs);
}

sorted.add(object);
}
}

includeObjects(savedObjects);

return [...sorted];
}

0 comments on commit 2dbc144

Please sign in to comment.