@azure-tools/typespec-ts 0.50.2 → 0.50.3
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 +6 -0
- package/dist/src/framework/hooks/binder.d.ts +1 -1
- package/dist/src/framework/hooks/binder.d.ts.map +1 -1
- package/dist/src/framework/hooks/binder.js +11 -3
- package/dist/src/framework/hooks/binder.js.map +1 -1
- package/dist/src/framework/load-static-helpers.d.ts +3 -0
- package/dist/src/framework/load-static-helpers.d.ts.map +1 -1
- package/dist/src/framework/load-static-helpers.js +49 -38
- package/dist/src/framework/load-static-helpers.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +19 -10
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib.d.ts +7 -0
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +5 -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 +1 -1
- package/dist/src/modular/buildOperations.js.map +1 -1
- package/dist/src/modular/emitModels.d.ts +8 -0
- package/dist/src/modular/emitModels.d.ts.map +1 -1
- package/dist/src/modular/emitModels.js +32 -2
- package/dist/src/modular/emitModels.js.map +1 -1
- package/dist/src/modular/emitSamples.js +9 -4
- package/dist/src/modular/emitSamples.js.map +1 -1
- package/dist/src/modular/emitTests.d.ts +7 -0
- package/dist/src/modular/emitTests.d.ts.map +1 -0
- package/dist/src/modular/emitTests.js +160 -0
- package/dist/src/modular/emitTests.js.map +1 -0
- package/dist/src/modular/external-dependencies.d.ts +42 -0
- package/dist/src/modular/external-dependencies.d.ts.map +1 -1
- package/dist/src/modular/external-dependencies.js +42 -0
- package/dist/src/modular/external-dependencies.js.map +1 -1
- package/dist/src/modular/helpers/exampleValueHelpers.d.ts +83 -0
- package/dist/src/modular/helpers/exampleValueHelpers.d.ts.map +1 -0
- package/dist/src/modular/helpers/exampleValueHelpers.js +631 -0
- package/dist/src/modular/helpers/exampleValueHelpers.js.map +1 -0
- package/dist/src/modular/helpers/operationHelpers.d.ts +22 -2
- package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.js +178 -9
- package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
- package/dist/src/modular/static-helpers-metadata.d.ts +12 -0
- package/dist/src/modular/static-helpers-metadata.d.ts.map +1 -1
- package/dist/src/modular/static-helpers-metadata.js +12 -0
- package/dist/src/modular/static-helpers-metadata.js.map +1 -1
- package/dist/src/transform/transfromRLCOptions.d.ts.map +1 -1
- package/dist/src/transform/transfromRLCOptions.js +10 -0
- package/dist/src/transform/transfromRLCOptions.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/framework/hooks/binder.ts +15 -5
- package/src/framework/load-static-helpers.ts +79 -51
- package/src/index.ts +22 -7
- package/src/lib.ts +13 -0
- package/src/modular/buildOperations.ts +2 -1
- package/src/modular/emitModels.ts +47 -2
- package/src/modular/emitSamples.ts +7 -1
- package/src/modular/emitTests.ts +227 -0
- package/src/modular/external-dependencies.ts +43 -0
- package/src/modular/helpers/exampleValueHelpers.ts +940 -0
- package/src/modular/helpers/operationHelpers.ts +229 -17
- package/src/modular/static-helpers-metadata.ts +13 -0
- package/src/transform/transfromRLCOptions.ts +14 -0
- package/static/static-helpers/serialization/get-binary-response-body-browser.mts +22 -0
- package/static/static-helpers/serialization/get-binary-response-body.ts +24 -0
- package/static/test-helpers/recordedClient.ts +30 -0
|
@@ -2,7 +2,8 @@ import {
|
|
|
2
2
|
FunctionDeclarationStructure,
|
|
3
3
|
OptionalKind,
|
|
4
4
|
ParameterDeclarationStructure,
|
|
5
|
-
StructureKind
|
|
5
|
+
StructureKind,
|
|
6
|
+
TypeAliasDeclarationStructure
|
|
6
7
|
} from "ts-morph";
|
|
7
8
|
import { NoTarget, Program } from "@typespec/compiler";
|
|
8
9
|
import {
|
|
@@ -179,17 +180,26 @@ export function getSendPrivateFunction(
|
|
|
179
180
|
|
|
180
181
|
export function getDeserializePrivateFunction(
|
|
181
182
|
context: SdkContext,
|
|
182
|
-
|
|
183
|
+
method: [string[], ServiceOperation]
|
|
183
184
|
): OptionalKind<FunctionDeclarationStructure> {
|
|
185
|
+
const operation = method[1];
|
|
184
186
|
const { name } = getOperationName(operation);
|
|
185
187
|
const dependencies = useDependencies();
|
|
186
188
|
const PathUncheckedResponseReference = resolveReference(
|
|
187
189
|
dependencies.PathUncheckedResponse
|
|
188
190
|
);
|
|
191
|
+
|
|
192
|
+
// Check if we need to wrap the non-model return type
|
|
193
|
+
const { shouldWrap, isBinary } = checkWrapNonModelReturn(context, operation);
|
|
194
|
+
// For binary wrap, the deserializer receives a StreamableMethod directly
|
|
195
|
+
const resultParamType =
|
|
196
|
+
shouldWrap && isBinary
|
|
197
|
+
? resolveReference(dependencies.StreamableMethod)
|
|
198
|
+
: PathUncheckedResponseReference;
|
|
189
199
|
const parameters: OptionalKind<ParameterDeclarationStructure>[] = [
|
|
190
200
|
{
|
|
191
201
|
name: "result",
|
|
192
|
-
type:
|
|
202
|
+
type: resultParamType
|
|
193
203
|
}
|
|
194
204
|
];
|
|
195
205
|
const isLroOnly = isLroOnlyOperation(operation);
|
|
@@ -201,6 +211,7 @@ export function getDeserializePrivateFunction(
|
|
|
201
211
|
const response = operation.response;
|
|
202
212
|
const restResponse = operation.operation.responses[0];
|
|
203
213
|
let returnType;
|
|
214
|
+
|
|
204
215
|
if (isLroOnly || isLroAndPaging) {
|
|
205
216
|
returnType = buildLroReturnType(context, operation);
|
|
206
217
|
} else if (isPagingOnly && restResponse?.type) {
|
|
@@ -210,6 +221,12 @@ export function getDeserializePrivateFunction(
|
|
|
210
221
|
name: (restResponse as any).name ?? "",
|
|
211
222
|
type: getTypeExpression(context, restResponse.type)
|
|
212
223
|
};
|
|
224
|
+
} else if (shouldWrap) {
|
|
225
|
+
// Use the wrapper response type name (resolved via binder for cross-file imports)
|
|
226
|
+
returnType = {
|
|
227
|
+
name: getOperationResponseTypeName(method),
|
|
228
|
+
type: resolveReference(refkey(operation, "response"))
|
|
229
|
+
};
|
|
213
230
|
} else if (response.type) {
|
|
214
231
|
returnType = {
|
|
215
232
|
name: (response as any).name ?? "",
|
|
@@ -230,15 +247,17 @@ export function getDeserializePrivateFunction(
|
|
|
230
247
|
const createRestErrorReference = resolveReference(
|
|
231
248
|
dependencies.createRestError
|
|
232
249
|
);
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
250
|
+
// For binary wrap, parameter is StreamableMethod - skip status check
|
|
251
|
+
if (!(shouldWrap && isBinary)) {
|
|
252
|
+
statements.push(
|
|
253
|
+
`const expectedStatuses = ${getExpectedStatuses(operation)};`
|
|
254
|
+
);
|
|
255
|
+
statements.push(
|
|
256
|
+
`if(!expectedStatuses.includes(result.status)){`,
|
|
257
|
+
`${getExceptionThrowStatement(context, operation)}`,
|
|
258
|
+
"}"
|
|
259
|
+
);
|
|
260
|
+
}
|
|
242
261
|
const deserializedType =
|
|
243
262
|
isLroOnly || isLroAndPaging
|
|
244
263
|
? operation?.lroMetadata?.finalResponse?.result
|
|
@@ -374,6 +393,40 @@ export function getDeserializePrivateFunction(
|
|
|
374
393
|
skipDiscriminatedUnionSuffix: false
|
|
375
394
|
}
|
|
376
395
|
);
|
|
396
|
+
// Handle wrap-non-model-return for non-LRO, non-paging operations
|
|
397
|
+
if (shouldWrap) {
|
|
398
|
+
if (isBinary) {
|
|
399
|
+
const getBinaryResponseBodyReference = resolveReference(
|
|
400
|
+
SerializationHelpers.getBinaryResponseBody
|
|
401
|
+
);
|
|
402
|
+
const deserializeError = getExceptionDetails(
|
|
403
|
+
context,
|
|
404
|
+
operation
|
|
405
|
+
).defaultDeserializer;
|
|
406
|
+
const deserializeErrorStr = deserializeError
|
|
407
|
+
? `, ${deserializeError}`
|
|
408
|
+
: "";
|
|
409
|
+
statements.push(
|
|
410
|
+
`return ${getBinaryResponseBodyReference}(result, ${getExpectedStatuses(operation)}${deserializeErrorStr});
|
|
411
|
+
`
|
|
412
|
+
);
|
|
413
|
+
} else {
|
|
414
|
+
// Non-model response: wrap with body property
|
|
415
|
+
// Generate the appropriate deserialization for the body value
|
|
416
|
+
const bodyValue = deserializeResponseValue(
|
|
417
|
+
context,
|
|
418
|
+
deserializedType,
|
|
419
|
+
"result.body",
|
|
420
|
+
true,
|
|
421
|
+
getEncodeForType(deserializedType)
|
|
422
|
+
);
|
|
423
|
+
statements.push(`return { body: ${bodyValue} };`);
|
|
424
|
+
}
|
|
425
|
+
return {
|
|
426
|
+
...functionStatement,
|
|
427
|
+
statements
|
|
428
|
+
};
|
|
429
|
+
}
|
|
377
430
|
if (deserializeFunctionName) {
|
|
378
431
|
statements.push(
|
|
379
432
|
`return ${deserializeFunctionName}(${deserializedRoot})${multipartCastSuffix}`
|
|
@@ -947,8 +1000,18 @@ export function getOperationFunction(
|
|
|
947
1000
|
bodyType = getTypeExpression(context, response.type!);
|
|
948
1001
|
}
|
|
949
1002
|
|
|
1003
|
+
// Check if we need to wrap the non-model return type
|
|
1004
|
+
const { shouldWrap: wrapReturn, isBinary: wrapReturnIsBinary } =
|
|
1005
|
+
checkWrapNonModelReturn(context, operation);
|
|
1006
|
+
|
|
950
1007
|
let returnType = { name: "", type: "void" };
|
|
951
|
-
if (
|
|
1008
|
+
if (wrapReturn) {
|
|
1009
|
+
// Use the wrapper response type name (resolved via binder for cross-file imports)
|
|
1010
|
+
returnType = {
|
|
1011
|
+
name: getOperationResponseTypeName(method),
|
|
1012
|
+
type: resolveReference(refkey(operation, "response"))
|
|
1013
|
+
};
|
|
1014
|
+
} else if (response.type) {
|
|
952
1015
|
const type = response.type;
|
|
953
1016
|
|
|
954
1017
|
// If feature flag enabled, we'll append the response headers to the operation response type.
|
|
@@ -1028,6 +1091,22 @@ export function getOperationFunction(
|
|
|
1028
1091
|
const resultVarName = generateLocallyUniqueName("result", paramNames);
|
|
1029
1092
|
|
|
1030
1093
|
const parameterList = parameters.map((p) => p.name).join(", ");
|
|
1094
|
+
// For binary wrap, pass the StreamableMethod directly to the deserializer.
|
|
1095
|
+
// The deserializer uses asBrowserStream()/asNodeStream() to build the wrapper.
|
|
1096
|
+
if (wrapReturn && wrapReturnIsBinary) {
|
|
1097
|
+
const streamableMethodVarName = generateLocallyUniqueName(
|
|
1098
|
+
"streamableMethod",
|
|
1099
|
+
paramNames
|
|
1100
|
+
);
|
|
1101
|
+
statements.push(
|
|
1102
|
+
`const ${streamableMethodVarName} = _${name}Send(${parameterList});`
|
|
1103
|
+
);
|
|
1104
|
+
statements.push(`return _${name}Deserialize(${streamableMethodVarName});`);
|
|
1105
|
+
return {
|
|
1106
|
+
...functionStatement,
|
|
1107
|
+
statements
|
|
1108
|
+
} as FunctionDeclarationStructure & { propertyName?: string };
|
|
1109
|
+
}
|
|
1031
1110
|
|
|
1032
1111
|
// When storage-compat is enabled, set up the onResponse interceptor before sending
|
|
1033
1112
|
const storageCompatVarName = generateLocallyUniqueName(
|
|
@@ -1542,7 +1621,8 @@ function buildHeaderParameter(
|
|
|
1542
1621
|
optionalParamName: string = "options"
|
|
1543
1622
|
): string {
|
|
1544
1623
|
const paramName = param.name;
|
|
1545
|
-
|
|
1624
|
+
const effectiveOptional = getEffectiveOptional(param);
|
|
1625
|
+
if (!effectiveOptional && isTypeNullable(param.type) === true) {
|
|
1546
1626
|
reportDiagnostic(program, {
|
|
1547
1627
|
code: "nullable-required-header",
|
|
1548
1628
|
target: NoTarget
|
|
@@ -1550,7 +1630,7 @@ function buildHeaderParameter(
|
|
|
1550
1630
|
return paramMap;
|
|
1551
1631
|
}
|
|
1552
1632
|
const conditions = [];
|
|
1553
|
-
if (
|
|
1633
|
+
if (effectiveOptional) {
|
|
1554
1634
|
conditions.push(`${optionalParamName}?.${paramName} !== undefined`);
|
|
1555
1635
|
}
|
|
1556
1636
|
if (isTypeNullable(param.type) === true) {
|
|
@@ -1782,8 +1862,34 @@ function getContentTypeValue(
|
|
|
1782
1862
|
}
|
|
1783
1863
|
}
|
|
1784
1864
|
|
|
1865
|
+
/**
|
|
1866
|
+
* Gets the effective optionality for an HTTP parameter by checking
|
|
1867
|
+
* the linked method parameter via methodParameterSegments.
|
|
1868
|
+
* This is needed because @@override can change a method parameter's
|
|
1869
|
+
* optionality without updating the HTTP parameter's optional flag.
|
|
1870
|
+
* For client-level parameters (onClient), preserve the HTTP parameter's own flag.
|
|
1871
|
+
*/
|
|
1872
|
+
function getEffectiveOptional(param: SdkHttpParameter): boolean {
|
|
1873
|
+
// For client-level parameters, the HTTP parameter's optional flag is authoritative
|
|
1874
|
+
if (param.onClient) {
|
|
1875
|
+
return Boolean(param.optional);
|
|
1876
|
+
}
|
|
1877
|
+
// For method-level parameters with a direct mapping to a single method param,
|
|
1878
|
+
// use the method parameter's optional flag (correctly reflects @@override changes)
|
|
1879
|
+
if (
|
|
1880
|
+
param.methodParameterSegments?.length === 1 &&
|
|
1881
|
+
param.methodParameterSegments[0]?.length === 1
|
|
1882
|
+
) {
|
|
1883
|
+
const methodParam = param.methodParameterSegments[0]![0];
|
|
1884
|
+
if (methodParam) {
|
|
1885
|
+
return Boolean(methodParam.optional);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
return Boolean(param.optional);
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1785
1891
|
function isRequired(param: SdkHttpParameter) {
|
|
1786
|
-
return !param
|
|
1892
|
+
return !getEffectiveOptional(param);
|
|
1787
1893
|
}
|
|
1788
1894
|
|
|
1789
1895
|
function getRequired(
|
|
@@ -1823,7 +1929,7 @@ function isConstant(param: SdkType): param is SdkConstantType {
|
|
|
1823
1929
|
}
|
|
1824
1930
|
|
|
1825
1931
|
function isOptional(param: SdkHttpParameter) {
|
|
1826
|
-
return
|
|
1932
|
+
return getEffectiveOptional(param);
|
|
1827
1933
|
}
|
|
1828
1934
|
|
|
1829
1935
|
function getOptional(
|
|
@@ -2926,3 +3032,109 @@ function buildHeaderOnlyResponseValue(
|
|
|
2926
3032
|
|
|
2927
3033
|
return `{ ${props.join(", ")} }`;
|
|
2928
3034
|
}
|
|
3035
|
+
|
|
3036
|
+
/**
|
|
3037
|
+
* Returns the name for a non-model response wrapper type.
|
|
3038
|
+
* The name follows the pattern: {OperationGroupName}{MethodName}Response
|
|
3039
|
+
* @param method - The method tuple [prefixes, operation]
|
|
3040
|
+
*/
|
|
3041
|
+
export function getOperationResponseTypeName(
|
|
3042
|
+
method: [string[], ServiceOperation]
|
|
3043
|
+
): string {
|
|
3044
|
+
const prefixes = method[0];
|
|
3045
|
+
const operation = method[1];
|
|
3046
|
+
const prefix = !operation.name.includes("_")
|
|
3047
|
+
? getClassicalLayerPrefix(prefixes, NameType.Interface)
|
|
3048
|
+
: "";
|
|
3049
|
+
return `${prefix}${normalizeName(operation.name, NameType.Interface)}Response`;
|
|
3050
|
+
}
|
|
3051
|
+
|
|
3052
|
+
/**
|
|
3053
|
+
* Determines whether wrapping the non-model return type is needed for an operation.
|
|
3054
|
+
* Returns an object with `shouldWrap` (whether to wrap) and `isBinary` (whether it's a binary response).
|
|
3055
|
+
*/
|
|
3056
|
+
export function checkWrapNonModelReturn(
|
|
3057
|
+
context: SdkContext,
|
|
3058
|
+
operation: ServiceOperation
|
|
3059
|
+
): { shouldWrap: boolean; isBinary: boolean } {
|
|
3060
|
+
const noWrap = { shouldWrap: false, isBinary: false };
|
|
3061
|
+
|
|
3062
|
+
// Only for non-LRO, non-paging normal operations
|
|
3063
|
+
if (
|
|
3064
|
+
isLroOnlyOperation(operation) ||
|
|
3065
|
+
isLroAndPagingOperation(operation) ||
|
|
3066
|
+
isPagingOnlyOperation(operation)
|
|
3067
|
+
) {
|
|
3068
|
+
return noWrap;
|
|
3069
|
+
}
|
|
3070
|
+
|
|
3071
|
+
// Only if the feature flag is enabled
|
|
3072
|
+
if (!context.rlcOptions?.wrapNonModelReturn) {
|
|
3073
|
+
return noWrap;
|
|
3074
|
+
}
|
|
3075
|
+
|
|
3076
|
+
const response = operation.response;
|
|
3077
|
+
if (!response.type) {
|
|
3078
|
+
return noWrap; // void return type - no wrap needed
|
|
3079
|
+
}
|
|
3080
|
+
|
|
3081
|
+
const type = response.type;
|
|
3082
|
+
const contentTypes = operation.operation.responses[0]?.contentTypes ?? [];
|
|
3083
|
+
|
|
3084
|
+
// Check if it's a binary (bytes with binary content type) response
|
|
3085
|
+
if (type.__raw && isBinaryPayload(context, type.__raw, contentTypes)) {
|
|
3086
|
+
return { shouldWrap: true, isBinary: true };
|
|
3087
|
+
}
|
|
3088
|
+
|
|
3089
|
+
// Check if it's a non-model, non-record type (array, scalar, enum, etc.)
|
|
3090
|
+
if (type.kind !== "model" && type.kind !== "dict") {
|
|
3091
|
+
return { shouldWrap: true, isBinary: false };
|
|
3092
|
+
}
|
|
3093
|
+
|
|
3094
|
+
return noWrap;
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
/**
|
|
3098
|
+
* Builds a TypeAliasDeclarationStructure for the non-model response wrapper type.
|
|
3099
|
+
* - For binary responses: { blobBody?: Promise<Blob>; readableStreamBody?: NodeJS.ReadableStream }
|
|
3100
|
+
* - For other non-model responses: { body: <type> }
|
|
3101
|
+
*/
|
|
3102
|
+
export function buildNonModelResponseTypeDeclaration(
|
|
3103
|
+
context: SdkContext,
|
|
3104
|
+
method: [string[], ServiceOperation],
|
|
3105
|
+
isBinary: boolean
|
|
3106
|
+
): TypeAliasDeclarationStructure {
|
|
3107
|
+
const typeName = getOperationResponseTypeName(method);
|
|
3108
|
+
const operation = method[1];
|
|
3109
|
+
let typeBody: string;
|
|
3110
|
+
|
|
3111
|
+
if (isBinary) {
|
|
3112
|
+
typeBody = `{
|
|
3113
|
+
/**
|
|
3114
|
+
* BROWSER ONLY
|
|
3115
|
+
*
|
|
3116
|
+
* The response body as a browser Blob.
|
|
3117
|
+
* Always \`undefined\` in node.js.
|
|
3118
|
+
*/
|
|
3119
|
+
blobBody?: Promise<Blob>;
|
|
3120
|
+
/**
|
|
3121
|
+
* NODEJS ONLY
|
|
3122
|
+
*
|
|
3123
|
+
* The response body as a node.js Readable stream.
|
|
3124
|
+
* Always \`undefined\` in the browser.
|
|
3125
|
+
*/
|
|
3126
|
+
readableStreamBody?: NodeJS.ReadableStream;
|
|
3127
|
+
}`;
|
|
3128
|
+
} else {
|
|
3129
|
+
const returnType = getTypeExpression(context, operation.response.type!);
|
|
3130
|
+
typeBody = `{ body: ${returnType} }`;
|
|
3131
|
+
}
|
|
3132
|
+
|
|
3133
|
+
return {
|
|
3134
|
+
kind: StructureKind.TypeAlias,
|
|
3135
|
+
name: typeName,
|
|
3136
|
+
type: typeBody,
|
|
3137
|
+
isExported: true,
|
|
3138
|
+
leadingTrivia: "\n"
|
|
3139
|
+
};
|
|
3140
|
+
}
|
|
@@ -59,6 +59,11 @@ export const SerializationHelpers = {
|
|
|
59
59
|
name: "getBinaryResponse",
|
|
60
60
|
location: "serialization/get-binary-response.ts"
|
|
61
61
|
},
|
|
62
|
+
getBinaryResponseBody: {
|
|
63
|
+
kind: "function",
|
|
64
|
+
name: "getBinaryResponseBody",
|
|
65
|
+
location: "serialization/get-binary-response-body.ts"
|
|
66
|
+
},
|
|
62
67
|
areAllPropsUndefined: {
|
|
63
68
|
kind: "function",
|
|
64
69
|
name: "areAllPropsUndefined",
|
|
@@ -163,6 +168,14 @@ export const CloudSettingHelpers = {
|
|
|
163
168
|
}
|
|
164
169
|
} as const;
|
|
165
170
|
|
|
171
|
+
export const CreateRecorderHelpers = {
|
|
172
|
+
createRecorder: {
|
|
173
|
+
kind: "function",
|
|
174
|
+
name: "createRecorder",
|
|
175
|
+
location: "recordedClient.ts"
|
|
176
|
+
}
|
|
177
|
+
} as const;
|
|
178
|
+
|
|
166
179
|
export const XmlHelpers = {
|
|
167
180
|
XmlSerializationOptions: {
|
|
168
181
|
kind: "interface",
|
|
@@ -99,6 +99,7 @@ function extractRLCOptions(
|
|
|
99
99
|
emitterOptions,
|
|
100
100
|
flavor
|
|
101
101
|
);
|
|
102
|
+
const wrapNonModelReturn = getWrapNonModelReturn(emitterOptions, flavor);
|
|
102
103
|
const isMultiService = (dpgContext.allServiceNamespaces?.length ?? 0) > 1;
|
|
103
104
|
|
|
104
105
|
return {
|
|
@@ -135,6 +136,7 @@ function extractRLCOptions(
|
|
|
135
136
|
ignoreEnumMemberNameNormalize,
|
|
136
137
|
hasSubscriptionId,
|
|
137
138
|
ignoreNullableOnOptional,
|
|
139
|
+
wrapNonModelReturn,
|
|
138
140
|
isMultiService,
|
|
139
141
|
enableStorageCompat
|
|
140
142
|
};
|
|
@@ -286,6 +288,18 @@ function getIgnoreNullableOnOptional(
|
|
|
286
288
|
return flavor === "azure";
|
|
287
289
|
}
|
|
288
290
|
|
|
291
|
+
function getWrapNonModelReturn(
|
|
292
|
+
emitterOptions: EmitterOptions,
|
|
293
|
+
flavor: PackageFlavor
|
|
294
|
+
): boolean {
|
|
295
|
+
// If explicitly set in options, use that value
|
|
296
|
+
if (emitterOptions["wrap-non-model-return"] !== undefined) {
|
|
297
|
+
return Boolean(emitterOptions["wrap-non-model-return"]);
|
|
298
|
+
}
|
|
299
|
+
// Default to true for Azure services to maintain HLC backward compatibility
|
|
300
|
+
return flavor === "azure";
|
|
301
|
+
}
|
|
302
|
+
|
|
289
303
|
function getIncludeShortcuts(emitterOptions: EmitterOptions) {
|
|
290
304
|
return Boolean(emitterOptions["include-shortcuts"]);
|
|
291
305
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createRestError, StreamableMethod } from "@azure-rest/core-client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get the binary response body from a StreamableMethod, returning it as a Blob in browser environments and as a NodeJS.ReadableStream in Node.js environments. This function checks the response status against expected statuses and throws a RestError if the status is unexpected, optionally deserializing error details using a provided deserialization function.
|
|
5
|
+
*/
|
|
6
|
+
export async function getBinaryResponseBody(
|
|
7
|
+
result: StreamableMethod,
|
|
8
|
+
expectedStatuses: string[] = ["200"],
|
|
9
|
+
deserializeError?: (error: any) => unknown
|
|
10
|
+
): Promise<{ blobBody?: Promise<Blob>; readableStreamBody?: NodeJS.ReadableStream }> {
|
|
11
|
+
const browserStream = await result.asBrowserStream();
|
|
12
|
+
if (!expectedStatuses.includes(browserStream.status)) {
|
|
13
|
+
const error = createRestError(browserStream);
|
|
14
|
+
if (deserializeError) {
|
|
15
|
+
error.details = deserializeError(browserStream.body);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
return { blobBody: new Response(browserStream.body).blob(), readableStreamBody: undefined };
|
|
21
|
+
|
|
22
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createRestError, StreamableMethod } from "@azure-rest/core-client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Gets the binary response body from a StreamableMethod, returning it as a NodeJS.ReadableStream in Node.js environments. In browser environments, this function returns `undefined` for the stream and instead provides a `blobBody` that is a Promise resolving to a Blob, since browser environments do not support NodeJS.ReadableStream.
|
|
5
|
+
*/
|
|
6
|
+
export async function getBinaryResponseBody(
|
|
7
|
+
result: StreamableMethod,
|
|
8
|
+
expectedStatuses: string[] = ["200"],
|
|
9
|
+
deserializeError?: (error: any) => unknown
|
|
10
|
+
): Promise<{
|
|
11
|
+
blobBody?: Promise<Blob>;
|
|
12
|
+
readableStreamBody?: NodeJS.ReadableStream;
|
|
13
|
+
}> {
|
|
14
|
+
const nodeStream = await result.asNodeStream();
|
|
15
|
+
if (!expectedStatuses.includes(nodeStream.status)) {
|
|
16
|
+
const error = createRestError(nodeStream);
|
|
17
|
+
if (deserializeError) {
|
|
18
|
+
error.details = deserializeError(nodeStream.body);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
return { blobBody: undefined, readableStreamBody: nodeStream.body };
|
|
24
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
RecorderStartOptions,
|
|
3
|
+
VitestTestContext
|
|
4
|
+
} from "@azure-tools/test-recorder";
|
|
5
|
+
import { Recorder } from "@azure-tools/test-recorder";
|
|
6
|
+
|
|
7
|
+
const replaceableVariables: Record<string, string> = {
|
|
8
|
+
SUBSCRIPTION_ID: "azure_subscription_id"
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const recorderEnvSetup: RecorderStartOptions = {
|
|
12
|
+
envSetupForPlayback: replaceableVariables,
|
|
13
|
+
removeCentralSanitizers: [
|
|
14
|
+
"AZSDK3493", // .name in the body is not a secret and is listed below in the beforeEach section
|
|
15
|
+
"AZSDK3430" // .id in the body is not a secret and is listed below in the beforeEach section
|
|
16
|
+
]
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* creates the recorder and reads the environment variables from the `.env` file.
|
|
21
|
+
* Should be called first in the test suite to make sure environment variables are
|
|
22
|
+
* read before they are being used.
|
|
23
|
+
*/
|
|
24
|
+
export async function createRecorder(
|
|
25
|
+
context: VitestTestContext
|
|
26
|
+
): Promise<Recorder> {
|
|
27
|
+
const recorder = new Recorder(context);
|
|
28
|
+
await recorder.start(recorderEnvSetup);
|
|
29
|
+
return recorder;
|
|
30
|
+
}
|