diff --git a/src/common/utils/composite/get-composite/get-composite.test.ts b/src/common/utils/composite/get-composite/get-composite.test.ts index 82df8aa5ed..38f135fe2f 100644 --- a/src/common/utils/composite/get-composite/get-composite.test.ts +++ b/src/common/utils/composite/get-composite/get-composite.test.ts @@ -3,39 +3,43 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { sortBy } from "lodash/fp"; import type { Composite } from "./get-composite"; -import getComposite from "./get-composite"; +import getCompositeFor from "./get-composite"; import { getCompositePaths } from "../get-composite-paths/get-composite-paths"; +import { sortBy } from "lodash/fp"; describe("get-composite", () => { it("given items and a specified root id, creates a composite", () => { + const getComposite = getCompositeFor<{ + id: string; + parentId: string | undefined; + }>({ + rootId: "some-root-id", + getId: (x) => x.id, + getParentId: (x) => x.parentId, + }); + const someRootItem = { - someId: "some-root-id", - someParentId: undefined, + id: "some-root-id", + parentId: undefined, someProperty: "some-root-content", }; const someItem = { - someId: "some-id", - someParentId: "some-root-id", + id: "some-id", + parentId: "some-root-id", someProperty: "some-content", }; const someNestedItem = { - someId: "some-nested-id", - someParentId: "some-id", + id: "some-nested-id", + parentId: "some-id", someProperty: "some-nested-content", }; const items = [someRootItem, someItem, someNestedItem]; - const composite = getComposite({ - source: items, - rootId: "some-root-id", - getId: (x) => x.someId, - getParentId: (x) => x.someParentId, - }); + const composite = getComposite(items); expect(composite).toEqual({ id: "some-root-id", @@ -62,34 +66,38 @@ describe("get-composite", () => { it("given items and an unspecified root id and single item without parent as root, creates a composite", () => { const someRootItem = { - someId: "some-root-id", + id: "some-root-id", someProperty: "some-root-content", - // Notice: no "someParentId" makes this the root. - someParentId: undefined, + // Notice: no "parentId" makes this the root. + parentId: undefined, }; const someItem = { - someId: "some-id", - someParentId: "some-root-id", + id: "some-id", + parentId: "some-root-id", someProperty: "some-content", }; const someNestedItem = { - someId: "some-nested-id", - someParentId: "some-id", + id: "some-nested-id", + parentId: "some-id", someProperty: "some-nested-content", }; const items = [someRootItem, someItem, someNestedItem]; - const composite = getComposite({ - source: items, + const getComposite = getCompositeFor<{ + id: string; + parentId: string | undefined; + }>({ // Notice: no root id // rootId: "some-root-id", - getId: (x) => x.someId, - getParentId: (x) => x.someParentId, + getId: (x) => x.id, + getParentId: (x) => x.parentId, }); + const composite = getComposite(items); + expect(composite).toEqual({ id: "some-root-id", value: someRootItem, @@ -115,27 +123,29 @@ describe("get-composite", () => { it("given items and an unspecified root id and multiple items without parent as root, throws", () => { const someRootItem = { - someId: "some-root-id", - // Notice: no "someParentId" makes this a root. - someParentId: undefined, + id: "some-root-id", + // Notice: no "parentId" makes this a root. + parentId: undefined, }; const someOtherRootItem = { - someId: "some-other-root-id", - // Notice: no "someParentId" makes also this a root. - someParentId: undefined, + id: "some-other-root-id", + // Notice: no "parentId" makes also this a root. + parentId: undefined, }; const items = [someRootItem, someOtherRootItem]; + const getComposite = getCompositeFor<{ + id: string; + parentId: string | undefined; + }>({ + getId: (x) => x.id, + getParentId: (x) => x.parentId, + }); + expect(() => { - getComposite({ - source: items, - // Notice: no root id - // rootId: "some-root-id", - getId: (x) => x.someId, - getParentId: (x) => x.someParentId, - }); + getComposite(items); }).toThrow( 'Tried to get a composite, but multiple roots where encountered: "some-root-id", "some-other-root-id"', ); @@ -143,24 +153,27 @@ describe("get-composite", () => { it("given non-unique ids, throws", () => { const someItem = { - someId: "some-id", - someParentId: "irrelevant", + id: "some-id", + parentId: "irrelevant", }; const someOtherItem = { - someId: "some-id", - someParentId: "irrelevant", + id: "some-id", + parentId: "irrelevant", }; const items = [someItem, someOtherItem]; + const getComposite = getCompositeFor<{ + id: string; + parentId: string | undefined; + }>({ + getId: (x) => x.id, + getParentId: (x) => x.parentId, + }); + expect(() => { - getComposite({ - source: items, - rootId: "irrelevant", - getId: (x) => x.someId, - getParentId: (x) => x.someParentId, - }); + getComposite(items); }).toThrow( 'Tried to get a composite but encountered non-unique ids: "some-id"', ); @@ -168,24 +181,27 @@ describe("get-composite", () => { it("given items with missing parent ids, when creating composite without handling for unknown parents, throws", () => { const someItem = { - someId: "some-id", - someParentId: undefined, + id: "some-id", + parentId: undefined, }; const someItemWithMissingParentId = { - someId: "some-other-id", - someParentId: "some-missing-id", + id: "some-other-id", + parentId: "some-missing-id", }; const items = [someItem, someItemWithMissingParentId]; + const getComposite = getCompositeFor<{ + id: string; + parentId: string | undefined; + }>({ + getId: (x) => x.id, + getParentId: (x) => x.parentId, + }); + expect(() => { - getComposite({ - source: items, - rootId: "irrelevant", - getId: (x) => x.someId, - getParentId: (x) => x.someParentId, - }); + getComposite(items); }).toThrow( `Tried to get a composite but encountered missing parent ids: "some-missing-id". @@ -202,7 +218,6 @@ Available parent ids are: beforeEach(() => { const someItem = { id: "some-root-id", - parentId: undefined, }; const someItemWithMissingParentId = { @@ -215,10 +230,16 @@ Available parent ids are: handleMissingParentIdMock = jest.fn(); - composite = getComposite({ - source: items, + const getComposite = getCompositeFor<{ + id: string; + parentId?: string; + }>({ + getId: (x) => x.id, + getParentId: (x) => x.parentId, handleMissingParentIds: handleMissingParentIdMock, }); + + composite = getComposite(items); }); it("creates composite without the orphan item, and without throwing", () => { @@ -243,151 +264,58 @@ Available parent ids are: const someRoot = { id: "root", - someParentId: undefined, + parentId: undefined, }; const items = [someItem, someRoot]; + const getComposite = getCompositeFor<{ + id: string; + parentId: string | undefined; + }>({ + getId: (x) => x.id, + getParentId: (x) => x.parentId, + }); + expect(() => { - getComposite({ - source: items, - }); - }).toThrow('Tried to get a composite, but found items with self as parent: "some-id"'); + getComposite(items); + }).toThrow( + 'Tried to get a composite, but found items with self as parent: "some-id"', + ); }); it("given undefined ids, throws", () => { const root = { - someParentId: undefined, - someId: "some-root", + parentId: undefined, + id: "some-root", }; const someItem = { - someParentId: "some-root", - someId: undefined, + parentId: "some-root", + id: undefined, }; const someOtherItem = { - someParentId: "some-root", - someId: undefined, + parentId: "some-root", + id: undefined, }; const items = [root, someItem, someOtherItem]; + const getComposite = getCompositeFor<{ + id: any; + parentId: string | undefined; + }>({ + getId: (x) => x.id, + getParentId: (x) => x.parentId, + }); + expect(() => { - getComposite({ - source: items, - rootId: "some-root", - getId: (x) => x.someId as any, - getParentId: (x) => x.someParentId, - }); + getComposite(items); }).toThrow("Tried to get a composite but encountered 2 undefined ids"); }); - it("given items with default properties for id and parentId, creates a composite", () => { - const someRootItem = { - id: "some-root-id", - }; - - const someItem = { - id: "some-id", - parentId: "some-root-id", - }; - - const someNestedItem = { - id: "some-nested-id", - parentId: "some-id", - }; - - const items = [someRootItem, someItem, someNestedItem]; - - const composite = getComposite({ - source: items, - // Notice: no need for functions - // getId: (x) => x.id, - // getParentId: (x) => x.parentId, - }); - - expect(composite).toEqual({ - id: "some-root-id", - value: someRootItem, - - children: [ - { - id: "some-id", - parentId: "some-root-id", - value: someItem, - - children: [ - { - id: "some-nested-id", - parentId: "some-id", - value: someNestedItem, - children: [], - }, - ], - }, - ], - }); - }); - - it("given explicitly ordered items, creates a composite with ordered children", () => { - const someRootItem = { - id: "some-root-id", - someOrderNumber: 1, - }; - - const someItem1 = { - id: "some-id-1", - parentId: "some-root-id", - someOrderNumber: 1, - }; - - const someItem2 = { - id: "some-id-2", - parentId: "some-root-id", - someOrderNumber: 2, - }; - - const someChildItem1 = { - id: "some-child-id-1", - parentId: "some-id-1", - someOrderNumber: 1, - }; - - const someChildItem2 = { - id: "some-child-id-2", - parentId: "some-id-1", - someOrderNumber: 2, - }; - - const items = [ - someRootItem, - // Note: not in order yet. - someItem2, - someItem1, - someChildItem2, - someChildItem1, - ]; - - const composite = getComposite({ - source: items, - // Note: this is the explicit function to order a composite's children. - getOrderedChildren: (things) => - sortBy((thing) => thing.someOrderNumber, things), - }); - - const orderedPaths = getCompositePaths(composite); - - expect(orderedPaths).toEqual([ - ["some-root-id"], - ["some-root-id", "some-id-1"], - ["some-root-id", "some-id-1", "some-child-id-1"], - ["some-root-id", "some-id-1", "some-child-id-2"], - ["some-root-id", "some-id-2"], - ]); - }); - - it("given implicitly ordered items, creates a composite with ordered children", () => { + it("given transformed children, creates a composite with transformed children", () => { const someRootItem = { id: "some-root-id", orderNumber: 1, @@ -426,12 +354,19 @@ Available parent ids are: someChildItem1, ]; - const composite = getComposite({ - source: items, - // Note: without explicit getOrderedChildren for ordering, implicit default value of "orderNumber" will be used, if it exists. - // getOrderedChildren: things => sortBy(thing => thing.orderNumber, things), + const getComposite = getCompositeFor<{ + id: string; + parentId?: string; + orderNumber?: number; + }>({ + getId: (x) => x.id, + getParentId: (x) => x.parentId, + transformChildren: (things) => + sortBy((thing) => thing.orderNumber, things), }); + const composite = getComposite(items); + const orderedPaths = getCompositePaths(composite); expect(orderedPaths).toEqual([ diff --git a/src/common/utils/composite/get-composite/get-composite.ts b/src/common/utils/composite/get-composite/get-composite.ts index 87f5951411..278b89e8ce 100644 --- a/src/common/utils/composite/get-composite/get-composite.ts +++ b/src/common/utils/composite/get-composite/get-composite.ts @@ -13,8 +13,7 @@ import { uniq, without, compact, - get, - sortBy, + identity, } from "lodash/fp"; export interface Composite { @@ -24,21 +23,21 @@ export interface Composite { children: Composite[]; } -export default ({ - source, - rootId, - getId = get("id"), - getParentId = get("parentId"), - getOrderedChildren = (things: T[]) => sortBy("orderNumber", things), - handleMissingParentIds = throwMissingParentIds, -}: { - source: T[]; +interface Configuration { rootId?: string; - getId?: (thing: T) => string; - getParentId?: (thing: T) => string | undefined; - getOrderedChildren?: (things: T[]) => T[]; + getId: (thing: T) => string; + getParentId: (thing: T) => string | undefined; + transformChildren?: (things: T[]) => T[]; handleMissingParentIds?: (parentIdsForHandling: ParentIdsForHandling) => void; -}) => { +} + +export default ({ + rootId = undefined, + getId, + getParentId, + transformChildren = identity, + handleMissingParentIds = throwMissingParentIds, +}: Configuration) => (source: T[]) => { const undefinedIds = pipeline( source, filter((x) => getId(x) === undefined), @@ -107,7 +106,7 @@ export default ({ return parentId !== undefined && parentId === thingId; }), - getOrderedChildren, + transformChildren, map(toComposite), ),