Skip to content

Commit

Permalink
Merge pull request #1347 from microsoft/andrueastman/specialCasedEnums
Browse files Browse the repository at this point in the history
Fixes deserialization of enums with special characters
  • Loading branch information
andrueastman authored Sep 3, 2024
2 parents b6936b9 + 6f990db commit b3b787c
Show file tree
Hide file tree
Showing 18 changed files with 95 additions and 59 deletions.
2 changes: 1 addition & 1 deletion packages/abstractions/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@microsoft/kiota-abstractions",
"version": "1.0.0-preview.61",
"version": "1.0.0-preview.62",
"description": "Core abstractions for kiota generated libraries in TypeScript and JavaScript",
"main": "dist/es/src/index.js",
"module": "dist/es/src/index.js",
Expand Down
21 changes: 21 additions & 0 deletions packages/abstractions/src/utils/enumUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* -------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
* See License in the project root for license information.
* -------------------------------------------------------------------------------------------
*/
function reverseRecord(input: Record<PropertyKey, PropertyKey>): Record<PropertyKey, PropertyKey> {
const entries = Object.entries(input).map(([key, value]) => [value, key]);
return Object.fromEntries(entries) as Record<PropertyKey, PropertyKey>;
}

/**
* Factory to create an UntypedString from a string.
* @param stringValue The string value to lookup the enum value from.
* @param originalType The type definition of the enum.
* @return The enu value.
*/
export function getEnumValueFromStringValue<T>(stringValue: string, originalType: Record<PropertyKey, PropertyKey>): T | undefined {
const reversed: Record<PropertyKey, PropertyKey> = reverseRecord(originalType);
return originalType[reversed[stringValue]] as T;
}
2 changes: 1 addition & 1 deletion packages/abstractions/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
* See License in the project root for license information.
* -------------------------------------------------------------------------------------------
*/
export * from "./stringUtils";
export * from "./enumUtils";
export * from "./guidUtils";
export * from "./inNodeEnv";
13 changes: 0 additions & 13 deletions packages/abstractions/src/utils/stringUtils.ts

This file was deleted.

