@azure-tools/typespec-ts 0.49.0 → 0.49.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/src/framework/hooks/binder.d.ts.map +1 -1
- package/dist/src/framework/hooks/binder.js +8 -17
- package/dist/src/framework/hooks/binder.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +13 -11
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib.d.ts +8 -0
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +10 -0
- package/dist/src/lib.js.map +1 -1
- package/dist/src/modular/buildOperations.d.ts.map +1 -1
- package/dist/src/modular/buildOperations.js +10 -5
- package/dist/src/modular/buildOperations.js.map +1 -1
- package/dist/src/modular/buildProjectFiles.d.ts.map +1 -1
- package/dist/src/modular/buildProjectFiles.js +13 -10
- package/dist/src/modular/buildProjectFiles.js.map +1 -1
- package/dist/src/modular/emitModels.d.ts.map +1 -1
- package/dist/src/modular/emitModels.js +43 -40
- package/dist/src/modular/emitModels.js.map +1 -1
- package/dist/src/modular/emitSamples.d.ts.map +1 -1
- package/dist/src/modular/emitSamples.js +9 -0
- package/dist/src/modular/emitSamples.js.map +1 -1
- package/dist/src/modular/helpers/clientHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/clientHelpers.js +4 -1
- package/dist/src/modular/helpers/clientHelpers.js.map +1 -1
- package/dist/src/modular/helpers/namingHelpers.d.ts +7 -0
- package/dist/src/modular/helpers/namingHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/namingHelpers.js +15 -0
- package/dist/src/modular/helpers/namingHelpers.js.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.d.ts +19 -2
- package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.js +410 -39
- package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
- package/dist/src/modular/serialization/buildDeserializerFunction.d.ts.map +1 -1
- package/dist/src/modular/serialization/buildDeserializerFunction.js +9 -3
- package/dist/src/modular/serialization/buildDeserializerFunction.js.map +1 -1
- package/dist/src/modular/serialization/buildXmlSerializerFunction.d.ts +12 -0
- package/dist/src/modular/serialization/buildXmlSerializerFunction.d.ts.map +1 -1
- package/dist/src/modular/serialization/buildXmlSerializerFunction.js +259 -18
- package/dist/src/modular/serialization/buildXmlSerializerFunction.js.map +1 -1
- package/dist/src/modular/static-helpers-metadata.d.ts +10 -0
- package/dist/src/modular/static-helpers-metadata.d.ts.map +1 -1
- package/dist/src/modular/static-helpers-metadata.js +10 -0
- package/dist/src/modular/static-helpers-metadata.js.map +1 -1
- package/dist/src/modular/type-expressions/get-model-expression.d.ts +3 -1
- package/dist/src/modular/type-expressions/get-model-expression.d.ts.map +1 -1
- package/dist/src/modular/type-expressions/get-model-expression.js +50 -9
- package/dist/src/modular/type-expressions/get-model-expression.js.map +1 -1
- package/dist/src/modular/type-expressions/get-nullable-expression.d.ts.map +1 -1
- package/dist/src/modular/type-expressions/get-nullable-expression.js +8 -0
- package/dist/src/modular/type-expressions/get-nullable-expression.js.map +1 -1
- package/dist/src/modular/type-expressions/get-type-expression.d.ts +3 -2
- package/dist/src/modular/type-expressions/get-type-expression.d.ts.map +1 -1
- package/dist/src/modular/type-expressions/get-type-expression.js.map +1 -1
- package/dist/src/transform/transform.d.ts.map +1 -1
- package/dist/src/transform/transform.js +10 -10
- package/dist/src/transform/transform.js.map +1 -1
- package/dist/src/transform/transformSchemas.d.ts.map +1 -1
- package/dist/src/transform/transformSchemas.js +4 -4
- package/dist/src/transform/transformSchemas.js.map +1 -1
- package/dist/src/transform/transfromRLCOptions.d.ts +1 -1
- package/dist/src/transform/transfromRLCOptions.d.ts.map +1 -1
- package/dist/src/transform/transfromRLCOptions.js +37 -24
- package/dist/src/transform/transfromRLCOptions.js.map +1 -1
- package/dist/src/utils/clientUtils.d.ts +1 -1
- package/dist/src/utils/clientUtils.d.ts.map +1 -1
- package/dist/src/utils/clientUtils.js +40 -17
- package/dist/src/utils/clientUtils.js.map +1 -1
- package/dist/src/utils/crossLanguageDef.d.ts.map +1 -1
- package/dist/src/utils/crossLanguageDef.js +9 -3
- package/dist/src/utils/crossLanguageDef.js.map +1 -1
- package/dist/src/utils/interfaces.d.ts +2 -1
- package/dist/src/utils/interfaces.d.ts.map +1 -1
- package/dist/src/utils/modelUtils.d.ts +2 -2
- package/dist/src/utils/modelUtils.d.ts.map +1 -1
- package/dist/src/utils/modelUtils.js +15 -16
- package/dist/src/utils/modelUtils.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +22 -22
- package/src/framework/hooks/binder.ts +12 -22
- package/src/index.ts +17 -10
- package/src/lib.ts +20 -0
- package/src/modular/buildOperations.ts +12 -4
- package/src/modular/buildProjectFiles.ts +19 -16
- package/src/modular/emitModels.ts +72 -44
- package/src/modular/emitSamples.ts +11 -1
- package/src/modular/helpers/clientHelpers.ts +5 -1
- package/src/modular/helpers/namingHelpers.ts +19 -0
- package/src/modular/helpers/operationHelpers.ts +533 -40
- package/src/modular/serialization/buildDeserializerFunction.ts +11 -2
- package/src/modular/serialization/buildXmlSerializerFunction.ts +375 -24
- package/src/modular/static-helpers-metadata.ts +10 -0
- package/src/modular/type-expressions/get-model-expression.ts +78 -13
- package/src/modular/type-expressions/get-nullable-expression.ts +9 -0
- package/src/modular/type-expressions/get-type-expression.ts +3 -1
- package/src/transform/transform.ts +5 -2
- package/src/transform/transformSchemas.ts +4 -1
- package/src/transform/transfromRLCOptions.ts +65 -24
- package/src/utils/clientUtils.ts +49 -16
- package/src/utils/crossLanguageDef.ts +8 -0
- package/src/utils/interfaces.ts +2 -1
- package/src/utils/modelUtils.ts +22 -18
- package/static/static-helpers/serialization/serialize-record.ts +3 -3
- package/static/static-helpers/serialization/xml-helpers.ts +91 -36
- package/static/static-helpers/urlTemplate.ts +5 -5
|
@@ -12,6 +12,7 @@ import { SdkContext } from "../../utils/interfaces.js";
|
|
|
12
12
|
import {
|
|
13
13
|
getAllAncestors,
|
|
14
14
|
getAllProperties,
|
|
15
|
+
getPropertySerializedName,
|
|
15
16
|
getResponseMapping
|
|
16
17
|
} from "../helpers/operationHelpers.js";
|
|
17
18
|
import {
|
|
@@ -218,8 +219,12 @@ function buildPolymorphicDeserializer(
|
|
|
218
219
|
`);
|
|
219
220
|
});
|
|
220
221
|
|
|
222
|
+
// Use wire format name for the switch since item is raw JSON from the service
|
|
223
|
+
const discriminatorWireName = getPropertySerializedName(
|
|
224
|
+
type.discriminatorProperty
|
|
225
|
+
);
|
|
221
226
|
statements.push(`
|
|
222
|
-
switch (item
|
|
227
|
+
switch (item["${discriminatorWireName}"]) {
|
|
223
228
|
${cases.join("\n")}
|
|
224
229
|
default:
|
|
225
230
|
return item;
|
|
@@ -300,8 +305,12 @@ function buildDiscriminatedUnionDeserializer(
|
|
|
300
305
|
return ${subtypeDeserializerName}(item as ${subTypeName});
|
|
301
306
|
`);
|
|
302
307
|
}
|
|
308
|
+
// Use wire format name for the switch since item is raw JSON from the service
|
|
309
|
+
const discriminatorWireName = type.discriminatorProperty
|
|
310
|
+
? getPropertySerializedName(type.discriminatorProperty)
|
|
311
|
+
: "unknown";
|
|
303
312
|
output.push(`
|
|
304
|
-
switch (item
|
|
313
|
+
switch (item["${discriminatorWireName}"]) {
|
|
305
314
|
${cases.join("\n")}
|
|
306
315
|
default:
|
|
307
316
|
return ${baseDeserializerName}(item);
|
|
@@ -29,8 +29,7 @@ import { NoTarget } from "@typespec/compiler";
|
|
|
29
29
|
import { isMetadata } from "@typespec/http";
|
|
30
30
|
import { normalizeModelPropertyName } from "../type-expressions/get-type-expression.js";
|
|
31
31
|
import { isReadOnly } from "@azure-tools/typespec-client-generator-core";
|
|
32
|
-
import {
|
|
33
|
-
import { buildModelDeserializer } from "./buildDeserializerFunction.js";
|
|
32
|
+
import { useDependencies } from "../../framework/hooks/useDependencies.js";
|
|
34
33
|
|
|
35
34
|
/**
|
|
36
35
|
* Checks if a model type has XML serialization options defined
|
|
@@ -172,6 +171,215 @@ export function buildXmlModelSerializer(
|
|
|
172
171
|
return serializerFunction;
|
|
173
172
|
}
|
|
174
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Builds an XML object serializer function for a model type.
|
|
176
|
+
* This serializer returns a plain object with XML property names (not an XML string).
|
|
177
|
+
* Used for nested objects within XML serialization.
|
|
178
|
+
*/
|
|
179
|
+
export function buildXmlObjectModelSerializer(
|
|
180
|
+
context: SdkContext,
|
|
181
|
+
type: SdkModelType,
|
|
182
|
+
options: ModelSerializeOptions = {
|
|
183
|
+
nameOnly: false,
|
|
184
|
+
skipDiscriminatedUnionSuffix: false
|
|
185
|
+
}
|
|
186
|
+
): FunctionDeclarationStructure | string | undefined {
|
|
187
|
+
if (!isSupportedSerializeType(type)) {
|
|
188
|
+
return undefined;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (!type.name) {
|
|
192
|
+
reportDiagnostic(context.program, {
|
|
193
|
+
code: "anonymous-type-serialization",
|
|
194
|
+
target: type.__raw || NoTarget
|
|
195
|
+
});
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (
|
|
200
|
+
!type.usage ||
|
|
201
|
+
(type.usage !== undefined &&
|
|
202
|
+
(type.usage & UsageFlags.Input) !== UsageFlags.Input)
|
|
203
|
+
) {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (isAzureCoreErrorType(context.program, type.__raw!)) {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const serializerFunctionName =
|
|
212
|
+
options.predefinedName ??
|
|
213
|
+
`${normalizeModelName(
|
|
214
|
+
context,
|
|
215
|
+
type,
|
|
216
|
+
NameType.Operation,
|
|
217
|
+
options.skipDiscriminatedUnionSuffix
|
|
218
|
+
)}XmlObjectSerializer`;
|
|
219
|
+
|
|
220
|
+
if (options.nameOnly) {
|
|
221
|
+
return resolveReference(refkey(type, "xmlObjectSerializer"));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const properties = getAllProperties(context, type, getAllAncestors(type));
|
|
225
|
+
|
|
226
|
+
// Build the object literal with XML property names
|
|
227
|
+
const propertyAssignments = buildXmlObjectPropertyAssignments(
|
|
228
|
+
context,
|
|
229
|
+
properties
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
const statements: string[] = [];
|
|
233
|
+
|
|
234
|
+
// Check if the model is a dictionary type (has additionalProperties)
|
|
235
|
+
// For Record<T> types, we need to spread the item properties
|
|
236
|
+
const isDictType = type.additionalProperties !== undefined;
|
|
237
|
+
|
|
238
|
+
if (isDictType && propertyAssignments.length === 0) {
|
|
239
|
+
// Pure dictionary type - spread all properties
|
|
240
|
+
statements.push(
|
|
241
|
+
`return { ...item } as ${resolveReference(XmlHelpers.XmlSerializedObject)};`
|
|
242
|
+
);
|
|
243
|
+
} else if (isDictType) {
|
|
244
|
+
// Model with both defined properties and additional properties
|
|
245
|
+
statements.push(`return {${propertyAssignments}, ...item};`);
|
|
246
|
+
} else {
|
|
247
|
+
statements.push(`return {${propertyAssignments}};`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const xmlSerializedObjectRef = resolveReference(
|
|
251
|
+
XmlHelpers.XmlSerializedObject
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
// Use _item when there are no properties and not a dict type to avoid unused parameter lint error
|
|
255
|
+
const paramName =
|
|
256
|
+
propertyAssignments.length === 0 && !isDictType ? "_item" : "item";
|
|
257
|
+
|
|
258
|
+
const serializerFunction: FunctionDeclarationStructure = {
|
|
259
|
+
kind: StructureKind.Function,
|
|
260
|
+
name: serializerFunctionName,
|
|
261
|
+
isExported: true,
|
|
262
|
+
parameters: [
|
|
263
|
+
{
|
|
264
|
+
name: paramName,
|
|
265
|
+
type: resolveReference(refkey(type))
|
|
266
|
+
}
|
|
267
|
+
],
|
|
268
|
+
returnType: xmlSerializedObjectRef,
|
|
269
|
+
statements
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
return serializerFunction;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Builds the property assignments for XML object serializer.
|
|
277
|
+
* Maps client property names to XML property names in the output object.
|
|
278
|
+
*/
|
|
279
|
+
function buildXmlObjectPropertyAssignments(
|
|
280
|
+
context: SdkContext,
|
|
281
|
+
properties: SdkModelPropertyType[]
|
|
282
|
+
): string {
|
|
283
|
+
const assignments: string[] = [];
|
|
284
|
+
|
|
285
|
+
for (const property of properties) {
|
|
286
|
+
if (property.kind !== "property") {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (isReadOnly(property)) {
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
if (isMetadata(context.program, property.__raw!)) {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const xmlOptions = property.serializationOptions?.xml;
|
|
297
|
+
const propertyName = normalizeModelPropertyName(context, property);
|
|
298
|
+
const cleanPropertyName = propertyName.replace(/^"|"$/g, "");
|
|
299
|
+
|
|
300
|
+
// Use XML name if available, fall back to serializedName, then JSON name, then property name
|
|
301
|
+
const xmlName = xmlOptions?.name ?? property.name;
|
|
302
|
+
|
|
303
|
+
// Handle nested objects and arrays
|
|
304
|
+
const nestedSerializer = getNestedXmlSerializer(context, property.type);
|
|
305
|
+
|
|
306
|
+
let valueExpr: string;
|
|
307
|
+
if (nestedSerializer && property.type.kind === "model") {
|
|
308
|
+
// Nested object - use XML object serializer
|
|
309
|
+
valueExpr = `item["${cleanPropertyName}"] !== undefined ? ${nestedSerializer}(item["${cleanPropertyName}"]) : undefined`;
|
|
310
|
+
} else if (
|
|
311
|
+
nestedSerializer &&
|
|
312
|
+
property.type.kind === "array" &&
|
|
313
|
+
property.type.valueType.kind === "model"
|
|
314
|
+
) {
|
|
315
|
+
// Array of objects - map each item through XML object serializer
|
|
316
|
+
valueExpr = `item["${cleanPropertyName}"]?.map((i: any) => ${nestedSerializer}(i))`;
|
|
317
|
+
} else {
|
|
318
|
+
// Handle type-specific serialization
|
|
319
|
+
valueExpr = buildXmlValueSerializationExpr(
|
|
320
|
+
context,
|
|
321
|
+
property.type,
|
|
322
|
+
`item["${cleanPropertyName}"]`
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
assignments.push(`"${xmlName}": ${valueExpr}`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return assignments.join(",\n ");
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Builds the serialization expression for a value based on its type.
|
|
334
|
+
* Handles bytes (base64), dates, and arrays of these types.
|
|
335
|
+
*/
|
|
336
|
+
function buildXmlValueSerializationExpr(
|
|
337
|
+
context: SdkContext,
|
|
338
|
+
type: SdkType,
|
|
339
|
+
valueExpr: string
|
|
340
|
+
): string {
|
|
341
|
+
const uint8ArrayToStringRef = resolveReference(
|
|
342
|
+
useDependencies().uint8ArrayToString
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
switch (type.kind) {
|
|
346
|
+
case "bytes":
|
|
347
|
+
// Convert Uint8Array to base64 string
|
|
348
|
+
return `${valueExpr} !== undefined ? ${uint8ArrayToStringRef}(${valueExpr}, "base64") : undefined`;
|
|
349
|
+
|
|
350
|
+
case "utcDateTime": {
|
|
351
|
+
// Convert Date to appropriate string format
|
|
352
|
+
const encoding = (type.encode as string) ?? "rfc3339";
|
|
353
|
+
if (encoding === "unixTimestamp") {
|
|
354
|
+
return `${valueExpr} !== undefined ? ${valueExpr}.getTime() : undefined`;
|
|
355
|
+
} else if (encoding === "rfc7231") {
|
|
356
|
+
return `${valueExpr} !== undefined ? ${valueExpr}.toUTCString() : undefined`;
|
|
357
|
+
}
|
|
358
|
+
// Default rfc3339
|
|
359
|
+
return `${valueExpr} !== undefined ? ${valueExpr}.toISOString() : undefined`;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
case "array": {
|
|
363
|
+
// Handle arrays - need to serialize each element
|
|
364
|
+
const itemExpr = buildXmlValueSerializationExpr(
|
|
365
|
+
context,
|
|
366
|
+
type.valueType,
|
|
367
|
+
"i"
|
|
368
|
+
);
|
|
369
|
+
if (itemExpr !== "i") {
|
|
370
|
+
// If items need transformation, map them
|
|
371
|
+
return `${valueExpr}?.map((i: any) => ${itemExpr})`;
|
|
372
|
+
}
|
|
373
|
+
// No transformation needed
|
|
374
|
+
return valueExpr;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
default:
|
|
378
|
+
// Primitive types - use directly
|
|
379
|
+
return valueExpr;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
175
383
|
/**
|
|
176
384
|
* Builds the property metadata array for XML serialization
|
|
177
385
|
*/
|
|
@@ -214,6 +422,12 @@ function buildPropertyMetadataArray(
|
|
|
214
422
|
if (typeInfo.dateEncoding) {
|
|
215
423
|
metadataObj.push(`dateEncoding: "${typeInfo.dateEncoding}"`);
|
|
216
424
|
}
|
|
425
|
+
if (typeInfo.bytesEncoding) {
|
|
426
|
+
metadataObj.push(`bytesEncoding: "${typeInfo.bytesEncoding}"`);
|
|
427
|
+
}
|
|
428
|
+
if (typeInfo.itemType) {
|
|
429
|
+
metadataObj.push(`itemType: "${typeInfo.itemType}"`);
|
|
430
|
+
}
|
|
217
431
|
|
|
218
432
|
// Add serializer for complex types
|
|
219
433
|
const nestedSerializer = getNestedXmlSerializer(context, property.type);
|
|
@@ -273,10 +487,30 @@ function buildXmlOptionsString(xmlOptions?: {
|
|
|
273
487
|
function getPropertyTypeInfo(type: SdkType): {
|
|
274
488
|
type?: "array" | "object" | "primitive" | "date" | "bytes" | "dict";
|
|
275
489
|
dateEncoding?: "rfc3339" | "rfc7231" | "unixTimestamp";
|
|
490
|
+
bytesEncoding?: "base64" | "base64url";
|
|
491
|
+
itemType?: "primitive" | "date" | "bytes";
|
|
276
492
|
} {
|
|
277
493
|
switch (type.kind) {
|
|
278
|
-
case "array":
|
|
279
|
-
|
|
494
|
+
case "array": {
|
|
495
|
+
// For arrays, also extract item type info for bytes/date items
|
|
496
|
+
const itemInfo = getPropertyTypeInfo(type.valueType);
|
|
497
|
+
const result: ReturnType<typeof getPropertyTypeInfo> = { type: "array" };
|
|
498
|
+
// Only include item type info for types that need special serialization
|
|
499
|
+
if (
|
|
500
|
+
itemInfo.type === "bytes" ||
|
|
501
|
+
itemInfo.type === "date" ||
|
|
502
|
+
itemInfo.type === "primitive"
|
|
503
|
+
) {
|
|
504
|
+
result.itemType = itemInfo.type as "primitive" | "date" | "bytes";
|
|
505
|
+
}
|
|
506
|
+
if (itemInfo.dateEncoding) {
|
|
507
|
+
result.dateEncoding = itemInfo.dateEncoding;
|
|
508
|
+
}
|
|
509
|
+
if (itemInfo.bytesEncoding) {
|
|
510
|
+
result.bytesEncoding = itemInfo.bytesEncoding;
|
|
511
|
+
}
|
|
512
|
+
return result;
|
|
513
|
+
}
|
|
280
514
|
case "model":
|
|
281
515
|
return { type: "object" };
|
|
282
516
|
case "dict":
|
|
@@ -287,24 +521,30 @@ function getPropertyTypeInfo(type: SdkType): {
|
|
|
287
521
|
dateEncoding:
|
|
288
522
|
(type.encode as "rfc3339" | "rfc7231" | "unixTimestamp") ?? "rfc3339"
|
|
289
523
|
};
|
|
290
|
-
case "bytes":
|
|
291
|
-
|
|
524
|
+
case "bytes": {
|
|
525
|
+
const encode = (type as any).encode as string | undefined;
|
|
526
|
+
// Default to base64 if no encoding specified
|
|
527
|
+
const bytesEncoding =
|
|
528
|
+
encode === "base64url" ? "base64url" : ("base64" as const);
|
|
529
|
+
return { type: "bytes", bytesEncoding };
|
|
530
|
+
}
|
|
292
531
|
default:
|
|
293
532
|
return { type: "primitive" };
|
|
294
533
|
}
|
|
295
534
|
}
|
|
296
535
|
|
|
297
536
|
/**
|
|
298
|
-
* Gets the nested XML serializer function reference for complex types
|
|
537
|
+
* Gets the nested XML serializer function reference for complex types.
|
|
538
|
+
* Uses the XML object serializer to properly handle XML property names.
|
|
299
539
|
*/
|
|
300
540
|
function getNestedXmlSerializer(
|
|
301
541
|
context: SdkContext,
|
|
302
542
|
type: SdkType
|
|
303
543
|
): string | undefined {
|
|
304
544
|
if (type.kind === "model") {
|
|
305
|
-
// For nested objects, use the
|
|
306
|
-
//
|
|
307
|
-
const serializerName =
|
|
545
|
+
// For nested objects, use the XML object serializer which returns objects
|
|
546
|
+
// with XML property names for proper serialization
|
|
547
|
+
const serializerName = buildXmlObjectModelSerializer(context, type, {
|
|
308
548
|
nameOnly: true,
|
|
309
549
|
skipDiscriminatedUnionSuffix: false
|
|
310
550
|
});
|
|
@@ -313,12 +553,16 @@ function getNestedXmlSerializer(
|
|
|
313
553
|
}
|
|
314
554
|
}
|
|
315
555
|
if (type.kind === "array" && type.valueType.kind === "model") {
|
|
316
|
-
// For arrays, use the
|
|
556
|
+
// For arrays, use the XML object serializer for items
|
|
317
557
|
// The XML helper calls this for each item and handles the array mapping
|
|
318
|
-
const itemSerializer =
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
558
|
+
const itemSerializer = buildXmlObjectModelSerializer(
|
|
559
|
+
context,
|
|
560
|
+
type.valueType,
|
|
561
|
+
{
|
|
562
|
+
nameOnly: true,
|
|
563
|
+
skipDiscriminatedUnionSuffix: false
|
|
564
|
+
}
|
|
565
|
+
);
|
|
322
566
|
if (typeof itemSerializer === "string") {
|
|
323
567
|
return itemSerializer;
|
|
324
568
|
}
|
|
@@ -465,6 +709,12 @@ function buildDeserializePropertyMetadataArray(
|
|
|
465
709
|
if (typeInfo.dateEncoding) {
|
|
466
710
|
metadataObj.push(`dateEncoding: "${typeInfo.dateEncoding}"`);
|
|
467
711
|
}
|
|
712
|
+
if (typeInfo.bytesEncoding) {
|
|
713
|
+
metadataObj.push(`bytesEncoding: "${typeInfo.bytesEncoding}"`);
|
|
714
|
+
}
|
|
715
|
+
if (typeInfo.itemType) {
|
|
716
|
+
metadataObj.push(`itemType: "${typeInfo.itemType}"`);
|
|
717
|
+
}
|
|
468
718
|
|
|
469
719
|
// Add deserializer for complex types
|
|
470
720
|
const nestedDeserializer = getNestedXmlDeserializer(context, property.type);
|
|
@@ -479,16 +729,17 @@ function buildDeserializePropertyMetadataArray(
|
|
|
479
729
|
}
|
|
480
730
|
|
|
481
731
|
/**
|
|
482
|
-
* Gets the nested XML deserializer function reference for complex types
|
|
732
|
+
* Gets the nested XML deserializer function reference for complex types.
|
|
733
|
+
* Uses the XML object deserializer to properly handle XML property names.
|
|
483
734
|
*/
|
|
484
735
|
function getNestedXmlDeserializer(
|
|
485
736
|
context: SdkContext,
|
|
486
737
|
type: SdkType
|
|
487
738
|
): string | undefined {
|
|
488
739
|
if (type.kind === "model") {
|
|
489
|
-
// For nested objects, use the
|
|
490
|
-
//
|
|
491
|
-
const deserializerName =
|
|
740
|
+
// For nested objects, use the XML object deserializer which takes parsed XML objects
|
|
741
|
+
// and uses XML property names for mapping
|
|
742
|
+
const deserializerName = buildXmlObjectModelDeserializer(context, type, {
|
|
492
743
|
nameOnly: true,
|
|
493
744
|
skipDiscriminatedUnionSuffix: false
|
|
494
745
|
});
|
|
@@ -497,15 +748,115 @@ function getNestedXmlDeserializer(
|
|
|
497
748
|
}
|
|
498
749
|
}
|
|
499
750
|
if (type.kind === "array" && type.valueType.kind === "model") {
|
|
500
|
-
// For arrays, use the
|
|
751
|
+
// For arrays, use the XML object deserializer for items
|
|
501
752
|
// The XML helper calls this for each item and handles the array mapping
|
|
502
|
-
const itemDeserializer =
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
753
|
+
const itemDeserializer = buildXmlObjectModelDeserializer(
|
|
754
|
+
context,
|
|
755
|
+
type.valueType,
|
|
756
|
+
{
|
|
757
|
+
nameOnly: true,
|
|
758
|
+
skipDiscriminatedUnionSuffix: false
|
|
759
|
+
}
|
|
760
|
+
);
|
|
506
761
|
if (typeof itemDeserializer === "string") {
|
|
507
762
|
return itemDeserializer;
|
|
508
763
|
}
|
|
509
764
|
}
|
|
510
765
|
return undefined;
|
|
511
766
|
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Builds an XML object deserializer function for a model type.
|
|
770
|
+
* This deserializer takes a pre-parsed XML object (not an XML string)
|
|
771
|
+
* and uses XML property names for mapping. Used for nested objects.
|
|
772
|
+
*/
|
|
773
|
+
export function buildXmlObjectModelDeserializer(
|
|
774
|
+
context: SdkContext,
|
|
775
|
+
type: SdkModelType,
|
|
776
|
+
options: ModelSerializeOptions = {
|
|
777
|
+
nameOnly: false,
|
|
778
|
+
skipDiscriminatedUnionSuffix: false
|
|
779
|
+
}
|
|
780
|
+
): FunctionDeclarationStructure | string | undefined {
|
|
781
|
+
if (!isSupportedSerializeType(type)) {
|
|
782
|
+
return undefined;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if (!type.name) {
|
|
786
|
+
reportDiagnostic(context.program, {
|
|
787
|
+
code: "anonymous-type-deserialization",
|
|
788
|
+
target: type.__raw || NoTarget
|
|
789
|
+
});
|
|
790
|
+
return undefined;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
if (
|
|
794
|
+
!type.usage ||
|
|
795
|
+
(type.usage !== undefined &&
|
|
796
|
+
(type.usage & UsageFlags.Output) !== UsageFlags.Output &&
|
|
797
|
+
(type.usage & UsageFlags.Exception) !== UsageFlags.Exception)
|
|
798
|
+
) {
|
|
799
|
+
return undefined;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
if (isAzureCoreErrorType(context.program, type.__raw!)) {
|
|
803
|
+
return undefined;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
const deserializerFunctionName =
|
|
807
|
+
options.predefinedName ??
|
|
808
|
+
`${normalizeModelName(
|
|
809
|
+
context,
|
|
810
|
+
type,
|
|
811
|
+
NameType.Operation,
|
|
812
|
+
options.skipDiscriminatedUnionSuffix
|
|
813
|
+
)}XmlObjectDeserializer`;
|
|
814
|
+
|
|
815
|
+
if (options.nameOnly) {
|
|
816
|
+
return resolveReference(refkey(type, "xmlObjectDeserializer"));
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
const deserializeXmlObjectRef = resolveReference(
|
|
820
|
+
XmlHelpers.deserializeXmlObject
|
|
821
|
+
);
|
|
822
|
+
const xmlPropertyDeserializeMetadataRef = resolveReference(
|
|
823
|
+
XmlHelpers.XmlPropertyDeserializeMetadata
|
|
824
|
+
);
|
|
825
|
+
|
|
826
|
+
const properties = getAllProperties(context, type, getAllAncestors(type));
|
|
827
|
+
|
|
828
|
+
// Build property metadata array for deserialization
|
|
829
|
+
const propertyMetadata = buildDeserializePropertyMetadataArray(
|
|
830
|
+
context,
|
|
831
|
+
properties
|
|
832
|
+
);
|
|
833
|
+
|
|
834
|
+
const statements: string[] = [];
|
|
835
|
+
|
|
836
|
+
// Generate the properties metadata constant
|
|
837
|
+
statements.push(
|
|
838
|
+
`const properties: ${xmlPropertyDeserializeMetadataRef}[] = [${propertyMetadata}];`
|
|
839
|
+
);
|
|
840
|
+
|
|
841
|
+
// Generate the deserialization call - no rootName needed for object deserializer
|
|
842
|
+
const typeRef = resolveReference(refkey(type));
|
|
843
|
+
statements.push(
|
|
844
|
+
`return ${deserializeXmlObjectRef}<${typeRef}>(xmlObject, properties);`
|
|
845
|
+
);
|
|
846
|
+
|
|
847
|
+
const deserializerFunction: FunctionDeclarationStructure = {
|
|
848
|
+
kind: StructureKind.Function,
|
|
849
|
+
name: deserializerFunctionName,
|
|
850
|
+
isExported: true,
|
|
851
|
+
parameters: [
|
|
852
|
+
{
|
|
853
|
+
name: "xmlObject",
|
|
854
|
+
type: "Record<string, unknown>"
|
|
855
|
+
}
|
|
856
|
+
],
|
|
857
|
+
returnType: resolveReference(refkey(type)),
|
|
858
|
+
statements
|
|
859
|
+
};
|
|
860
|
+
|
|
861
|
+
return deserializerFunction;
|
|
862
|
+
}
|
|
@@ -209,6 +209,11 @@ export const XmlHelpers = {
|
|
|
209
209
|
name: "deserializeFromXml",
|
|
210
210
|
location: "serialization/xml-helpers.ts"
|
|
211
211
|
},
|
|
212
|
+
deserializeXmlObject: {
|
|
213
|
+
kind: "function",
|
|
214
|
+
name: "deserializeXmlObject",
|
|
215
|
+
location: "serialization/xml-helpers.ts"
|
|
216
|
+
},
|
|
212
217
|
isXmlContentType: {
|
|
213
218
|
kind: "function",
|
|
214
219
|
name: "isXmlContentType",
|
|
@@ -218,5 +223,10 @@ export const XmlHelpers = {
|
|
|
218
223
|
kind: "function",
|
|
219
224
|
name: "isJsonContentType",
|
|
220
225
|
location: "serialization/xml-helpers.ts"
|
|
226
|
+
},
|
|
227
|
+
XmlSerializedObject: {
|
|
228
|
+
kind: "interface",
|
|
229
|
+
name: "XmlSerializedObject",
|
|
230
|
+
location: "serialization/xml-helpers.ts"
|
|
221
231
|
}
|
|
222
232
|
} as const;
|
|
@@ -4,12 +4,17 @@ import {
|
|
|
4
4
|
normalizeModelPropertyName
|
|
5
5
|
} from "./get-type-expression.js";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
SdkModelPropertyType,
|
|
9
|
+
SdkModelType,
|
|
10
|
+
SdkServiceResponseHeader
|
|
11
|
+
} from "@azure-tools/typespec-client-generator-core";
|
|
8
12
|
import { refkey } from "../../framework/refkey.js";
|
|
9
13
|
import { resolveReference } from "../../framework/reference.js";
|
|
10
14
|
import { shouldEmitInline } from "./utils.js";
|
|
11
15
|
import { useContext } from "../../contextManager.js";
|
|
12
16
|
import { SdkContext } from "../../utils/interfaces.js";
|
|
17
|
+
import { MultipartHelpers } from "../static-helpers-metadata.js";
|
|
13
18
|
|
|
14
19
|
export interface ModelExpressionOptions extends EmitTypeOptions {
|
|
15
20
|
skipPolymorphicUnion?: boolean;
|
|
@@ -26,18 +31,7 @@ export function getModelExpression(
|
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
if (shouldEmitInline(type, options)) {
|
|
29
|
-
|
|
30
|
-
if (type.properties.length === 0) {
|
|
31
|
-
return "Record<string, any>";
|
|
32
|
-
}
|
|
33
|
-
return `{
|
|
34
|
-
${type.properties
|
|
35
|
-
.map(
|
|
36
|
-
(p) =>
|
|
37
|
-
`${normalizeModelPropertyName(context, p)}${p.optional ? "?" : ""}: ${getTypeExpression(context, p.type)}`
|
|
38
|
-
)
|
|
39
|
-
.join(",\n")}
|
|
40
|
-
}`;
|
|
34
|
+
return emitInlineModel(context, type.properties);
|
|
41
35
|
} else {
|
|
42
36
|
if (!options.skipPolymorphicUnion && type.discriminatedSubtypes) {
|
|
43
37
|
return resolveReference(refkey(type, "polymorphicType"));
|
|
@@ -52,6 +46,77 @@ const externalModels: Record<string, string> = {
|
|
|
52
46
|
"Azure.Core.Foundations.ErrorResponse": "AzureCoreErrorResponse"
|
|
53
47
|
};
|
|
54
48
|
|
|
49
|
+
export function emitInlineModel(
|
|
50
|
+
context: SdkContext,
|
|
51
|
+
properties: (SdkModelPropertyType | SdkServiceResponseHeader)[]
|
|
52
|
+
): string {
|
|
53
|
+
// generate Record<string, any> for empty anonymous object
|
|
54
|
+
if (properties.length === 0) {
|
|
55
|
+
return "Record<string, any>";
|
|
56
|
+
}
|
|
57
|
+
return `{
|
|
58
|
+
${properties
|
|
59
|
+
.map(
|
|
60
|
+
(p) =>
|
|
61
|
+
`${normalizeModelPropertyName(context, p)}${p.optional ? "?" : ""}: ${getPropertyTypeExpression(context, p)}`
|
|
62
|
+
)
|
|
63
|
+
.join(",\n")}
|
|
64
|
+
}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getPropertyTypeExpression(
|
|
68
|
+
context: SdkContext,
|
|
69
|
+
property: SdkModelPropertyType | SdkServiceResponseHeader
|
|
70
|
+
): string {
|
|
71
|
+
if (
|
|
72
|
+
property.kind === "property" &&
|
|
73
|
+
property.serializationOptions.multipart?.isFilePart
|
|
74
|
+
) {
|
|
75
|
+
return getMultipartFileTypeExpression(context, property);
|
|
76
|
+
}
|
|
77
|
+
return getTypeExpression(context, property.type);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function getMultipartFileTypeExpression(
|
|
81
|
+
context: SdkContext,
|
|
82
|
+
property: SdkModelPropertyType
|
|
83
|
+
): string {
|
|
84
|
+
const multipartOptions = property.serializationOptions.multipart;
|
|
85
|
+
|
|
86
|
+
const isContentTypeOptional =
|
|
87
|
+
multipartOptions?.contentType === undefined ||
|
|
88
|
+
multipartOptions.contentType.optional ||
|
|
89
|
+
multipartOptions.defaultContentTypes.length > 0;
|
|
90
|
+
const isFilenameOptional =
|
|
91
|
+
multipartOptions?.filename === undefined ||
|
|
92
|
+
multipartOptions.filename.optional;
|
|
93
|
+
|
|
94
|
+
const contentTypeType = multipartOptions?.contentType
|
|
95
|
+
? getTypeExpression(context, multipartOptions.contentType.type)
|
|
96
|
+
: "string";
|
|
97
|
+
const filenameType = multipartOptions?.filename
|
|
98
|
+
? getTypeExpression(context, multipartOptions.filename.type)
|
|
99
|
+
: "string";
|
|
100
|
+
|
|
101
|
+
let typeExpression = "{";
|
|
102
|
+
typeExpression += `contents: ${resolveReference(MultipartHelpers.FileContents)};`;
|
|
103
|
+
typeExpression += `contentType${isContentTypeOptional ? "?" : ""}: ${contentTypeType};`;
|
|
104
|
+
typeExpression += `filename${isFilenameOptional ? "?" : ""}: ${filenameType};`;
|
|
105
|
+
typeExpression += "}";
|
|
106
|
+
|
|
107
|
+
if (isContentTypeOptional && isFilenameOptional) {
|
|
108
|
+
typeExpression = `(${resolveReference(MultipartHelpers.FileContents)}) | ${typeExpression}`;
|
|
109
|
+
} else {
|
|
110
|
+
typeExpression = `File | ${typeExpression}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (property.type.kind === "array") {
|
|
114
|
+
typeExpression = `Array<${typeExpression}>`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return typeExpression;
|
|
118
|
+
}
|
|
119
|
+
|
|
55
120
|
export function getExternalModel(type: SdkModelType) {
|
|
56
121
|
const commonName = externalModels[type.crossLanguageDefinitionId];
|
|
57
122
|
|
|
@@ -10,7 +10,16 @@ export function getNullableExpression(
|
|
|
10
10
|
options: EmitTypeOptions = {}
|
|
11
11
|
): string {
|
|
12
12
|
if (shouldEmitInline(type, options)) {
|
|
13
|
+
// Check if we should ignore null for optional properties
|
|
14
|
+
const ignoreNullableOnOptional =
|
|
15
|
+
context.rlcOptions?.ignoreNullableOnOptional ?? false;
|
|
16
|
+
const isOptional = options.isOptional ?? false;
|
|
17
|
+
|
|
13
18
|
const nonNullableType = getTypeExpression(context, type.type, options);
|
|
19
|
+
// If the property is optional and we should ignore nullable, just return the non-nullable type
|
|
20
|
+
if (ignoreNullableOnOptional && isOptional) {
|
|
21
|
+
return nonNullableType;
|
|
22
|
+
}
|
|
14
23
|
return `(${nonNullableType}) | null`;
|
|
15
24
|
} else {
|
|
16
25
|
return resolveReference(type);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SdkHttpParameter,
|
|
3
3
|
SdkModelPropertyType,
|
|
4
|
+
SdkServiceResponseHeader,
|
|
4
5
|
SdkType
|
|
5
6
|
} from "@azure-tools/typespec-client-generator-core";
|
|
6
7
|
import { getCredentialExpression } from "./get-credential-expression.js";
|
|
@@ -13,11 +14,12 @@ import { getNullableExpression } from "./get-nullable-expression.js";
|
|
|
13
14
|
|
|
14
15
|
export interface EmitTypeOptions {
|
|
15
16
|
emitInline?: boolean;
|
|
17
|
+
isOptional?: boolean;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export function normalizeModelPropertyName(
|
|
19
21
|
context: SdkContext,
|
|
20
|
-
property: SdkModelPropertyType | SdkHttpParameter
|
|
22
|
+
property: SdkModelPropertyType | SdkHttpParameter | SdkServiceResponseHeader
|
|
21
23
|
): string {
|
|
22
24
|
const normalizedPropName = normalizeName(property.name, NameType.Property);
|
|
23
25
|
return context.rlcOptions?.ignorePropertyNameNormalize
|