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

Validate the serialization/deserialization of composed types in typescript #1290

Merged
merged 6 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion packages/serialization/json/test/common/JsonParseNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { assert, describe, it } from "vitest";
import { JsonParseNode } from "../../src/index";
import { createTestParserFromDiscriminatorValue, type TestBackedModel, createTestBackedModelFromDiscriminatorValue, type TestParser } from "./testEntity";
import { createTestParserFromDiscriminatorValue, type TestBackedModel, createTestBackedModelFromDiscriminatorValue, type TestParser, TestUnionObject, BarResponse } from "./testEntity";
import { UntypedTestEntity, createUntypedTestEntityFromDiscriminatorValue } from "./untypedTestEntiy";
import { UntypedNode, UntypedObject, isUntypedArray, isUntypedBoolean, isUntypedNode, isUntypedNumber, isUntypedObject } from "@microsoft/kiota-abstractions";

Expand Down Expand Up @@ -288,4 +288,30 @@ describe("JsonParseNode", () => {
const result5 = new JsonParseNode("true");
assert.isUndefined(result5.getBooleanValue());
});

it("should parse a union of objects and primitive values when value is primitive", async () => {
const result = new JsonParseNode({
testUnionObject: "Test String Value",
}).getObjectValue(createTestParserFromDiscriminatorValue) as TestParser;
assert.equal(result.testUnionObject, "Test String Value");
});

it("should parse a union of objects and primitive values when value is an object", async () => {
const barResponse = {
propA: "property A test value",
propB: "property B test value",
propC: undefined,
};
const result = new JsonParseNode({
testUnionObject: barResponse as TestUnionObject,
}).getObjectValue(createTestParserFromDiscriminatorValue) as TestParser;
assert.equal(JSON.stringify(result.testUnionObject), JSON.stringify(barResponse));
});

it("should parse a union of objects and primitive values when value is a number", async () => {
const result = new JsonParseNode({
testUnionObject: 1234,
}).getObjectValue(createTestParserFromDiscriminatorValue) as TestParser;
assert.equal(result.testUnionObject, 1234);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -230,4 +230,51 @@ describe("JsonParseNode", () => {
const contentAsStr = decoder.decode(serializedContent);
assert.equal(contentAsStr, '{"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"}}}}');
});

it("it should serialize a union of object and primitive when the value is a string", async () => {
const inputObject: TestParser = {
baywet marked this conversation as resolved.
Show resolved Hide resolved
testUnionObject: "Test String value",
};
const writer = new JsonSerializationWriter();
writer.writeObjectValue("", inputObject, serializeTestParser);
const serializedContent = writer.getSerializedContent();
const decoder = new TextDecoder();
const contentAsStr = decoder.decode(serializedContent);
const result = JSON.parse(contentAsStr);
assert.isTrue("testUnionObject" in result);
assert.equal(result["testUnionObject"], "Test String value");
});

it("it should serialize a union of object and primitive when the value is a number", async () => {
const inputObject: TestParser = {
testUnionObject: 1234,
};
const writer = new JsonSerializationWriter();
writer.writeObjectValue("", inputObject, serializeTestParser);
const serializedContent = writer.getSerializedContent();
const decoder = new TextDecoder();
const contentAsStr = decoder.decode(serializedContent);
const result = JSON.parse(contentAsStr);
assert.isTrue("testUnionObject" in result);
assert.equal(result["testUnionObject"], 1234);
});

it("it should serialize a union of object and primitive when the value is an object", async () => {
const barResponse = {
propA: "property A test value",
propB: "property B test value",
propC: undefined,
};
const inputObject: TestParser = {
testUnionObject: barResponse,
};
const writer = new JsonSerializationWriter();
writer.writeObjectValue("", inputObject, serializeTestParser);
const serializedContent = writer.getSerializedContent();
const decoder = new TextDecoder();
const contentAsStr = decoder.decode(serializedContent);
const result = JSON.parse(contentAsStr);
assert.isTrue("testUnionObject" in result);
assert.equal(JSON.stringify(result["testUnionObject"]), JSON.stringify(barResponse));
});
});
32 changes: 32 additions & 0 deletions packages/serialization/json/test/common/testEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import type { BackedModel, BackingStore, Parsable, ParseNode, SerializationWriter } from "@microsoft/kiota-abstractions";
import { Guid } from "guid-typescript";
import { write } from "node:fs";
import test from "node:test";
baywet marked this conversation as resolved.
Show resolved Hide resolved

const fakeBackingStore: BackingStore = {} as BackingStore;

Expand All @@ -22,6 +24,7 @@ export interface TestParser {
id?: string | undefined;
testNumber?: number | undefined;
testGuid?: Guid | undefined;
testUnionObject?: TestUnionObject | undefined;
}
export interface TestBackedModel extends TestParser, BackedModel {
backingStoreEnabled?: boolean | undefined;
Expand All @@ -35,6 +38,7 @@ export interface BarResponse extends Parsable {
propB?: string | undefined;
propC?: Date | undefined;
}
export type TestUnionObject = FooResponse | BarResponse | string | number;

export function createTestParserFromDiscriminatorValue(parseNode: ParseNode | undefined) {
if (!parseNode) throw new Error("parseNode cannot be undefined");
Expand Down Expand Up @@ -85,6 +89,9 @@ export function deserializeTestParser(testParser: TestParser | undefined = {}):
testGuid: (n) => {
testParser.testGuid = n.getGuidValue();
},
testUnionObject: (n) => {
testParser.testUnionObject = n.getStringValue() ?? n.getNumberValue() ?? n.getObjectValue(createTestUnionObjectFromDiscriminatorValue);
},
};
}

Expand Down Expand Up @@ -137,6 +144,13 @@ export function serializeTestParser(writer: SerializationWriter, entity: TestPar
writer.writeObjectValue("testObject", entity.testObject, serializeTestObject);
writer.writeCollectionOfObjectValues("foos", entity.foos, serializeFoo);
writer.writeAdditionalData(entity.additionalData);
if (typeof entity.testUnionObject === "string") {
writer.writeStringValue("testUnionObject", entity.testUnionObject);
} else if (typeof entity.testUnionObject === "number") {
writer.writeNumberValue("testUnionObject", entity.testUnionObject);
} else {
writer.writeObjectValue("testUnionObject", entity.testUnionObject as any, serializeTestUnionObject);
}
}

export function serializeFoo(writer: SerializationWriter, entity: FooResponse | undefined = {}): void {
Expand All @@ -153,3 +167,21 @@ export function serializeBar(writer: SerializationWriter, entity: BarResponse |
export function serializeTestBackModel(writer: SerializationWriter, entity: TestBackedModel | undefined = {}): void {
serializeTestParser(writer, entity);
}

// Factory Method
export function createTestUnionObjectFromDiscriminatorValue(parseNode: ParseNode | undefined): (instance?: Parsable) => Record<string, (node: ParseNode) => void> {
return deserializeIntoTestUnionObject;
}

// Deserialization methods
export function deserializeIntoTestUnionObject(fooBar: Partial<TestUnionObject> | undefined = {}): Record<string, (node: ParseNode) => void> {
return {
...deserializeFooParser(fooBar as FooResponse),
...deserializeBarParser(fooBar as BarResponse),
};
}

export function serializeTestUnionObject(writer: SerializationWriter, fooBar: Partial<TestUnionObject> | undefined = {}): void {
serializeFoo(writer, fooBar as FooResponse);
serializeBar(writer, fooBar as BarResponse);
}
Loading