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

Implement tsp namespace for http-client-csharp #5443

Merged
merged 70 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
4e7b1e3
add clientnamespace to relevant types
ArcturusZhang Dec 25, 2024
6738385
Merge branch 'main' into implement-namespace
ArcturusZhang Jan 2, 2025
73e8358
format code
ArcturusZhang Jan 2, 2025
bf11ad7
model done
ArcturusZhang Jan 2, 2025
0ebfb9c
enum done
ArcturusZhang Jan 2, 2025
3380f8c
fix a bug
ArcturusZhang Jan 2, 2025
22a3a40
regen
ArcturusZhang Jan 2, 2025
c137e51
regen and fix test cases
ArcturusZhang Jan 2, 2025
bd03a7d
regen everything and remove a test until we have a solution
ArcturusZhang Jan 2, 2025
6514f2d
regen everything
ArcturusZhang Jan 3, 2025
b1b5f23
Merge remote-tracking branch 'origin/main' into implement-namespace
ArcturusZhang Jan 8, 2025
66a9da5
add a mechanism to automatically prepend underscore when the namespac…
ArcturusZhang Jan 8, 2025
94287c7
Merge branch 'main' into implement-namespace
ArcturusZhang Jan 8, 2025
66acf99
regen everything
ArcturusZhang Jan 8, 2025
3e651c4
format
ArcturusZhang Jan 8, 2025
fc83d8b
fix linter
ArcturusZhang Jan 8, 2025
271151a
a workaround
ArcturusZhang Jan 8, 2025
c114fda
fix and regen
ArcturusZhang Jan 9, 2025
ade80c5
Merge remote-tracking branch 'origin/main' into implement-namespace
ArcturusZhang Jan 9, 2025
66c349d
fix test case compilation
ArcturusZhang Jan 9, 2025
c426628
now client also honors namespace
ArcturusZhang Jan 9, 2025
fd933a7
it is so complicated to make everything compile
ArcturusZhang Jan 9, 2025
a544bef
make the test project individually for each cadl ranch project
ArcturusZhang Jan 9, 2025
1fe6ef8
Merge remote-tracking branch 'origin/main' into implement-namespace
ArcturusZhang Jan 14, 2025
f01d39d
some progress
ArcturusZhang Jan 14, 2025
4c549d8
remove usemodelnamespace and modelnamespace configuration
ArcturusZhang Jan 14, 2025
cc6244a
Merge remote-tracking branch 'origin/main' into implement-namespace
ArcturusZhang Jan 17, 2025
b79e1d4
remove the added csproj for test projects
ArcturusZhang Jan 17, 2025
dc641fe
revert unnecessary changes
ArcturusZhang Jan 17, 2025
9f72224
update
ArcturusZhang Jan 17, 2025
141db44
regen everything
ArcturusZhang Jan 17, 2025
8dcff70
fix all test cases
ArcturusZhang Jan 17, 2025
13b0401
fix a few unit tests
ArcturusZhang Jan 17, 2025
6a06f2c
revert changes to factory method for input model
ArcturusZhang Jan 17, 2025
39a9676
fix all unit tests
ArcturusZhang Jan 17, 2025
3132c21
fix cadl ranch test issues
ArcturusZhang Jan 17, 2025
9a05ba1
format
ArcturusZhang Jan 17, 2025
8730887
Merge branch 'main' into implement-namespace
ArcturusZhang Jan 17, 2025
ecb32ea
fix test failures and regen
ArcturusZhang Jan 17, 2025
dd2c21b
Merge remote-tracking branch 'origin/main' into implement-namespace
ArcturusZhang Jan 20, 2025
7424e5d
resolve comments
ArcturusZhang Jan 20, 2025
4914e36
Merge branch 'main' into implement-namespace
ArcturusZhang Jan 20, 2025
9630a57
resolve comments
ArcturusZhang Jan 20, 2025
69a3310
remove the project that cannot build
ArcturusZhang Jan 20, 2025
4a24ce9
fix the issue
ArcturusZhang Jan 15, 2025
0bbdfec
Merge branch 'main' into implement-namespace
ArcturusZhang Jan 21, 2025
c09ff89
remove special logic and regen
ArcturusZhang Jan 21, 2025
fa142b3
fix issues
ArcturusZhang Jan 21, 2025
50845ba
update the configurations
ArcturusZhang Jan 22, 2025
1a43ae7
fix and regen after we removed those two configurations
ArcturusZhang Jan 22, 2025
eb548e9
resolve comments
ArcturusZhang Jan 22, 2025
a337c26
format
ArcturusZhang Jan 22, 2025
f355516
Merge branch 'main' into implement-namespace
ArcturusZhang Jan 22, 2025
3edb99e
regen
ArcturusZhang Jan 22, 2025
aae3f91
Merge branch 'main' into implement-namespace
ArcturusZhang Jan 22, 2025
b2a2a3f
Merge remote-tracking branch 'origin/main' into implement-namespace
ArcturusZhang Jan 23, 2025
826aa8a
resolve comments
ArcturusZhang Jan 24, 2025
f1c5409
update to add the configuration namespace back
ArcturusZhang Jan 24, 2025
50c26f2
Merge remote-tracking branch 'origin/main' into implement-namespace
ArcturusZhang Jan 24, 2025
add1da9
resolve comments
ArcturusZhang Jan 24, 2025
322b4df
Merge branch 'main' into implement-namespace
ArcturusZhang Jan 24, 2025
c0243e4
fix issues
ArcturusZhang Jan 24, 2025
cf46e93
Merge remote-tracking branch 'origin/main' into implement-namespace
ArcturusZhang Feb 5, 2025
d2a489b
some fixes after merge
ArcturusZhang Feb 5, 2025
eecebd1
Merge branch 'main' into implement-namespace
ArcturusZhang Feb 5, 2025
20a908d
Merge branch 'main' into implement-namespace
ArcturusZhang Feb 6, 2025
fb35541
previously we have two layers of getting library-name logic, we canno…
ArcturusZhang Feb 6, 2025
1f79e8e
Merge remote-tracking branch 'origin/main' into implement-namespace
ArcturusZhang Feb 6, 2025
9f2669c
Merge branch 'main' into implement-namespace
ArcturusZhang Feb 6, 2025
16f000c
Merge branch 'main' into implement-namespace
ArcturusZhang Feb 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 2 additions & 6 deletions packages/http-client-csharp/emitter/src/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ export async function $onEmit(context: EmitContext<NetEmitterOptions>) {
logDiagnostics(context.program.diagnostics, context.program.host.logSink);
process.exit(1);
}
const tspNamespace = root.Name; // this is the top-level namespace defined in the typespec file, which is actually always different from the namespace of the SDK
// await program.host.writeFile(outPath, prettierOutput(JSON.stringify(root, null, 2)));

