Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 22 additions & 27 deletions packages/typespec-ts/src/modular/buildClientContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,13 @@ export function buildClientContext(
);
})
.map((p) => {
const paramName = getClientParameterName(p);
return {
name: getClientParameterName(p),
type: getTypeExpression(dpgContext, p.type),
name: paramName,
type:
paramName.toLowerCase() === "apiversion" || p.name.toLowerCase() === "apiversion" || p.name === "api-version"
? "string"
: getTypeExpression(dpgContext, p.type),
hasQuestionToken: false,
docs: getDocsWithKnownVersion(dpgContext, p)
};
Expand All @@ -110,10 +114,11 @@ export function buildClientContext(
})
.filter((p) => getClientParameterName(p) !== "endpoint")
.map((p) => {
const paramName = getClientParameterName(p);
return {
name: getClientParameterName(p),
name: paramName,
type:
p.name.toLowerCase() === "apiversion"
paramName.toLowerCase() === "apiversion" || p.name.toLowerCase() === "apiversion" || p.name === "api-version"
? "string"
: getTypeExpression(dpgContext, p.type),
hasQuestionToken: true,
Expand Down Expand Up @@ -161,10 +166,7 @@ export function buildClientContext(
docs: getDocsFromDescription(client.doc),
name: `create${name}`,
returnType: `${rlcClientName}`,
parameters: getClientParametersDeclaration(client, dpgContext, {
onClientOnly: false,
requiredOnly: true
}),
parameters: requiredParams,
isExported: true
});

Expand Down Expand Up @@ -221,27 +223,20 @@ export function buildClientContext(
: [];
const apiVersionInEndpoint =
templateArguments && templateArguments.find((p) => p.isApiVersionParam);
if (!apiVersionInEndpoint && apiVersionParam.clientDefaultValue) {
apiVersionPolicyStatement += `const apiVersion = options.apiVersion ?? "${apiVersionParam.clientDefaultValue}";`;

// Check if API version is a required parameter (not in endpoint and not optional)
const apiVersionIsRequired = getClientParameters(client, dpgContext, {
onClientOnly: false,
requiredOnly: true,
apiVersionAsRequired: true
}).some(p => p.isApiVersionParam);

if (!apiVersionInEndpoint && apiVersionParam.clientDefaultValue && !apiVersionIsRequired) {
apiVersionPolicyStatement += `const apiVersionValue = options.apiVersion ?? "${apiVersionParam.clientDefaultValue}";`;
}

if (apiVersionParam.kind === "method") {
apiVersionPolicyStatement += `
clientContext.pipeline.addPolicy({
name: 'ClientApiVersionPolicy',
sendRequest: (req, next) => {
// Use the apiVersion defined in request url directly
// Append one if there is no apiVersion and we have one at client options
const url = new URL(req.url);
if (!url.searchParams.get("api-version")) {
req.url = \`\${req.url}\${
Array.from(url.searchParams.keys()).length > 0 ? "&" : "?"
}api-version=\${${getClientParameterName(apiVersionParam)}}\`;
}

return next(req);
},
});`;
// Skip API version policy for now
}
} else if (isAzurePackage(emitterOptions)) {
apiVersionPolicyStatement += `
Expand All @@ -268,7 +263,7 @@ export function buildClientContext(
.map((p) => {
return p.name;
})
.join(", ")}} as ${rlcClientName};`
.join(", ")} } as ${rlcClientName};`
);
} else {
factoryFunction.addStatements(`return clientContext;`);
Expand Down
8 changes: 7 additions & 1 deletion packages/typespec-ts/src/modular/helpers/clientHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export function getClientParameters(
p.type.templateArguments[0] &&
hasDefaultValue(p.type.templateArguments[0])
)) ||
(options.apiVersionAsRequired && p.isApiVersionParam));
(options.apiVersionAsRequired && p.isApiVersionParam && !hasDefaultValue(p)));
const isOptional = (p: SdkParameter) => p.optional || hasDefaultValue(p);
const skipCredentials = (p: SdkParameter) => p.kind !== "credential";
const skipMethodParam = (p: SdkParameter) => p.kind !== "method";
Expand Down Expand Up @@ -141,6 +141,12 @@ function getClientParameterTypeExpression(
context: SdkContext,
parameter: SdkParameter
) {
// Special handling for apiVersion parameters to use string type
const paramName = getClientParameterName(parameter);
if (paramName.toLowerCase() === "apiversion" || parameter.name.toLowerCase() === "apiversion" || parameter.name === "api-version") {
return "string";
}

// Special handle to work around the fact that TCGC creates a union type for endpoint. The reason they do this
// is to provide a way for users to either pass the value to fill in the template of the whole endpoint. Basically they are
// inserting a variant with {endpoint}.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# should generate apiVersion property in client context interface when API version is a required client parameter

This scenario tests that when a client has an API version parameter that should be exposed as a required property on the client context interface, it is properly included.

## TypeSpec

```tsp
import "@typespec/http";
import "@typespec/rest";
import "@azure-tools/typespec-azure-core";

using TypeSpec.Http;
using TypeSpec.Rest;
using Azure.Core;

@server(
"{endpoint}",
"",
{
@doc("Service endpoint")
endpoint: url,
}
)
@service(#{
title: "DataMap"
})
namespace Azure.Analytics.Purview.DataMap;

model ApiVersionParameter {
@query
"api-version": string;
}

@route("/atlas/v2/types/typedefs")
@get
op getAtlasTypeDef(...ApiVersionParameter): void;
```

The config would be like:

```yaml
needOptions: false
withRawContent: true
```

## clientContext

```ts clientContext
import { logger } from "../logger.js";
import { Client, ClientOptions, getClient } from "@azure-rest/core-client";

export interface DataMapContext extends Client {
/** The API version to use for this operation. */
apiVersion: string;
}

/** Optional parameters for the client. */
export interface DataMapClientOptionalParams extends ClientOptions {}

export function createDataMap(
endpointParam: string,
apiVersion: string,
options: DataMapClientOptionalParams = {},
): DataMapContext {
const endpointUrl = options.endpoint ?? String(endpointParam);
const prefixFromOptions = options?.userAgentOptions?.userAgentPrefix;
const userAgentPrefix = prefixFromOptions
? `${prefixFromOptions} azsdk-js-api`
: `azsdk-js-api`;
const { apiVersion: _, ...updatedOptions } = {
...options,
userAgentOptions: { userAgentPrefix },
loggingOptions: { logger: options.loggingOptions?.logger ?? logger.info },
};
const clientContext = getClient(endpointUrl, undefined, updatedOptions);
clientContext.pipeline.removePolicy({ name: "ApiVersionPolicy" });
return { ...clientContext, apiVersion } as DataMapContext;
}
```
Loading