Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tcgc] Namespace support #1786

Merged
merged 14 commits into from
Nov 6, 2024
7 changes: 7 additions & 0 deletions .chronus/changes/namespace_support-2024-10-1-18-10-34.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@azure-tools/typespec-client-generator-core"
---

support client namespace
35 changes: 35 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,40 @@ op nameInService: void;
op nameInService: void;
```

#### `@clientNamespace`

Changes the namespace of a client, or model generated in the client SDK

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

##### Target

`unknown`

##### 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(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

current impl for @clientNamespace will need author to use full qualified name to do the namespace renaming.

target: Namespace | Interface | Model | Enum | Union,
rename: valueof string,
scope?: valueof string
);
3 changes: 2 additions & 1 deletion packages/typespec-client-generator-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
31 changes: 31 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,32 @@ 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;
return entity.kind === "Namespace"
? getNamespaceFullName(entity)
: entity.namespace
? getNamespaceFullName(entity.namespace)
: "";
}
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 @@ -90,11 +90,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 @@ -307,12 +311,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 @@ -343,6 +349,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 @@ -357,6 +364,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 @@ -691,6 +699,17 @@ export interface SdkPackage<TServiceOperation extends SdkServiceOperation> {
enums: SdkEnumType[];
unions: (SdkUnionType | SdkNullableType)[];
crossLanguagePackageId: string;
namespaces: SdkNamespace<TServiceOperation>[];
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of providing helper function, i put the namespaces hierarchy info into the current SdkPackage type and introduced another SdkNamepace type to wrapper the namespace contents.

}

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
Loading