diff --git a/etc/api.report.api.md b/etc/api.report.api.md index 8b546054e..7b1985aca 100644 --- a/etc/api.report.api.md +++ b/etc/api.report.api.md @@ -655,6 +655,7 @@ export namespace Osdk { linksType?: any; } ? Q["linksType"] : Q extends ObjectTypeDefinition ? OsdkObjectLinksObject : never; readonly $as: >(type: NEW_Q | string) => Osdk.Instance>; + readonly $cloneAndUpdate: , T extends Osdk.Instance>(newObject: T) => Osdk.Instance; } & (IsNever extends true ? {} : IsAny extends true ? {} : "$rid" extends OPTIONS ? { readonly $rid: string; } : {}); diff --git a/etc/client.report.api.md b/etc/client.report.api.md index 8aa09b9d1..b329a928d 100644 --- a/etc/client.report.api.md +++ b/etc/client.report.api.md @@ -23,6 +23,7 @@ import type { InterfaceMetadata } from '@osdk/api'; import { isOk } from '@osdk/api'; import type { MinimalObjectSet } from '@osdk/api/unstable'; import { ObjectMetadata } from '@osdk/api'; +import type { ObjectOrInterfaceDefinition } from '@osdk/api'; import type { ObjectQueryDataType } from '@osdk/api'; import { ObjectSet } from '@osdk/api'; import type { ObjectSetQueryDataType } from '@osdk/api'; @@ -94,6 +95,9 @@ export interface Client extends SharedClient, SharedClient_2 { fetchMetadata | QueryDefinition)>(o: Q): Promise ? ActionMetadata : Q extends QueryDefinition ? QueryMetadata : never>; } +// @public (undocumented) +export function consolidateOsdkObject, U extends Osdk.Instance, V extends ObjectOrInterfaceDefinition>(oldObject: T, upToDateObject: U): T; + // @public (undocumented) export function createAttachmentUpload(data: Blob, name: string): AttachmentUpload; diff --git a/examples-extra/docs_example/src/osdkExample.tsx b/examples-extra/docs_example/src/osdkExample.tsx index 6c315664d..8443c790e 100644 --- a/examples-extra/docs_example/src/osdkExample.tsx +++ b/examples-extra/docs_example/src/osdkExample.tsx @@ -1,4 +1,4 @@ -import { createClient } from "@osdk/client"; +import { consolidateOsdkObject, createClient, Osdk } from "@osdk/client"; import { $ontologyRid, Employee, @@ -182,4 +182,51 @@ export async function osdkObjectSetExample() { unionObjectSet, subtractObjectSet, ); + + /** + * SUBSCRIPTIONS + */ + + const objects: Record> = + {}; + + const subscription = client(Employee).subscribe({ + onChange: (update) => { + if (update.state === "ADDED_OR_UPDATED") { + console.log( + `Employee with primary key ${update.object.$primaryKey} was added or updated`, + ); + + objects[update.object.$primaryKey] = objects[update.object.$primaryKey] + .$cloneAndUpdate(update.object as any); + } else if (update.state === "REMOVED") { + console.log( + `Employee with primary key ${update.object.$primaryKey} was deleted`, + ); + + delete objects[update.object.$primaryKey]; + } + }, + onOutOfDate: async () => { + console.log("Object Set Out of Date. Reloading Object Set"); + + for await (const obj of client(Employee).asyncIter()) { + objects[obj.$primaryKey] = obj; + } + }, + onSuccessfulSubscription() { + console.log("Subscription successful"); + setTimeout(() => { + subscription.unsubscribe(); + }, 10000); + }, + onError(err) { + console.error("Error in subscription and subscription closed", err); + }, + // The properties that will be returned in updates can optionally be specified below. Updates may still be sent for properties not specified here, + // and not all properties may be returned. + }, { properties: ["employeeId"] }); + + // An empty subscription will request all properties + client(Employee).subscribe({ onChange: () => {} }); } diff --git a/packages/api/src/OsdkObjectFrom.ts b/packages/api/src/OsdkObjectFrom.ts index 82829ecf4..b8b9597fe 100644 --- a/packages/api/src/OsdkObjectFrom.ts +++ b/packages/api/src/OsdkObjectFrom.ts @@ -203,6 +203,16 @@ export namespace Osdk { OPTIONS, ConvertProps >; + readonly $cloneAndUpdate: < + T extends Osdk.Instance, + L extends PropertyKeys, + >( + newObject: T, + ) => Osdk.Instance< + Q, + OPTIONS, + P + >; } // We are hiding the $rid field if it wasn't requested as we want to discourage its use & (IsNever extends true ? {} diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 38f294a78..de5196a7a 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -58,3 +58,5 @@ export { extractDateInLocalTime, extractDateInUTC, } from "./util/datetimeConverters.js"; + +export { consolidateOsdkObject } from "./util/consolidateOsdkObject.js"; diff --git a/packages/client/src/object/convertWireToOsdkObjects.test.ts b/packages/client/src/object/convertWireToOsdkObjects.test.ts index 66f010a3d..ea8a3a38f 100644 --- a/packages/client/src/object/convertWireToOsdkObjects.test.ts +++ b/packages/client/src/object/convertWireToOsdkObjects.test.ts @@ -35,16 +35,10 @@ import { import { additionalContext, type Client } from "../Client.js"; import { createClient } from "../createClient.js"; import { createMinimalClient } from "../createMinimalClient.js"; -import { - convertWireToOsdkObjects, - convertWireToOsdkObjects2, -} from "./convertWireToOsdkObjects.js"; +import { convertWireToOsdkObjects } from "./convertWireToOsdkObjects.js"; describe("convertWireToOsdkObjects", () => { let client: Client; - const interfaceToObjectTypeMappings = { - FooInterface: { Employee: { fooSpt: "fullName" } }, - }; beforeAll(async () => { apiServer.listen(); @@ -68,7 +62,6 @@ describe("convertWireToOsdkObjects", () => { "office", "class", "startDate", - "employeeSensor", "employeeStatus", "$apiName", "$objectType", @@ -150,7 +143,7 @@ describe("convertWireToOsdkObjects", () => { expect(emptyAttachmentArray).toBeUndefined(); }); - it("works even with unknown apiNames - old", async () => { + it("works even with unknown apiNames", async () => { const clientCtx = createMinimalClient( { ontologyRid: $ontologyRid }, "https://stack.palantir.com", @@ -180,37 +173,7 @@ describe("convertWireToOsdkObjects", () => { expect(prototypeBefore).not.toBe(prototypeAfter); }); - it("works even with unknown apiNames - new", async () => { - const clientCtx = createMinimalClient( - { ontologyRid: $ontologyRid }, - "https://stack.palantir.com", - async () => "myAccessToken", - ); - createSharedClientContext( - "https://stack.palantir.com", - async () => "myAccessToken", - "userAgent", - ); - - let object = { - __apiName: Employee.apiName, - __primaryKey: 0, - } as const; - const prototypeBefore = Object.getPrototypeOf(object); - let object2 = await convertWireToOsdkObjects2( - clientCtx, - [object], - undefined, - undefined, - undefined, - false, - ); - const prototypeAfter = Object.getPrototypeOf(object2); - - expect(prototypeBefore).not.toBe(prototypeAfter); - }); - - it("updates interface when underlying changes - old", async () => { + it("updates interface when underlying changes", async () => { const clientCtx = createMinimalClient( { ontologyRid: $ontologyRid }, "https://stack.palantir.com", @@ -271,68 +234,7 @@ describe("convertWireToOsdkObjects", () => { expect(objAsFoo).toBe(obj.$as(FooInterface)); }); - it("updates interface when underlying changes - new", async () => { - const clientCtx = createMinimalClient( - { ontologyRid: $ontologyRid }, - "https://stack.palantir.com", - async () => "myAccessToken", - ); - - let objectFromWire = { - __apiName: "Employee" as const, - __primaryKey: 0, - __title: "Steve", - fullName: "Steve", - employeeId: "5", - } satisfies OntologyObjectV2; - - const [obj] = (await convertWireToOsdkObjects2( - clientCtx, - [objectFromWire], - undefined, - )) as unknown as Osdk[]; - - expect(obj.fullName).toEqual("Steve"); - expect(Object.keys(obj).sort()).toEqual([ - "$apiName", - "$objectType", - "$primaryKey", - "$title", - "employeeId", - "fullName", - ].sort()); - - const objAsFoo = obj.$as(FooInterface); - expect(objAsFoo).toMatchObject({ - fooSpt: obj.fullName, - $apiName: FooInterface.apiName, - $primaryKey: obj.$primaryKey, - $objectType: obj.$objectType, - $title: obj.$title, - }); - - console.log(obj); - console.log(objAsFoo); - - (obj as any).$updateInternalValues({ - fullName: "Bob", - }); - expect(obj.fullName).toEqual("Bob"); - expect(objAsFoo.fooSpt).toEqual(obj.fullName); - - expect(Object.keys(objAsFoo).sort()).toEqual([ - "$apiName", - "$objectType", - "$primaryKey", - "$title", - "fooSpt", - ].sort()); - - expect(obj).toBe(objAsFoo.$as(Employee)); - expect(objAsFoo).toBe(obj.$as(FooInterface)); - }); - - it("reconstitutes interfaces properly without rid - old", async () => { + it("reconstitutes interfaces properly without rid", async () => { const clientCtx = createMinimalClient( { ontologyRid: $ontologyRid }, "https://stack.palantir.com", @@ -377,54 +279,6 @@ describe("convertWireToOsdkObjects", () => { `); }); - it("reconstitutes interfaces properly without rid - new", async () => { - const clientCtx = createMinimalClient( - { ontologyRid: $ontologyRid }, - "https://stack.palantir.com", - async () => "myAccessToken", - ); - - let objectFromWire = { - __apiName: "Employee" as const, - __primaryKey: 0, - __title: "Steve", - fullName: "Steve", - } satisfies OntologyObjectV2; - - const [objAsFoo] = (await convertWireToOsdkObjects2( - clientCtx, - [objectFromWire], - FooInterface.apiName, - false, - undefined, - false, - interfaceToObjectTypeMappings, - )) as unknown as Osdk[]; - - expect(objAsFoo).toMatchInlineSnapshot(` - { - "$apiName": "FooInterface", - "$objectType": "Employee", - "$primaryKey": 0, - "$title": "Steve", - "fooSpt": "Steve", - } - `); - - const obj = objAsFoo.$as(Employee); - expect(obj.fullName).toEqual("Steve"); - - expect(obj).toMatchInlineSnapshot(` - { - "$apiName": "Employee", - "$objectType": "Employee", - "$primaryKey": 0, - "$title": "Steve", - "fullName": "Steve", - } - `); - }); - it("reconstitutes interfaces properly with rid", async () => { const clientCtx = createMinimalClient( { ontologyRid: $ontologyRid }, @@ -475,61 +329,6 @@ describe("convertWireToOsdkObjects", () => { expect(obj.$rid).toEqual("hiMom"); }); - it("reconstitutes interfaces properly with rid - new", async () => { - const clientCtx = createMinimalClient( - { ontologyRid: $ontologyRid }, - "https://stack.palantir.com", - async () => "myAccessToken", - ); - - let objectFromWire = { - __apiName: "Employee" as const, - __primaryKey: 0, - __title: "Steve", - __rid: "hiMom", - fullName: "Steve", - employeeId: 0, - } satisfies OntologyObjectV2; - - const [objAsFoo] = (await convertWireToOsdkObjects2( - clientCtx, - [objectFromWire], - FooInterface.apiName, - false, - undefined, - false, - interfaceToObjectTypeMappings, - )) as unknown as Osdk[]; - - expect(objAsFoo).toMatchInlineSnapshot(` - { - "$apiName": "FooInterface", - "$objectType": "Employee", - "$primaryKey": 0, - "$rid": "hiMom", - "$title": "Steve", - "fooSpt": "Steve", - } - `); - expect(objAsFoo.$rid).toEqual("hiMom"); - - const obj = objAsFoo.$as(Employee); - expect(obj.fullName).toEqual("Steve"); - - expect(obj).toMatchInlineSnapshot(` - { - "$apiName": "Employee", - "$objectType": "Employee", - "$primaryKey": 0, - "$rid": "hiMom", - "$title": "Steve", - "employeeId": 0, - "fullName": "Steve", - } - `); - expect(obj.$rid).toEqual("hiMom"); - }); - describe("selection keys", () => { it("throws when required is missing", async () => { let object = { @@ -606,7 +405,7 @@ describe("convertWireToOsdkObjects", () => { }); }); - describe("selection keys - new", () => { + describe("without selection keys", () => { it("throws when required is missing", async () => { let object = { __apiName: "Employee", @@ -614,12 +413,12 @@ describe("convertWireToOsdkObjects", () => { } as const; await expect(() => - convertWireToOsdkObjects2( + convertWireToOsdkObjects( client[additionalContext], [object], undefined, undefined, - ["employeeId"], + undefined, "throw", ) ).rejects.toThrowErrorMatchingInlineSnapshot( @@ -627,19 +426,20 @@ describe("convertWireToOsdkObjects", () => { ); }); - it("does not throw when optional is missing", async () => { + it("does not throw when required is present", async () => { let object = { __apiName: "Employee", __primaryKey: 0, + "employeeId": 0, } as const; await expect( - convertWireToOsdkObjects2( + convertWireToOsdkObjects( client[additionalContext], [object], undefined, undefined, - ["fullName"], + undefined, "throw", ), ).resolves.to.not.toBeUndefined(); @@ -651,12 +451,12 @@ describe("convertWireToOsdkObjects", () => { __primaryKey: 0, } as const; - const result = await convertWireToOsdkObjects2( + const result = await convertWireToOsdkObjects( client[additionalContext], [object], undefined, undefined, - ["employeeId"], + undefined, "drop", ); @@ -667,14 +467,15 @@ describe("convertWireToOsdkObjects", () => { const object = { __apiName: "Employee", __primaryKey: 0, + "employeeId": 0, } as const; - const result = await convertWireToOsdkObjects2( + const result = await convertWireToOsdkObjects( client[additionalContext], [object], undefined, undefined, - ["fullName"], + undefined, "drop", ); @@ -682,180 +483,171 @@ describe("convertWireToOsdkObjects", () => { }); }); - describe("without selection keys", () => { - it("throws when required is missing", async () => { - let object = { - __apiName: "Employee", - __primaryKey: 0, - } as const; - - await expect(() => - convertWireToOsdkObjects2( - client[additionalContext], - [object], - undefined, - undefined, - undefined, - "throw", - ) - ).rejects.toThrowErrorMatchingInlineSnapshot( - `[Error: Unable to safely convert objects as some non nullable properties are null]`, - ); - }); - - it("does not throw when required is present", async () => { - let object = { - __apiName: "Employee", - __primaryKey: 0, - "employeeId": 0, - } as const; + it("behaves correctly when converting", async () => { + const object = { + __apiName: "Employee", + __primaryKey: 0, + fooSpt: "hi", + } as const; - await expect( - convertWireToOsdkObjects2( - client[additionalContext], - [object], - undefined, - undefined, - undefined, - "throw", - ), - ).resolves.to.not.toBeUndefined(); - }); + const result = await convertWireToOsdkObjects( + client[additionalContext], + [object], + "FooInterface", + undefined, + ["fooSpt"], + "drop", + ); - it("filters when it should", async () => { - const object = { - __apiName: "Employee", - __primaryKey: 0, - } as const; + expect(result.length).toBe(1); + }); - const result = await convertWireToOsdkObjects2( + describe("$cloneAndUpdate", async () => { + it("combines two objects where new object is scoped to same props", async () => { + const [object1] = await convertWireToOsdkObjects( client[additionalContext], - [object], + [{ + __apiName: "Employee", + __primaryKey: 0, + "employeeId": 0, + "fullName": "Steve", + }], undefined, undefined, undefined, "drop", - ); - - expect(result.length).toBe(0); - }); - - it("does not filter when it shouldn't", async () => { - const object = { - __apiName: "Employee", - __primaryKey: 0, - "employeeId": 0, - } as const; + ) as Osdk.Instance[]; - const result = await convertWireToOsdkObjects2( + const [object2] = await convertWireToOsdkObjects( client[additionalContext], - [object], + [{ + __apiName: "Employee", + __primaryKey: 0, + "employeeId": 1, + "fullName": "Steve", + }], undefined, undefined, undefined, "drop", - ); + ) as Osdk.Instance[]; - expect(result.length).toBe(1); - }); - }); - - describe("without selection keys - new", () => { - it("throws when required is missing", async () => { - let object = { - __apiName: "Employee", - __primaryKey: 0, - } as const; + const result = object1.$cloneAndUpdate(object2); - await expect(() => - convertWireToOsdkObjects2( - client[additionalContext], - [object], - undefined, - undefined, - undefined, - "throw", - ) - ).rejects.toThrowErrorMatchingInlineSnapshot( - `[Error: Unable to safely convert objects as some non nullable properties are null]`, - ); - }); + expect(result).not.toBe(object1); - it("does not throw when required is present", async () => { - let object = { - __apiName: "Employee", - __primaryKey: 0, - "employeeId": 0, - } as const; + expectTypeOf(result).toEqualTypeOf< + Osdk.Instance + >(); - await expect( - convertWireToOsdkObjects2( - client[additionalContext], - [object], - undefined, - undefined, - undefined, - "throw", - ), - ).resolves.to.not.toBeUndefined(); + expect(result).toMatchInlineSnapshot(` + { + "$apiName": "Employee", + "$objectType": "Employee", + "$primaryKey": 0, + "employeeId": 1, + "fullName": "Steve", + } + `); }); - - it("filters when it should", async () => { - const object = { - __apiName: "Employee", - __primaryKey: 0, - } as const; - - const result = await convertWireToOsdkObjects2( + it("combines two objects where new object is scoped to less props", async () => { + const [object1] = await convertWireToOsdkObjects( client[additionalContext], - [object], + [{ + __apiName: "Employee", + __primaryKey: 0, + "employeeId": 0, + "fullName": "Steve", + "office": "NYC", + }], undefined, undefined, undefined, "drop", - ); + ) as Osdk.Instance[]; - expect(result.length).toBe(0); + const [object2] = await convertWireToOsdkObjects( + client[additionalContext], + [{ + __apiName: "Employee", + __primaryKey: 0, + "employeeId": 0, + "fullName": "Steven", + }], + undefined, + undefined, + undefined, + "drop", + ) as Osdk.Instance[]; + + const result = object1.$cloneAndUpdate(object2 as any); + + expect(result).not.toBe(object1); + + expectTypeOf(result).toEqualTypeOf< + Osdk.Instance + >(); + + expect(result).toMatchInlineSnapshot(` + { + "$apiName": "Employee", + "$objectType": "Employee", + "$primaryKey": 0, + "employeeId": 0, + "fullName": "Steven", + "office": "NYC", + } + `); }); - it("does not filter when it shouldn't", async () => { - const object = { - __apiName: "Employee", - __primaryKey: 0, - "employeeId": 0, - } as const; - - const result = await convertWireToOsdkObjects2( + it("combines two objects where new object is scoped to more props", async () => { + const [object1] = await convertWireToOsdkObjects( client[additionalContext], - [object], + [{ + __apiName: "Employee", + __primaryKey: 0, + "employeeId": 0, + "fullName": "Steve", + }], + undefined, undefined, + ["employeeId", "fullName"], + "drop", + ) as Osdk.Instance[]; + + const [object2] = await convertWireToOsdkObjects( + client[additionalContext], + [{ + __apiName: "Employee", + __primaryKey: 0, + "employeeId": 0, + "fullName": "Steven", + "office": "NYC", + }], undefined, undefined, + ["employeeId", "fullName", "office"], "drop", - ); + ) as Osdk.Instance[]; - expect(result.length).toBe(1); - }); - }); + const result = object1.$cloneAndUpdate(object2); - it("behaves correctly when converting", async () => { - const object = { - __apiName: "Employee", - __primaryKey: 0, - fooSpt: "hi", - } as const; + expect(result).not.toBe(object1); - const result = await convertWireToOsdkObjects2( - client[additionalContext], - [object], - "FooInterface", - undefined, - ["fooSpt"], - "drop", - interfaceToObjectTypeMappings, - ); + expectTypeOf(result).toEqualTypeOf< + Osdk.Instance + >(); - expect(result.length).toBe(1); + expect(result).toMatchInlineSnapshot(` + { + "$apiName": "Employee", + "$objectType": "Employee", + "$primaryKey": 0, + "employeeId": 0, + "fullName": "Steven", + } + `); + }); }); }); diff --git a/packages/client/src/object/convertWireToOsdkObjects/ObjectHolder.ts b/packages/client/src/object/convertWireToOsdkObjects/ObjectHolder.ts index f29d43cbe..d991cc8ea 100644 --- a/packages/client/src/object/convertWireToOsdkObjects/ObjectHolder.ts +++ b/packages/client/src/object/convertWireToOsdkObjects/ObjectHolder.ts @@ -14,12 +14,13 @@ * limitations under the License. */ -import type { Osdk } from "@osdk/api"; +import type { Osdk, OsdkBase } from "@osdk/api"; import type { OntologyObjectV2 } from "@osdk/internal.foundry.core"; import type { MinimalClient } from "../../MinimalClientContext.js"; import type { FetchedObjectTypeDefinition } from "../../ontology/OntologyProvider.js"; import type { DollarAsFn } from "./getDollarAs.js"; import type { get$link } from "./getDollarLink.js"; +import type { InterfaceHolder } from "./InterfaceHolder.js"; import type { CachedPropertiesRef, ClientRef, @@ -35,7 +36,16 @@ export interface ObjectHolderPrototypeOwnProps { readonly "$as": DollarAsFn; readonly "$link": ReturnType; readonly "$updateInternalValues": (newValues: Record) => void; + readonly "$cloneAndUpdate": CloneAndUpdateFn; } + +type CloneAndUpdateFn = < + Q extends FetchedObjectTypeDefinition, + T extends Osdk.Instance, +>( + newDef: T, +) => OsdkBase; + /** @internal */ export interface ObjectHolderOwnProperties { [RawObject]: OntologyObjectV2; diff --git a/packages/client/src/object/convertWireToOsdkObjects/createOsdkObject.ts b/packages/client/src/object/convertWireToOsdkObjects/createOsdkObject.ts index f486f8d10..a8b1683e2 100644 --- a/packages/client/src/object/convertWireToOsdkObjects/createOsdkObject.ts +++ b/packages/client/src/object/convertWireToOsdkObjects/createOsdkObject.ts @@ -61,6 +61,7 @@ const objectPrototypeCache = createClientCache( this: ObjectHolder, newValues: Record, ) { + objectPrototypeCache.get(client, objectDef); this[RawObject] = Object.assign( {}, this[RawObject], @@ -68,7 +69,28 @@ const objectPrototypeCache = createClientCache( ); }, }, - } satisfies PropertyDescriptorRecord, + "$cloneAndUpdate": { + value: function( + this: ObjectHolder, + newValues: any, + ) { + const filteredSource = Object.keys(newValues[RawObject]) + .filter(key => Object.keys(this[RawObject]).includes(key)) + .reduce((obj, key) => { + obj[key] = newValues[RawObject][key]; + return obj; + }, {} as Record); + + return createOsdkObject( + client, + objectDef, + Object.assign({}, this[RawObject], filteredSource), + ); + }, + }, + } satisfies PropertyDescriptorRecord< + ObjectHolderPrototypeOwnProps + >, ); }, ); diff --git a/packages/client/src/util/consolidateOsdkObject.ts b/packages/client/src/util/consolidateOsdkObject.ts new file mode 100644 index 000000000..efa90ef14 --- /dev/null +++ b/packages/client/src/util/consolidateOsdkObject.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2024 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { ObjectOrInterfaceDefinition, Osdk } from "@osdk/api"; + +export function consolidateOsdkObject< + T extends Osdk.Instance, + U extends Osdk.Instance, + V extends ObjectOrInterfaceDefinition, +>( + oldObject: T, + upToDateObject: U, +): T { + return oldObject.$cloneAndUpdate( + upToDateObject.$internalValues, + ) as T; +} diff --git a/packages/client/src/util/util.test.ts b/packages/client/src/util/util.test.ts new file mode 100644 index 000000000..a589c6957 --- /dev/null +++ b/packages/client/src/util/util.test.ts @@ -0,0 +1,110 @@ +/* + * Copyright 2024 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Osdk } from "@osdk/api"; +import type { Employee, Todo } from "@osdk/client.test.ontology"; +import type { OntologyObjectV2 } from "@osdk/internal.foundry.core"; +import { describe, expect, expectTypeOf, it } from "vitest"; +import { createMinimalClient } from "../createMinimalClient.js"; +import { convertWireToOsdkObjects } from "../object/convertWireToOsdkObjects.js"; +import { consolidateOsdkObject } from "./consolidateOsdkObject.js"; + +describe(consolidateOsdkObject, () => { + it("combines two objects where new object is scoped to less props", async () => { + const clientCtx = createMinimalClient( + { + ontologyRid: + "ri.ontology.main.ontology.698267cc-6b48-4d98-beff-29beb24e9361", + }, + "https://stack.palantir.com", + async () => "myAccessToken", + ); + + let objectFromWire = { + __apiName: "Employee" as const, + __primaryKey: 0, + __title: "Steve", + fullName: "Steve", + employeeId: "5", + } satisfies OntologyObjectV2; + + const [oldObject] = (await convertWireToOsdkObjects( + clientCtx, + [objectFromWire], + undefined, + )) as unknown as Osdk.Instance[]; + + const [newObject] = (await convertWireToOsdkObjects( + clientCtx, + [objectFromWire], + undefined, + )) as unknown as Osdk.Instance[]; + + console.log( + oldObject, + "A", + oldObject.$cloneAndUpdate, + ); + const result = oldObject.$cloneAndUpdate(newObject); + + expectTypeOf(result).toEqualTypeOf>(); + + expect(result).toMatchInlineSnapshot(` + { + "$apiName": "Todo", + "$objectType": "type", + "$primaryKey": 1, + "$title": "Employee", + "id": 1, + "text": "hi", + } + `); + }); + + // it("combines two objects where new object is scoped to more props", () => { + // const oldObject: Osdk.Instance = { + // $apiName: "Todo", + // $objectType: "type", + // $primaryKey: 1, + // $title: "Employee", + // id: 3, + // text: "text", + // } as any; + + // const upToDateObject: Osdk.Instance = { + // $apiName: "Todo", + // $objectType: "type", + // $primaryKey: 1, + // $title: "Employee", + // id: 1, + // } as any; + + // const result = oldObject.$cloneAndUpdate(upToDateObject); + + // expectTypeOf(result).toEqualTypeOf>(); + + // expect(result).toMatchInlineSnapshot(` + // { + // "$apiName": "Todo", + // "$objectType": "type", + // "$primaryKey": 1, + // "$title": "Employee", + // "id": 1, + // "text": "hi", + // } + // `); + // }); +});