Skip to content

Commit

Permalink
Backmerge/release/november 2024 2024 11 06 (#1819)
Browse files Browse the repository at this point in the history
Co-authored-by: Chenjie Shi <[email protected]>
Co-authored-by: iscai-msft <[email protected]>
  • Loading branch information
3 people authored Nov 6, 2024
1 parent f265f96 commit 8c661a2
Show file tree
Hide file tree
Showing 22 changed files with 1,252 additions and 396 deletions.
8 changes: 8 additions & 0 deletions packages/typespec-client-generator-core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Change Log - @azure-tools/typespec-client-generator-core

## 0.48.1

### Bug Fixes

- [#1813](https://github.com/Azure/typespec-azure/pull/1813) fix wrong encode for body response of binary type
- [#1786](https://github.com/Azure/typespec-azure/pull/1786) support client namespace


## 0.48.0

### Bug Fixes
Expand Down
36 changes: 36 additions & 0 deletions packages/typespec-client-generator-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ npm install @azure-tools/typespec-client-generator-core
- [`@client`](#@client)
- [`@clientInitialization`](#@clientinitialization)
- [`@clientName`](#@clientname)
- [`@clientNamespace`](#@clientnamespace)
- [`@convenientAPI`](#@convenientapi)
- [`@flattenProperty`](#@flattenproperty)
- [`@operationGroup`](#@operationgroup)
Expand Down Expand Up @@ -291,6 +292,41 @@ op nameInService: void;
op nameInService: void;
```

#### `@clientNamespace`

Changes the namespace of a client, model, enum or union generated in the client SDK.
By default, the client namespace for them will follow the TypeSpec namespace.

```typespec
@Azure.ClientGenerator.Core.clientNamespace(rename: valueof string, scope?: valueof string)
```

##### Target

`Namespace | Interface | Model | Enum | Union`

##### Parameters

| Name | Type | Description |
| ------ | ---------------- | ------------------------------------------------------------------------------------------------------------- |
| rename | `valueof string` | The rename you want applied to the object |
| scope | `valueof string` | The language scope you want this decorator to apply to. If not specified, will apply to all language emitters |

##### Examples

```typespec
@clientNamespace("ContosoClient")
namespace Contoso;
```

```typespec
@clientName("ContosoJava", "java")
@clientName("ContosoPython", "python")
@clientName("ContosoCSharp", "csharp")
@clientName("ContosoJavascript", "javascript")
namespace Contoso;
```

#### `@convenientAPI`

Whether you want to generate an operation as a convenient operation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,33 @@ export type ParamAliasDecorator = (
scope?: string,
) => void;

/**
* Changes the namespace of a client, model, enum or union generated in the client SDK.
* By default, the client namespace for them will follow the TypeSpec namespace.
*
* @param rename The rename you want applied to the object
* @param scope The language scope you want this decorator to apply to. If not specified, will apply to all language emitters
* @example
* ```typespec
* @clientNamespace("ContosoClient")
* namespace Contoso;
* ```
* @example
* ```typespec
* @clientName("ContosoJava", "java")
* @clientName("ContosoPython", "python")
* @clientName("ContosoCSharp", "csharp")
* @clientName("ContosoJavascript", "javascript")
* namespace Contoso;
* ```
*/
export type ClientNamespaceDecorator = (
context: DecoratorContext,
target: Namespace | Interface | Model | Enum | Union,
rename: string,
scope?: string,
) => void;

export type AzureClientGeneratorCoreDecorators = {
clientName: ClientNameDecorator;
convenientAPI: ConvenientAPIDecorator;
Expand All @@ -507,4 +534,5 @@ export type AzureClientGeneratorCoreDecorators = {
useSystemTextJsonConverter: UseSystemTextJsonConverterDecorator;
clientInitialization: ClientInitializationDecorator;
paramAlias: ParamAliasDecorator;
clientNamespace: ClientNamespaceDecorator;
};
27 changes: 27 additions & 0 deletions packages/typespec-client-generator-core/lib/decorators.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -474,3 +474,30 @@ extern dec clientInitialization(
* ```
*/
extern dec paramAlias(original: ModelProperty, paramAlias: valueof string, scope?: valueof string);

/**
* Changes the namespace of a client, model, enum or union generated in the client SDK.
* By default, the client namespace for them will follow the TypeSpec namespace.
* @param rename The rename you want applied to the object
* @param scope The language scope you want this decorator to apply to. If not specified, will apply to all language emitters
*
* @example
* ```typespec
* @clientNamespace("ContosoClient")
* namespace Contoso;
* ```
*
* @example
* ```typespec
* @clientName("ContosoJava", "java")
* @clientName("ContosoPython", "python")
* @clientName("ContosoCSharp", "csharp")
* @clientName("ContosoJavascript", "javascript")
* namespace Contoso;
* ```
*/
extern dec clientNamespace(
target: Namespace | Interface | Model | Enum | Union,
rename: valueof string,
scope?: valueof string
);
5 changes: 3 additions & 2 deletions packages/typespec-client-generator-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@azure-tools/typespec-client-generator-core",
"version": "0.48.0",
"version": "0.48.1",
"author": "Microsoft Corporation",
"description": "TypeSpec Data Plane Generation library",
"homepage": "https://azure.github.io/typespec-azure",
Expand All @@ -23,7 +23,8 @@
"exports": {
".": {
"types": "./dist/src/index.d.ts",
"default": "./dist/src/index.js"
"default": "./dist/src/index.js",
"typespec": "./lib/main.tsp"
},
"./testing": {
"types": "./dist/src/testing/index.d.ts",
Expand Down
48 changes: 48 additions & 0 deletions packages/typespec-client-generator-core/src/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
ClientDecorator,
ClientInitializationDecorator,
ClientNameDecorator,
ClientNamespaceDecorator,
ConvenientAPIDecorator,
FlattenPropertyDecorator,
OperationGroupDecorator,
Expand Down Expand Up @@ -61,6 +62,7 @@ import {
import {
AllScopes,
clientNameKey,
clientNamespaceKey,
getValidApiVersion,
isAzureCoreTspModel,
parseEmitterName,
Expand Down Expand Up @@ -1041,3 +1043,49 @@ export const paramAliasDecorator: ParamAliasDecorator = (
export function getParamAlias(context: TCGCContext, original: ModelProperty): string | undefined {
return getScopedDecoratorData(context, paramAliasKey, original);
}

export const $clientNamespace: ClientNamespaceDecorator = (
context: DecoratorContext,
entity: Namespace | Interface | Model | Enum | Union,
value: string,
scope?: LanguageScopes,
) => {
if (value.trim() === "") {
reportDiagnostic(context.program, {
code: "empty-client-namespace",
format: {},
target: entity,
});
}
setScopedDecoratorData(context, $clientNamespace, clientNamespaceKey, entity, value, scope);
};

export function getClientNamespace(
context: TCGCContext,
entity: Namespace | Interface | Model | Enum | Union,
): string {
const override = getScopedDecoratorData(context, clientNamespaceKey, entity);
if (override) return override;
if (!entity.namespace) {
return "";
}
if (entity.kind === "Namespace") {
return getNamespaceFullNameWithOverride(context, entity);
}
return getNamespaceFullNameWithOverride(context, entity.namespace);
}

function getNamespaceFullNameWithOverride(context: TCGCContext, namespace: Namespace): string {
const segments = [];
let current: Namespace | undefined = namespace;
while (current && current.name !== "") {
const override = getScopedDecoratorData(context, clientNamespaceKey, current);
if (override) {
segments.unshift(override);
break;
}
segments.unshift(current.name);
current = current.namespace;
}
return segments.join(".");
}
15 changes: 11 additions & 4 deletions packages/typespec-client-generator-core/src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,15 +424,16 @@ function getSdkHttpResponseAndExceptions(
for (const response of httpOperation.responses) {
const headers: SdkServiceResponseHeader[] = [];
let body: Type | undefined;
let type: SdkType | undefined;
let contentTypes: string[] = [];

for (const innerResponse of response.responses) {
const defaultContentType = innerResponse.body?.contentTypes.includes("application/json")
? "application/json"
: innerResponse.body?.contentTypes[0];
for (const header of getHttpOperationResponseHeaders(innerResponse)) {
if (isNeverOrVoidType(header.type)) continue;
const clientType = diagnostics.pipe(getClientTypeWithDiagnostics(context, header.type));
const defaultContentType = innerResponse.body?.contentTypes.includes("application/json")
? "application/json"
: innerResponse.body?.contentTypes[0];
addEncodeInfo(context, header, clientType, defaultContentType);
headers.push({
__raw: header,
Expand Down Expand Up @@ -463,11 +464,17 @@ function getSdkHttpResponseAndExceptions(
innerResponse.body.type.kind === "Model"
? getEffectivePayloadType(context, innerResponse.body.type)
: innerResponse.body.type;
type = diagnostics.pipe(
getClientTypeWithDiagnostics(context, body, httpOperation.operation),
);
if (innerResponse.body.property) {
addEncodeInfo(context, innerResponse.body.property, type, defaultContentType);
}
}
}
const sdkResponse = {
__raw: response,
type: body ? diagnostics.pipe(getClientTypeWithDiagnostics(context, body)) : undefined,
type,
headers,
contentTypes: contentTypes.length > 0 ? contentTypes : undefined,
defaultContentType: contentTypes.includes("application/json")
Expand Down
19 changes: 19 additions & 0 deletions packages/typespec-client-generator-core/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,15 @@ export interface SdkClientType<TServiceOperation extends SdkServiceOperation>
__raw: SdkClient | SdkOperationGroup;
kind: "client";
name: string;
clientNamespace: string; // fully qualified namespace
doc?: string;
summary?: string;
initialization: SdkInitializationType;
methods: SdkMethod<TServiceOperation>[];
apiVersions: string[];
/**
* @deprecated Use `clientNamespace` instead.
*/
nameSpace: string; // fully qualified
crossLanguageDefinitionId: string;
parent?: SdkClientType<TServiceOperation>;
Expand Down Expand Up @@ -306,12 +310,14 @@ export interface SdkNullableType extends SdkTypeBase {
type: SdkType;
usage: UsageFlags;
access: AccessFlags;
clientNamespace: string; // fully qualified namespace
}

export interface SdkEnumType extends SdkTypeBase {
kind: "enum";
name: string;
isGeneratedName: boolean;
clientNamespace: string; // fully qualified namespace
valueType: SdkBuiltInType;
values: SdkEnumValueType[];
isFixed: boolean;
Expand Down Expand Up @@ -342,6 +348,7 @@ export interface SdkConstantType extends SdkTypeBase {
export interface SdkUnionType<TValueType extends SdkTypeBase = SdkType> extends SdkTypeBase {
name: string;
isGeneratedName: boolean;
clientNamespace: string; // fully qualified namespace
kind: "union";
variantTypes: TValueType[];
crossLanguageDefinitionId: string;
Expand All @@ -356,6 +363,7 @@ export interface SdkModelType extends SdkTypeBase {
properties: SdkModelPropertyType[];
name: string;
isGeneratedName: boolean;
clientNamespace: string; // fully qualified namespace
access: AccessFlags;
usage: UsageFlags;
additionalProperties?: SdkType;
Expand Down Expand Up @@ -690,6 +698,17 @@ export interface SdkPackage<TServiceOperation extends SdkServiceOperation> {
enums: SdkEnumType[];
unions: (SdkUnionType | SdkNullableType)[];
crossLanguagePackageId: string;
namespaces: SdkNamespace<TServiceOperation>[];
}

export interface SdkNamespace<TServiceOperation extends SdkServiceOperation> {
name: string;
fullName: string;
clients: SdkClientType<TServiceOperation>[];
models: SdkModelType[];
enums: SdkEnumType[];
unions: (SdkUnionType | SdkNullableType)[];
namespaces: SdkNamespace<TServiceOperation>[];
}

export type SdkHttpPackage = SdkPackage<SdkHttpOperation>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import { getClientTypeWithDiagnostics } from "./types.js";
export const AllScopes = Symbol.for("@azure-core/typespec-client-generator-core/all-scopes");

export const clientNameKey = createStateSymbol("clientName");
export const clientNamespaceKey = createStateSymbol("clientNamespace");

/**
*
Expand Down
6 changes: 6 additions & 0 deletions packages/typespec-client-generator-core/src/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,12 @@ export const $lib = createTypeSpecLibrary({
default: paramMessage`Decorator ${"decoratorName"} cannot be used twice on the same declaration with same scope.`,
},
},
"empty-client-namespace": {
severity: "warning",
messages: {
default: `Cannot pass an empty value to the @clientNamespace decorator`,
},
},
},
});

Expand Down
Loading

0 comments on commit 8c661a2

Please sign in to comment.