Skip to content

Commit 0a09f5c

Browse files
authored
fix: implements serialization of enum collections (#1578)
fix: implements serialization of enum collections
1 parent 860fa87 commit 0a09f5c

File tree

10 files changed

+163
-3
lines changed

10 files changed

+163
-3
lines changed

packages/abstractions/src/serialization/serializationWriter.ts

+7
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ export interface SerializationWriter {
9393
* @param values the value to write to the stream.
9494
*/
9595
writeEnumValue<T>(key?: string, ...values: (T | null | undefined)[]): void;
96+
97+
/**
98+
* Writes the specified collection of enum values to the stream with an optional given key.
99+
* @param key the key to write the value with.
100+
* @param values the value to write to the stream.
101+
*/
102+
writeCollectionOfEnumValues<T>(key?: string, values?: (T | null | undefined)[]): void;
96103
/**
97104
* Writes a null value for the specified key.
98105
* @param key the key to write the value with.

packages/serialization/form/src/formSerializationWriter.ts

+9
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,15 @@ export class FormSerializationWriter implements SerializationWriter {
131131
}
132132
}
133133
};
134+
public writeCollectionOfEnumValues = <T>(key?: string, values?: (T | null | undefined)[]): void => {
135+
if (key && values && values.length > 0) {
136+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
137+
const rawValues = values.filter((x) => x !== undefined).map((x) => `${x}`);
138+
if (rawValues.length > 0) {
139+
this.writeCollectionOfPrimitiveValues<string>(key, rawValues);
140+
}
141+
}
142+
};
134143
public getSerializedContent = (): ArrayBuffer => {
135144
return this.convertStringToArrayBuffer(this.writer.join(``));
136145
};

packages/serialization/form/test/common/formSerializationWriter.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { DateOnly, Duration, TimeOnly } from "@microsoft/kiota-abstractions";
99
import { assert, describe, it } from "vitest";
1010

1111
import { FormSerializationWriter } from "../../src";
12-
import { serializeTestEntity, type TestEntity } from "../testEntity";
12+
import { LongRunningOperationStatusObject, serializeTestEntity, type TestEntity } from "../testEntity";
1313

1414
describe("FormSerializationWriter", () => {
1515
it("writesSampleObjectValue", () => {
@@ -34,6 +34,8 @@ describe("FormSerializationWriter", () => {
3434
testEntity.additionalData["jobTitle"] = "Author";
3535
testEntity.additionalData["createdDateTime"] = new Date(0);
3636
testEntity.deviceNames = ["device1", "device2"];
37+
testEntity.status = LongRunningOperationStatusObject.NotStarted;
38+
testEntity.nextStatuses = [LongRunningOperationStatusObject.Running, LongRunningOperationStatusObject.Succeeded];
3739
const formSerializationWriter = new FormSerializationWriter();
3840
formSerializationWriter.writeObjectValue(undefined, testEntity, serializeTestEntity);
3941
const formContent = formSerializationWriter.getSerializedContent();
@@ -51,6 +53,9 @@ describe("FormSerializationWriter", () => {
5153
"deviceNames=device2", // Serializes collections
5254
"officeLocation=null", // Serializes null values
5355
"endWorkTime=null", // Serializes null values
56+
"status=notStarted", // Serializes enum values
57+
"nextStatuses=running", // Serializes collection of enum values
58+
"nextStatuses=succeeded",
5459
];
5560
const arr = form.split("&");
5661
let count = 0;
@@ -65,6 +70,15 @@ describe("FormSerializationWriter", () => {
6570
assert.equal(arr.length, 0);
6671
});
6772

73+
it("writeCollectionOfEnumValues", () => {
74+
const enums = [LongRunningOperationStatusObject.Running, LongRunningOperationStatusObject.Succeeded];
75+
const formSerializationWriter = new FormSerializationWriter();
76+
formSerializationWriter.writeCollectionOfEnumValues("nextStatuses", enums);
77+
const formContent = formSerializationWriter.getSerializedContent();
78+
const form = new TextDecoder().decode(formContent);
79+
assert.equal("nextStatuses=running&nextStatuses=succeeded&", form);
80+
});
81+
6882
it("writesSampleCollectionOfObjectValues", () => {
6983
const testEntity = {} as TestEntity;
7084
testEntity.id = "48d31887-5fad-4d73-a9f5-3c356e68a038";

packages/serialization/form/test/testEntity.ts

+20
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,19 @@ export interface TestEntity extends Parsable, AdditionalDataHolder {
1616
endWorkTime?: TimeOnly | null;
1717
officeLocation?: string | null;
1818
deviceNames?: string[] | null;
19+
status?: LongRunningOperationStatus | null;
20+
nextStatuses?: LongRunningOperationStatus[] | null;
1921
}
22+
23+
export const LongRunningOperationStatusObject = {
24+
NotStarted: "notStarted",
25+
Running: "running",
26+
Succeeded: "succeeded",
27+
Failed: "failed",
28+
UnknownFutureValue: "unknownFutureValue",
29+
} as const;
30+
export type LongRunningOperationStatus = (typeof LongRunningOperationStatusObject)[keyof typeof LongRunningOperationStatusObject];
31+
2032
export function createTestParserFromDiscriminatorValue(parseNode: ParseNode | undefined) {
2133
if (!parseNode) throw new Error("parseNode cannot be undefined");
2234
return deserializeTestEntity;
@@ -48,6 +60,12 @@ export function deserializeTestEntity(testEntity: TestEntity | undefined = {}):
4860
deviceNames: (n) => {
4961
testEntity.deviceNames = n.getCollectionOfPrimitiveValues();
5062
},
63+
status: (n) => {
64+
testEntity.status = n.getEnumValue<LongRunningOperationStatus>(LongRunningOperationStatusObject);
65+
},
66+
nextStatuses: (n) => {
67+
testEntity.nextStatuses = n.getCollectionOfEnumValues<LongRunningOperationStatus>(LongRunningOperationStatusObject);
68+
},
5169
};
5270
}
5371

@@ -61,4 +79,6 @@ export function serializeTestEntity(writer: SerializationWriter, testEntity: Tes
6179
writer.writeStringValue("officeLocation", testEntity.officeLocation);
6280
writer.writeAdditionalData(testEntity.additionalData);
6381
writer.writeCollectionOfPrimitiveValues("deviceNames", testEntity.deviceNames);
82+
writer.writeEnumValue<LongRunningOperationStatus>("status", testEntity.status);
83+
writer.writeCollectionOfEnumValues<LongRunningOperationStatus>("nextStatuses", testEntity.nextStatuses);
6484
}

packages/serialization/json/src/jsonSerializationWriter.ts

+16
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,22 @@ export class JsonSerializationWriter implements SerializationWriter {
221221
}
222222
}
223223
};
224+
225+
public writeCollectionOfEnumValues = <T>(key?: string, values?: (T | undefined | null)[]): void => {
226+
if (values && values.length > 0) {
227+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
228+
const rawValues = values.filter((x) => x !== undefined).map((x) => `${x}`);
229+
230+
if (rawValues.length === 0) {
231+
return;
232+
}
233+
234+
key && this.writePropertyName(key);
235+
this.writer.push(JSON.stringify(rawValues));
236+
key && this.writer.push(JsonSerializationWriter.propertySeparator);
237+
}
238+
};
239+
224240
public getSerializedContent = (): ArrayBuffer => {
225241
return this.convertStringToArrayBuffer(this.writer.join(``));
226242
};

packages/serialization/json/test/common/jsonSerializationWriter.ts

+32-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import { assert, describe, it, beforeEach } from "vitest";
99

1010
import { JsonParseNode, JsonSerializationWriter } from "../../src/index";
11-
import { createTestBackedModelFromDiscriminatorValue, createTestParserFromDiscriminatorValue, serializeTestParser, TestBackedModel, type TestParser } from "./testEntity";
11+
import { createTestBackedModelFromDiscriminatorValue, createTestParserFromDiscriminatorValue, LongRunningOperationStatusObject, serializeTestParser, TestBackedModel, type TestParser } from "./testEntity";
1212
import { UntypedTestEntity, serializeUntypedTestEntity } from "./untypedTestEntiy";
1313
import { BackingStore, BackingStoreFactorySingleton, createBackedModelProxyHandler, createUntypedArray, createUntypedBoolean, createUntypedNull, createUntypedNumber, createUntypedObject, createUntypedString } from "@microsoft/kiota-abstractions";
1414

@@ -59,6 +59,37 @@ describe("JsonParseNode", () => {
5959
assert.deepEqual(stringValueResult, expectedObject);
6060
});
6161

62+
it("Test enum serialization", async () => {
63+
const inputObject: TestParser = {
64+
status: LongRunningOperationStatusObject.NotStarted,
65+
nextStatuses: [LongRunningOperationStatusObject.Succeeded, LongRunningOperationStatusObject.Failed],
66+
};
67+
const expectedObject: TestParser = {
68+
status: LongRunningOperationStatusObject.NotStarted,
69+
nextStatuses: [LongRunningOperationStatusObject.Succeeded, LongRunningOperationStatusObject.Failed],
70+
};
71+
72+
const writer = new JsonSerializationWriter();
73+
writer.writeObjectValue("", inputObject, serializeTestParser);
74+
const serializedContent = writer.getSerializedContent();
75+
const decoder = new TextDecoder();
76+
const contentAsStr = decoder.decode(serializedContent);
77+
const result = JSON.parse(contentAsStr);
78+
const stringValueResult = new JsonParseNode(result).getObjectValue(createTestParserFromDiscriminatorValue) as TestParser;
79+
assert.deepEqual(stringValueResult, expectedObject);
80+
});
81+
82+
it("Test collection of enum serialization", async () => {
83+
const writer = new JsonSerializationWriter();
84+
const enums = [LongRunningOperationStatusObject.NotStarted, LongRunningOperationStatusObject.Succeeded];
85+
86+
writer.writeCollectionOfEnumValues("enum", enums);
87+
const serializedContent = writer.getSerializedContent();
88+
const decoder = new TextDecoder();
89+
const contentAsStr = decoder.decode(serializedContent);
90+
assert.equal(contentAsStr, '"enum":["notStarted","succeeded"],');
91+
});
92+
6293
it("encodes characters properly", async () => {
6394
const inputObject: TestParser = {
6495
testCollection: ["2", "3"],

packages/serialization/json/test/common/testEntity.ts

+20
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,19 @@ export interface TestParser {
2222
testNumber?: number | null | undefined;
2323
testGuid?: Guid | null | undefined;
2424
testUnionObject?: TestUnionObject | null | undefined;
25+
status?: LongRunningOperationStatus | null;
26+
nextStatuses?: LongRunningOperationStatus[] | null;
2527
}
28+
29+
export const LongRunningOperationStatusObject = {
30+
NotStarted: "notStarted",
31+
Running: "running",
32+
Succeeded: "succeeded",
33+
Failed: "failed",
34+
UnknownFutureValue: "unknownFutureValue",
35+
} as const;
36+
export type LongRunningOperationStatus = (typeof LongRunningOperationStatusObject)[keyof typeof LongRunningOperationStatusObject];
37+
2638
export interface TestBackedModel extends TestParser, BackedModel {
2739
backingStoreEnabled?: boolean | undefined;
2840
}
@@ -89,6 +101,12 @@ export function deserializeTestParser(testParser: TestParser | undefined = {}):
89101
testUnionObject: (n) => {
90102
testParser.testUnionObject = n.getStringValue() ?? n.getNumberValue() ?? n.getObjectValue(createTestUnionObjectFromDiscriminatorValue);
91103
},
104+
status: (n) => {
105+
testParser.status = n.getEnumValue<LongRunningOperationStatus>(LongRunningOperationStatusObject);
106+
},
107+
nextStatuses: (n) => {
108+
testParser.nextStatuses = n.getCollectionOfEnumValues<LongRunningOperationStatus>(LongRunningOperationStatusObject);
109+
},
92110
};
93111
}
94112

@@ -148,6 +166,8 @@ export function serializeTestParser(writer: SerializationWriter, entity: TestPar
148166
} else {
149167
writer.writeObjectValue("testUnionObject", entity.testUnionObject as any, serializeTestUnionObject);
150168
}
169+
writer.writeEnumValue("status", entity.status);
170+
writer.writeCollectionOfEnumValues("nextStatuses", entity.nextStatuses);
151171
}
152172

153173
export function serializeFoo(writer: SerializationWriter, entity: FooResponse | undefined = {}): void {

packages/serialization/multipart/src/multipartSerializationWriter.ts

+8
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ export class MultipartSerializationWriter implements SerializationWriter {
113113
): void => {
114114
throw new Error(`serialization of enum values is not supported with multipart`);
115115
};
116+
public writeCollectionOfEnumValues = <T>(
117+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
118+
key?: string,
119+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
120+
values?: (T | null | undefined)[],
121+
): void => {
122+
throw new Error(`serialization of collection of enum values is not supported with multipart`);
123+
};
116124
public getSerializedContent = (): ArrayBuffer => {
117125
return this.writer;
118126
};

packages/serialization/text/src/textSerializationWriter.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,14 @@ export class TextSerializationWriter implements SerializationWriter {
128128
if (rawValues.length > 0) {
129129
this.writeStringValue(
130130
key,
131-
rawValues.reduce((x, y) => `${x}, ${y}`),
131+
rawValues.reduce((x, y) => `${x},${y}`),
132132
);
133133
}
134134
}
135135
};
136+
public writeCollectionOfEnumValues = <T>(key?: string, values?: T[] | null): void => {
137+
this.writeEnumValue(key, values);
138+
};
136139
public getSerializedContent = (): ArrayBuffer => {
137140
return this.convertStringToArrayBuffer(this.writer.join(``));
138141
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { assert, describe, it } from "vitest";
2+
import { TextSerializationWriter } from "../../src";
3+
4+
describe("TextSerializationWriter", () => {
5+
it("writeEnumValue", () => {
6+
const textSerializationWriter = new TextSerializationWriter();
7+
8+
const statuses = [LongRunningOperationStatusObject.NotStarted, LongRunningOperationStatusObject.Running];
9+
textSerializationWriter.writeEnumValue("", ...statuses);
10+
const formContent = textSerializationWriter.getSerializedContent();
11+
const form = new TextDecoder().decode(formContent);
12+
const expectedString = "notStarted,running";
13+
assert.equal(form, expectedString);
14+
});
15+
it("writeCollectionOfEnumValues", () => {
16+
const textSerializationWriter = new TextSerializationWriter();
17+
const statuses = [LongRunningOperationStatusObject.NotStarted, LongRunningOperationStatusObject.Running];
18+
textSerializationWriter.writeCollectionOfEnumValues("", statuses);
19+
const formContent = textSerializationWriter.getSerializedContent();
20+
const form = new TextDecoder().decode(formContent);
21+
const expectedString = "notStarted,running";
22+
assert.equal(form, expectedString);
23+
});
24+
});
25+
26+
export const LongRunningOperationStatusObject = {
27+
NotStarted: "notStarted",
28+
Running: "running",
29+
Succeeded: "succeeded",
30+
Failed: "failed",
31+
UnknownFutureValue: "unknownFutureValue",
32+
} as const;

0 commit comments

Comments
 (0)