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

[TypeScript] Untyped Nodes #1084

Merged
merged 14 commits into from
Mar 20, 2024
Prev Previous commit
Next Next commit
Untyped nodes in typescript
Andrew Omondi committed Feb 25, 2024

Verified

This commit was signed with the committer’s verified signature.
rygine Ry Racherbaumer
commit 8b7ccbddc42c688419bdca101b09348719b72a54
8 changes: 6 additions & 2 deletions packages/abstractions/src/serialization/untypedArray.ts
Original file line number Diff line number Diff line change
@@ -10,6 +10,10 @@ export class UntypedArray extends UntypedNode {
}

export function isUntypedArray(node: UntypedNode): node is UntypedArray {
const value = (node as UntypedArray).value;
return value instanceof Array && value.every((item) => isUntypedNode(item));
const proposedNode = node as UntypedArray;
return (
proposedNode &&
proposedNode.value instanceof Array &&
proposedNode.value.every((item) => isUntypedNode(item))
);
}
Original file line number Diff line number Diff line change
@@ -10,5 +10,5 @@ export class UntypedBoolean extends UntypedNode {
}

export function isUntypedBoolean(node: UntypedNode): node is UntypedBoolean {
return typeof (node as UntypedBoolean).value === "boolean";
return typeof (node as UntypedBoolean)?.value === "boolean";
}
11 changes: 9 additions & 2 deletions packages/abstractions/src/serialization/untypedNode.ts
Original file line number Diff line number Diff line change
@@ -25,9 +25,16 @@ export function isUntypedNode(node: any): node is UntypedNode {
}

export function deserializeIntoUntypedNode(
_untypedNode: Partial<UntypedNode> | undefined = {},
untypedNode: Partial<UntypedNode> | undefined = {},
): Record<string, (node: ParseNode) => void> {
return {};
return {
value: (n) => {
untypedNode.value = null;
},
getValue: (n) => {
untypedNode.getValue = () => untypedNode.value;
},
};
}

export function serializeUntypedNode(
2 changes: 1 addition & 1 deletion packages/abstractions/src/serialization/untypedNull.ts
Original file line number Diff line number Diff line change
@@ -10,5 +10,5 @@ export class UntypedNull extends UntypedNode {
}

export function isUntypedNull(node: UntypedNode): node is UntypedNull {
return (node as UntypedNull).value === null;
return (node as UntypedNull)?.value === null;
}
2 changes: 1 addition & 1 deletion packages/abstractions/src/serialization/untypedNumber.ts
Original file line number Diff line number Diff line change
@@ -10,5 +10,5 @@ export class UntypedNumber extends UntypedNode {
}

export function isUntypedNumber(node: UntypedNode): node is UntypedNumber {
return typeof (node as UntypedNumber).value === "number";
return typeof (node as UntypedNumber)?.value === "number";
}
2 changes: 1 addition & 1 deletion packages/abstractions/src/serialization/untypedObject.ts
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ export class UntypedObject extends UntypedNode {
}

export function isUntypedObject(node: UntypedNode): node is UntypedObject {
const value = (node as UntypedObject).value;
const value = (node as UntypedObject)?.value;
return (
value instanceof Object &&
value instanceof Array === false &&
2 changes: 1 addition & 1 deletion packages/abstractions/src/serialization/untypedString.ts
Original file line number Diff line number Diff line change
@@ -10,5 +10,5 @@ export class UntypedString extends UntypedNode {
}

export function isUntypedString(node: UntypedNode): node is UntypedString {
return typeof (node as UntypedString).value === "string";
return typeof (node as UntypedString)?.value === "string";
}
25 changes: 14 additions & 11 deletions packages/serialization/json/src/jsonParseNode.ts
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ import {
UntypedObject,
UntypedString,
createUntypedNodeFromDiscriminatorValue,
UntypedNull,
} from "@microsoft/kiota-abstractions";

export class JsonParseNode implements ParseNode {
@@ -81,33 +82,35 @@ export class JsonParseNode implements ParseNode {
): T => {
const temp: T = {} as T;
if (isUntypedNode(parsableFactory(this)(temp))) {
const valueType = typeof temp;
const valueType = typeof this._jsonNode;
let value: T = temp;
if (valueType === "boolean") {
value = new UntypedBoolean(value as any as boolean) as any as T;
value = new UntypedBoolean(this._jsonNode as boolean) as any as T;
} else if (valueType === "string") {
value = new UntypedString(value as any as string) as any as T;
value = new UntypedString(this._jsonNode as string) as any as T;
} else if (valueType === "number") {
value = new UntypedNumber(value as any as number) as any as T;
} else if (Array.isArray(value)) {
value = new UntypedNumber(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
value.forEach((v, idx) => {
const currentParseNode = new JsonParseNode(v);
const untypedNode: UntypedNode = currentParseNode.getObjectValue(
createUntypedNodeFromDiscriminatorValue,
(this._jsonNode as any[]).forEach((x) => {
nodes.push(
new JsonParseNode(x).getObjectValue(
createUntypedNodeFromDiscriminatorValue,
),
);
nodes.push(untypedNode);
});
value = new UntypedArray(nodes) as any as T;
} else if (valueType === "object") {
} else if (this._jsonNode && valueType === "object") {
const properties: Record<string, UntypedNode> = {};
Object.entries(this._jsonNode as any).forEach(([k, v]) => {
properties[k] = new JsonParseNode(v).getObjectValue(
createUntypedNodeFromDiscriminatorValue,
);
});
value = new UntypedObject(properties) as any as T;
} else if (!this._jsonNode) {
value = new UntypedNull() as any as T;
}
return value;
}
71 changes: 70 additions & 1 deletion packages/serialization/json/test/common/JsonParseNode.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { assert } from "chai";

import { JsonParseNode } from "../../src/index";
import {
createTestParserFromDiscriminatorValue,
type TestBackedModel,
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",
);
});
});
12 changes: 6 additions & 6 deletions packages/serialization/json/test/common/untypedTestEntiy.ts
Original file line number Diff line number Diff line change
@@ -32,19 +32,19 @@ export function deserializeUntypedTestEntity(
untypedTestEntity.title = n.getStringValue();
},
location: (n) => {
untypedTestEntity.location = n.getObjectValue(
untypedTestEntity.location = n.getObjectValue<UntypedNode>(
createUntypedNodeFromDiscriminatorValue,
) as UntypedNode;
);
},
keywords: (n) => {
untypedTestEntity.keywords = n.getObjectValue(
untypedTestEntity.keywords = n.getObjectValue<UntypedNode>(
createUntypedNodeFromDiscriminatorValue,
) as UntypedNode;
);
},
detail: (n) => {
untypedTestEntity.detail = n.getObjectValue(
untypedTestEntity.detail = n.getObjectValue<UntypedNode>(
createUntypedNodeFromDiscriminatorValue,
) as UntypedNode;
);
},
};
}