1 |
|
2 |
|
3 |
|
4 | import * as base64 from "./util/base64";
|
5 | import * as utils from "./util/utils";
|
6 |
|
7 | export class Serializer {
|
8 | constructor(
|
9 | public readonly modelMappers: { [key: string]: any } = {},
|
10 | public readonly isXML?: boolean
|
11 | ) {}
|
12 |
|
13 | validateConstraints(mapper: Mapper, value: any, objectName: string): void {
|
14 | const failValidation = (constraintName: keyof MapperConstraints, constraintValue: any) => {
|
15 | throw new Error(
|
16 | `"${objectName}" with value "${value}" should satisfy the constraint "${constraintName}": ${constraintValue}.`
|
17 | );
|
18 | };
|
19 | if (mapper.constraints && value != undefined) {
|
20 | const {
|
21 | ExclusiveMaximum,
|
22 | ExclusiveMinimum,
|
23 | InclusiveMaximum,
|
24 | InclusiveMinimum,
|
25 | MaxItems,
|
26 | MaxLength,
|
27 | MinItems,
|
28 | MinLength,
|
29 | MultipleOf,
|
30 | Pattern,
|
31 | UniqueItems,
|
32 | } = mapper.constraints;
|
33 | if (ExclusiveMaximum != undefined && value >= ExclusiveMaximum) {
|
34 | failValidation("ExclusiveMaximum", ExclusiveMaximum);
|
35 | }
|
36 | if (ExclusiveMinimum != undefined && value <= ExclusiveMinimum) {
|
37 | failValidation("ExclusiveMinimum", ExclusiveMinimum);
|
38 | }
|
39 | if (InclusiveMaximum != undefined && value > InclusiveMaximum) {
|
40 | failValidation("InclusiveMaximum", InclusiveMaximum);
|
41 | }
|
42 | if (InclusiveMinimum != undefined && value < InclusiveMinimum) {
|
43 | failValidation("InclusiveMinimum", InclusiveMinimum);
|
44 | }
|
45 | if (MaxItems != undefined && value.length > MaxItems) {
|
46 | failValidation("MaxItems", MaxItems);
|
47 | }
|
48 | if (MaxLength != undefined && value.length > MaxLength) {
|
49 | failValidation("MaxLength", MaxLength);
|
50 | }
|
51 | if (MinItems != undefined && value.length < MinItems) {
|
52 | failValidation("MinItems", MinItems);
|
53 | }
|
54 | if (MinLength != undefined && value.length < MinLength) {
|
55 | failValidation("MinLength", MinLength);
|
56 | }
|
57 | if (MultipleOf != undefined && value % MultipleOf !== 0) {
|
58 | failValidation("MultipleOf", MultipleOf);
|
59 | }
|
60 | if (Pattern) {
|
61 | const pattern: RegExp = typeof Pattern === "string" ? new RegExp(Pattern) : Pattern;
|
62 | if (typeof value !== "string" || value.match(pattern) === null) {
|
63 | failValidation("Pattern", Pattern);
|
64 | }
|
65 | }
|
66 | if (
|
67 | UniqueItems &&
|
68 | value.some((item: any, i: number, ar: Array<any>) => ar.indexOf(item) !== i)
|
69 | ) {
|
70 | failValidation("UniqueItems", UniqueItems);
|
71 | }
|
72 | }
|
73 | }
|
74 |
|
75 | |
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 | serialize(mapper: Mapper, object: any, objectName?: string): any {
|
87 | let payload: any = {};
|
88 | const mapperType = mapper.type.name as string;
|
89 | if (!objectName) {
|
90 | objectName = mapper.serializedName!;
|
91 | }
|
92 | if (mapperType.match(/^Sequence$/gi) !== null) {
|
93 | payload = [];
|
94 | }
|
95 |
|
96 | if (mapper.isConstant) {
|
97 | object = mapper.defaultValue;
|
98 | }
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 | const { required, nullable } = mapper;
|
111 |
|
112 | if (required && nullable && object === undefined) {
|
113 | throw new Error(`${objectName} cannot be undefined.`);
|
114 | }
|
115 | if (required && !nullable && object == undefined) {
|
116 | throw new Error(`${objectName} cannot be null or undefined.`);
|
117 | }
|
118 | if (!required && nullable === false && object === null) {
|
119 | throw new Error(`${objectName} cannot be null.`);
|
120 | }
|
121 |
|
122 | if (object == undefined) {
|
123 | payload = object;
|
124 | } else {
|
125 |
|
126 | this.validateConstraints(mapper, object, objectName);
|
127 | if (mapperType.match(/^any$/gi) !== null) {
|
128 | payload = object;
|
129 | } else if (mapperType.match(/^(Number|String|Boolean|Object|Stream|Uuid)$/gi) !== null) {
|
130 | payload = serializeBasicTypes(mapperType, objectName, object);
|
131 | } else if (mapperType.match(/^Enum$/gi) !== null) {
|
132 | const enumMapper: EnumMapper = mapper as EnumMapper;
|
133 | payload = serializeEnumType(objectName, enumMapper.type.allowedValues, object);
|
134 | } else if (
|
135 | mapperType.match(/^(Date|DateTime|TimeSpan|DateTimeRfc1123|UnixTime)$/gi) !== null
|
136 | ) {
|
137 | payload = serializeDateTypes(mapperType, object, objectName);
|
138 | } else if (mapperType.match(/^ByteArray$/gi) !== null) {
|
139 | payload = serializeByteArrayType(objectName, object);
|
140 | } else if (mapperType.match(/^Base64Url$/gi) !== null) {
|
141 | payload = serializeBase64UrlType(objectName, object);
|
142 | } else if (mapperType.match(/^Sequence$/gi) !== null) {
|
143 | payload = serializeSequenceType(this, mapper as SequenceMapper, object, objectName);
|
144 | } else if (mapperType.match(/^Dictionary$/gi) !== null) {
|
145 | payload = serializeDictionaryType(this, mapper as DictionaryMapper, object, objectName);
|
146 | } else if (mapperType.match(/^Composite$/gi) !== null) {
|
147 | payload = serializeCompositeType(this, mapper as CompositeMapper, object, objectName);
|
148 | }
|
149 | }
|
150 | return payload;
|
151 | }
|
152 |
|
153 | |
154 |
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 | deserialize(mapper: Mapper, responseBody: any, objectName: string): any {
|
165 | if (responseBody == undefined) {
|
166 | if (this.isXML && mapper.type.name === "Sequence" && !mapper.xmlIsWrapped) {
|
167 |
|
168 |
|
169 |
|
170 | responseBody = [];
|
171 | }
|
172 |
|
173 | if (mapper.defaultValue !== undefined) {
|
174 | responseBody = mapper.defaultValue;
|
175 | }
|
176 | return responseBody;
|
177 | }
|
178 |
|
179 | let payload: any;
|
180 | const mapperType = mapper.type.name;
|
181 | if (!objectName) {
|
182 | objectName = mapper.serializedName!;
|
183 | }
|
184 |
|
185 | if (mapperType.match(/^Composite$/gi) !== null) {
|
186 | payload = deserializeCompositeType(this, mapper as CompositeMapper, responseBody, objectName);
|
187 | } else {
|
188 | if (this.isXML) {
|
189 | |
190 |
|
191 |
|
192 |
|
193 |
|
194 | if (responseBody["$"] != undefined && responseBody["_"] != undefined) {
|
195 | responseBody = responseBody["_"];
|
196 | }
|
197 | }
|
198 |
|
199 | if (mapperType.match(/^Number$/gi) !== null) {
|
200 | payload = parseFloat(responseBody);
|
201 | if (isNaN(payload)) {
|
202 | payload = responseBody;
|
203 | }
|
204 | } else if (mapperType.match(/^Boolean$/gi) !== null) {
|
205 | if (responseBody === "true") {
|
206 | payload = true;
|
207 | } else if (responseBody === "false") {
|
208 | payload = false;
|
209 | } else {
|
210 | payload = responseBody;
|
211 | }
|
212 | } else if (mapperType.match(/^(String|Enum|Object|Stream|Uuid|TimeSpan|any)$/gi) !== null) {
|
213 | payload = responseBody;
|
214 | } else if (mapperType.match(/^(Date|DateTime|DateTimeRfc1123)$/gi) !== null) {
|
215 | payload = new Date(responseBody);
|
216 | } else if (mapperType.match(/^UnixTime$/gi) !== null) {
|
217 | payload = unixTimeToDate(responseBody);
|
218 | } else if (mapperType.match(/^ByteArray$/gi) !== null) {
|
219 | payload = base64.decodeString(responseBody);
|
220 | } else if (mapperType.match(/^Base64Url$/gi) !== null) {
|
221 | payload = base64UrlToByteArray(responseBody);
|
222 | } else if (mapperType.match(/^Sequence$/gi) !== null) {
|
223 | payload = deserializeSequenceType(this, mapper as SequenceMapper, responseBody, objectName);
|
224 | } else if (mapperType.match(/^Dictionary$/gi) !== null) {
|
225 | payload = deserializeDictionaryType(
|
226 | this,
|
227 | mapper as DictionaryMapper,
|
228 | responseBody,
|
229 | objectName
|
230 | );
|
231 | }
|
232 | }
|
233 |
|
234 | if (mapper.isConstant) {
|
235 | payload = mapper.defaultValue;
|
236 | }
|
237 |
|
238 | return payload;
|
239 | }
|
240 | }
|
241 |
|
242 | function trimEnd(str: string, ch: string) {
|
243 | let len = str.length;
|
244 | while (len - 1 >= 0 && str[len - 1] === ch) {
|
245 | --len;
|
246 | }
|
247 | return str.substr(0, len);
|
248 | }
|
249 |
|
250 | function bufferToBase64Url(buffer: any): string | undefined {
|
251 | if (!buffer) {
|
252 | return undefined;
|
253 | }
|
254 | if (!(buffer instanceof Uint8Array)) {
|
255 | throw new Error(`Please provide an input of type Uint8Array for converting to Base64Url.`);
|
256 | }
|
257 |
|
258 | const str = base64.encodeByteArray(buffer);
|
259 |
|
260 | return trimEnd(str, "=").replace(/\+/g, "-").replace(/\//g, "_");
|
261 | }
|
262 |
|
263 | function base64UrlToByteArray(str: string): Uint8Array | undefined {
|
264 | if (!str) {
|
265 | return undefined;
|
266 | }
|
267 | if (str && typeof str.valueOf() !== "string") {
|
268 | throw new Error("Please provide an input of type string for converting to Uint8Array");
|
269 | }
|
270 |
|
271 | str = str.replace(/\-/g, "+").replace(/\_/g, "/");
|
272 |
|
273 | return base64.decodeString(str);
|
274 | }
|
275 |
|
276 | function splitSerializeName(prop: string | undefined): string[] {
|
277 | const classes: string[] = [];
|
278 | let partialclass = "";
|
279 | if (prop) {
|
280 | const subwords = prop.split(".");
|
281 |
|
282 | for (const item of subwords) {
|
283 | if (item.charAt(item.length - 1) === "\\") {
|
284 | partialclass += item.substr(0, item.length - 1) + ".";
|
285 | } else {
|
286 | partialclass += item;
|
287 | classes.push(partialclass);
|
288 | partialclass = "";
|
289 | }
|
290 | }
|
291 | }
|
292 |
|
293 | return classes;
|
294 | }
|
295 |
|
296 | function dateToUnixTime(d: string | Date): number | undefined {
|
297 | if (!d) {
|
298 | return undefined;
|
299 | }
|
300 |
|
301 | if (typeof d.valueOf() === "string") {
|
302 | d = new Date(d as string);
|
303 | }
|
304 | return Math.floor((d as Date).getTime() / 1000);
|
305 | }
|
306 |
|
307 | function unixTimeToDate(n: number): Date | undefined {
|
308 | if (!n) {
|
309 | return undefined;
|
310 | }
|
311 | return new Date(n * 1000);
|
312 | }
|
313 |
|
314 | function serializeBasicTypes(typeName: string, objectName: string, value: any): any {
|
315 | if (value !== null && value !== undefined) {
|
316 | if (typeName.match(/^Number$/gi) !== null) {
|
317 | if (typeof value !== "number") {
|
318 | throw new Error(`${objectName} with value ${value} must be of type number.`);
|
319 | }
|
320 | } else if (typeName.match(/^String$/gi) !== null) {
|
321 | if (typeof value.valueOf() !== "string") {
|
322 | throw new Error(`${objectName} with value "${value}" must be of type string.`);
|
323 | }
|
324 | } else if (typeName.match(/^Uuid$/gi) !== null) {
|
325 | if (!(typeof value.valueOf() === "string" && utils.isValidUuid(value))) {
|
326 | throw new Error(
|
327 | `${objectName} with value "${value}" must be of type string and a valid uuid.`
|
328 | );
|
329 | }
|
330 | } else if (typeName.match(/^Boolean$/gi) !== null) {
|
331 | if (typeof value !== "boolean") {
|
332 | throw new Error(`${objectName} with value ${value} must be of type boolean.`);
|
333 | }
|
334 | } else if (typeName.match(/^Stream$/gi) !== null) {
|
335 | const objectType = typeof value;
|
336 | if (
|
337 | objectType !== "string" &&
|
338 | objectType !== "function" &&
|
339 | !(value instanceof ArrayBuffer) &&
|
340 | !ArrayBuffer.isView(value) &&
|
341 | !(typeof Blob === "function" && value instanceof Blob)
|
342 | ) {
|
343 | throw new Error(
|
344 | `${objectName} must be a string, Blob, ArrayBuffer, ArrayBufferView, or a function returning NodeJS.ReadableStream.`
|
345 | );
|
346 | }
|
347 | }
|
348 | }
|
349 | return value;
|
350 | }
|
351 |
|
352 | function serializeEnumType(objectName: string, allowedValues: Array<any>, value: any): any {
|
353 | if (!allowedValues) {
|
354 | throw new Error(
|
355 | `Please provide a set of allowedValues to validate ${objectName} as an Enum Type.`
|
356 | );
|
357 | }
|
358 | const isPresent = allowedValues.some((item) => {
|
359 | if (typeof item.valueOf() === "string") {
|
360 | return item.toLowerCase() === value.toLowerCase();
|
361 | }
|
362 | return item === value;
|
363 | });
|
364 | if (!isPresent) {
|
365 | throw new Error(
|
366 | `${value} is not a valid value for ${objectName}. The valid values are: ${JSON.stringify(
|
367 | allowedValues
|
368 | )}.`
|
369 | );
|
370 | }
|
371 | return value;
|
372 | }
|
373 |
|
374 | function serializeByteArrayType(objectName: string, value: any): any {
|
375 | if (value != undefined) {
|
376 | if (!(value instanceof Uint8Array)) {
|
377 | throw new Error(`${objectName} must be of type Uint8Array.`);
|
378 | }
|
379 | value = base64.encodeByteArray(value);
|
380 | }
|
381 | return value;
|
382 | }
|
383 |
|
384 | function serializeBase64UrlType(objectName: string, value: any): any {
|
385 | if (value != undefined) {
|
386 | if (!(value instanceof Uint8Array)) {
|
387 | throw new Error(`${objectName} must be of type Uint8Array.`);
|
388 | }
|
389 | value = bufferToBase64Url(value);
|
390 | }
|
391 | return value;
|
392 | }
|
393 |
|
394 | function serializeDateTypes(typeName: string, value: any, objectName: string) {
|
395 | if (value != undefined) {
|
396 | if (typeName.match(/^Date$/gi) !== null) {
|
397 | if (
|
398 | !(
|
399 | value instanceof Date ||
|
400 | (typeof value.valueOf() === "string" && !isNaN(Date.parse(value)))
|
401 | )
|
402 | ) {
|
403 | throw new Error(`${objectName} must be an instanceof Date or a string in ISO8601 format.`);
|
404 | }
|
405 | value =
|
406 | value instanceof Date
|
407 | ? value.toISOString().substring(0, 10)
|
408 | : new Date(value).toISOString().substring(0, 10);
|
409 | } else if (typeName.match(/^DateTime$/gi) !== null) {
|
410 | if (
|
411 | !(
|
412 | value instanceof Date ||
|
413 | (typeof value.valueOf() === "string" && !isNaN(Date.parse(value)))
|
414 | )
|
415 | ) {
|
416 | throw new Error(`${objectName} must be an instanceof Date or a string in ISO8601 format.`);
|
417 | }
|
418 | value = value instanceof Date ? value.toISOString() : new Date(value).toISOString();
|
419 | } else if (typeName.match(/^DateTimeRfc1123$/gi) !== null) {
|
420 | if (
|
421 | !(
|
422 | value instanceof Date ||
|
423 | (typeof value.valueOf() === "string" && !isNaN(Date.parse(value)))
|
424 | )
|
425 | ) {
|
426 | throw new Error(`${objectName} must be an instanceof Date or a string in RFC-1123 format.`);
|
427 | }
|
428 | value = value instanceof Date ? value.toUTCString() : new Date(value).toUTCString();
|
429 | } else if (typeName.match(/^UnixTime$/gi) !== null) {
|
430 | if (
|
431 | !(
|
432 | value instanceof Date ||
|
433 | (typeof value.valueOf() === "string" && !isNaN(Date.parse(value)))
|
434 | )
|
435 | ) {
|
436 | throw new Error(
|
437 | `${objectName} must be an instanceof Date or a string in RFC-1123/ISO8601 format ` +
|
438 | `for it to be serialized in UnixTime/Epoch format.`
|
439 | );
|
440 | }
|
441 | value = dateToUnixTime(value);
|
442 | } else if (typeName.match(/^TimeSpan$/gi) !== null) {
|
443 | if (!utils.isDuration(value)) {
|
444 | throw new Error(
|
445 | `${objectName} must be a string in ISO 8601 format. Instead was "${value}".`
|
446 | );
|
447 | }
|
448 | value = value;
|
449 | }
|
450 | }
|
451 | return value;
|
452 | }
|
453 |
|
454 | function serializeSequenceType(
|
455 | serializer: Serializer,
|
456 | mapper: SequenceMapper,
|
457 | object: any,
|
458 | objectName: string
|
459 | ) {
|
460 | if (!Array.isArray(object)) {
|
461 | throw new Error(`${objectName} must be of type Array.`);
|
462 | }
|
463 | const elementType = mapper.type.element;
|
464 | if (!elementType || typeof elementType !== "object") {
|
465 | throw new Error(
|
466 | `element" metadata for an Array must be defined in the ` +
|
467 | `mapper and it must of type "object" in ${objectName}.`
|
468 | );
|
469 | }
|
470 | const tempArray = [];
|
471 | for (let i = 0; i < object.length; i++) {
|
472 | tempArray[i] = serializer.serialize(elementType, object[i], objectName);
|
473 | }
|
474 | return tempArray;
|
475 | }
|
476 |
|
477 | function serializeDictionaryType(
|
478 | serializer: Serializer,
|
479 | mapper: DictionaryMapper,
|
480 | object: any,
|
481 | objectName: string
|
482 | ) {
|
483 | if (typeof object !== "object") {
|
484 | throw new Error(`${objectName} must be of type object.`);
|
485 | }
|
486 | const valueType = mapper.type.value;
|
487 | if (!valueType || typeof valueType !== "object") {
|
488 | throw new Error(
|
489 | `"value" metadata for a Dictionary must be defined in the ` +
|
490 | `mapper and it must of type "object" in ${objectName}.`
|
491 | );
|
492 | }
|
493 | const tempDictionary: { [key: string]: any } = {};
|
494 | for (const key of Object.keys(object)) {
|
495 | tempDictionary[key] = serializer.serialize(valueType, object[key], objectName + "." + key);
|
496 | }
|
497 | return tempDictionary;
|
498 | }
|
499 |
|
500 |
|
501 |
|
502 |
|
503 |
|
504 |
|
505 | function resolveModelProperties(
|
506 | serializer: Serializer,
|
507 | mapper: CompositeMapper,
|
508 | objectName: string
|
509 | ): { [propertyName: string]: Mapper } {
|
510 | let modelProps = mapper.type.modelProperties;
|
511 | if (!modelProps) {
|
512 | const className = mapper.type.className;
|
513 | if (!className) {
|
514 | throw new Error(
|
515 | `Class name for model "${objectName}" is not provided in the mapper "${JSON.stringify(
|
516 | mapper,
|
517 | undefined,
|
518 | 2
|
519 | )}".`
|
520 | );
|
521 | }
|
522 |
|
523 | const modelMapper = serializer.modelMappers[className];
|
524 | if (!modelMapper) {
|
525 | throw new Error(`mapper() cannot be null or undefined for model "${className}".`);
|
526 | }
|
527 | modelProps = modelMapper.type.modelProperties;
|
528 | if (!modelProps) {
|
529 | throw new Error(
|
530 | `modelProperties cannot be null or undefined in the ` +
|
531 | `mapper "${JSON.stringify(
|
532 | modelMapper
|
533 | )}" of type "${className}" for object "${objectName}".`
|
534 | );
|
535 | }
|
536 | }
|
537 |
|
538 | return modelProps;
|
539 | }
|
540 |
|
541 | function serializeCompositeType(
|
542 | serializer: Serializer,
|
543 | mapper: CompositeMapper,
|
544 | object: any,
|
545 | objectName: string
|
546 | ) {
|
547 | if (getPolymorphicDiscriminatorRecursively(serializer, mapper)) {
|
548 | mapper = getPolymorphicMapper(serializer, mapper, object, "clientName");
|
549 | }
|
550 |
|
551 | if (object != undefined) {
|
552 | const payload: any = {};
|
553 | const modelProps = resolveModelProperties(serializer, mapper, objectName);
|
554 | for (const key of Object.keys(modelProps)) {
|
555 | const propertyMapper = modelProps[key];
|
556 | if (propertyMapper.readOnly) {
|
557 | continue;
|
558 | }
|
559 |
|
560 | let propName: string | undefined;
|
561 | let parentObject: any = payload;
|
562 | if (serializer.isXML) {
|
563 | if (propertyMapper.xmlIsWrapped) {
|
564 | propName = propertyMapper.xmlName;
|
565 | } else {
|
566 | propName = propertyMapper.xmlElementName || propertyMapper.xmlName;
|
567 | }
|
568 | } else {
|
569 | const paths = splitSerializeName(propertyMapper.serializedName!);
|
570 | propName = paths.pop();
|
571 |
|
572 | for (const pathName of paths) {
|
573 | const childObject = parentObject[pathName];
|
574 | if (childObject == undefined && object[key] != undefined) {
|
575 | parentObject[pathName] = {};
|
576 | }
|
577 | parentObject = parentObject[pathName];
|
578 | }
|
579 | }
|
580 |
|
581 | if (parentObject != undefined) {
|
582 | const propertyObjectName =
|
583 | propertyMapper.serializedName !== ""
|
584 | ? objectName + "." + propertyMapper.serializedName
|
585 | : objectName;
|
586 |
|
587 | let toSerialize = object[key];
|
588 | const polymorphicDiscriminator = getPolymorphicDiscriminatorRecursively(serializer, mapper);
|
589 | if (
|
590 | polymorphicDiscriminator &&
|
591 | polymorphicDiscriminator.clientName === key &&
|
592 | toSerialize == undefined
|
593 | ) {
|
594 | toSerialize = mapper.serializedName;
|
595 | }
|
596 |
|
597 | const serializedValue = serializer.serialize(
|
598 | propertyMapper,
|
599 | toSerialize,
|
600 | propertyObjectName
|
601 | );
|
602 | if (serializedValue !== undefined && propName != undefined) {
|
603 | if (propertyMapper.xmlIsAttribute) {
|
604 |
|
605 |
|
606 |
|
607 | parentObject.$ = parentObject.$ || {};
|
608 | parentObject.$[propName] = serializedValue;
|
609 | } else if (propertyMapper.xmlIsWrapped) {
|
610 | parentObject[propName] = { [propertyMapper.xmlElementName!]: serializedValue };
|
611 | } else {
|
612 | parentObject[propName] = serializedValue;
|
613 | }
|
614 | }
|
615 | }
|
616 | }
|
617 |
|
618 | const additionalPropertiesMapper = mapper.type.additionalProperties;
|
619 | if (additionalPropertiesMapper) {
|
620 | const propNames = Object.keys(modelProps);
|
621 | for (const clientPropName in object) {
|
622 | const isAdditionalProperty = propNames.every((pn) => pn !== clientPropName);
|
623 | if (isAdditionalProperty) {
|
624 | payload[clientPropName] = serializer.serialize(
|
625 | additionalPropertiesMapper,
|
626 | object[clientPropName],
|
627 | objectName + '["' + clientPropName + '"]'
|
628 | );
|
629 | }
|
630 | }
|
631 | }
|
632 |
|
633 | return payload;
|
634 | }
|
635 | return object;
|
636 | }
|
637 |
|
638 | function isSpecialXmlProperty(propertyName: string): boolean {
|
639 | return ["$", "_"].includes(propertyName);
|
640 | }
|
641 |
|
642 | function deserializeCompositeType(
|
643 | serializer: Serializer,
|
644 | mapper: CompositeMapper,
|
645 | responseBody: any,
|
646 | objectName: string
|
647 | ): any {
|
648 | if (getPolymorphicDiscriminatorRecursively(serializer, mapper)) {
|
649 | mapper = getPolymorphicMapper(serializer, mapper, responseBody, "serializedName");
|
650 | }
|
651 |
|
652 | const modelProps = resolveModelProperties(serializer, mapper, objectName);
|
653 | let instance: { [key: string]: any } = {};
|
654 | const handledPropertyNames: string[] = [];
|
655 |
|
656 | for (const key of Object.keys(modelProps)) {
|
657 | const propertyMapper = modelProps[key];
|
658 | const paths = splitSerializeName(modelProps[key].serializedName!);
|
659 | handledPropertyNames.push(paths[0]);
|
660 | const { serializedName, xmlName, xmlElementName } = propertyMapper;
|
661 | let propertyObjectName = objectName;
|
662 | if (serializedName !== "" && serializedName !== undefined) {
|
663 | propertyObjectName = objectName + "." + serializedName;
|
664 | }
|
665 |
|
666 | const headerCollectionPrefix = (propertyMapper as DictionaryMapper).headerCollectionPrefix;
|
667 | if (headerCollectionPrefix) {
|
668 | const dictionary: any = {};
|
669 | for (const headerKey of Object.keys(responseBody)) {
|
670 | if (headerKey.startsWith(headerCollectionPrefix)) {
|
671 | dictionary[headerKey.substring(headerCollectionPrefix.length)] = serializer.deserialize(
|
672 | (propertyMapper as DictionaryMapper).type.value,
|
673 | responseBody[headerKey],
|
674 | propertyObjectName
|
675 | );
|
676 | }
|
677 |
|
678 | handledPropertyNames.push(headerKey);
|
679 | }
|
680 | instance[key] = dictionary;
|
681 | } else if (serializer.isXML) {
|
682 | if (propertyMapper.xmlIsAttribute && responseBody.$) {
|
683 | instance[key] = serializer.deserialize(
|
684 | propertyMapper,
|
685 | responseBody.$[xmlName!],
|
686 | propertyObjectName
|
687 | );
|
688 | } else {
|
689 | const propertyName = xmlElementName || xmlName || serializedName;
|
690 | let unwrappedProperty = responseBody[propertyName!];
|
691 | if (propertyMapper.xmlIsWrapped) {
|
692 | unwrappedProperty = responseBody[xmlName!];
|
693 | unwrappedProperty = unwrappedProperty && unwrappedProperty[xmlElementName!];
|
694 |
|
695 | const isEmptyWrappedList = unwrappedProperty === undefined;
|
696 | if (isEmptyWrappedList) {
|
697 | unwrappedProperty = [];
|
698 | }
|
699 | }
|
700 | instance[key] = serializer.deserialize(
|
701 | propertyMapper,
|
702 | unwrappedProperty,
|
703 | propertyObjectName
|
704 | );
|
705 | }
|
706 | } else {
|
707 |
|
708 | let propertyInstance;
|
709 | let res = responseBody;
|
710 |
|
711 | for (const item of paths) {
|
712 | if (!res) break;
|
713 | res = res[item];
|
714 | }
|
715 | propertyInstance = res;
|
716 | const polymorphicDiscriminator = mapper.type.polymorphicDiscriminator;
|
717 |
|
718 |
|
719 |
|
720 |
|
721 |
|
722 |
|
723 |
|
724 |
|
725 |
|
726 | if (
|
727 | polymorphicDiscriminator &&
|
728 | key === polymorphicDiscriminator.clientName &&
|
729 | propertyInstance == undefined
|
730 | ) {
|
731 | propertyInstance = mapper.serializedName;
|
732 | }
|
733 |
|
734 | let serializedValue;
|
735 |
|
736 | if (Array.isArray(responseBody[key]) && modelProps[key].serializedName === "") {
|
737 | propertyInstance = responseBody[key];
|
738 | const arrayInstance = serializer.deserialize(
|
739 | propertyMapper,
|
740 | propertyInstance,
|
741 | propertyObjectName
|
742 | );
|
743 |
|
744 |
|
745 | for (const [key, value] of Object.entries(instance)) {
|
746 | if (!arrayInstance.hasOwnProperty(key)) {
|
747 | arrayInstance[key] = value;
|
748 | }
|
749 | }
|
750 | instance = arrayInstance;
|
751 | } else if (propertyInstance !== undefined || propertyMapper.defaultValue !== undefined) {
|
752 | serializedValue = serializer.deserialize(
|
753 | propertyMapper,
|
754 | propertyInstance,
|
755 | propertyObjectName
|
756 | );
|
757 | instance[key] = serializedValue;
|
758 | }
|
759 | }
|
760 | }
|
761 |
|
762 | const additionalPropertiesMapper = mapper.type.additionalProperties;
|
763 | if (additionalPropertiesMapper) {
|
764 | const isAdditionalProperty = (responsePropName: string) => {
|
765 | for (const clientPropName in modelProps) {
|
766 | const paths = splitSerializeName(modelProps[clientPropName].serializedName);
|
767 | if (paths[0] === responsePropName) {
|
768 | return false;
|
769 | }
|
770 | }
|
771 | return true;
|
772 | };
|
773 |
|
774 | for (const responsePropName in responseBody) {
|
775 | if (isAdditionalProperty(responsePropName)) {
|
776 | instance[responsePropName] = serializer.deserialize(
|
777 | additionalPropertiesMapper,
|
778 | responseBody[responsePropName],
|
779 | objectName + '["' + responsePropName + '"]'
|
780 | );
|
781 | }
|
782 | }
|
783 | } else if (responseBody) {
|
784 | for (const key of Object.keys(responseBody)) {
|
785 | if (
|
786 | instance[key] === undefined &&
|
787 | !handledPropertyNames.includes(key) &&
|
788 | !isSpecialXmlProperty(key)
|
789 | ) {
|
790 | instance[key] = responseBody[key];
|
791 | }
|
792 | }
|
793 | }
|
794 |
|
795 | return instance;
|
796 | }
|
797 |
|
798 | function deserializeDictionaryType(
|
799 | serializer: Serializer,
|
800 | mapper: DictionaryMapper,
|
801 | responseBody: any,
|
802 | objectName: string
|
803 | ): any {
|
804 |
|
805 | const value = mapper.type.value;
|
806 | if (!value || typeof value !== "object") {
|
807 | throw new Error(
|
808 | `"value" metadata for a Dictionary must be defined in the ` +
|
809 | `mapper and it must of type "object" in ${objectName}`
|
810 | );
|
811 | }
|
812 | if (responseBody) {
|
813 | const tempDictionary: { [key: string]: any } = {};
|
814 | for (const key of Object.keys(responseBody)) {
|
815 | tempDictionary[key] = serializer.deserialize(value, responseBody[key], objectName);
|
816 | }
|
817 | return tempDictionary;
|
818 | }
|
819 | return responseBody;
|
820 | }
|
821 |
|
822 | function deserializeSequenceType(
|
823 | serializer: Serializer,
|
824 | mapper: SequenceMapper,
|
825 | responseBody: any,
|
826 | objectName: string
|
827 | ): any {
|
828 |
|
829 | const element = mapper.type.element;
|
830 | if (!element || typeof element !== "object") {
|
831 | throw new Error(
|
832 | `element" metadata for an Array must be defined in the ` +
|
833 | `mapper and it must of type "object" in ${objectName}`
|
834 | );
|
835 | }
|
836 | if (responseBody) {
|
837 | if (!Array.isArray(responseBody)) {
|
838 |
|
839 | responseBody = [responseBody];
|
840 | }
|
841 |
|
842 | const tempArray = [];
|
843 | for (let i = 0; i < responseBody.length; i++) {
|
844 | tempArray[i] = serializer.deserialize(element, responseBody[i], `${objectName}[${i}]`);
|
845 | }
|
846 | return tempArray;
|
847 | }
|
848 | return responseBody;
|
849 | }
|
850 |
|
851 | function getPolymorphicMapper(
|
852 | serializer: Serializer,
|
853 | mapper: CompositeMapper,
|
854 | object: any,
|
855 | polymorphicPropertyName: "clientName" | "serializedName"
|
856 | ): CompositeMapper {
|
857 | const polymorphicDiscriminator = getPolymorphicDiscriminatorRecursively(serializer, mapper);
|
858 | if (polymorphicDiscriminator) {
|
859 | const discriminatorName = polymorphicDiscriminator[polymorphicPropertyName];
|
860 | if (discriminatorName != undefined) {
|
861 | const discriminatorValue = object[discriminatorName];
|
862 | if (discriminatorValue != undefined) {
|
863 | const typeName = mapper.type.uberParent || mapper.type.className;
|
864 | const indexDiscriminator =
|
865 | discriminatorValue === typeName
|
866 | ? discriminatorValue
|
867 | : typeName + "." + discriminatorValue;
|
868 | const polymorphicMapper = serializer.modelMappers.discriminators[indexDiscriminator];
|
869 | if (polymorphicMapper) {
|
870 | mapper = polymorphicMapper;
|
871 | }
|
872 | }
|
873 | }
|
874 | }
|
875 | return mapper;
|
876 | }
|
877 |
|
878 | function getPolymorphicDiscriminatorRecursively(
|
879 | serializer: Serializer,
|
880 | mapper: CompositeMapper
|
881 | ): PolymorphicDiscriminator | undefined {
|
882 | return (
|
883 | mapper.type.polymorphicDiscriminator ||
|
884 | getPolymorphicDiscriminatorSafely(serializer, mapper.type.uberParent) ||
|
885 | getPolymorphicDiscriminatorSafely(serializer, mapper.type.className)
|
886 | );
|
887 | }
|
888 |
|
889 | function getPolymorphicDiscriminatorSafely(serializer: Serializer, typeName?: string) {
|
890 | return (
|
891 | typeName &&
|
892 | serializer.modelMappers[typeName] &&
|
893 | serializer.modelMappers[typeName].type.polymorphicDiscriminator
|
894 | );
|
895 | }
|
896 |
|
897 | export interface MapperConstraints {
|
898 | InclusiveMaximum?: number;
|
899 | ExclusiveMaximum?: number;
|
900 | InclusiveMinimum?: number;
|
901 | ExclusiveMinimum?: number;
|
902 | MaxLength?: number;
|
903 | MinLength?: number;
|
904 | Pattern?: RegExp;
|
905 | MaxItems?: number;
|
906 | MinItems?: number;
|
907 | UniqueItems?: true;
|
908 | MultipleOf?: number;
|
909 | }
|
910 |
|
911 | export type MapperType =
|
912 | | SimpleMapperType
|
913 | | CompositeMapperType
|
914 | | SequenceMapperType
|
915 | | DictionaryMapperType
|
916 | | EnumMapperType;
|
917 |
|
918 | export interface SimpleMapperType {
|
919 | name:
|
920 | | "Base64Url"
|
921 | | "Boolean"
|
922 | | "ByteArray"
|
923 | | "Date"
|
924 | | "DateTime"
|
925 | | "DateTimeRfc1123"
|
926 | | "Object"
|
927 | | "Stream"
|
928 | | "String"
|
929 | | "TimeSpan"
|
930 | | "UnixTime"
|
931 | | "Uuid"
|
932 | | "Number"
|
933 | | "any";
|
934 | }
|
935 |
|
936 | export interface CompositeMapperType {
|
937 | name: "Composite";
|
938 |
|
939 |
|
940 |
|
941 |
|
942 | className?: string;
|
943 |
|
944 | modelProperties?: { [propertyName: string]: Mapper };
|
945 | additionalProperties?: Mapper;
|
946 |
|
947 | uberParent?: string;
|
948 | polymorphicDiscriminator?: PolymorphicDiscriminator;
|
949 | }
|
950 |
|
951 | export interface SequenceMapperType {
|
952 | name: "Sequence";
|
953 | element: Mapper;
|
954 | }
|
955 |
|
956 | export interface DictionaryMapperType {
|
957 | name: "Dictionary";
|
958 | value: Mapper;
|
959 | }
|
960 |
|
961 | export interface EnumMapperType {
|
962 | name: "Enum";
|
963 | allowedValues: any[];
|
964 | }
|
965 |
|
966 | export interface BaseMapper {
|
967 | xmlName?: string;
|
968 | xmlIsAttribute?: boolean;
|
969 | xmlElementName?: string;
|
970 | xmlIsWrapped?: boolean;
|
971 | readOnly?: boolean;
|
972 | isConstant?: boolean;
|
973 | required?: boolean;
|
974 | nullable?: boolean;
|
975 | serializedName?: string;
|
976 | type: MapperType;
|
977 | defaultValue?: any;
|
978 | constraints?: MapperConstraints;
|
979 | }
|
980 |
|
981 | export type Mapper = BaseMapper | CompositeMapper | SequenceMapper | DictionaryMapper | EnumMapper;
|
982 |
|
983 | export interface PolymorphicDiscriminator {
|
984 | serializedName: string;
|
985 | clientName: string;
|
986 | [key: string]: string;
|
987 | }
|
988 |
|
989 | export interface CompositeMapper extends BaseMapper {
|
990 | type: CompositeMapperType;
|
991 | }
|
992 |
|
993 | export interface SequenceMapper extends BaseMapper {
|
994 | type: SequenceMapperType;
|
995 | }
|
996 |
|
997 | export interface DictionaryMapper extends BaseMapper {
|
998 | type: DictionaryMapperType;
|
999 | headerCollectionPrefix?: string;
|
1000 | }
|
1001 |
|
1002 | export interface EnumMapper extends BaseMapper {
|
1003 | type: EnumMapperType;
|
1004 | }
|
1005 |
|
1006 | export interface UrlParameterValue {
|
1007 | value: string;
|
1008 | skipUrlEncoding: boolean;
|
1009 | }
|
1010 |
|
1011 |
|
1012 | export function serializeObject(toSerialize: any): any {
|
1013 | if (toSerialize == undefined) return undefined;
|
1014 | if (toSerialize instanceof Uint8Array) {
|
1015 | toSerialize = base64.encodeByteArray(toSerialize);
|
1016 | return toSerialize;
|
1017 | } else if (toSerialize instanceof Date) {
|
1018 | return toSerialize.toISOString();
|
1019 | } else if (Array.isArray(toSerialize)) {
|
1020 | const array = [];
|
1021 | for (let i = 0; i < toSerialize.length; i++) {
|
1022 | array.push(serializeObject(toSerialize[i]));
|
1023 | }
|
1024 | return array;
|
1025 | } else if (typeof toSerialize === "object") {
|
1026 | const dictionary: { [key: string]: any } = {};
|
1027 | for (const property in toSerialize) {
|
1028 | dictionary[property] = serializeObject(toSerialize[property]);
|
1029 | }
|
1030 | return dictionary;
|
1031 | }
|
1032 | return toSerialize;
|
1033 | }
|
1034 |
|
1035 |
|
1036 |
|
1037 |
|
1038 | function strEnum<T extends string>(o: Array<T>): { [K in T]: K } {
|
1039 | const result: any = {};
|
1040 | for (const key of o) {
|
1041 | result[key] = key;
|
1042 | }
|
1043 | return result;
|
1044 | }
|
1045 |
|
1046 | export const MapperType = strEnum([
|
1047 | "Base64Url",
|
1048 | "Boolean",
|
1049 | "ByteArray",
|
1050 | "Composite",
|
1051 | "Date",
|
1052 | "DateTime",
|
1053 | "DateTimeRfc1123",
|
1054 | "Dictionary",
|
1055 | "Enum",
|
1056 | "Number",
|
1057 | "Object",
|
1058 | "Sequence",
|
1059 | "String",
|
1060 | "Stream",
|
1061 | "TimeSpan",
|
1062 | "UnixTime",
|
1063 | ]);
|