if (root) {
const generatedFolder = resolvePath(outputFolder, "src", "Generated");

Expand All @@ -88,16 +87,13 @@ export async function $onEmit(context: EmitContext<NetEmitterOptions>) {
);

//emit configuration.json
const namespace = options.namespace ?? tspNamespace;
const configurations: Configuration = {
"output-folder": ".",
namespace: namespace,
"library-name": options["library-name"] ?? namespace,
"library-name": options["library-name"] ?? root.Name,
"single-top-level-client": options["single-top-level-client"],
"unreferenced-types-handling": options["unreferenced-types-handling"],
"keep-non-overloadable-protocol-signature":
options["keep-non-overloadable-protocol-signature"],
"model-namespace": options["model-namespace"],
"models-to-treat-empty-string-as-null": options["models-to-treat-empty-string-as-null"],
"intrinsic-types-to-treat-empty-string-as-null": options[
"models-to-treat-empty-string-as-null"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,19 @@ import {
UsageFlags,
} from "@azure-tools/typespec-client-generator-core";
import { NoTarget } from "@typespec/compiler";
import { NetEmitterOptions, resolveOptions } from "../options.js";
import { NetEmitterOptions } from "../options.js";
import { CodeModel } from "../type/code-model.js";
import { InputClient } from "../type/input-client.js";
import { InputOperationParameterKind } from "../type/input-operation-parameter-kind.js";
import { InputParameter } from "../type/input-parameter.js";
import { InputEnumType, InputModelType, InputType } from "../type/input-type.js";
import { RequestLocation } from "../type/request-location.js";
import { SdkTypeMap } from "../type/sdk-type-map.js";
import { fromSdkType } from "./converter.js";
import { reportDiagnostic } from "./lib.js";
import { Logger } from "./logger.js";
import { navigateModels } from "./model.js";
import { fromSdkServiceMethod, getParameterDefaultValue } from "./operation-converter.js";
import { processServiceAuthentication } from "./service-authentication.js";
import { fromSdkType } from "./type-converter.js";

export function createModel(sdkContext: SdkContext<NetEmitterOptions>): CodeModel {
const sdkPackage = sdkContext.sdkPackage;
Expand All @@ -51,8 +50,9 @@ export function createModel(sdkContext: SdkContext<NetEmitterOptions>): CodeMode
? sdkApiVersionEnums[0].values.map((v) => v.value as string).flat()
: rootClients[0].apiVersions;

// this is a set tracking the bad namespace segments
const inputClients: InputClient[] = [];
fromSdkClients(rootClients, inputClients, []);
fromSdkClients(sdkContext, rootClients, inputClients, []);

const clientModel: CodeModel = {
Name: sdkPackage.rootNamespace,
Expand All @@ -62,33 +62,55 @@ export function createModel(sdkContext: SdkContext<NetEmitterOptions>): CodeMode
Clients: inputClients,
Auth: processServiceAuthentication(sdkContext, sdkPackage),
};

return clientModel;

function fromSdkClients(
sdkContext: SdkContext<NetEmitterOptions>,
clients: SdkClientType<SdkHttpOperation>[],
inputClients: InputClient[],
parentClientNames: string[],
) {
for (const client of clients) {
const inputClient = emitClient(client, parentClientNames);
const inputClient = fromSdkClient(sdkContext, client, parentClientNames);
inputClients.push(inputClient);
const subClients = client.methods
.filter((m) => m.kind === "clientaccessor")
.map((m) => m.response as SdkClientType<SdkHttpOperation>);
parentClientNames.push(inputClient.Name);
fromSdkClients(subClients, inputClients, parentClientNames);
fromSdkClients(sdkContext, subClients, inputClients, parentClientNames);
parentClientNames.pop();
}
}

function emitClient(client: SdkClientType<SdkHttpOperation>, parentNames: string[]): InputClient {
function fromSdkClient(
sdkContext: SdkContext<NetEmitterOptions>,
client: SdkClientType<SdkHttpOperation>,
parentNames: string[],
): InputClient {
const endpointParameter = client.initialization.properties.find(
(p) => p.kind === "endpoint",
) as SdkEndpointParameter;
const uri = getMethodUri(endpointParameter);
const clientParameters = fromSdkEndpointParameter(endpointParameter);
const clientName = getClientName(client, parentNames);
// see if this namespace is a sub-namespace of an existing bad namespace
const segments = client.clientNamespace.split(".");
const lastSegment = segments[segments.length - 1];
if (lastSegment === clientName) {
// we report diagnostics when the last segment of the namespace is the same as the client name
// because in our design, a sub namespace will be generated as a sub client with exact the same name as the namespace
// in csharp, this will cause a conflict between the namespace and the class name
reportDiagnostic(sdkContext.program, {
code: "client-namespace-conflict",
format: { clientNamespace: client.clientNamespace, clientName },
target: client.__raw.type ?? NoTarget,
});
}

return {
Name: getClientName(client, parentNames),
Name: clientName,
ClientNamespace: client.clientNamespace,
ArcturusZhang marked this conversation as resolved.
Show resolved Hide resolved
Summary: client.summary,
Doc: client.doc,
Operations: client.methods
Expand Down Expand Up @@ -119,14 +141,6 @@ export function createModel(sdkContext: SdkContext<NetEmitterOptions>): CodeMode
if (parentClientNames.length >= 2)
return `${parentClientNames.slice(parentClientNames.length - 1).join("")}${clientName}`;

if (
clientName === "Models" &&
resolveOptions(sdkContext.emitContext)["model-namespace"] !== false
) {
Logger.getInstance().warn(`Invalid client name "${clientName}"`);
return "ModelsOps";
}

return clientName;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import {
} from "../type/input-type.js";
import { OperationResponse } from "../type/operation-response.js";
import { SdkTypeMap } from "../type/sdk-type-map.js";
import { fromSdkType } from "./converter.js";
import { fromSdkType } from "./type-converter.js";

export function fromSdkHttpExamples(
sdkContext: SdkContext<NetEmitterOptions>,
Expand Down
12 changes: 6 additions & 6 deletions packages/http-client-csharp/emitter/src/lib/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@ const $lib = createTypeSpecLibrary({
default: paramMessage`No Route for service for service ${"service"}`,
},
},
"invalid-name": {
severity: "warning",
messages: {
default: paramMessage`Invalid interface or operation group name ${"name"} when configuration "model-namespace" is on`,
},
},
"general-warning": {
severity: "warning",
messages: {
Expand Down Expand Up @@ -58,6 +52,12 @@ const $lib = createTypeSpecLibrary({
default: paramMessage`${"message"}`,
},
},
"client-namespace-conflict": {
severity: "warning",
messages: {
default: paramMessage`namespace ${"clientNamespace"} conflicts with client ${"clientName"}, please use @clientName to specify a different name for the client.`,
},
},
},
emitter: {
options: NetEmitterOptionsSchema,
Expand Down
10 changes: 5 additions & 5 deletions packages/http-client-csharp/emitter/src/lib/logger.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

import { NoTarget, Program, Tracer } from "@typespec/compiler";
import { DiagnosticTarget, NoTarget, Program, Tracer } from "@typespec/compiler";
import { getTracer, reportDiagnostic } from "./lib.js";
import { LoggerLevel } from "./log-level.js";

Expand Down Expand Up @@ -63,19 +63,19 @@ export class Logger {
}
}

warn(message: string): void {
warn(message: string, target?: DiagnosticTarget | typeof NoTarget): void {
reportDiagnostic(this.program, {
code: "general-warning",
format: { message: message },
target: NoTarget,
target: target ?? NoTarget,
});
}

error(message: string): void {
error(message: string, target?: DiagnosticTarget | typeof NoTarget): void {
reportDiagnostic(this.program, {
code: "general-error",
format: { message: message },
target: NoTarget,
target: target ?? NoTarget,
});
}
}
2 changes: 1 addition & 1 deletion packages/http-client-csharp/emitter/src/lib/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import { NetEmitterOptions } from "../options.js";
import { InputType } from "../type/input-type.js";
import { LiteralTypeContext } from "../type/literal-type-context.js";
import { SdkTypeMap } from "../type/sdk-type-map.js";
import { fromSdkEnumType, fromSdkModelType, fromSdkType } from "./converter.js";
import { Logger } from "./logger.js";
import { fromSdkEnumType, fromSdkModelType, fromSdkType } from "./type-converter.js";

/**
* If type is an anonymous model, tries to find a named model that has the same
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ import { OperationResponse } from "../type/operation-response.js";
import { RequestLocation } from "../type/request-location.js";
import { parseHttpRequestMethod } from "../type/request-method.js";
import { SdkTypeMap } from "../type/sdk-type-map.js";
import { fromSdkModelType, fromSdkType } from "./converter.js";
import { getExternalDocs, getOperationId } from "./decorators.js";
import { fromSdkHttpExamples } from "./example-converter.js";
import { Logger } from "./logger.js";
import { fromSdkModelType, fromSdkType } from "./type-converter.js";
import { isSdkPathParameter } from "./utils.js";

export function fromSdkServiceMethod(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export function fromSdkType(
retVar = {
kind: "nullable",
type: inputType,
clientNamespace: sdkType.clientNamespace,
};
break;
case "model":
Expand Down Expand Up @@ -115,6 +116,7 @@ export function fromSdkModelType(
inputModelType = {
kind: "model",
name: modelTypeName,
clientNamespace: modelType.clientNamespace,
crossLanguageDefinitionId: modelType.crossLanguageDefinitionId,
access: getAccessOverride(
context,
Expand Down Expand Up @@ -142,6 +144,7 @@ export function fromSdkModelType(
const ourProperty = fromSdkModelProperty(property, {
ModelName: modelTypeName,
Usage: modelType.usage,
ClientNamespace: modelType.clientNamespace,
} as LiteralTypeContext);
propertiesDict.set(property, ourProperty);
}
Expand Down Expand Up @@ -226,6 +229,7 @@ export function fromSdkEnumType(
context,
enumType.__raw as any,
) /* when tcgc provide a way to identify if the access is override or not, we can get the accessibility from the enumType.access,*/,
clientNamespace: enumType.clientNamespace,
deprecation: enumType.deprecation,
summary: enumType.summary,
doc: enumType.doc,
Expand Down Expand Up @@ -303,6 +307,7 @@ function fromUnionType(
kind: "union",
name: union.name,
variantTypes: variantTypes,
clientNamespace: union.clientNamespace,
decorators: union.decorators,
};
}
Expand Down Expand Up @@ -340,6 +345,7 @@ function fromSdkConstantType(
values: values,
crossLanguageDefinitionId: "",
access: undefined,
clientNamespace: literalTypeContext.ClientNamespace,
doc: `The ${enumName}`, // TODO -- what should we put here?
isFixed: false,
isFlags: false,
Expand Down
4 changes: 0 additions & 4 deletions packages/http-client-csharp/emitter/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ export interface NetEmitterOptions extends SdkEmitterOptions {
"api-version"?: string;
outputFile?: string;
logFile?: string;
namespace: string;
"library-name": string;
"single-top-level-client"?: boolean;
skipSDKGeneration?: boolean;
"unreferenced-types-handling"?: "removeOrInternalize" | "internalize" | "keepAll";
"new-project"?: boolean;
"clear-output-folder"?: boolean;
"save-inputs"?: boolean;
"model-namespace"?: boolean;
"existing-project-folder"?: string;
"keep-non-overloadable-protocol-signature"?: boolean;
debug?: boolean;
Expand Down Expand Up @@ -45,7 +43,6 @@ export const NetEmitterOptionsSchema: JSONSchemaType<NetEmitterOptions> = {
"api-version": { type: "string", nullable: true },
outputFile: { type: "string", nullable: true },
logFile: { type: "string", nullable: true },
namespace: { type: "string" },
"library-name": { type: "string" },
"single-top-level-client": { type: "boolean", nullable: true },
skipSDKGeneration: { type: "boolean", default: false, nullable: true },
Expand All @@ -57,7 +54,6 @@ export const NetEmitterOptionsSchema: JSONSchemaType<NetEmitterOptions> = {
"new-project": { type: "boolean", nullable: true },
"clear-output-folder": { type: "boolean", nullable: true },
"save-inputs": { type: "boolean", nullable: true },
"model-namespace": { type: "boolean", nullable: true },
"generate-protocol-methods": { type: "boolean", nullable: true },
"generate-convenience-methods": { type: "boolean", nullable: true },
"flatten-union-as-enum": { type: "boolean", nullable: true },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@

export interface Configuration {
"output-folder": string;
namespace: string;
"library-name": string | null;
"library-name": string;
flavor?: string;
"single-top-level-client"?: boolean;
"unreferenced-types-handling"?: "removeOrInternalize" | "internalize" | "keepAll";
"model-namespace"?: boolean;
"models-to-treat-empty-string-as-null"?: string[];
"additional-intrinsic-types-to-treat-empty-string-as-null"?: string[];
"methods-to-keep-client-default-value"?: string[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Protocols } from "./protocols.js";

export interface InputClient {
Name: string;
ClientNamespace: string;
Summary?: string;
Doc?: string;
Operations: InputOperation[];
Expand Down
4 changes: 4 additions & 0 deletions packages/http-client-csharp/emitter/src/type/input-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export interface InputUnionType extends InputTypeBase {
kind: "union";
name: string;
variantTypes: InputType[];
clientNamespace: string;
}

export function isInputUnionType(type: InputType): type is InputUnionType {
Expand All @@ -91,6 +92,7 @@ export interface InputModelType extends InputTypeBase {
crossLanguageDefinitionId: string;
access?: AccessFlags;
usage: UsageFlags;
clientNamespace: string;
additionalProperties?: InputType;
discriminatorValue?: string;
discriminatedSubtypes?: Record<string, InputModelType>;
Expand Down Expand Up @@ -124,6 +126,7 @@ export interface InputEnumType extends InputTypeBase {
isFlags: boolean;
usage: UsageFlags;
access?: AccessFlags;
clientNamespace: string;
}

export interface InputEnumTypeValue extends InputTypeBase {
Expand All @@ -137,6 +140,7 @@ export interface InputEnumTypeValue extends InputTypeBase {
export interface InputNullableType extends InputTypeBase {
kind: "nullable";
type: InputType;
clientNamespace: string;
}

export function isInputEnumType(type: InputType): type is InputEnumType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export interface LiteralTypeContext {
ModelName: string;
PropertyName: string;
Usage: UsageFlags;
ClientNamespace: string;
}
3 changes: 2 additions & 1 deletion packages/http-client-csharp/eng/scripts/Generate.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ $failingSpecs = @(
Join-Path 'http' 'payload' 'xml'
Join-Path 'http' 'type' 'model' 'flatten'
Join-Path 'http' 'type' 'model' 'templated'
Join-Path 'http' 'client' 'naming'
ArcturusZhang marked this conversation as resolved.
Show resolved Hide resolved
)

$azureAllowSpecs = @(
Join-Path 'http' 'client' 'naming'
Join-Path 'http' 'client' 'structure' 'client-operation-group'
Join-Path 'http' 'client' 'structure' 'default'
Join-Path 'http' 'client' 'structure' 'multi-client'
Expand Down Expand Up @@ -110,6 +110,7 @@ foreach ($directory in $directories) {
}

if ($folders.Contains("versioning")) {
Write-Host "Generating versioning for $subPath" -ForegroundColor Cyan
Generate-Versioning $directory.FullName $generationDir -generateStub $stubbed
$cadlRanchLaunchProjects.Add($($folders -join "-") + "-v1", $("TestProjects/CadlRanch/$($subPath.Replace([System.IO.Path]::DirectorySeparatorChar, '/'))") + "/v1")
$cadlRanchLaunchProjects.Add($($folders -join "-") + "-v2", $("TestProjects/CadlRanch/$($subPath.Replace([System.IO.Path]::DirectorySeparatorChar, '/'))") + "/v2")
Expand Down
Loading