diff --git a/packages/abstractions/package.json b/packages/abstractions/package.json index dfaadcf646..1e6fcd7097 100644 --- a/packages/abstractions/package.json +++ b/packages/abstractions/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/kiota-abstractions", - "version": "1.0.0-preview.47", + "version": "1.0.0-preview.48", "description": "Core abstractions for kiota generated libraries in TypeScript and JavaScript", "main": "dist/cjs/src/index.js", "module": "dist/es/src/index.js", diff --git a/packages/abstractions/src/serialization/index.ts b/packages/abstractions/src/serialization/index.ts index e5235b6d07..30196f7e22 100644 --- a/packages/abstractions/src/serialization/index.ts +++ b/packages/abstractions/src/serialization/index.ts @@ -12,3 +12,10 @@ export * from "./serializationWriterFactory"; export * from "./serializationWriterFactoryRegistry"; export * from "./serializationWriterProxyFactory"; export * from "./serializationFunctionTypes"; +export * from "./untypedNode"; +export * from "./untypedNumber"; +export * from "./untypedArray"; +export * from "./untypedNull"; +export * from "./untypedObject"; +export * from "./untypedString"; +export * from "./untypedBoolean"; diff --git a/packages/abstractions/src/serialization/untypedArray.ts b/packages/abstractions/src/serialization/untypedArray.ts new file mode 100644 index 0000000000..d976491950 --- /dev/null +++ b/packages/abstractions/src/serialization/untypedArray.ts @@ -0,0 +1,35 @@ +import { isUntypedNode, UntypedNode } from "./untypedNode"; + +/** Defines an interface for defining an untyped array. */ +export interface UntypedArray extends UntypedNode { + /** + * Gets the value of the UntypedNode as an array of UntypedNodes. + */ + getValue(): UntypedNode[]; +} + +/** + * Type guard to assert that an UntypedNode instance is an UntypedArray. + * @param node The UntypedNode to check. + * @return boolean indicating if the node is an UntypedArray. + */ +export function isUntypedArray(node: UntypedNode): node is UntypedArray { + const proposedNode = node as UntypedArray; + return ( + proposedNode && + proposedNode.value instanceof Array && + proposedNode.value.every((item) => isUntypedNode(item)) + ); +} + +/** + * Factory to create an UntypedArray from an array of UntypedNodes. + * @param value The value to create from. + * @return The created UntypedArray. + */ +export function createUntypedArray(value: UntypedNode[]): UntypedArray { + return { + value: value, + getValue: () => value as UntypedNode[], + }; +} diff --git a/packages/abstractions/src/serialization/untypedBoolean.ts b/packages/abstractions/src/serialization/untypedBoolean.ts new file mode 100644 index 0000000000..9e71190d23 --- /dev/null +++ b/packages/abstractions/src/serialization/untypedBoolean.ts @@ -0,0 +1,31 @@ +import { UntypedNode } from "./untypedNode"; + +/** Defines an interface for defining an untyped boolean. */ +export interface UntypedBoolean extends UntypedNode { + /** + * Gets the value of the UntypedNode as a boolean value. + */ + getValue(): boolean; +} + +/** + * Type guard to assert that an UntypedNode instance is an UntypedBoolean. + * @param node The UntypedNode to check. + * @return boolean indicating if the node is an UntypedBoolean. + */ +export function isUntypedBoolean(node: UntypedNode): node is UntypedBoolean { + const proposedNode = node as UntypedBoolean; + return proposedNode && typeof proposedNode.value === "boolean"; +} + +/** + * Factory to create an UntypedBoolean from a boolean. + * @param value The boolean value to create from. + * @return The created UntypedBoolean. + */ +export function createUntypedBoolean(value: boolean): UntypedBoolean { + return { + value: value, + getValue: () => value as boolean, + }; +} diff --git a/packages/abstractions/src/serialization/untypedNode.ts b/packages/abstractions/src/serialization/untypedNode.ts new file mode 100644 index 0000000000..314d4b9eaf --- /dev/null +++ b/packages/abstractions/src/serialization/untypedNode.ts @@ -0,0 +1,61 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import type { Parsable } from "./parsable"; +import type { ParseNode } from "./parseNode"; +import type { SerializationWriter } from "./serializationWriter"; + +/** Defines the base interface for defining an untyped node. */ +export interface UntypedNode extends Parsable { + /** + * Gets the value of the UntypedNode. + */ + getValue(): any; + /** + * The value represented by the UntypedNode. + */ + value?: any; +} + +/** + * Factory to create an UntypedNode from a string during deserialization. + */ +export function createUntypedNodeFromDiscriminatorValue( + _parseNode: ParseNode | undefined, +): (instance?: Parsable) => Record void> { + return deserializeIntoUntypedNode; +} + +/** + * Type guard to assert that an object instance is an UntypedNode. + * @param node The object to check. + * @return boolean indicating if the node is an UntypedNode. + */ +export function isUntypedNode(node: any): node is UntypedNode { + const potentialNode = node as UntypedNode; + return potentialNode && potentialNode.getValue !== undefined; +} + +/** + * The deserialization implementation for UntypedNode. + */ +export function deserializeIntoUntypedNode( + untypedNode: Partial | undefined = {}, +): Record void> { + return { + value: (n) => { + untypedNode.value = null; + }, + getValue: (n) => { + untypedNode.getValue = () => untypedNode.value; + }, + }; +} + +/** + * The serialization implementation for UntypedNode. + */ +export function serializeUntypedNode( + _writer: SerializationWriter, + _errorDetails: Partial | undefined = {}, +): void { + return; +} diff --git a/packages/abstractions/src/serialization/untypedNull.ts b/packages/abstractions/src/serialization/untypedNull.ts new file mode 100644 index 0000000000..bd7076c705 --- /dev/null +++ b/packages/abstractions/src/serialization/untypedNull.ts @@ -0,0 +1,29 @@ +import { UntypedNode } from "./untypedNode"; + +/** Defines the interface for defining an untyped null value. */ +export interface UntypedNull extends UntypedNode { + /** + * Gets the value of the UntypedNode as null. + */ + getValue(): null; +} + +/** + * Type guard to assert that an object instance is an UntypedNull. + * @param node The object to check. + * @return boolean indicating if the node is an UntypedNull. + */ +export function isUntypedNull(node: UntypedNode): node is UntypedNull { + return node.value === null; +} + +/** + * Factory to create an UntypedNull from a boolean. + * @return The created UntypedNull. + */ +export function createUntypedNull(): UntypedNull { + return { + value: null, + getValue: () => null, + }; +} diff --git a/packages/abstractions/src/serialization/untypedNumber.ts b/packages/abstractions/src/serialization/untypedNumber.ts new file mode 100644 index 0000000000..42822e1dfc --- /dev/null +++ b/packages/abstractions/src/serialization/untypedNumber.ts @@ -0,0 +1,31 @@ +import { UntypedNode } from "./untypedNode"; + +/** Defines the interface for defining an untyped number value. */ +export interface UntypedNumber extends UntypedNode { + /** + * Gets the value of the UntypedNode as a number. + */ + getValue(): number; +} + +/** + * Type guard to assert that an object instance is an UntypedNumber. + * @param node The object to check. + * @return boolean indicating if the node is an UntypedNumber. + */ +export function isUntypedNumber(node: UntypedNode): node is UntypedNumber { + const proposedNode = node as UntypedNumber; + return proposedNode && typeof proposedNode.value === "number"; +} + +/** + * Factory to create an UntypedNumber from a number. + * @param value The number value to create from. + * @return The created UntypedNumber. + */ +export function createUntypedNumber(value: number): UntypedNumber { + return { + value: value, + getValue: () => value as number, + }; +} diff --git a/packages/abstractions/src/serialization/untypedObject.ts b/packages/abstractions/src/serialization/untypedObject.ts new file mode 100644 index 0000000000..83c57f4513 --- /dev/null +++ b/packages/abstractions/src/serialization/untypedObject.ts @@ -0,0 +1,38 @@ +import { isUntypedNode, UntypedNode } from "./untypedNode"; + +/** Defines the interface for defining an untyped object value. */ +export interface UntypedObject extends UntypedNode { + /** + * Gets the value of the UntypedNode as a Record. + */ + getValue(): Record; +} + +/** + * Type guard to assert that an object instance is an UntypedObject. + * @param node The object to check. + * @return boolean indicating if the node is an UntypedObject. + */ +export function isUntypedObject(node: UntypedNode): node is UntypedObject { + const proposedNode = node as UntypedObject; + return ( + proposedNode && + proposedNode.value instanceof Object && + proposedNode.value instanceof Array === false && + Object.values(proposedNode.value).every((item) => isUntypedNode(item)) + ); +} + +/** + * Factory to create an UntypedObject from a Record. + * @param value The Record value to create from. + * @return The created UntypedObject. + */ +export function createUntypedObject( + value: Record, +): UntypedObject { + return { + value: value, + getValue: () => value as Record, + }; +} diff --git a/packages/abstractions/src/serialization/untypedString.ts b/packages/abstractions/src/serialization/untypedString.ts new file mode 100644 index 0000000000..cbcbd6300b --- /dev/null +++ b/packages/abstractions/src/serialization/untypedString.ts @@ -0,0 +1,31 @@ +import { UntypedNode } from "./untypedNode"; + +/** Defines the interface for defining an untyped string value. */ +export interface UntypedString extends UntypedNode { + /** + * Gets the value of the UntypedNode as a Record. + */ + getValue(): string; +} + +/** + * Type guard to assert that an object instance is an UntypedString. + * @param node The object to check. + * @return boolean indicating if the node is an UntypedString. + */ +export function isUntypedString(node: UntypedNode): node is UntypedString { + const proposedNode = node as UntypedString; + return proposedNode && typeof proposedNode.value === "string"; +} + +/** + * Factory to create an UntypedString from a string. + * @param value The string value to create from. + * @return The created UntypedString. + */ +export function createUntypedString(value: string): UntypedString { + return { + value: value, + getValue: () => value as string, + }; +} diff --git a/packages/authentication/azure/package.json b/packages/authentication/azure/package.json index 6a355c27e5..62e883df92 100644 --- a/packages/authentication/azure/package.json +++ b/packages/authentication/azure/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/kiota-authentication-azure", - "version": "1.0.0-preview.42", + "version": "1.0.0-preview.43", "description": "Authentication provider for Kiota using Azure Identity", "main": "dist/cjs/src/index.js", "module": "dist/es/src/index.js", @@ -30,7 +30,7 @@ "homepage": "https://github.com/microsoft/kiota-typescript#readme", "dependencies": { "@azure/core-auth": "^1.5.0", - "@microsoft/kiota-abstractions": "^1.0.0-preview.47", + "@microsoft/kiota-abstractions": "^1.0.0-preview.48", "@opentelemetry/api": "^1.7.0", "tslib": "^2.6.2" }, diff --git a/packages/authentication/spfx/package.json b/packages/authentication/spfx/package.json index 4a17696a72..2f5a0d859b 100644 --- a/packages/authentication/spfx/package.json +++ b/packages/authentication/spfx/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/kiota-authentication-spfx", - "version": "1.0.0-preview.37", + "version": "1.0.0-preview.38", "description": "Authentication provider for using Kiota in SPFx solutions", "main": "dist/cjs/src/index.js", "module": "dist/es/src/index.js", @@ -39,7 +39,7 @@ }, "homepage": "https://github.com/microsoft/kiota-typescript#readme", "dependencies": { - "@microsoft/kiota-abstractions": "^1.0.0-preview.47", + "@microsoft/kiota-abstractions": "^1.0.0-preview.48", "@microsoft/sp-http": "^1.15.2", "@opentelemetry/api": "^1.7.0", "tslib": "^2.6.2" diff --git a/packages/http/fetch/package.json b/packages/http/fetch/package.json index 74efcfb446..6bd63ccc05 100644 --- a/packages/http/fetch/package.json +++ b/packages/http/fetch/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/kiota-http-fetchlibrary", - "version": "1.0.0-preview.46", + "version": "1.0.0-preview.47", "description": "Kiota request adapter implementation with fetch", "keywords": [ "Kiota", @@ -38,7 +38,7 @@ "test:cjs": "mocha 'dist/cjs/test/common/**/*.js' && mocha 'dist/cjs/test/node/**/*.js'" }, "dependencies": { - "@microsoft/kiota-abstractions": "^1.0.0-preview.47", + "@microsoft/kiota-abstractions": "^1.0.0-preview.48", "@opentelemetry/api": "^1.7.0", "guid-typescript": "^1.0.9", "tslib": "^2.6.2" diff --git a/packages/serialization/form/package.json b/packages/serialization/form/package.json index 0e655bfcbe..cc37d61925 100644 --- a/packages/serialization/form/package.json +++ b/packages/serialization/form/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/kiota-serialization-form", - "version": "1.0.0-preview.36", + "version": "1.0.0-preview.37", "description": "Implementation of Kiota Serialization interfaces for URI from encoded", "main": "dist/cjs/src/index.js", "browser": { @@ -39,7 +39,7 @@ }, "homepage": "https://github.com/microsoft/kiota-typescript#readme", "dependencies": { - "@microsoft/kiota-abstractions": "^1.0.0-preview.47", + "@microsoft/kiota-abstractions": "^1.0.0-preview.48", "guid-typescript": "^1.0.9", "tslib": "^2.6.2" }, diff --git a/packages/serialization/json/package.json b/packages/serialization/json/package.json index 5f35f5adc5..aaef5c5e21 100644 --- a/packages/serialization/json/package.json +++ b/packages/serialization/json/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/kiota-serialization-json", - "version": "1.0.0-preview.47", + "version": "1.0.0-preview.48", "description": "Implementation of Kiota Serialization interfaces for JSON", "main": "dist/cjs/src/index.js", "browser": { @@ -39,7 +39,7 @@ }, "homepage": "https://github.com/microsoft/kiota-typescript#readme", "dependencies": { - "@microsoft/kiota-abstractions": "^1.0.0-preview.47", + "@microsoft/kiota-abstractions": "^1.0.0-preview.48", "guid-typescript": "^1.0.9", "tslib": "^2.6.2" }, diff --git a/packages/serialization/json/src/jsonParseNode.ts b/packages/serialization/json/src/jsonParseNode.ts index e5f32d7b94..3a57b77b3e 100644 --- a/packages/serialization/json/src/jsonParseNode.ts +++ b/packages/serialization/json/src/jsonParseNode.ts @@ -9,6 +9,21 @@ import { TimeOnly, isBackingStoreEnabled, toFirstCharacterUpper, + isUntypedNode, + UntypedNode, + UntypedArray, + UntypedBoolean, + UntypedNumber, + UntypedObject, + UntypedString, + createUntypedNodeFromDiscriminatorValue, + UntypedNull, + createUntypedBoolean, + createUntypedString, + createUntypedNumber, + createUntypedArray, + createUntypedObject, + createUntypedNull, } from "@microsoft/kiota-abstractions"; export class JsonParseNode implements ParseNode { @@ -78,6 +93,39 @@ export class JsonParseNode implements ParseNode { parsableFactory: ParsableFactory, ): T => { const temp: T = {} as T; + if (isUntypedNode(parsableFactory(this)(temp))) { + const valueType = typeof this._jsonNode; + let value: T = temp; + if (valueType === "boolean") { + value = createUntypedBoolean(this._jsonNode as boolean) as any as T; + } else if (valueType === "string") { + value = createUntypedString(this._jsonNode as string) as any as T; + } else if (valueType === "number") { + value = createUntypedNumber(this._jsonNode as number) as any as T; + } else if (Array.isArray(this._jsonNode)) { + const nodes: UntypedNode[] = []; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + (this._jsonNode as any[]).forEach((x) => { + nodes.push( + new JsonParseNode(x).getObjectValue( + createUntypedNodeFromDiscriminatorValue, + ), + ); + }); + value = createUntypedArray(nodes) as any as T; + } else if (this._jsonNode && valueType === "object") { + const properties: Record = {}; + Object.entries(this._jsonNode as any).forEach(([k, v]) => { + properties[k] = new JsonParseNode(v).getObjectValue( + createUntypedNodeFromDiscriminatorValue, + ); + }); + value = createUntypedObject(properties) as any as T; + } else if (!this._jsonNode) { + value = createUntypedNull() as any as T; + } + return value; + } const enableBackingStore = isBackingStoreEnabled(parsableFactory(this)(temp)); const value: T = enableBackingStore ? new Proxy(temp, createBackedModelProxyHandler()) : temp; if (this.onBeforeAssignFieldValues) { diff --git a/packages/serialization/json/src/jsonSerializationWriter.ts b/packages/serialization/json/src/jsonSerializationWriter.ts index 529e3832e7..0abd0e2b0f 100644 --- a/packages/serialization/json/src/jsonSerializationWriter.ts +++ b/packages/serialization/json/src/jsonSerializationWriter.ts @@ -2,10 +2,19 @@ import { DateOnly, Duration, + isUntypedNode, type ModelSerializerFunction, type Parsable, type SerializationWriter, - TimeOnly} from "@microsoft/kiota-abstractions"; + TimeOnly, + type UntypedNode, + isUntypedBoolean, + isUntypedString, + isUntypedNull, + isUntypedNumber, + isUntypedObject, + isUntypedArray, +} from "@microsoft/kiota-abstractions"; import type { Guid } from "guid-typescript"; export class JsonSerializationWriter implements SerializationWriter { @@ -103,12 +112,72 @@ export class JsonSerializationWriter implements SerializationWriter { value: T, serializerMethod: ModelSerializerFunction, ): void { - if (key) { + if (isUntypedNode(value)) { + const untypedNode = value as UntypedNode; + if (isUntypedBoolean(untypedNode)) { + this.writeBooleanValue(key, untypedNode.getValue()); + } else if (isUntypedString(untypedNode)) { + this.writeStringValue(key, untypedNode.getValue()); + } else if (isUntypedNull(untypedNode)) { + this.writeNullValue(key); + } else if (isUntypedNumber(untypedNode)) { + this.writeNumberValue(key, untypedNode.getValue()); + } else if (isUntypedObject(untypedNode)) { + const value = untypedNode.getValue(); + if (key && value) { + this.writePropertyName(key); + } + value && this.writer.push(`{`); + for (const key in value) { + this.writeObjectValue( + key, + value[key] as unknown as T, + serializerMethod, + ); + } + if ( + this.writer.length > 0 && + this.writer[this.writer.length - 1] === + JsonSerializationWriter.propertySeparator + ) { + //removing the last separator + this.writer.pop(); + } + value && this.writer.push(`}`); + key && this.writer.push(JsonSerializationWriter.propertySeparator); + } else if (isUntypedArray(untypedNode)) { + if (key) { + this.writePropertyName(key); + } + const value = untypedNode.getValue(); + this.writer.push(`[`); + value.forEach((v, idx) => { + this.writeObjectValue(undefined, v as unknown as T, serializerMethod); + idx + 1 < value.length && + this.writer.push(JsonSerializationWriter.propertySeparator); + }); + if ( + this.writer.length > 0 && + this.writer[this.writer.length - 1] === + JsonSerializationWriter.propertySeparator + ) { + //removing the last separator + this.writer.pop(); + } + this.writer.push(`]`); + key && this.writer.push(JsonSerializationWriter.propertySeparator); + } else { + this.writeAnyValue(key, untypedNode.getValue()); + } + return; // nothing to do here, the value has been written + } + + if (key && value) { this.writePropertyName(key); } this.onBeforeObjectSerialization && this.onBeforeObjectSerialization(value as unknown as Parsable); - this.writer.push(`{`); + value && this.writer.push(`{`); this.onStartObjectSerialization && this.onStartObjectSerialization(value as unknown as Parsable, this); @@ -124,7 +193,7 @@ export class JsonSerializationWriter implements SerializationWriter { //removing the last separator this.writer.pop(); } - this.writer.push(`}`); + value && this.writer.push(`}`); key && this.writer.push(JsonSerializationWriter.propertySeparator); } diff --git a/packages/serialization/json/test/common/JsonParseNode.ts b/packages/serialization/json/test/common/JsonParseNode.ts index 84d379ff68..7591dda878 100644 --- a/packages/serialization/json/test/common/JsonParseNode.ts +++ b/packages/serialization/json/test/common/JsonParseNode.ts @@ -1,5 +1,4 @@ import { assert } from "chai"; - import { JsonParseNode } from "../../src/index"; import { createTestParserFromDiscriminatorValue, @@ -7,6 +6,8 @@ import { createTestBackedModelFromDiscriminatorValue, type TestParser } from "./testEntity"; +import { UntypedTestEntity, createUntypedTestEntityFromDiscriminatorValue } from "./untypedTestEntiy"; +import { UntypedNode, UntypedObject, isUntypedArray, isUntypedBoolean, isUntypedNode, isUntypedNumber, isUntypedObject } from "@microsoft/kiota-abstractions"; describe("JsonParseNode", () => { it("jsonParseNode:initializes", async () => { @@ -169,4 +170,72 @@ describe("JsonParseNode", () => { assert.equal(jsonObjectStr, resultStr); }); + it("untyped nodes are deserialized correctly", async () => { + const jsonObject = { + id: "1", + title: "title", + location: { + address: { + city: "Redmond", + postalCode: "98052", + state: "Washington", + street: "NE 36th St", + }, + coordinates: { + latitude: 47.678581, + longitude: -122.131577, + }, + displayName: "Microsoft Building 25", + floorCount: 50, + hasReception: true, + contact: null, + }, + keywords: [ + { + created: "2023-07-26T10:41:26Z", + label: "Keyword1", + termGuid: "10e9cc83-b5a4-4c8d-8dab-4ada1252dd70", + wssId: 6442450941, + }, + { + created: "2023-07-26T10:51:26Z", + label: "Keyword2", + termGuid: "2cae6c6a-9bb8-4a78-afff-81b88e735fef", + wssId: 6442450942, + }, + ], + extra: { + value: { + createdDateTime: { + value: "2024-01-15T00:00:00+00:00", + }, + }, + }, + }; + + const result = new JsonParseNode(jsonObject).getObjectValue( + createUntypedTestEntityFromDiscriminatorValue, + ) as UntypedTestEntity; + assert.equal(result.id, "1"); + assert.equal(result.title, "title"); + assert.isNotNull(result.location); + assert.isTrue(isUntypedNode(result.location)); + const location = result.location as UntypedObject; + const locationProperties = location.getValue(); + assert.isTrue(isUntypedObject(location)); + assert.isTrue(isUntypedObject(locationProperties["address"])); + assert.isTrue(isUntypedObject(locationProperties["coordinates"])); + assert.isTrue(isUntypedBoolean(locationProperties["hasReception"])); + assert.isTrue(isUntypedNumber(locationProperties["floorCount"])); + assert.isTrue(isUntypedBoolean(locationProperties["hasReception"])); + assert.equal(locationProperties["hasReception"].getValue(), true); + assert.equal(locationProperties["contact"].getValue(), null); + assert.equal(locationProperties["floorCount"].getValue(), 50); + const keywords = result.keywords as UntypedNode; + assert.isTrue(isUntypedArray(keywords)); + assert.equal( + locationProperties["displayName"].getValue(), + "Microsoft Building 25", + ); + }); }); diff --git a/packages/serialization/json/test/common/jsonSerializationWriter.ts b/packages/serialization/json/test/common/jsonSerializationWriter.ts index 03b62bd1dd..24c0069dc7 100644 --- a/packages/serialization/json/test/common/jsonSerializationWriter.ts +++ b/packages/serialization/json/test/common/jsonSerializationWriter.ts @@ -6,6 +6,8 @@ import { serializeTestParser, type TestParser, } from "./testEntity"; +import { UntypedTestEntity, serializeUntypedTestEntity } from "./untypedTestEntiy"; +import { UntypedArray, UntypedBoolean, UntypedNull, UntypedNumber, UntypedObject, UntypedString, createUntypedArray, createUntypedBoolean, createUntypedNull, createUntypedNumber, createUntypedObject, createUntypedString } from "@microsoft/kiota-abstractions"; describe("JsonParseNode", () => { it("Test object serialization", async () => { @@ -76,4 +78,54 @@ describe("JsonParseNode", () => { const result = JSON.parse(contentAsStr); assert.equal(result.testComplexString, "BÅ‚onie"); }); + it("serializes untyped nodes as expected", async () => { + const inputObject: UntypedTestEntity = { + id: "1", + title: "title", + location: createUntypedObject({ + address: createUntypedObject({ + city: createUntypedString("Redmond"), + postalCode: createUntypedString("98052"), + state: createUntypedString("Washington"), + street: createUntypedString("NE 36th St"), + }), + coordinates: createUntypedObject({ + latitude: createUntypedNumber(47.678581), + longitude: createUntypedNumber(-122.131577), + }), + displayName: createUntypedString("Microsoft Building 25"), + floorCount: createUntypedNumber(50), + hasReception: createUntypedBoolean(true), + contact: createUntypedNull(), + }), + keywords: createUntypedArray([ + createUntypedObject({ + created: createUntypedString("2023-07-26T10:41:26Z"), + label: createUntypedString("Keyword1"), + termGuid: createUntypedString("10e9cc83-b5a4-4c8d-8dab-4ada1252dd70"), + wssId: createUntypedNumber(6442450941), + }), + createUntypedObject({ + created: createUntypedString("2023-07-26T10:51:26Z"), + label: createUntypedString("Keyword2"), + termGuid: createUntypedString("2cae6c6a-9bb8-4a78-afff-81b88e735fef"), + wssId: createUntypedNumber(6442450942), + }), + ]), + additionalData: { + extra: createUntypedObject({ + createdDateTime: createUntypedString("2024-01-15T00:00:00+00:00"), + }), + }, + }; + const writer = new JsonSerializationWriter(); + writer.writeObjectValue("", inputObject, serializeUntypedTestEntity); + const serializedContent = writer.getSerializedContent(); + const decoder = new TextDecoder(); + const contentAsStr = decoder.decode(serializedContent); + assert.equal( + '{"id":"1","title":"title","location":{"address":{"city":"Redmond","postalCode":"98052","state":"Washington","street":"NE 36th St"},"coordinates":{"latitude":47.678581,"longitude":-122.131577},"displayName":"Microsoft Building 25","floorCount":50,"hasReception":true,"contact":null},"keywords":[{"created":"2023-07-26T10:41:26Z","label":"Keyword1","termGuid":"10e9cc83-b5a4-4c8d-8dab-4ada1252dd70","wssId":6442450941},{"created":"2023-07-26T10:51:26Z","label":"Keyword2","termGuid":"2cae6c6a-9bb8-4a78-afff-81b88e735fef","wssId":6442450942}],"extra":{"value":{"createdDateTime":{"value":"2024-01-15T00:00:00+00:00"}}}}', + contentAsStr, + ); + }); }); diff --git a/packages/serialization/json/test/common/untypedTestEntiy.ts b/packages/serialization/json/test/common/untypedTestEntiy.ts new file mode 100644 index 0000000000..87e1e817b0 --- /dev/null +++ b/packages/serialization/json/test/common/untypedTestEntiy.ts @@ -0,0 +1,62 @@ +import { + createUntypedNodeFromDiscriminatorValue, + SerializationWriter, + type ParseNode, + type UntypedNode, +} from "@microsoft/kiota-abstractions"; + +export interface UntypedTestEntity { + id?: string | undefined; + title?: string | undefined; + location?: UntypedNode | undefined; + keywords?: UntypedNode | undefined; + detail?: UntypedNode | undefined; + additionalData?: Record; +} + +export function createUntypedTestEntityFromDiscriminatorValue( + parseNode: ParseNode | undefined, +) { + if (!parseNode) throw new Error("parseNode cannot be undefined"); + return deserializeUntypedTestEntity; +} + +export function deserializeUntypedTestEntity( + untypedTestEntity: UntypedTestEntity | undefined = {}, +): Record void> { + return { + id: (n) => { + untypedTestEntity.id = n.getStringValue(); + }, + title: (n) => { + untypedTestEntity.title = n.getStringValue(); + }, + location: (n) => { + untypedTestEntity.location = n.getObjectValue( + createUntypedNodeFromDiscriminatorValue, + ); + }, + keywords: (n) => { + untypedTestEntity.keywords = n.getObjectValue( + createUntypedNodeFromDiscriminatorValue, + ); + }, + detail: (n) => { + untypedTestEntity.detail = n.getObjectValue( + createUntypedNodeFromDiscriminatorValue, + ); + }, + }; +} + +export function serializeUntypedTestEntity( + writer: SerializationWriter, + untypedTestEntity: UntypedTestEntity | undefined = {}, +): void { + writer.writeStringValue("id", untypedTestEntity.id); + writer.writeStringValue("title", untypedTestEntity.title); + writer.writeObjectValue("location", untypedTestEntity.location); + writer.writeObjectValue("keywords", untypedTestEntity.keywords); + writer.writeObjectValue("detail", untypedTestEntity.detail); + writer.writeAdditionalData(untypedTestEntity.additionalData); +} diff --git a/packages/serialization/multipart/package.json b/packages/serialization/multipart/package.json index d73c8e88a5..3794a48644 100644 --- a/packages/serialization/multipart/package.json +++ b/packages/serialization/multipart/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/kiota-serialization-multipart", - "version": "1.0.0-preview.26", + "version": "1.0.0-preview.27", "description": "Implementation of Kiota Serialization interfaces for multipart form data", "main": "dist/cjs/src/index.js", "module": "dist/es/src/index.js", @@ -40,7 +40,7 @@ "tslib": "^2.6.2" }, "devDependencies": { - "@microsoft/kiota-serialization-json": "^1.0.0-preview.47" + "@microsoft/kiota-serialization-json": "^1.0.0-preview.48" }, "publishConfig": { "access": "public" diff --git a/packages/serialization/text/package.json b/packages/serialization/text/package.json index f3fe6fdf9d..5929e9fba9 100644 --- a/packages/serialization/text/package.json +++ b/packages/serialization/text/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/kiota-serialization-text", - "version": "1.0.0-preview.44", + "version": "1.0.0-preview.45", "description": "Implementation of Kiota Serialization interfaces for text", "main": "dist/cjs/src/index.js", "browser": { @@ -39,7 +39,7 @@ }, "homepage": "https://github.com/microsoft/kiota-typescript#readme", "dependencies": { - "@microsoft/kiota-abstractions": "^1.0.0-preview.47", + "@microsoft/kiota-abstractions": "^1.0.0-preview.48", "guid-typescript": "^1.0.9", "tslib": "^2.6.2" },