26 changes: 0 additions & 26 deletions packages/abstractions/test/common/stringUtils.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/authentication/azure/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@microsoft/kiota-authentication-azure",
"version": "1.0.0-preview.56",
"version": "1.0.0-preview.57",
"description": "Authentication provider for Kiota using Azure Identity",
"main": "dist/es/src/index.js",
"module": "dist/es/src/index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/authentication/spfx/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@microsoft/kiota-authentication-spfx",
"version": "1.0.0-preview.50",
"version": "1.0.0-preview.51",
"description": "Authentication provider for using Kiota in SPFx solutions",
"main": "dist/es/src/index.js",
"module": "dist/es/src/index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/bundle/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@microsoft/kiota-bundle",
"version": "1.0.0-preview.4",
"version": "1.0.0-preview.5",
"description": "Kiota Bundle package providing default implementations for client setup for kiota generated libraries in TypeScript and JavaScript",
"main": "dist/es/src/index.js",
"module": "dist/es/src/index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/http/fetch/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@microsoft/kiota-http-fetchlibrary",
"version": "1.0.0-preview.60",
"version": "1.0.0-preview.61",
"description": "Kiota request adapter implementation with fetch",
"keywords": [
"Kiota",
Expand Down
11 changes: 5 additions & 6 deletions packages/serialization/form/src/formParseNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* -------------------------------------------------------------------------------------------
*/

import { createBackedModelProxyHandler, DateOnly, Duration, type Parsable, type ParsableFactory, parseGuidString, type ParseNode, TimeOnly, isBackingStoreEnabled, toFirstCharacterUpper } from "@microsoft/kiota-abstractions";
import { createBackedModelProxyHandler, DateOnly, Duration, type Parsable, type ParsableFactory, parseGuidString, type ParseNode, TimeOnly, isBackingStoreEnabled, getEnumValueFromStringValue } from "@microsoft/kiota-abstractions";

export class FormParseNode implements ParseNode {
private readonly _fields: Record<string, string> = {};
Expand Down Expand Up @@ -104,15 +104,14 @@ export class FormParseNode implements ParseNode {
if (!rawValues) {
return [];
}
return rawValues.split(",").map((x) => type[toFirstCharacterUpper(x)] as T);
return rawValues.split(",").map((x) => getEnumValueFromStringValue(x, type) as T);
};
public getEnumValue = <T>(type: any): T | undefined => {
const values = this.getCollectionOfEnumValues(type);
if (values.length > 0) {
return values[0] as T;
} else {
const rawValue = this.getStringValue();
if (!rawValue) {
return undefined;
}
return getEnumValueFromStringValue(rawValue, type as Record<PropertyKey, PropertyKey>) as T;
};
private assignFieldValues = <T extends Parsable>(model: T, parsableFactory: ParsableFactory<T>): void => {
const fields = parsableFactory(this)(model);
Expand Down
17 changes: 17 additions & 0 deletions packages/serialization/form/test/common/formParseNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,21 @@ describe("FormParseNode", () => {
const imaginaryNode = parseNode.getChildNode("imaginaryNode");
assert.isUndefined(imaginaryNode);
});
it("Test enum values with special or reserved characters", async () => {
type Test_status = (typeof Test_statusObject)[keyof typeof Test_statusObject];
const Test_statusObject = {
IN_PROGRESS: "IN_PROGRESS",
INPROGRESS: "IN PROGRESS",
NEW_ESCAPED: "NEW",
} as const;

const enumValueResult = new FormParseNode("NEW").getEnumValue(Test_statusObject) as Test_status;
assert.equal(enumValueResult, Test_statusObject.NEW_ESCAPED);

const enumValueResult2 = new FormParseNode("IN_PROGRESS").getEnumValue(Test_statusObject) as Test_status;
assert.equal(enumValueResult2, Test_statusObject.IN_PROGRESS);

const enumValueResult3 = new FormParseNode("IN PROGRESS").getEnumValue(Test_statusObject) as Test_status;
assert.equal(enumValueResult3, Test_statusObject.INPROGRESS);
});
});
2 changes: 1 addition & 1 deletion packages/serialization/json/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@microsoft/kiota-serialization-json",
"version": "1.0.0-preview.61",
"version": "1.0.0-preview.62",
"description": "Implementation of Kiota Serialization interfaces for JSON",
"main": "dist/es/src/index.js",
"type": "module",
Expand Down
6 changes: 3 additions & 3 deletions packages/serialization/json/src/jsonParseNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
* -------------------------------------------------------------------------------------------
*/

import { DateOnly, Duration, TimeOnly, UntypedNode, createBackedModelProxyHandler, createUntypedArray, createUntypedBoolean, createUntypedNodeFromDiscriminatorValue, createUntypedNull, createUntypedNumber, createUntypedObject, createUntypedString, inNodeEnv, isBackingStoreEnabled, isUntypedNode, parseGuidString, toFirstCharacterUpper, type Parsable, type ParsableFactory, type ParseNode } from "@microsoft/kiota-abstractions";

import { DateOnly, Duration, TimeOnly, UntypedNode, createBackedModelProxyHandler, createUntypedArray, createUntypedBoolean, createUntypedNodeFromDiscriminatorValue, createUntypedNull, createUntypedNumber, createUntypedObject, createUntypedString, inNodeEnv, isBackingStoreEnabled, isUntypedNode, parseGuidString, getEnumValueFromStringValue, type Parsable, type ParsableFactory, type ParseNode } from "@microsoft/kiota-abstractions";
export class JsonParseNode implements ParseNode {
constructor(private readonly _jsonNode: unknown) {}
public onBeforeAssignFieldValues: ((value: Parsable) => void) | undefined;
Expand Down Expand Up @@ -125,11 +124,12 @@ export class JsonParseNode implements ParseNode {
}
return [];
};

public getEnumValue = <T>(type: unknown): T | undefined => {
const rawValue = this.getStringValue();
if (!rawValue) {
return undefined;
}
return (type as Record<string, T>)[toFirstCharacterUpper(rawValue)];
return getEnumValueFromStringValue(rawValue, type as Record<PropertyKey, PropertyKey>) as T;
};
}
18 changes: 18 additions & 0 deletions packages/serialization/json/test/common/JsonParseNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,24 @@ describe("JsonParseNode", () => {
assert.equal(enumValueResult, TestEnumObject.A);
});

it("Test enum values with special or reserved characters", async () => {
type Test_status = (typeof Test_statusObject)[keyof typeof Test_statusObject];
const Test_statusObject = {
IN_PROGRESS: "IN_PROGRESS",
INPROGRESS: "IN PROGRESS",
NEW_ESCAPED: "NEW",
} as const;

const enumValueResult = new JsonParseNode("NEW").getEnumValue(Test_statusObject) as Test_status;
assert.equal(enumValueResult, Test_statusObject.NEW_ESCAPED);

const enumValueResult2 = new JsonParseNode("IN_PROGRESS").getEnumValue(Test_statusObject) as Test_status;
assert.equal(enumValueResult2, Test_statusObject.IN_PROGRESS);

const enumValueResult3 = new JsonParseNode("IN PROGRESS").getEnumValue(Test_statusObject) as Test_status;
assert.equal(enumValueResult3, Test_statusObject.INPROGRESS);
});

it("Test an undefined collection of object values", async () => {
const result = new JsonParseNode({
foos: [
Expand Down
2 changes: 1 addition & 1 deletion packages/serialization/multipart/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@microsoft/kiota-serialization-multipart",
"version": "1.0.0-preview.39",
"version": "1.0.0-preview.40",
"description": "Implementation of Kiota Serialization interfaces for multipart form data",
"main": "dist/es/src/index.js",
"module": "dist/es/src/index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/serialization/text/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@microsoft/kiota-serialization-text",
"version": "1.0.0-preview.58",
"version": "1.0.0-preview.59",
"description": "Implementation of Kiota Serialization interfaces for text",
"main": "dist/es/src/index.js",
"browser": {
Expand Down
8 changes: 6 additions & 2 deletions packages/serialization/text/src/textParseNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* -------------------------------------------------------------------------------------------
*/

import { DateOnly, Duration, type Parsable, type ParsableFactory, parseGuidString, type ParseNode, TimeOnly, toFirstCharacterUpper, inNodeEnv } from "@microsoft/kiota-abstractions";
import { DateOnly, Duration, type Parsable, type ParsableFactory, parseGuidString, type ParseNode, TimeOnly, inNodeEnv, getEnumValueFromStringValue } from "@microsoft/kiota-abstractions";

export class TextParseNode implements ParseNode {
private static noStructuredDataMessage = "text does not support structured data";
Expand Down Expand Up @@ -64,6 +64,10 @@ export class TextParseNode implements ParseNode {
throw new Error(TextParseNode.noStructuredDataMessage);
};
public getEnumValue = <T>(type: any): T | undefined => {
return type[toFirstCharacterUpper(this.text)] as T;
const rawValue = this.getStringValue();
if (!rawValue) {
return undefined;
}
return getEnumValueFromStringValue(rawValue, type as Record<PropertyKey, PropertyKey>) as T;
};
}
16 changes: 16 additions & 0 deletions packages/serialization/text/test/common/textParseNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,23 @@ describe("textParseNode", () => {
const textParseNode = new TextParseNode("Test");
assert.isDefined(textParseNode);
});
it("Test enum values with special or reserved characters", async () => {
type Test_status = (typeof Test_statusObject)[keyof typeof Test_statusObject];
const Test_statusObject = {
IN_PROGRESS: "IN_PROGRESS",
INPROGRESS: "IN PROGRESS",
NEW_ESCAPED: "NEW",
} as const;

const enumValueResult = new TextParseNode("NEW").getEnumValue(Test_statusObject) as Test_status;
assert.equal(enumValueResult, Test_statusObject.NEW_ESCAPED);

const enumValueResult2 = new TextParseNode("IN_PROGRESS").getEnumValue(Test_statusObject) as Test_status;
assert.equal(enumValueResult2, Test_statusObject.IN_PROGRESS);

const enumValueResult3 = new TextParseNode("IN PROGRESS").getEnumValue(Test_statusObject) as Test_status;
assert.equal(enumValueResult3, Test_statusObject.INPROGRESS);
});
it("parses guid values", async () => {
const emptyGuidParseNode = new TextParseNode("00000000-0000-0000-0000-000000000000");
assert.isDefined(emptyGuidParseNode);
Expand Down

0 comments on commit b3b787c

Please sign in to comment.