diff --git a/packages/http-client-csharp/emitter/src/emitter.ts b/packages/http-client-csharp/emitter/src/emitter.ts index c49b452f58f..936f79dd191 100644 --- a/packages/http-client-csharp/emitter/src/emitter.ts +++ b/packages/http-client-csharp/emitter/src/emitter.ts @@ -3,6 +3,8 @@ import { createSdkContext, SdkContext } from "@azure-tools/typespec-client-generator-core"; import { + createDiagnosticCollector, + Diagnostic, EmitContext, getDirectoryPath, joinPaths, @@ -20,11 +22,13 @@ import { tspOutputFileName, } from "./constants.js"; import { createModel } from "./lib/client-model-builder.js"; +import { createDiagnostic } from "./lib/lib.js"; import { LoggerLevel } from "./lib/logger-level.js"; import { Logger } from "./lib/logger.js"; import { execAsync, execCSharpGenerator } from "./lib/utils.js"; import { CSharpEmitterOptions, resolveOptions } from "./options.js"; import { createCSharpEmitterContext, CSharpEmitterContext } from "./sdk-context.js"; +import { CodeModel } from "./type/code-model.js"; import { Configuration } from "./type/configuration.js"; /** @@ -48,11 +52,34 @@ function findProjectRoot(path: string): string | undefined { } /** - * The entry point for the emitter. This function is called by the typespec compiler. + * Creates a code model by executing the full emission logic. + * This function can be called by downstream emitters to generate a code model and collect diagnostics. + * + * @example + * ```typescript + * import { emitCodeModel } from "@typespec/http-client-csharp"; + * + * export async function $onEmit(context: EmitContext) { + * const updateCodeModel = (model: CodeModel, context: CSharpEmitterContext) => { + * // Customize the code model here + * return model; + * }; + * const [, diagnostics] = await emitCodeModel(context, updateCodeModel); + * // Process diagnostics as needed + * context.program.reportDiagnostics(diagnostics); + * } + * ``` + * * @param context - The emit context + * @param updateCodeModel - Optional callback to modify the code model before emission + * @returns A tuple containing void and any diagnostics that were generated during the emission * @beta */ -export async function $onEmit(context: EmitContext) { +export async function emitCodeModel( + context: EmitContext, + updateCodeModel?: (model: CodeModel, context: CSharpEmitterContext) => CodeModel, +): Promise<[void, readonly Diagnostic[]]> { + const diagnostics = createDiagnosticCollector(); const program: Program = context.program; const options = resolveOptions(context); const outputFolder = context.emitterOutputDir; @@ -70,12 +97,16 @@ export async function $onEmit(context: EmitContext) { ), logger, ); - program.reportDiagnostics(sdkContext.diagnostics); + for (const diag of sdkContext.diagnostics) { + diagnostics.add(diag); + } - let root = createModel(sdkContext); + const root = diagnostics.pipe(createModel(sdkContext)); if (root) { - root = options["update-code-model"](root, sdkContext); + // Apply optional code model update callback + const updatedRoot = updateCodeModel ? updateCodeModel(root, sdkContext) : root; + const generatedFolder = resolvePath(outputFolder, "src", "Generated"); if (!fs.existsSync(generatedFolder)) { @@ -83,9 +114,9 @@ export async function $onEmit(context: EmitContext) { } // emit tspCodeModel.json - await writeCodeModel(sdkContext, root, outputFolder); + await writeCodeModel(sdkContext, updatedRoot, outputFolder); - const namespace = root.name; + const namespace = updatedRoot.name; const configurations: Configuration = createConfiguration(options, namespace, sdkContext); //emit configuration.json @@ -113,7 +144,9 @@ export async function $onEmit(context: EmitContext) { debug: options.debug ?? false, }); if (result.exitCode !== 0) { - const isValid = await _validateDotNetSdk(sdkContext, _minSupportedDotNetSdkVersion); + const isValid = diagnostics.pipe( + await _validateDotNetSdk(sdkContext, _minSupportedDotNetSdkVersion), + ); // if the dotnet sdk is valid, the error is not dependency issue, log it as normal if (isValid) { throw new Error( @@ -122,7 +155,9 @@ export async function $onEmit(context: EmitContext) { } } } catch (error: any) { - const isValid = await _validateDotNetSdk(sdkContext, _minSupportedDotNetSdkVersion); + const isValid = diagnostics.pipe( + await _validateDotNetSdk(sdkContext, _minSupportedDotNetSdkVersion), + ); // if the dotnet sdk is valid, the error is not dependency issue, log it as normal if (isValid) throw new Error(error, { cause: error }); } @@ -133,6 +168,18 @@ export async function $onEmit(context: EmitContext) { } } } + + return diagnostics.wrap(undefined); +} + +/** + * The entry point for the emitter. This function is called by the typespec compiler. + * @param context - The emit context + * @beta + */ +export async function $onEmit(context: EmitContext) { + const [, diagnostics] = await emitCodeModel(context); + context.program.reportDiagnostics(diagnostics); } export function createConfiguration( @@ -142,7 +189,6 @@ export function createConfiguration( ): Configuration { const skipKeys = [ "new-project", - "update-code-model", "sdk-context-options", "save-inputs", "generator-name", @@ -172,29 +218,34 @@ export function createConfiguration( * Report diagnostic if dotnet sdk is not installed or its version does not meet prerequisite * @param sdkContext - The SDK context * @param minVersionRequisite - The minimum required major version - * @param logger - The logger + * @returns A tuple containing whether the SDK is valid and any diagnostics * @internal */ export async function _validateDotNetSdk( sdkContext: CSharpEmitterContext, minMajorVersion: number, -): Promise { +): Promise<[boolean, readonly Diagnostic[]]> { + const diagnostics = createDiagnosticCollector(); try { const result = await execAsync("dotnet", ["--version"], { stdio: "pipe" }); - return validateDotNetSdkVersionCore(sdkContext, result.stdout, minMajorVersion); + return diagnostics.wrap( + diagnostics.pipe(validateDotNetSdkVersionCore(sdkContext, result.stdout, minMajorVersion)), + ); } catch (error: any) { if (error && "code" in error && error["code"] === "ENOENT") { - sdkContext.logger.reportDiagnostic({ - code: "invalid-dotnet-sdk-dependency", - messageId: "missing", - format: { - dotnetMajorVersion: `${minMajorVersion}`, - downloadUrl: "https://dotnet.microsoft.com/", - }, - target: NoTarget, - }); + diagnostics.add( + createDiagnostic({ + code: "invalid-dotnet-sdk-dependency", + messageId: "missing", + format: { + dotnetMajorVersion: `${minMajorVersion}`, + downloadUrl: "https://dotnet.microsoft.com/", + }, + target: NoTarget, + }), + ); } - return false; + return diagnostics.wrap(false); } } @@ -202,32 +253,41 @@ function validateDotNetSdkVersionCore( sdkContext: CSharpEmitterContext, version: string, minMajorVersion: number, -): boolean { +): [boolean, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); if (version) { const dotIndex = version.indexOf("."); const firstPart = dotIndex === -1 ? version : version.substring(0, dotIndex); const major = Number(firstPart); if (isNaN(major)) { - return false; + return diagnostics.wrap(false); } if (major < minMajorVersion) { - sdkContext.logger.reportDiagnostic({ - code: "invalid-dotnet-sdk-dependency", - messageId: "invalidVersion", - format: { - installedVersion: version, - dotnetMajorVersion: `${minMajorVersion}`, - downloadUrl: "https://dotnet.microsoft.com/", - }, - target: NoTarget, - }); - return false; + diagnostics.add( + createDiagnostic({ + code: "invalid-dotnet-sdk-dependency", + messageId: "invalidVersion", + format: { + installedVersion: version, + dotnetMajorVersion: `${minMajorVersion}`, + downloadUrl: "https://dotnet.microsoft.com/", + }, + target: NoTarget, + }), + ); + return diagnostics.wrap(false); } - return true; + return diagnostics.wrap(true); } else { - sdkContext.logger.error("Cannot get the installed .NET SDK version."); - return false; + diagnostics.add( + createDiagnostic({ + code: "general-error", + format: { message: "Cannot get the installed .NET SDK version." }, + target: NoTarget, + }), + ); + return diagnostics.wrap(false); } } diff --git a/packages/http-client-csharp/emitter/src/index.ts b/packages/http-client-csharp/emitter/src/index.ts index 5a0b24094c7..fb26b5a8b09 100644 --- a/packages/http-client-csharp/emitter/src/index.ts +++ b/packages/http-client-csharp/emitter/src/index.ts @@ -3,7 +3,7 @@ export { writeCodeModel } from "./code-model-writer.js"; export { configurationFileName, tspOutputFileName } from "./constants.js"; -export { $onEmit } from "./emitter.js"; +export { $onEmit, emitCodeModel } from "./emitter.js"; // we export `createModel` only for autorest.csharp because it uses the emitter to generate the code model file but not calling the dll here // we could remove this export when in the future we deprecate autorest.csharp export { createModel } from "./lib/client-model-builder.js"; diff --git a/packages/http-client-csharp/emitter/src/lib/client-converter.ts b/packages/http-client-csharp/emitter/src/lib/client-converter.ts index c37171e0137..52586f2ddf0 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-converter.ts @@ -9,7 +9,7 @@ import { SdkHttpOperation, SdkMethodParameter, } from "@azure-tools/typespec-client-generator-core"; -import { NoTarget } from "@typespec/compiler"; +import { createDiagnosticCollector, Diagnostic, NoTarget } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { InputParameterScope } from "../type/input-parameter-scope.js"; import { @@ -18,6 +18,7 @@ import { InputParameter, InputType, } from "../type/input-type.js"; +import { createDiagnostic } from "./lib.js"; import { fromMethodParameter, fromSdkServiceMethod, @@ -33,24 +34,26 @@ export function fromSdkClients( sdkContext: CSharpEmitterContext, clients: SdkClientType[], rootApiVersions: string[], -): InputClient[] { +): [InputClient[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const inputClients: InputClient[] = []; for (const client of clients) { - const inputClient = fromSdkClient(sdkContext, client, rootApiVersions); + const inputClient = diagnostics.pipe(fromSdkClient(sdkContext, client, rootApiVersions)); inputClients.push(inputClient); } - return inputClients; + return diagnostics.wrap(inputClients); } function fromSdkClient( sdkContext: CSharpEmitterContext, client: SdkClientType, rootApiVersions: string[], -): InputClient { +): [InputClient, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); let inputClient: InputClient | undefined = sdkContext.__typeCache.clients.get(client); if (inputClient) { - return inputClient; + return diagnostics.wrap(inputClient); } const endpointParameter = client.clientInitialization.parameters.find( (p) => p.kind === "endpoint", @@ -58,10 +61,12 @@ function fromSdkClient( const uri = getMethodUri(endpointParameter); // Convert all clientInitialization parameters - const clientParameters = fromSdkClientInitializationParameters( - sdkContext, - client.clientInitialization.parameters, - client.namespace, + const clientParameters = diagnostics.pipe( + fromSdkClientInitializationParameters( + sdkContext, + client.clientInitialization.parameters, + client.namespace, + ), ); const isMultiService = isMultiServiceClient(client); @@ -77,7 +82,11 @@ function fromSdkClient( doc: client.doc, summary: client.summary, methods: client.methods - .map((m) => fromSdkServiceMethod(sdkContext, m, uri, rootApiVersions, client.namespace)) + .map((m) => + diagnostics.pipe( + fromSdkServiceMethod(sdkContext, m, uri, rootApiVersions, client.namespace), + ), + ) .filter((m) => m !== undefined), parameters: clientParameters, initializedBy: client.clientInitialization.initializedBy, @@ -93,62 +102,73 @@ function fromSdkClient( // fill parent if (client.parent) { - inputClient.parent = fromSdkClient(sdkContext, client.parent, rootApiVersions); + inputClient.parent = diagnostics.pipe( + fromSdkClient(sdkContext, client.parent, rootApiVersions), + ); } // fill children if (client.children) { inputClient.children = client.children.map((c) => - fromSdkClient(sdkContext, c, rootApiVersions), + diagnostics.pipe(fromSdkClient(sdkContext, c, rootApiVersions)), ); } - return inputClient; + return diagnostics.wrap(inputClient); function fromSdkClientInitializationParameters( sdkContext: CSharpEmitterContext, parameters: (SdkEndpointParameter | SdkCredentialParameter | SdkMethodParameter)[], namespace: string, - ): InputParameter[] { + ): [InputParameter[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const inputParameters: InputParameter[] = []; for (const param of parameters) { if (param.kind === "endpoint") { // Convert endpoint parameters - const endpointParams = fromSdkEndpointParameter(param); + const endpointParams = diagnostics.pipe(fromSdkEndpointParameter(param)); inputParameters.push(...endpointParams); } else if (param.kind === "method") { // Convert method parameters - const methodParam = fromMethodParameter(sdkContext, param, namespace); + const methodParam = diagnostics.pipe(fromMethodParameter(sdkContext, param, namespace)); inputParameters.push(methodParam); } // Note: credential parameters are handled separately in service-authentication.ts // and are not included in the client parameters list } - return inputParameters; + return diagnostics.wrap(inputParameters); } - function fromSdkEndpointParameter(p: SdkEndpointParameter): InputEndpointParameter[] { + function fromSdkEndpointParameter( + p: SdkEndpointParameter, + ): [InputEndpointParameter[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); if (p.type.kind === "union") { - return fromSdkEndpointType(p.type.variantTypes[0]); + return diagnostics.wrap(diagnostics.pipe(fromSdkEndpointType(p.type.variantTypes[0]))); } else { - return fromSdkEndpointType(p.type); + return diagnostics.wrap(diagnostics.pipe(fromSdkEndpointType(p.type))); } } - function fromSdkEndpointType(type: SdkEndpointType): InputEndpointParameter[] { + function fromSdkEndpointType( + type: SdkEndpointType, + ): [InputEndpointParameter[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); // TODO: support free-style endpoint url with multiple parameters const endpointExpr = type.serverUrl .replace("https://", "") .replace("http://", "") .split("/")[0]; if (!/^\{\w+\}$/.test(endpointExpr)) { - sdkContext.logger.reportDiagnostic({ - code: "unsupported-endpoint-url", - format: { endpoint: type.serverUrl }, - target: NoTarget, - }); - return []; + diagnostics.add( + createDiagnostic({ + code: "unsupported-endpoint-url", + format: { endpoint: type.serverUrl }, + target: NoTarget, + }), + ); + return diagnostics.wrap([]); } const endpointVariableName = endpointExpr.substring(1, endpointExpr.length - 1); @@ -162,7 +182,7 @@ function fromSdkClient( crossLanguageDefinitionId: parameter.type.kind === "string" ? "TypeSpec.string" : "TypeSpec.url", } - : fromSdkType(sdkContext, parameter.type); // TODO: consolidate with converter.fromSdkEndpointType + : diagnostics.pipe(fromSdkType(sdkContext, parameter.type)); // TODO: consolidate with converter.fromSdkEndpointType parameters.push({ kind: "endpoint", name: parameter.name, @@ -174,19 +194,19 @@ function fromSdkClient( optional: parameter.optional, scope: InputParameterScope.Client, isEndpoint: isEndpoint, - defaultValue: getParameterDefaultValue( - sdkContext, - parameter.clientDefaultValue, - parameterType, + defaultValue: diagnostics.pipe( + getParameterDefaultValue(sdkContext, parameter.clientDefaultValue, parameterType), ), serverUrlTemplate: type.serverUrl, skipUrlEncoding: false, readOnly: isReadOnly(parameter), crossLanguageDefinitionId: parameter.crossLanguageDefinitionId, - methodParameterSegments: getMethodParameterSegments(sdkContext, parameter), + methodParameterSegments: diagnostics.pipe( + getMethodParameterSegments(sdkContext, parameter), + ), }); } - return parameters; + return diagnostics.wrap(parameters); } } diff --git a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts index 15c4da86c86..f0fc8413bff 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts @@ -6,6 +6,7 @@ import { SdkEnumType, SdkHttpOperation, } from "@azure-tools/typespec-client-generator-core"; +import { createDiagnosticCollector, Diagnostic } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { CodeModel } from "../type/code-model.js"; import { InputEnumType, InputLiteralType, InputModelType } from "../type/input-type.js"; @@ -17,17 +18,29 @@ import { firstLetterToUpperCase, getClientNamespaceString } from "./utils.js"; /** * Creates the code model from the SDK context. + * This function follows TypeSpec best practices by returning diagnostics alongside the result. + * + * @example + * ```typescript + * import { createModel } from "@typespec/http-client-csharp"; + * + * const sdkContext = createCSharpEmitterContext(context, logger); + * const [codeModel, diagnostics] = createModel(sdkContext); + * // Process the code model and handle diagnostics + * ``` + * * @param sdkContext - The SDK context - * @returns The code model + * @returns A tuple containing the code model and any diagnostics that were generated * @beta */ -export function createModel(sdkContext: CSharpEmitterContext): CodeModel { +export function createModel(sdkContext: CSharpEmitterContext): [CodeModel, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const sdkPackage = sdkContext.sdkPackage; // TO-DO: Consider exposing the namespace hierarchy in the code model https://github.com/microsoft/typespec/issues/8332 - fromSdkNamespaces(sdkContext, sdkPackage.namespaces); + diagnostics.pipe(fromSdkNamespaces(sdkContext, sdkPackage.namespaces)); // TO-DO: Consider using the TCGC model + enum cache once https://github.com/Azure/typespec-azure/issues/3180 is resolved - navigateModels(sdkContext); + diagnostics.pipe(navigateModels(sdkContext)); const types = Array.from(sdkContext.__typeCache.types.values()); const [models, enums] = [ @@ -37,7 +50,7 @@ export function createModel(sdkContext: CSharpEmitterContext): CodeModel { const rootClients = sdkPackage.clients; const rootApiVersions = parseApiVersions(sdkPackage.enums, rootClients); - const inputClients = fromSdkClients(sdkContext, rootClients, rootApiVersions); + const inputClients = diagnostics.pipe(fromSdkClients(sdkContext, rootClients, rootApiVersions)); // TODO -- TCGC now does not have constants field in its sdkPackage, they might add it in the future. const constants = Array.from(sdkContext.__typeCache.constants.values()); @@ -52,10 +65,10 @@ export function createModel(sdkContext: CSharpEmitterContext): CodeModel { constants: constants, models: models, clients: inputClients, - auth: processServiceAuthentication(sdkContext, sdkPackage), + auth: diagnostics.pipe(processServiceAuthentication(sdkContext, sdkPackage)), }; - return clientModel; + return diagnostics.wrap(clientModel); } /** @@ -139,11 +152,13 @@ function fixNamingConflicts(models: InputModelType[], constants: InputLiteralTyp } } -function navigateModels(sdkContext: CSharpEmitterContext) { +function navigateModels(sdkContext: CSharpEmitterContext): [void, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); for (const m of sdkContext.sdkPackage.models) { - fromSdkType(sdkContext, m); + diagnostics.pipe(fromSdkType(sdkContext, m)); } for (const e of sdkContext.sdkPackage.enums) { - fromSdkType(sdkContext, e); + diagnostics.pipe(fromSdkType(sdkContext, e)); } + return diagnostics.wrap(undefined as void); } diff --git a/packages/http-client-csharp/emitter/src/lib/example-converter.ts b/packages/http-client-csharp/emitter/src/lib/example-converter.ts index 74b20a8c479..ce23c24eb70 100644 --- a/packages/http-client-csharp/emitter/src/lib/example-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/example-converter.ts @@ -16,6 +16,7 @@ import { SdkUnionExampleValue, SdkUnknownExampleValue, } from "@azure-tools/typespec-client-generator-core"; +import { createDiagnosticCollector, Diagnostic } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { InputArrayExampleValue, @@ -47,8 +48,13 @@ import { fromSdkType } from "./type-converter.js"; export function fromSdkHttpExamples( sdkContext: CSharpEmitterContext, examples: SdkHttpOperationExample[], -): InputHttpOperationExample[] { - return examples.map((example) => fromSdkHttpExample(example)); +): [InputHttpOperationExample[], readonly Diagnostic[]] { + // Create a diagnostics collector for internal use + // Any errors in examples won't prevent the code model from being generated + const diagnostics = createDiagnosticCollector(); + + const result = examples.map((example) => fromSdkHttpExample(example)); + return diagnostics.wrap(result); function fromSdkHttpExample(example: SdkHttpOperationExample): InputHttpOperationExample { return { @@ -76,7 +82,7 @@ export function fromSdkHttpExamples( responseValue: SdkHttpResponseExampleValue, ): OperationResponseExample { return { - response: fromSdkHttpOperationResponse(sdkContext, responseValue.response), + response: diagnostics.pipe(fromSdkHttpOperationResponse(sdkContext, responseValue.response)), statusCode: responseValue.statusCode, bodyValue: responseValue.bodyValue ? fromSdkExample(responseValue.bodyValue) : undefined, }; @@ -108,7 +114,7 @@ export function fromSdkHttpExamples( function fromSdkStringExample(example: SdkStringExampleValue): InputStringExampleValue { return { kind: "string", - type: fromSdkType(sdkContext, example.type), + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)), value: example.value, }; } @@ -116,7 +122,7 @@ export function fromSdkHttpExamples( function fromSdkNumberExample(example: SdkNumberExampleValue): InputNumberExampleValue { return { kind: "number", - type: fromSdkType(sdkContext, example.type), + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)), value: example.value, }; } @@ -124,7 +130,7 @@ export function fromSdkHttpExamples( function fromSdkBooleanExample(example: SdkBooleanExampleValue): InputBooleanExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputPrimitiveType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputPrimitiveType, value: example.value, }; } @@ -132,7 +138,7 @@ export function fromSdkHttpExamples( function fromSdkUnionExample(example: SdkUnionExampleValue): InputUnionExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputUnionType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputUnionType, value: example.value, }; } @@ -140,7 +146,7 @@ export function fromSdkHttpExamples( function fromSdkArrayExample(example: SdkArrayExampleValue): InputArrayExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputArrayType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputArrayType, value: example.value.map((v) => fromSdkExample(v)), }; } @@ -150,7 +156,7 @@ export function fromSdkHttpExamples( ): InputDictionaryExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputDictionaryType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputDictionaryType, value: fromExampleRecord(example.value), }; } @@ -158,7 +164,7 @@ export function fromSdkHttpExamples( function fromSdkModelExample(example: SdkModelExampleValue): InputModelExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputModelType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputModelType, value: fromExampleRecord(example.value), additionalPropertiesValue: example.additionalPropertiesValue ? fromExampleRecord(example.additionalPropertiesValue) @@ -169,7 +175,7 @@ export function fromSdkHttpExamples( function fromSdkAnyExample(example: SdkUnknownExampleValue): InputUnknownExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputPrimitiveType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputPrimitiveType, value: example.value, }; } @@ -177,7 +183,7 @@ export function fromSdkHttpExamples( function fromSdkNullExample(example: SdkNullExampleValue): InputNullExampleValue { return { kind: example.kind, - type: fromSdkType(sdkContext, example.type) as InputNullableType, + type: diagnostics.pipe(fromSdkType(sdkContext, example.type)) as InputNullableType, value: example.value, }; } diff --git a/packages/http-client-csharp/emitter/src/lib/logger.ts b/packages/http-client-csharp/emitter/src/lib/logger.ts index 8b087a400f3..8b1682f0c27 100644 --- a/packages/http-client-csharp/emitter/src/lib/logger.ts +++ b/packages/http-client-csharp/emitter/src/lib/logger.ts @@ -1,12 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -import { DiagnosticReport, NoTarget, Program, Tracer } from "@typespec/compiler"; -import { - DiagnosticMessagesMap, - getTracer, - reportDiagnostic as libReportDiagnostic, -} from "./lib.js"; +import { Program, Tracer } from "@typespec/compiler"; +import { getTracer } from "./lib.js"; import { LoggerLevel } from "./logger-level.js"; /** @@ -16,12 +12,10 @@ import { LoggerLevel } from "./logger-level.js"; export class Logger { private tracer: Tracer; private level: LoggerLevel; - private program: Program; public constructor(program: Program, level: LoggerLevel) { this.tracer = getTracer(program); this.level = level; - this.program = program; } trace(level: LoggerLevel, message: string): void { @@ -60,25 +54,7 @@ export class Logger { } } - reportDiagnostic( - diag: DiagnosticReport, - ): void { - libReportDiagnostic(this.program, diag); - } - warn(message: string): void { - this.reportDiagnostic({ - code: "general-warning", - format: { message: message }, - target: NoTarget, - }); - } - - error(message: string): void { - this.reportDiagnostic({ - code: "general-error", - format: { message: message }, - target: NoTarget, - }); + this.tracer.trace("warning", message); } } diff --git a/packages/http-client-csharp/emitter/src/lib/namespace-converter.ts b/packages/http-client-csharp/emitter/src/lib/namespace-converter.ts index ef3f8f38195..cfc2eb761a0 100644 --- a/packages/http-client-csharp/emitter/src/lib/namespace-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/namespace-converter.ts @@ -1,18 +1,20 @@ import { SdkHttpOperation, SdkNamespace } from "@azure-tools/typespec-client-generator-core"; +import { createDiagnosticCollector, Diagnostic } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { InputNamespace } from "../type/input-type.js"; export function fromSdkNamespaces( sdkContext: CSharpEmitterContext, namespaces: SdkNamespace[], -): InputNamespace[] { +): [InputNamespace[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const inputNamespaces: InputNamespace[] = []; for (const namespace of namespaces) { const inputNamespace = fromSdkNamespace(sdkContext, namespace); inputNamespaces.push(inputNamespace); } - return inputNamespaces; + return diagnostics.wrap(inputNamespaces); } function fromSdkNamespace( diff --git a/packages/http-client-csharp/emitter/src/lib/operation-converter.ts b/packages/http-client-csharp/emitter/src/lib/operation-converter.ts index a061c8ee503..decc442eb79 100644 --- a/packages/http-client-csharp/emitter/src/lib/operation-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/operation-converter.ts @@ -28,7 +28,13 @@ import { shouldGenerateConvenient, shouldGenerateProtocol, } from "@azure-tools/typespec-client-generator-core"; -import { getDeprecated, isErrorModel, NoTarget } from "@typespec/compiler"; +import { + createDiagnosticCollector, + Diagnostic, + getDeprecated, + isErrorModel, + NoTarget, +} from "@typespec/compiler"; import { HttpStatusCodeRange } from "@typespec/http"; import { getResourceOperation } from "@typespec/rest"; import { CSharpEmitterContext } from "../sdk-context.js"; @@ -65,6 +71,7 @@ import { parseHttpRequestMethod } from "../type/request-method.js"; import { ResponseLocation } from "../type/response-location.js"; import { getExternalDocs, getOperationId } from "./decorators.js"; import { fromSdkHttpExamples } from "./example-converter.js"; +import { createDiagnostic } from "./lib.js"; import { fromSdkType } from "./type-converter.js"; import { getClientNamespaceString, isReadOnly } from "./utils.js"; @@ -74,75 +81,83 @@ export function fromSdkServiceMethod( uri: string, rootApiVersions: string[], namespace: string, -): InputServiceMethod | undefined { +): [InputServiceMethod | undefined, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + let method = sdkContext.__typeCache.methods.get(sdkMethod); if (method) { - return method; + return diagnostics.wrap(method); } const methodKind = sdkMethod.kind; switch (methodKind) { case "basic": - method = createServiceMethod( - sdkContext, - sdkMethod, - uri, - rootApiVersions, - namespace, + method = diagnostics.pipe( + createServiceMethod( + sdkContext, + sdkMethod, + uri, + rootApiVersions, + namespace, + ), ); break; case "paging": - const pagingServiceMethod = createServiceMethod( - sdkContext, - sdkMethod, - uri, - rootApiVersions, - namespace, + const pagingServiceMethod = diagnostics.pipe( + createServiceMethod( + sdkContext, + sdkMethod, + uri, + rootApiVersions, + namespace, + ), ); - pagingServiceMethod.pagingMetadata = loadPagingServiceMetadata( - sdkContext, - sdkMethod, - rootApiVersions, - uri, - namespace, + pagingServiceMethod.pagingMetadata = diagnostics.pipe( + loadPagingServiceMetadata(sdkContext, sdkMethod, rootApiVersions, uri, namespace), ); method = pagingServiceMethod; break; case "lro": - const lroServiceMethod = createServiceMethod( - sdkContext, - sdkMethod, - uri, - rootApiVersions, - namespace, + const lroServiceMethod = diagnostics.pipe( + createServiceMethod( + sdkContext, + sdkMethod, + uri, + rootApiVersions, + namespace, + ), + ); + lroServiceMethod.lroMetadata = diagnostics.pipe( + loadLongRunningMetadata(sdkContext, sdkMethod), ); - lroServiceMethod.lroMetadata = loadLongRunningMetadata(sdkContext, sdkMethod); method = lroServiceMethod; break; case "lropaging": - const lroPagingMethod = createServiceMethod( - sdkContext, - sdkMethod, - uri, - rootApiVersions, - namespace, + const lroPagingMethod = diagnostics.pipe( + createServiceMethod( + sdkContext, + sdkMethod, + uri, + rootApiVersions, + namespace, + ), ); - lroPagingMethod.lroMetadata = loadLongRunningMetadata(sdkContext, sdkMethod); - lroPagingMethod.pagingMetadata = loadPagingServiceMetadata( - sdkContext, - sdkMethod, - rootApiVersions, - uri, - namespace, + lroPagingMethod.lroMetadata = diagnostics.pipe( + loadLongRunningMetadata(sdkContext, sdkMethod), + ); + lroPagingMethod.pagingMetadata = diagnostics.pipe( + loadPagingServiceMetadata(sdkContext, sdkMethod, rootApiVersions, uri, namespace), ); method = lroPagingMethod; break; default: - sdkContext.logger.reportDiagnostic({ - code: "unsupported-service-method", - format: { methodKind: methodKind }, - target: NoTarget, - }); + diagnostics.add( + createDiagnostic({ + code: "unsupported-service-method", + format: { methodKind: methodKind }, + target: NoTarget, + }), + ); method = undefined; break; } @@ -151,7 +166,7 @@ export function fromSdkServiceMethod( sdkContext.__typeCache.updateSdkMethodReferences(sdkMethod, method); } - return method; + return diagnostics.wrap(method); } export function fromSdkServiceMethodOperation( @@ -159,21 +174,25 @@ export function fromSdkServiceMethodOperation( method: SdkServiceMethod, uri: string, rootApiVersions: string[], -): InputOperation { +): [InputOperation, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + let operation = sdkContext.__typeCache.operations.get(method.operation); if (operation) { - return operation; + return diagnostics.wrap(operation); } let generateConvenience = shouldGenerateConvenient(sdkContext, method.operation.__raw.operation); if (method.operation.verb === "patch" && generateConvenience) { - sdkContext.logger.reportDiagnostic({ - code: "unsupported-patch-convenience-method", - format: { - methodCrossLanguageDefinitionId: method.crossLanguageDefinitionId, - }, - target: method.__raw ?? NoTarget, - }); + diagnostics.add( + createDiagnostic({ + code: "unsupported-patch-convenience-method", + format: { + methodCrossLanguageDefinitionId: method.crossLanguageDefinitionId, + }, + target: method.__raw ?? NoTarget, + }), + ); generateConvenience = false; } @@ -187,8 +206,12 @@ export function fromSdkServiceMethodOperation( summary: method.summary, doc: method.doc, accessibility: method.access, - parameters: fromSdkOperationParameters(sdkContext, method.operation, rootApiVersions), - responses: fromSdkHttpOperationResponses(sdkContext, method.operation.responses), + parameters: diagnostics.pipe( + fromSdkOperationParameters(sdkContext, method.operation, rootApiVersions), + ), + responses: diagnostics.pipe( + fromSdkHttpOperationResponses(sdkContext, method.operation.responses), + ), httpMethod: parseHttpRequestMethod(method.operation.verb), uri: uri, path: method.operation.path, @@ -200,7 +223,7 @@ export function fromSdkServiceMethodOperation( crossLanguageDefinitionId: method.crossLanguageDefinitionId, decorators: method.decorators, examples: method.operation.examples - ? fromSdkHttpExamples(sdkContext, method.operation.examples) + ? diagnostics.pipe(fromSdkHttpExamples(sdkContext, method.operation.examples)) : undefined, namespace: method.__raw?.namespace ? getClientNamespace(sdkContext, method.__raw.namespace) @@ -209,31 +232,33 @@ export function fromSdkServiceMethodOperation( sdkContext.__typeCache.updateSdkOperationReferences(method.operation, operation); - return operation; + return diagnostics.wrap(operation); } export function getParameterDefaultValue( sdkContext: CSharpEmitterContext, clientDefaultValue: any, parameterType: InputType, -): InputConstant | undefined { +): [InputConstant | undefined, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + if ( clientDefaultValue === undefined || // a constant parameter should overwrite client default value parameterType.kind === "constant" ) { - return undefined; + return diagnostics.wrap(undefined); } - const kind = getValueType(sdkContext, clientDefaultValue); - return { + const kind = diagnostics.pipe(getValueType(sdkContext, clientDefaultValue)); + return diagnostics.wrap({ type: { kind: kind, name: kind, crossLanguageDefinitionId: `TypeSpec.${kind}`, }, value: clientDefaultValue, - }; + }); } function createServiceMethod( @@ -242,44 +267,57 @@ function createServiceMethod( uri: string, rootApiVersions: string[], namespace: string, -): T { - return { +): [T, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + + return diagnostics.wrap({ kind: method.kind, name: method.name, accessibility: method.access, apiVersions: method.apiVersions, doc: method.doc, summary: method.summary, - operation: fromSdkServiceMethodOperation(sdkContext, method, uri, rootApiVersions), - parameters: fromSdkServiceMethodParameters(sdkContext, method, rootApiVersions, namespace), - response: fromSdkServiceMethodResponse(sdkContext, method.response), + operation: diagnostics.pipe( + fromSdkServiceMethodOperation(sdkContext, method, uri, rootApiVersions), + ), + parameters: diagnostics.pipe( + fromSdkServiceMethodParameters(sdkContext, method, rootApiVersions, namespace), + ), + response: diagnostics.pipe(fromSdkServiceMethodResponse(sdkContext, method.response)), exception: method.exception - ? fromSdkServiceMethodResponse(sdkContext, method.exception) + ? diagnostics.pipe(fromSdkServiceMethodResponse(sdkContext, method.exception)) : undefined, isOverride: method.isOverride, generateConvenient: method.generateConvenient, generateProtocol: method.generateProtocol, crossLanguageDefinitionId: method.crossLanguageDefinitionId, - } as T; + } as T); } -function getValueType(sdkContext: CSharpEmitterContext, value: any): SdkBuiltInKinds { +function getValueType( + sdkContext: CSharpEmitterContext, + value: any, +): [SdkBuiltInKinds, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + switch (typeof value) { case "string": - return "string"; + return diagnostics.wrap("string"); case "number": - return "int32"; + return diagnostics.wrap("int32"); case "boolean": - return "boolean"; + return diagnostics.wrap("boolean"); case "bigint": - return "int64"; + return diagnostics.wrap("int64"); default: - sdkContext.logger.reportDiagnostic({ - code: "unsupported-default-value-type", - format: { valueType: typeof value }, - target: NoTarget, - }); - return "unknown"; + diagnostics.add( + createDiagnostic({ + code: "unsupported-default-value-type", + format: { valueType: typeof value }, + target: NoTarget, + }), + ); + return diagnostics.wrap("unknown"); } } @@ -288,11 +326,12 @@ function fromSdkServiceMethodParameters( method: SdkServiceMethod, rootApiVersions: string[], namespace: string, -): InputMethodParameter[] { +): [InputMethodParameter[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const parameters: InputMethodParameter[] = []; for (const p of method.parameters) { - const methodInputParameter = fromMethodParameter(sdkContext, p, namespace); + const methodInputParameter = diagnostics.pipe(fromMethodParameter(sdkContext, p, namespace)); const operationHttpParameter = getHttpOperationParameter(method, p); if (!operationHttpParameter) { @@ -306,11 +345,12 @@ function fromSdkServiceMethodParameters( methodInputParameter, operationHttpParameter, rootApiVersions, + diagnostics, ); parameters.push(methodInputParameter); } - return parameters; + return diagnostics.wrap(parameters); } function updateMethodParameter( @@ -318,13 +358,12 @@ function updateMethodParameter( methodParameter: InputMethodParameter, operationHttpParameter: SdkHttpParameter | SdkModelPropertyType, rootApiVersions: string[], + diagnostics: ReturnType, ): void { // for content type parameter if (isContentType(operationHttpParameter)) { - methodParameter.type = fromSdkType( - sdkContext, - operationHttpParameter.type, - operationHttpParameter, + methodParameter.type = diagnostics.pipe( + fromSdkType(sdkContext, operationHttpParameter.type, operationHttpParameter), ); } methodParameter.serializedName = getNameInRequest(operationHttpParameter); @@ -337,10 +376,8 @@ function updateMethodParameter( if (methodParameter.location === RequestLocation.Body) { // Convert constants to enums if (methodParameter.type.kind === "constant") { - methodParameter.type = fromSdkType( - sdkContext, - operationHttpParameter.type, - operationHttpParameter, + methodParameter.type = diagnostics.pipe( + fromSdkType(sdkContext, operationHttpParameter.type, operationHttpParameter), ); } } @@ -349,75 +386,87 @@ function updateMethodParameter( function fromSdkServiceMethodResponse( sdkContext: CSharpEmitterContext, methodResponse: SdkMethodResponse, -): InputServiceMethodResponse { - return { - type: getResponseType(sdkContext, methodResponse.type), +): [InputServiceMethodResponse, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + + return diagnostics.wrap({ + type: diagnostics.pipe(getResponseType(sdkContext, methodResponse.type)), resultSegments: methodResponse.resultSegments?.map((segment) => getResponseSegmentName(segment), ), - }; + }); } function fromSdkOperationParameters( sdkContext: CSharpEmitterContext, operation: SdkHttpOperation, rootApiVersions: string[], -): InputHttpParameter[] { +): [InputHttpParameter[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const parameters: InputHttpParameter[] = []; + for (const p of operation.parameters) { if (p.kind === "cookie") { - sdkContext.logger.reportDiagnostic({ - code: "unsupported-cookie-parameter", - format: { parameterName: p.name, path: operation.path }, - target: NoTarget, - }); - return parameters; + diagnostics.add( + createDiagnostic({ + code: "unsupported-cookie-parameter", + format: { parameterName: p.name, path: operation.path }, + target: NoTarget, + }), + ); + return diagnostics.wrap(parameters); } - const param = fromParameter(sdkContext, p, rootApiVersions); + const param = diagnostics.pipe(fromParameter(sdkContext, p, rootApiVersions)); if (param) { parameters.push(param); } } if (operation.bodyParam) { - const bodyParam = fromParameter(sdkContext, operation.bodyParam, rootApiVersions); + const bodyParam = diagnostics.pipe( + fromParameter(sdkContext, operation.bodyParam, rootApiVersions), + ); if (bodyParam) { parameters.push(bodyParam); } } - return parameters; + return diagnostics.wrap(parameters); } export function fromParameter( sdkContext: CSharpEmitterContext, p: SdkHttpParameter | SdkModelPropertyType, rootApiVersions: string[], -): InputHttpParameter | undefined { +): [InputHttpParameter | undefined, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + let parameter = sdkContext.__typeCache.operationParameters.get(p); if (parameter) { - return parameter; + return diagnostics.wrap(parameter); } const parameterKind = p.kind; switch (parameterKind) { case "query": - parameter = fromQueryParameter(sdkContext, p, rootApiVersions); + parameter = diagnostics.pipe(fromQueryParameter(sdkContext, p, rootApiVersions)); break; case "path": - parameter = fromPathParameter(sdkContext, p, rootApiVersions); + parameter = diagnostics.pipe(fromPathParameter(sdkContext, p, rootApiVersions)); break; case "header": - parameter = fromHeaderParameter(sdkContext, p, rootApiVersions); + parameter = diagnostics.pipe(fromHeaderParameter(sdkContext, p, rootApiVersions)); break; case "body": - parameter = fromBodyParameter(sdkContext, p, rootApiVersions); + parameter = diagnostics.pipe(fromBodyParameter(sdkContext, p, rootApiVersions)); break; default: - sdkContext.logger.reportDiagnostic({ - code: "unsupported-parameter-kind", - format: { parameterKind }, - target: p.__raw ?? NoTarget, - }); + diagnostics.add( + createDiagnostic({ + code: "unsupported-parameter-kind", + format: { parameterKind }, + target: p.__raw ?? NoTarget, + }), + ); parameter = undefined; break; } @@ -425,15 +474,16 @@ export function fromParameter( if (parameter) { sdkContext.__typeCache.operationParameters.set(p, parameter); } - return parameter; + return diagnostics.wrap(parameter); } function fromQueryParameter( sdkContext: CSharpEmitterContext, p: SdkQueryParameter, rootApiVersions: string[], -): InputQueryParameter { - const parameterType = fromSdkType(sdkContext, p.type, p); +): [InputQueryParameter, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + const parameterType = diagnostics.pipe(fromSdkType(sdkContext, p.type, p)); const retVar: InputQueryParameter = { kind: "query", @@ -444,26 +494,29 @@ function fromQueryParameter( type: parameterType, isApiVersion: p.isApiVersionParam, explode: isExploded(p), - defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType), + defaultValue: diagnostics.pipe( + getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType), + ), arraySerializationDelimiter: getArraySerializationDelimiter(p), optional: p.optional, scope: getParameterScope(p, parameterType, rootApiVersions.length > 0), decorators: p.decorators, crossLanguageDefinitionId: p.crossLanguageDefinitionId, readOnly: isReadOnly(p), - methodParameterSegments: getMethodParameterSegments(sdkContext, p), + methodParameterSegments: diagnostics.pipe(getMethodParameterSegments(sdkContext, p)), }; sdkContext.__typeCache.updateSdkOperationParameterReferences(p, retVar); - return retVar; + return diagnostics.wrap(retVar); } function fromPathParameter( sdkContext: CSharpEmitterContext, p: SdkPathParameter, rootApiVersions: string[], -): InputPathParameter { - const parameterType = fromSdkType(sdkContext, p.type, p); +): [InputPathParameter, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + const parameterType = diagnostics.pipe(fromSdkType(sdkContext, p.type, p)); const retVar: InputPathParameter = { kind: "path", @@ -477,25 +530,28 @@ function fromPathParameter( style: p.style, allowReserved: p.allowReserved, skipUrlEncoding: p.allowReserved, - defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType), + defaultValue: diagnostics.pipe( + getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType), + ), optional: p.optional, scope: getParameterScope(p, parameterType, rootApiVersions.length > 0), decorators: p.decorators, readOnly: isReadOnly(p), crossLanguageDefinitionId: p.crossLanguageDefinitionId, - methodParameterSegments: getMethodParameterSegments(sdkContext, p), + methodParameterSegments: diagnostics.pipe(getMethodParameterSegments(sdkContext, p)), }; sdkContext.__typeCache.updateSdkOperationParameterReferences(p, retVar); - return retVar; + return diagnostics.wrap(retVar); } function fromHeaderParameter( sdkContext: CSharpEmitterContext, p: SdkHeaderParameter, rootApiVersions: string[], -): InputHeaderParameter { - const parameterType = fromSdkType(sdkContext, p.type, p); +): [InputHeaderParameter, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + const parameterType = diagnostics.pipe(fromSdkType(sdkContext, p.type, p)); const retVar: InputHeaderParameter = { kind: "header", @@ -507,27 +563,30 @@ function fromHeaderParameter( isApiVersion: p.isApiVersionParam, collectionFormat: p.collectionFormat, arraySerializationDelimiter: getArraySerializationDelimiter(p), - defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType), + defaultValue: diagnostics.pipe( + getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType), + ), optional: p.optional, isContentType: isContentType(p), scope: getParameterScope(p, parameterType, rootApiVersions.length > 0), readOnly: isReadOnly(p), decorators: p.decorators, crossLanguageDefinitionId: p.crossLanguageDefinitionId, - methodParameterSegments: getMethodParameterSegments(sdkContext, p), - collectionHeaderPrefix: getCollectionHeaderPrefix(sdkContext, p), + methodParameterSegments: diagnostics.pipe(getMethodParameterSegments(sdkContext, p)), + collectionHeaderPrefix: diagnostics.pipe(getCollectionHeaderPrefix(sdkContext, p)), }; sdkContext.__typeCache.updateSdkOperationParameterReferences(p, retVar); - return retVar; + return diagnostics.wrap(retVar); } function fromBodyParameter( sdkContext: CSharpEmitterContext, p: SdkBodyParameter, rootApiVersions: string[], -): InputBodyParameter { - const parameterType = fromSdkType(sdkContext, p.type, p); +): [InputBodyParameter, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + const parameterType = diagnostics.pipe(fromSdkType(sdkContext, p.type, p)); const retVar: InputBodyParameter = { kind: "body", @@ -544,24 +603,26 @@ function fromBodyParameter( decorators: p.decorators, readOnly: isReadOnly(p), crossLanguageDefinitionId: p.crossLanguageDefinitionId, - methodParameterSegments: getMethodParameterSegments(sdkContext, p), + methodParameterSegments: diagnostics.pipe(getMethodParameterSegments(sdkContext, p)), }; sdkContext.__typeCache.updateSdkOperationParameterReferences(p, retVar); - return retVar; + return diagnostics.wrap(retVar); } export function fromMethodParameter( sdkContext: CSharpEmitterContext, p: SdkMethodParameter, namespace: string, -): InputMethodParameter { +): [InputMethodParameter, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + let retVar = sdkContext.__typeCache.methodParmeters.get(p); if (retVar) { - return retVar as InputMethodParameter; + return diagnostics.wrap(retVar as InputMethodParameter); } - const parameterType = fromSdkType(sdkContext, p.type, p, namespace); + const parameterType = diagnostics.pipe(fromSdkType(sdkContext, p.type, p, namespace)); const paramAlias = p.__raw ? getParamAlias(sdkContext, p.__raw) : undefined; @@ -574,7 +635,9 @@ export function fromMethodParameter( type: parameterType, location: RequestLocation.None, isApiVersion: p.isApiVersionParam, - defaultValue: getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType), + defaultValue: diagnostics.pipe( + getParameterDefaultValue(sdkContext, p.clientDefaultValue, parameterType), + ), optional: p.optional, scope: InputParameterScope.Method, crossLanguageDefinitionId: p.crossLanguageDefinitionId, @@ -585,14 +648,16 @@ export function fromMethodParameter( }; sdkContext.__typeCache.updateSdkMethodParameterReferences(p, retVar); - return retVar; + return diagnostics.wrap(retVar); } function loadLongRunningMetadata( sdkContext: CSharpEmitterContext, method: SdkLroServiceMethod | SdkLroPagingServiceMethod, -): InputLongRunningServiceMetadata { - return { +): [InputLongRunningServiceMetadata, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + + return diagnostics.wrap({ finalStateVia: convertLroFinalStateVia(method.lroMetadata.finalStateVia), finalResponse: { // in swagger, we allow delete to return some meaningful body content @@ -600,60 +665,70 @@ function loadLongRunningMetadata( statusCodes: method.operation.verb === "delete" ? [204] : [200], bodyType: method.lroMetadata.finalResponse?.envelopeResult !== undefined - ? fromSdkType(sdkContext, method.lroMetadata.finalResponse.envelopeResult) + ? diagnostics.pipe( + fromSdkType(sdkContext, method.lroMetadata.finalResponse.envelopeResult), + ) : undefined, } as OperationResponse, resultPath: method.lroMetadata.finalResultPath, - }; + }); } function fromSdkHttpOperationResponses( sdkContext: CSharpEmitterContext, operationResponses: SdkHttpResponse[], -): OperationResponse[] { +): [OperationResponse[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const responses: OperationResponse[] = []; + for (const r of operationResponses) { - responses.push(fromSdkHttpOperationResponse(sdkContext, r)); + responses.push(diagnostics.pipe(fromSdkHttpOperationResponse(sdkContext, r))); } - return responses; + return diagnostics.wrap(responses); } export function fromSdkHttpOperationResponse( sdkContext: CSharpEmitterContext, sdkResponse: SdkHttpResponse, -): OperationResponse { +): [OperationResponse, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + let retVar = sdkContext.__typeCache.responses.get(sdkResponse); if (retVar) { - return retVar; + return diagnostics.wrap(retVar); } const range = sdkResponse.statusCodes; retVar = { statusCodes: toStatusCodesArray(range), - bodyType: getResponseType(sdkContext, sdkResponse.type), - headers: fromSdkServiceResponseHeaders(sdkContext, sdkResponse.headers), + bodyType: diagnostics.pipe(getResponseType(sdkContext, sdkResponse.type)), + headers: diagnostics.pipe(fromSdkServiceResponseHeaders(sdkContext, sdkResponse.headers)), isErrorResponse: sdkResponse.type !== undefined && isErrorModel(sdkContext.program, sdkResponse.type.__raw!), contentTypes: sdkResponse.contentTypes, }; sdkContext.__typeCache.updateSdkResponseReferences(sdkResponse, retVar); - return retVar; + return diagnostics.wrap(retVar); } function fromSdkServiceResponseHeaders( sdkContext: CSharpEmitterContext, headers: SdkServiceResponseHeader[], -): HttpResponseHeader[] { - return headers.map( - (h) => - ({ - name: h.__raw!.name, - nameInResponse: h.serializedName, - summary: h.summary, - doc: h.doc, - type: fromSdkType(sdkContext, h.type), - }) as HttpResponseHeader, +): [HttpResponseHeader[], readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + + return diagnostics.wrap( + headers.map( + (h) => + ({ + name: h.__raw!.name, + nameInResponse: h.serializedName, + summary: h.summary, + doc: h.doc, + type: diagnostics.pipe(fromSdkType(sdkContext, h.type)), + }) as HttpResponseHeader, + ), ); } @@ -706,27 +781,29 @@ function loadPagingServiceMetadata( rootApiVersions: string[], uri: string, namespace: string, -): InputPagingServiceMetadata { +): [InputPagingServiceMetadata, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + let nextLink: InputNextLink | undefined; if (method.pagingMetadata.nextLinkSegments) { nextLink = { responseSegments: method.pagingMetadata.nextLinkSegments.map((segment) => getResponseSegmentName(segment), ), - responseLocation: getResponseLocation( - context, - method, - method.pagingMetadata.nextLinkSegments[0], + responseLocation: diagnostics.pipe( + getResponseLocation(context, method, method.pagingMetadata.nextLinkSegments[0]), ), }; if (method.pagingMetadata.nextLinkOperation) { - nextLink.operation = fromSdkServiceMethod( - context, - method.pagingMetadata.nextLinkOperation, - uri, - rootApiVersions, - namespace, + nextLink.operation = diagnostics.pipe( + fromSdkServiceMethod( + context, + method.pagingMetadata.nextLinkOperation, + uri, + rootApiVersions, + namespace, + ), ); } @@ -742,7 +819,9 @@ function loadPagingServiceMetadata( ] as SdkModelPropertyType; const operationParameter = getHttpOperationParameter(method, lastParameterSegment); if (operationParameter) { - const parameter = fromParameter(context, operationParameter, rootApiVersions); + const parameter = diagnostics.pipe( + fromParameter(context, operationParameter, rootApiVersions), + ); if (parameter) { nextLinkReInjectedParameters.push(parameter); } @@ -763,10 +842,12 @@ function loadPagingServiceMetadata( const lastParameterSegment = method.pagingMetadata.continuationTokenParameterSegments[ method.pagingMetadata.continuationTokenParameterSegments.length - 1 ] as SdkModelPropertyType; - const continuationTokenParameter = fromParameter( - context, - getHttpOperationParameter(method, lastParameterSegment)!, - rootApiVersions, + const continuationTokenParameter = diagnostics.pipe( + fromParameter( + context, + getHttpOperationParameter(method, lastParameterSegment)!, + rootApiVersions, + ), ); if (continuationTokenParameter) { continuationToken = { @@ -774,10 +855,12 @@ function loadPagingServiceMetadata( responseSegments: method.pagingMetadata.continuationTokenResponseSegments!.map((segment) => getResponseSegmentName(segment), ), - responseLocation: getResponseLocation( - context, - method, - method.pagingMetadata.continuationTokenResponseSegments?.[0], + responseLocation: diagnostics.pipe( + getResponseLocation( + context, + method, + method.pagingMetadata.continuationTokenResponseSegments?.[0], + ), ), }; } @@ -790,12 +873,12 @@ function loadPagingServiceMetadata( ); } - return { + return diagnostics.wrap({ itemPropertySegments: method.response.resultSegments!.map((s) => getResponseSegmentName(s)), nextLink: nextLink, continuationToken: continuationToken, pageSizeParameterSegments: pageSizeParameterSegments, - }; + }); } function getResponseSegmentName(segment: SdkServiceResponseHeader | SdkModelPropertyType): string { @@ -814,23 +897,27 @@ function getResponseLocation( context: CSharpEmitterContext, method: SdkPagingServiceMethod | SdkLroPagingServiceMethod, p: SdkServiceResponseHeader | SdkModelPropertyType, -): ResponseLocation { +): [ResponseLocation, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + if (p.kind === "responseheader") { - return ResponseLocation.Header; + return diagnostics.wrap(ResponseLocation.Header); } if (isHttpMetadata(context, p)) { - context.logger.reportDiagnostic({ - code: "unsupported-continuation-location", - format: { - crossLanguageDefinitionId: method.crossLanguageDefinitionId, - }, - target: NoTarget, - }); - return ResponseLocation.None; + diagnostics.add( + createDiagnostic({ + code: "unsupported-continuation-location", + format: { + crossLanguageDefinitionId: method.crossLanguageDefinitionId, + }, + target: NoTarget, + }), + ); + return diagnostics.wrap(ResponseLocation.None); } - return ResponseLocation.Body; + return diagnostics.wrap(ResponseLocation.Body); } // TODO: https://github.com/Azure/typespec-azure/issues/1441 @@ -941,19 +1028,20 @@ function getArraySerializationDelimiter( export function getMethodParameterSegments( sdkContext: CSharpEmitterContext, p: SdkHttpParameter | SdkModelPropertyType, -): InputMethodParameter[] | undefined { +): [InputMethodParameter[] | undefined, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); // methodParameterSegments is a 2D array where each segment array represents a path to a method parameter // For spread body cases, there could be multiple paths, but we simplify by taking the first element // We need the complete segment path (e.g., ['Params', 'foo'] for accessing params.foo) const methodParameterSegments = (p as any).methodParameterSegments; if (!methodParameterSegments || methodParameterSegments.length === 0) { - return undefined; + return diagnostics.wrap(undefined); } // Take the first segment path (simplification - no spector scenario for multiple paths yet) const firstSegmentPath = methodParameterSegments[0]; if (!firstSegmentPath || firstSegmentPath.length === 0) { - return undefined; + return diagnostics.wrap(undefined); } const namespace = getClientNamespaceString(sdkContext) ?? ""; @@ -963,18 +1051,20 @@ export function getMethodParameterSegments( // This preserves the full path information (e.g., ['Params', 'foo']) for (const segment of firstSegmentPath) { const methodParam = segment as SdkMethodParameter; - methodParams.push(fromMethodParameter(sdkContext, methodParam, namespace)); + methodParams.push(diagnostics.pipe(fromMethodParameter(sdkContext, methodParam, namespace))); } - return methodParams.length > 0 ? methodParams : undefined; + return diagnostics.wrap(methodParams.length > 0 ? methodParams : undefined); } function getResponseType( sdkContext: CSharpEmitterContext, type: SdkType | undefined, -): InputType | undefined { +): [InputType | undefined, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + if (!type) { - return undefined; + return diagnostics.wrap(undefined); } // handle anonymous union enum response types by defaulting to the enum value type in the case of @@ -988,25 +1078,28 @@ function getResponseType( function getCollectionHeaderPrefix( sdkContext: CSharpEmitterContext, p: SdkHeaderParameter, -): string | undefined { +): [string | undefined, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const value = getClientOptions(p, "collectionHeaderPrefix"); if (value === undefined) { - return undefined; + return diagnostics.wrap(undefined); } // Only apply to dictionary types (unwrap nullable) const rawType = p.type.kind === "nullable" ? p.type.type : p.type; if (rawType.kind !== "dict") { - return undefined; + return diagnostics.wrap(undefined); } if (typeof value !== "string") { - sdkContext.logger.reportDiagnostic({ - code: "general-warning", - format: { - message: `The 'collectionHeaderPrefix' client option must be a string value, but got '${typeof value}'. The option will be ignored.`, - }, - target: p.__raw ?? NoTarget, - }); - return undefined; - } - return value; + diagnostics.add( + createDiagnostic({ + code: "general-warning", + format: { + message: `The 'collectionHeaderPrefix' client option must be a string value, but got '${typeof value}'. The option will be ignored.`, + }, + target: p.__raw ?? NoTarget, + }), + ); + return diagnostics.wrap(undefined); + } + return diagnostics.wrap(value); } diff --git a/packages/http-client-csharp/emitter/src/lib/service-authentication.ts b/packages/http-client-csharp/emitter/src/lib/service-authentication.ts index 728cd30c5f2..52b1b44de5a 100644 --- a/packages/http-client-csharp/emitter/src/lib/service-authentication.ts +++ b/packages/http-client-csharp/emitter/src/lib/service-authentication.ts @@ -7,16 +7,18 @@ import { SdkHttpOperation, SdkPackage, } from "@azure-tools/typespec-client-generator-core"; -import { NoTarget } from "@typespec/compiler"; +import { createDiagnosticCollector, Diagnostic, NoTarget } from "@typespec/compiler"; import { Oauth2Auth, OAuth2Flow } from "@typespec/http"; import { CSharpEmitterContext } from "../sdk-context.js"; import { InputAuth } from "../type/input-auth.js"; import { InputOAuth2Flow } from "../type/input-oauth2-auth.js"; +import { createDiagnostic } from "./lib.js"; export function processServiceAuthentication( sdkContext: CSharpEmitterContext, sdkPackage: SdkPackage, -): InputAuth | undefined { +): [InputAuth | undefined, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); let authClientParameter: SdkCredentialParameter | undefined = undefined; for (const client of sdkPackage.clients) { for (const parameter of client.clientInitialization.parameters) { @@ -28,29 +30,31 @@ export function processServiceAuthentication( } if (!authClientParameter) { - return undefined; + return diagnostics.wrap(undefined); } const inputAuth: InputAuth = {}; if (authClientParameter.type.kind === "credential") { - const auth = processAuthType(sdkContext, authClientParameter.type); + const auth = diagnostics.pipe(processAuthType(sdkContext, authClientParameter.type)); if (!auth && authClientParameter.type.scheme.type !== "noAuth") { - sdkContext.logger.reportDiagnostic({ - code: "unsupported-auth", - messageId: "onlyUnsupportedAuthProvided", - target: authClientParameter.type.__raw ?? NoTarget, - }); + diagnostics.add( + createDiagnostic({ + code: "unsupported-auth", + messageId: "onlyUnsupportedAuthProvided", + target: authClientParameter.type.__raw ?? NoTarget, + }), + ); - return inputAuth; + return diagnostics.wrap(inputAuth); } - return auth; + return diagnostics.wrap(auth); } let containsNoAuth = false; for (const authType of authClientParameter.type.variantTypes) { containsNoAuth = containsNoAuth || authType.scheme.type === "noAuth"; - const auth = processAuthType(sdkContext, authType); + const auth = diagnostics.pipe(processAuthType(sdkContext, authType)); if (auth?.apiKey) { inputAuth.apiKey = auth.apiKey; } @@ -60,75 +64,86 @@ export function processServiceAuthentication( } if (containsNoAuth && !inputAuth.apiKey && !inputAuth.oAuth2) { - return undefined; + return diagnostics.wrap(undefined); } if (!inputAuth?.apiKey && !inputAuth?.oAuth2) { - sdkContext.logger.reportDiagnostic({ - code: "unsupported-auth", - messageId: "onlyUnsupportedAuthProvided", - target: authClientParameter.type.__raw ?? NoTarget, - }); + diagnostics.add( + createDiagnostic({ + code: "unsupported-auth", + messageId: "onlyUnsupportedAuthProvided", + target: authClientParameter.type.__raw ?? NoTarget, + }), + ); } - return inputAuth; + return diagnostics.wrap(inputAuth); } function processAuthType( sdkContext: CSharpEmitterContext, credentialType: SdkCredentialType, -): InputAuth | undefined { +): [InputAuth | undefined, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const scheme = credentialType.scheme; switch (scheme.type) { case "apiKey": if (scheme.in !== "header") { - sdkContext.logger.reportDiagnostic({ - code: "unsupported-auth", - format: { - message: `Only header is supported for ApiKey authentication. ${scheme.in} is not supported.`, - }, - target: credentialType.__raw ?? NoTarget, - }); - return undefined; + diagnostics.add( + createDiagnostic({ + code: "unsupported-auth", + format: { + message: `Only header is supported for ApiKey authentication. ${scheme.in} is not supported.`, + }, + target: credentialType.__raw ?? NoTarget, + }), + ); + return diagnostics.wrap(undefined); } - return { apiKey: { name: scheme.name, in: scheme.in } } as InputAuth; + return diagnostics.wrap({ apiKey: { name: scheme.name, in: scheme.in } } as InputAuth); case "oauth2": - return processOAuth2(scheme); + return diagnostics.wrap(processOAuth2(scheme)); case "http": { const schemeOrApiKeyPrefix = scheme.scheme; switch (schemeOrApiKeyPrefix) { case "Basic": - sdkContext.logger.reportDiagnostic({ - code: "unsupported-auth", - format: { message: `${schemeOrApiKeyPrefix} auth method is currently not supported.` }, - target: credentialType.__raw ?? NoTarget, - }); - return undefined; + diagnostics.add( + createDiagnostic({ + code: "unsupported-auth", + format: { + message: `${schemeOrApiKeyPrefix} auth method is currently not supported.`, + }, + target: credentialType.__raw ?? NoTarget, + }), + ); + return diagnostics.wrap(undefined); case "Bearer": - return { + return diagnostics.wrap({ apiKey: { name: "Authorization", in: "header", prefix: "Bearer", }, - }; + }); default: - return { + return diagnostics.wrap({ apiKey: { name: "Authorization", in: "header", prefix: schemeOrApiKeyPrefix, }, - }; + }); } } default: - sdkContext.logger.reportDiagnostic({ - code: "unsupported-auth", - format: { message: `un-supported authentication scheme ${scheme.type}` }, - target: credentialType.__raw ?? NoTarget, - }); - return undefined; + diagnostics.add( + createDiagnostic({ + code: "unsupported-auth", + format: { message: `un-supported authentication scheme ${scheme.type}` }, + target: credentialType.__raw ?? NoTarget, + }), + ); + return diagnostics.wrap(undefined); } } diff --git a/packages/http-client-csharp/emitter/src/lib/type-converter.ts b/packages/http-client-csharp/emitter/src/lib/type-converter.ts index 7107cc7d4b1..ded35eb85e8 100644 --- a/packages/http-client-csharp/emitter/src/lib/type-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/type-converter.ts @@ -3,6 +3,8 @@ import { DecoratorInfo, + getAccessOverride, + isHttpMetadata, SdkArrayType, SdkBuiltInType, SdkConstantType, @@ -17,10 +19,8 @@ import { SdkType, SdkUnionType, UsageFlags, - getAccessOverride, - isHttpMetadata, } from "@azure-tools/typespec-client-generator-core"; -import { Model, NoTarget } from "@typespec/compiler"; +import { createDiagnosticCollector, Diagnostic, Model, NoTarget } from "@typespec/compiler"; import { CSharpEmitterContext } from "../sdk-context.js"; import { InputArrayType, @@ -39,6 +39,7 @@ import { InputType, InputUnionType, } from "../type/input-type.js"; +import { createDiagnostic } from "./lib.js"; import { isReadOnly } from "./utils.js"; // we have this complicated type here to let the caller of fromSdkType could infer the real return type of this function. @@ -75,36 +76,37 @@ export function fromSdkType( sdkType: T, sdkProperty?: SdkModelPropertyTypeBase, namespace?: string, -): InputReturnType { +): [InputReturnType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); let retVar = sdkContext.__typeCache.types.get(sdkType); if (retVar) { - return retVar as any; + return diagnostics.wrap(retVar as any); } switch (sdkType.kind) { case "nullable": const nullableType: InputNullableType = { kind: "nullable", - type: fromSdkType(sdkContext, sdkType.type, sdkProperty, namespace), + type: diagnostics.pipe(fromSdkType(sdkContext, sdkType.type, sdkProperty, namespace)), namespace: sdkType.namespace, external: fromSdkExternalTypeInfo(sdkType), }; retVar = nullableType; break; case "model": - retVar = fromSdkModelType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromSdkModelType(sdkContext, sdkType)); break; case "enum": - retVar = fromSdkEnumType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromSdkEnumType(sdkContext, sdkType)); break; case "enumvalue": - retVar = fromSdkEnumValueType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromSdkEnumValueType(sdkContext, sdkType)); break; case "dict": - retVar = fromSdkDictionaryType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromSdkDictionaryType(sdkContext, sdkType)); break; case "array": - retVar = fromSdkArrayType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromSdkArrayType(sdkContext, sdkType)); break; case "constant": // Don't transform optional Content-Type headers into enums - keep them as constants @@ -124,27 +126,29 @@ export function fromSdkType( sdkType.valueType.kind !== "boolean" ) { // turn the constant into an extensible enum - retVar = createEnumType(sdkContext, sdkType, namespace!); + retVar = diagnostics.pipe(createEnumType(sdkContext, sdkType, namespace!)); } else { - retVar = fromSdkConstantType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromSdkConstantType(sdkContext, sdkType)); } break; case "union": - retVar = fromUnionType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromUnionType(sdkContext, sdkType)); break; case "utcDateTime": case "offsetDateTime": - retVar = fromSdkDateTimeType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromSdkDateTimeType(sdkContext, sdkType)); break; case "duration": - retVar = fromSdkDurationType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromSdkDurationType(sdkContext, sdkType)); break; case "tuple": - sdkContext.logger.reportDiagnostic({ - code: "unsupported-sdk-type", - format: { sdkType: "tuple" }, - target: sdkType.__raw ?? NoTarget, - }); + diagnostics.add( + createDiagnostic({ + code: "unsupported-sdk-type", + format: { sdkType: "tuple" }, + target: sdkType.__raw ?? NoTarget, + }), + ); const tupleType: InputPrimitiveType = { kind: "unknown", name: "tuple", @@ -160,11 +164,13 @@ export function fromSdkType( retVar = fromSdkEndpointType(); break; case "credential": - sdkContext.logger.reportDiagnostic({ - code: "unsupported-sdk-type", - format: { sdkType: "credential" }, - target: sdkType.__raw ?? NoTarget, - }); + diagnostics.add( + createDiagnostic({ + code: "unsupported-sdk-type", + format: { sdkType: "credential" }, + target: sdkType.__raw ?? NoTarget, + }), + ); const credentialType: InputPrimitiveType = { kind: "unknown", name: "credential", @@ -175,19 +181,20 @@ export function fromSdkType( retVar = credentialType; break; default: - retVar = fromSdkBuiltInType(sdkContext, sdkType); + retVar = diagnostics.pipe(fromSdkBuiltInType(sdkContext, sdkType)); break; } sdkContext.__typeCache.updateSdkTypeReferences(sdkType, retVar); // we have to cast to any because TypeScript's type narrowing does not automatically infer the return type for conditional types - return retVar as any; + return diagnostics.wrap(retVar as any); } function fromSdkModelType( sdkContext: CSharpEmitterContext, modelType: SdkModelType, -): InputModelType { +): [InputModelType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); // get all unique decorators for the model type from the namespace level and the model level let decorators: DecoratorInfo[] = modelType.decorators; const namespace = sdkContext.__typeCache.namespaces.get(modelType.namespace); @@ -213,12 +220,12 @@ function fromSdkModelType( sdkContext.__typeCache.updateSdkTypeReferences(modelType, inputModelType); inputModelType.additionalProperties = modelType.additionalProperties - ? fromSdkType(sdkContext, modelType.additionalProperties) + ? diagnostics.pipe(fromSdkType(sdkContext, modelType.additionalProperties)) : undefined; const properties: InputModelProperty[] = []; for (const property of modelType.properties) { - const ourProperty = fromSdkModelProperty(sdkContext, property, modelType); + const ourProperty = diagnostics.pipe(fromSdkModelProperty(sdkContext, property, modelType)); if (ourProperty) { properties.push(ourProperty); @@ -226,11 +233,11 @@ function fromSdkModelType( } inputModelType.discriminatorProperty = modelType.discriminatorProperty - ? fromSdkModelProperty(sdkContext, modelType.discriminatorProperty, modelType) + ? diagnostics.pipe(fromSdkModelProperty(sdkContext, modelType.discriminatorProperty, modelType)) : undefined; inputModelType.baseModel = modelType.baseModel - ? fromSdkType(sdkContext, modelType.baseModel) + ? diagnostics.pipe(fromSdkType(sdkContext, modelType.baseModel)) : undefined; inputModelType.properties = properties; @@ -239,25 +246,26 @@ function fromSdkModelType( const discriminatedSubtypes: Record = {}; for (const key in modelType.discriminatedSubtypes) { const subtype = modelType.discriminatedSubtypes[key]; - discriminatedSubtypes[key] = fromSdkType(sdkContext, subtype); + discriminatedSubtypes[key] = diagnostics.pipe(fromSdkType(sdkContext, subtype)); } inputModelType.discriminatedSubtypes = discriminatedSubtypes; } - return inputModelType; + return diagnostics.wrap(inputModelType); } function fromSdkModelProperty( sdkContext: CSharpEmitterContext, sdkProperty: SdkModelPropertyType, sdkModel: SdkModelType, -): InputModelProperty | undefined { +): [InputModelProperty | undefined, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); // TODO -- this returns undefined because some properties we do not support yet. let property = sdkContext.__typeCache.properties.get(sdkProperty) as | InputModelProperty | undefined; if (property) { - return property; + return diagnostics.wrap(property); } const serializedName = @@ -270,7 +278,9 @@ function fromSdkModelProperty( serializedName: serializedName, summary: sdkProperty.summary, doc: sdkProperty.doc, - type: fromSdkType(sdkContext, sdkProperty.type, sdkProperty, sdkModel.namespace), + type: diagnostics.pipe( + fromSdkType(sdkContext, sdkProperty.type, sdkProperty, sdkModel.namespace), + ), optional: sdkProperty.optional, readOnly: isReadOnly(sdkProperty), discriminator: sdkProperty.discriminator, @@ -287,18 +297,25 @@ function fromSdkModelProperty( sdkContext.__typeCache.updateSdkPropertyReferences(sdkProperty, property); } - return property; + return diagnostics.wrap(property); } -function fromSdkEnumType(sdkContext: CSharpEmitterContext, enumType: SdkEnumType): InputEnumType { - return createEnumType(sdkContext, enumType, enumType.namespace); +function fromSdkEnumType( + sdkContext: CSharpEmitterContext, + enumType: SdkEnumType, +): [InputEnumType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap( + diagnostics.pipe(createEnumType(sdkContext, enumType, enumType.namespace)), + ); } function createEnumType( sdkContext: CSharpEmitterContext, sdkType: SdkConstantType | SdkEnumType, namespace: string, -): InputEnumType { +): [InputEnumType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const values: InputEnumValueType[] = []; const inputEnumType: InputEnumType = { @@ -307,8 +324,8 @@ function createEnumType( crossLanguageDefinitionId: sdkType.kind === "enum" ? sdkType.crossLanguageDefinitionId : "", valueType: sdkType.kind === "enum" - ? fromSdkType(sdkContext, sdkType.valueType) - : fromSdkBuiltInType(sdkContext, sdkType.valueType), + ? (diagnostics.pipe(fromSdkType(sdkContext, sdkType.valueType)) as InputPrimitiveType) + : diagnostics.pipe(fromSdkBuiltInType(sdkContext, sdkType.valueType)), values: values, // constantType.access, TODO - constant type now does not have access. TCGC will add it later access: @@ -329,112 +346,130 @@ function createEnumType( if (sdkType.kind === "enum") { for (const v of sdkType.values) { - values.push(createEnumValueType(sdkContext, v, inputEnumType)); + values.push(diagnostics.pipe(createEnumValueType(sdkContext, v, inputEnumType))); } } else { - values.push(createEnumValueType(sdkContext, sdkType, inputEnumType)); + values.push(diagnostics.pipe(createEnumValueType(sdkContext, sdkType, inputEnumType))); } - return inputEnumType; + return diagnostics.wrap(inputEnumType); } function fromSdkDateTimeType( sdkContext: CSharpEmitterContext, dateTimeType: SdkDateTimeType, -): InputDateTimeType { - return { +): [InputDateTimeType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap({ kind: dateTimeType.kind, name: dateTimeType.name, encode: dateTimeType.encode, - wireType: fromSdkType(sdkContext, dateTimeType.wireType), + wireType: diagnostics.pipe(fromSdkType(sdkContext, dateTimeType.wireType)), crossLanguageDefinitionId: dateTimeType.crossLanguageDefinitionId, - baseType: dateTimeType.baseType ? fromSdkType(sdkContext, dateTimeType.baseType) : undefined, + baseType: dateTimeType.baseType + ? diagnostics.pipe(fromSdkType(sdkContext, dateTimeType.baseType)) + : undefined, decorators: dateTimeType.decorators, external: fromSdkExternalTypeInfo(dateTimeType), - }; + }); } function fromSdkDurationType( sdkContext: CSharpEmitterContext, durationType: SdkDurationType, -): InputDurationType { - return { +): [InputDurationType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap({ kind: durationType.kind, name: durationType.name, encode: durationType.encode, - wireType: fromSdkType(sdkContext, durationType.wireType), + wireType: diagnostics.pipe(fromSdkType(sdkContext, durationType.wireType)), crossLanguageDefinitionId: durationType.crossLanguageDefinitionId, - baseType: durationType.baseType ? fromSdkType(sdkContext, durationType.baseType) : undefined, + baseType: durationType.baseType + ? diagnostics.pipe(fromSdkType(sdkContext, durationType.baseType)) + : undefined, decorators: durationType.decorators, external: fromSdkExternalTypeInfo(durationType), - }; + }); } function fromSdkBuiltInType( sdkContext: CSharpEmitterContext, builtInType: SdkBuiltInType, -): InputPrimitiveType { - return { +): [InputPrimitiveType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap({ kind: builtInType.kind, name: builtInType.name, encode: builtInType.encode !== builtInType.kind ? builtInType.encode : undefined, crossLanguageDefinitionId: builtInType.crossLanguageDefinitionId, - baseType: builtInType.baseType ? fromSdkType(sdkContext, builtInType.baseType) : undefined, + baseType: builtInType.baseType + ? diagnostics.pipe(fromSdkType(sdkContext, builtInType.baseType)) + : undefined, decorators: builtInType.decorators, external: fromSdkExternalTypeInfo(builtInType), - }; + }); } -function fromUnionType(sdkContext: CSharpEmitterContext, union: SdkUnionType): InputUnionType { +function fromUnionType( + sdkContext: CSharpEmitterContext, + union: SdkUnionType, +): [InputUnionType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const variantTypes: InputType[] = []; for (const value of union.variantTypes) { - const variantType = fromSdkType(sdkContext, value); + const variantType = diagnostics.pipe(fromSdkType(sdkContext, value)); variantTypes.push(variantType); } - return { + return diagnostics.wrap({ kind: "union", name: union.name, variantTypes: variantTypes, namespace: union.namespace, decorators: union.decorators, external: fromSdkExternalTypeInfo(union), - }; + }); } function fromSdkConstantType( sdkContext: CSharpEmitterContext, constantType: SdkConstantType, -): InputLiteralType { +): [InputLiteralType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); const literalType = { kind: constantType.kind, name: constantType.name, namespace: "", // constantType.namespace, TODO - constant type now does not have namespace. TCGC will add it later access: undefined, // constantType.access, TODO - constant type now does not have access. TCGC will add it later usage: UsageFlags.None, // constantType.usage, TODO - constant type now does not have usage. TCGC will add it later - valueType: fromSdkType(sdkContext, constantType.valueType), + valueType: diagnostics.pipe(fromSdkType(sdkContext, constantType.valueType)), value: constantType.value, decorators: constantType.decorators, }; sdkContext.__typeCache.updateConstantCache(constantType, literalType); - return literalType; + return diagnostics.wrap(literalType); } function fromSdkEnumValueType( sdkContext: CSharpEmitterContext, enumValueType: SdkEnumValueType, -): InputEnumValueType { - return createEnumValueType(sdkContext, enumValueType, enumValueType.enumType); +): [InputEnumValueType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap( + diagnostics.pipe(createEnumValueType(sdkContext, enumValueType, enumValueType.enumType)), + ); } function createEnumValueType( sdkContext: CSharpEmitterContext, sdkType: SdkEnumValueType | SdkConstantType, enumType: InputEnumType, -): InputEnumValueType { - return { +): [InputEnumValueType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap({ kind: "enumvalue", name: sdkType.kind === "constant" @@ -444,39 +479,43 @@ function createEnumValueType( : sdkType.name, value: typeof sdkType.value === "boolean" ? (sdkType.value ? 1 : 0) : sdkType.value, valueType: - sdkType.kind === "constant" ? sdkType.valueType : fromSdkType(sdkContext, sdkType.valueType), + sdkType.kind === "constant" + ? sdkType.valueType + : diagnostics.pipe(fromSdkType(sdkContext, sdkType.valueType)), enumType: enumType, summary: sdkType.summary, doc: sdkType.doc, decorators: sdkType.decorators, - }; + }); } function fromSdkDictionaryType( sdkContext: CSharpEmitterContext, dictionaryType: SdkDictionaryType, -): InputDictionaryType { - return { +): [InputDictionaryType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap({ kind: "dict", - keyType: fromSdkType(sdkContext, dictionaryType.keyType), - valueType: fromSdkType(sdkContext, dictionaryType.valueType), + keyType: diagnostics.pipe(fromSdkType(sdkContext, dictionaryType.keyType)), + valueType: diagnostics.pipe(fromSdkType(sdkContext, dictionaryType.valueType)), decorators: dictionaryType.decorators, external: fromSdkExternalTypeInfo(dictionaryType), - }; + }); } function fromSdkArrayType( sdkContext: CSharpEmitterContext, arrayType: SdkArrayType, -): InputArrayType { - return { +): [InputArrayType, readonly Diagnostic[]] { + const diagnostics = createDiagnosticCollector(); + return diagnostics.wrap({ kind: "array", name: arrayType.name, - valueType: fromSdkType(sdkContext, arrayType.valueType), + valueType: diagnostics.pipe(fromSdkType(sdkContext, arrayType.valueType)), crossLanguageDefinitionId: arrayType.crossLanguageDefinitionId, decorators: arrayType.decorators, external: fromSdkExternalTypeInfo(arrayType), - }; + }); } function fromSdkEndpointType(): InputPrimitiveType { diff --git a/packages/http-client-csharp/emitter/src/lib/typespec-server.ts b/packages/http-client-csharp/emitter/src/lib/typespec-server.ts index 77cfe50b341..f31b9830dda 100644 --- a/packages/http-client-csharp/emitter/src/lib/typespec-server.ts +++ b/packages/http-client-csharp/emitter/src/lib/typespec-server.ts @@ -2,7 +2,13 @@ // Licensed under the MIT License. See License.txt in the project root for license information. import { getClientType } from "@azure-tools/typespec-client-generator-core"; -import { getDoc, getSummary, Value } from "@typespec/compiler"; +import { + createDiagnosticCollector, + Diagnostic, + getDoc, + getSummary, + Value, +} from "@typespec/compiler"; import { HttpServer } from "@typespec/http"; import { getExtensions } from "@typespec/openapi"; import { CSharpEmitterContext } from "../sdk-context.js"; @@ -20,8 +26,11 @@ export interface TypeSpecServer { export function resolveServers( sdkContext: CSharpEmitterContext, servers: HttpServer[], -): TypeSpecServer[] { - return servers.map((server) => { +): [TypeSpecServer[], readonly Diagnostic[]] { + // Create a diagnostics collector for internal use + const diagnostics = createDiagnosticCollector(); + + const result = servers.map((server) => { const parameters: InputEndpointParameter[] = []; let url: string = server.url; const endpoint: string = url.replace("http://", "").replace("https://", "").split("/")[0]; @@ -35,7 +44,7 @@ export function resolveServers( name: "url", crossLanguageDefinitionId: "TypeSpec.url", } - : fromSdkType(sdkContext, getClientType(sdkContext, prop)); + : diagnostics.pipe(fromSdkType(sdkContext, getClientType(sdkContext, prop))); if (value) { defaultValue = { @@ -101,6 +110,8 @@ export function resolveServers( parameters, }; }); + + return diagnostics.wrap(result); } function getDefaultValue(value: Value): any { diff --git a/packages/http-client-csharp/emitter/src/options.ts b/packages/http-client-csharp/emitter/src/options.ts index 42196440cac..9ae08884a9e 100644 --- a/packages/http-client-csharp/emitter/src/options.ts +++ b/packages/http-client-csharp/emitter/src/options.ts @@ -1,10 +1,8 @@ import { CreateSdkContextOptions } from "@azure-tools/typespec-client-generator-core"; import { EmitContext, JSONSchemaType } from "@typespec/compiler"; import { _defaultGeneratorName } from "./constants.js"; -import { CSharpEmitterContext } from "./index.js"; import { DYNAMIC_MODEL_DECORATOR_PATTERN } from "./lib/decorators.js"; import { LoggerLevel } from "./lib/logger-level.js"; -import { CodeModel } from "./type/code-model.js"; /** * The emitter options for the CSharp emitter. @@ -20,7 +18,6 @@ export interface CSharpEmitterOptions { "disable-xml-docs"?: boolean; "generator-name"?: string; "emitter-extension-path"?: string; - "update-code-model"?: (model: CodeModel, context: CSharpEmitterContext) => CodeModel; "sdk-context-options"?: CreateSdkContextOptions; "generate-protocol-methods"?: boolean; "generate-convenience-methods"?: boolean; @@ -116,12 +113,6 @@ export const CSharpEmitterOptionsSchema: JSONSchemaType = description: "Allows emitter authors to specify the path to a custom emitter package, allowing you to extend the emitter behavior. This should be set to `import.meta.url` if you are using a custom emitter.", }, - "update-code-model": { - type: "object", - nullable: true, - description: - "Allows emitter authors to specify a custom function to modify the generated code model before emitting. This is useful for modifying the code model before it is passed to the generator.", - }, license: { type: "object", additionalProperties: false, @@ -160,7 +151,6 @@ export const defaultOptions = { debug: undefined, logLevel: LoggerLevel.INFO, "generator-name": _defaultGeneratorName, - "update-code-model": (model: CodeModel, context: CSharpEmitterContext) => model, "sdk-context-options": { additionalDecorators: [DYNAMIC_MODEL_DECORATOR_PATTERN], }, diff --git a/packages/http-client-csharp/emitter/test/Unit/auth.test.ts b/packages/http-client-csharp/emitter/test/Unit/auth.test.ts index ee94b078d41..ceac4514668 100644 --- a/packages/http-client-csharp/emitter/test/Unit/auth.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/auth.test.ts @@ -30,8 +30,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); - const diagnostics = context.program.diagnostics; + const [root, diagnostics] = createModel(sdkContext); const noAuthDiagnostics = diagnostics.filter( (d) => @@ -72,8 +71,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); - const diagnostics = context.program.diagnostics; + const [root, diagnostics] = createModel(sdkContext); const noAuthDiagnostics = diagnostics.filter( (d) => @@ -114,8 +112,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); - const diagnostics = context.program.diagnostics; + const [root, diagnostics] = createModel(sdkContext); const noAuthDiagnostics = diagnostics.filter( (d) => @@ -154,8 +151,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); - const diagnostics = context.program.diagnostics; + const [root, diagnostics] = createModel(sdkContext); const noAuthDiagnostic = diagnostics.find( (d) => d.code === "@typespec/http-client-csharp/unsupported-auth", @@ -182,8 +178,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); - const diagnostics = context.program.diagnostics; + const [root, diagnostics] = createModel(sdkContext); const noAuthDiagnostics = diagnostics.filter( (d) => d.code === "@typespec/http-client-csharp/unsupported-auth", @@ -216,8 +211,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); - const diagnostics = context.program.diagnostics; + const [root, diagnostics] = createModel(sdkContext); const noAuthDiagnostics = diagnostics.filter( (d) => @@ -249,8 +243,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); - const diagnostics = context.program.diagnostics; + const [root, diagnostics] = createModel(sdkContext); // Should have no auth-related diagnostics const authDiagnostics = diagnostics.filter( @@ -290,7 +283,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -322,7 +315,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -352,7 +345,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -389,7 +382,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 2); @@ -426,7 +419,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -454,7 +447,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); @@ -482,7 +475,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // Should have both OAuth2 and API key auth ok(root.auth?.oAuth2); @@ -513,7 +506,7 @@ describe("Test auth", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.auth?.oAuth2); strictEqual(root.auth.oAuth2.flows.length, 1); diff --git a/packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts index 49d80ee6033..8aa7561cbf7 100644 --- a/packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/client-converter.test.ts @@ -28,7 +28,7 @@ describe("isMultiServiceClient", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok(client, "Client should exist"); @@ -81,7 +81,7 @@ describe("isMultiServiceClient", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.name, "Service.MultiService", "Root namespace should be Service.MultiService"); const client = root.clients[0]; @@ -147,7 +147,7 @@ describe("isMultiServiceClient", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.name, "Service.MultiService", "Root namespace should be Service.MultiService"); const client = root.clients[0]; @@ -219,7 +219,7 @@ describe("isMultiServiceClient", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.name, "Service.MultiService", "Root namespace should be Service.MultiService"); const clients = root.clients; @@ -293,7 +293,7 @@ describe("client name suffix", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok(client, "Client should exist"); @@ -338,7 +338,7 @@ describe("client name suffix", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok(client, "Client should exist"); @@ -383,7 +383,7 @@ describe("client name suffix", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok(client, "Client should exist"); @@ -435,7 +435,7 @@ describe("client name suffix", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok(client, "Client should exist"); diff --git a/packages/http-client-csharp/emitter/test/Unit/client-initialization.test.ts b/packages/http-client-csharp/emitter/test/Unit/client-initialization.test.ts index 0af11ee1ccc..e03f8fcffb5 100644 --- a/packages/http-client-csharp/emitter/test/Unit/client-initialization.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/client-initialization.test.ts @@ -35,7 +35,7 @@ describe("ClientInitialization", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok(client, "Client should exist"); @@ -60,7 +60,7 @@ describe("ClientInitialization", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const client = root.clients[0]; // initializedBy field should exist on the client (may be undefined or have a value) @@ -85,7 +85,7 @@ describe("ClientInitialization", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok(client.parameters, "Client should have parameters"); @@ -114,7 +114,7 @@ describe("ClientInitialization", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok("initializedBy" in client, "Parent client should have initializedBy field"); @@ -149,7 +149,7 @@ describe("ClientInitialization", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok(client, "Client should exist"); @@ -186,7 +186,7 @@ describe("ClientInitialization", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const client = root.clients[0]; ok(client, "Client should exist"); diff --git a/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts b/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts index 054bdde1853..908ed25ab19 100644 --- a/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/client-model-builder.test.ts @@ -58,7 +58,7 @@ describe("fixNamingConflicts", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // Find the real enum const realEnum = root.enums.find( @@ -142,7 +142,7 @@ describe("fixNamingConflicts", () => { namespace: targetNamespace, } as any); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // Get all ErrorResponse models - fixNamingConflicts should have resolved the conflicts const errorModels = root.models.filter( @@ -194,7 +194,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // The root apiVersions should include the version from the Versions enum // which is defined in the default namespace with version "2023-01-01-preview" @@ -228,7 +228,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // The root apiVersions should include all versions from the TestVersions enum strictEqual(root.apiVersions.length, 3, "Root apiVersions should have 3 versions"); @@ -247,7 +247,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // Single service client should have apiVersions from the @versioned decorator ok(root.apiVersions.length > 0, "Root apiVersions should not be empty for single service"); @@ -299,7 +299,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.apiVersions.length === 0, "Root apiVersions should be empty for multiservice"); @@ -358,7 +358,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root.apiVersions.length === 0, "Root apiVersions should be empty for multiservice"); }); @@ -415,7 +415,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok( root.apiVersions.length === 0, @@ -464,7 +464,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // Verify all three versions are present in the root apiVersions strictEqual(root.apiVersions.length, 3, "Should have 3 apiVersions"); @@ -496,7 +496,7 @@ describe("parseApiVersions", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // Verify versions are in the order TCGC provides them strictEqual(root.apiVersions.length, 3, "Should have 3 apiVersions"); @@ -505,3 +505,66 @@ describe("parseApiVersions", () => { strictEqual(root.apiVersions[2], "2025-01-01", "Third version should be 2025-01-01"); }); }); + +describe("createModel diagnostic collection", () => { + let runner: TestHost; + + beforeEach(async () => { + runner = await createEmitterTestHost(); + }); + + it("should return a tuple with CodeModel and diagnostics array", async () => { + const program = await typeSpecCompile( + ` + model TestModel { + name: string; + } + + @route("/test") + op test(): TestModel; + `, + runner, + ); + const context = createEmitterContext(program); + const sdkContext = await createCSharpSdkContext(context); + const result = createModel(sdkContext); + + // Verify the result is a tuple + ok(Array.isArray(result), "Result should be an array (tuple)"); + strictEqual(result.length, 2, "Result should have exactly 2 elements"); + + const [codeModel, diagnostics] = result; + + // Verify the code model + ok(codeModel, "CodeModel should be defined"); + strictEqual( + codeModel.name, + "Azure.Csharp.Testing", + "CodeModel name should be Azure.Csharp.Testing", + ); + + // Verify diagnostics is an array + ok(Array.isArray(diagnostics), "Diagnostics should be an array"); + }); + + it("should collect diagnostics when using diagnostic collection mode", async () => { + const program = await typeSpecCompile( + ` + model TestModel { + name: string; + } + + @route("/test") + op test(): TestModel; + `, + runner, + ); + const context = createEmitterContext(program); + const sdkContext = await createCSharpSdkContext(context); + const [, diagnostics] = createModel(sdkContext); + + // Verify diagnostics array exists (may be empty or contain diagnostics) + ok(diagnostics !== undefined, "Diagnostics should not be undefined"); + ok(Array.isArray(diagnostics), "Diagnostics should be an array"); + }); +}); diff --git a/packages/http-client-csharp/emitter/test/Unit/constant-type.test.ts b/packages/http-client-csharp/emitter/test/Unit/constant-type.test.ts index 595e936d2f5..3730f08dbdd 100644 --- a/packages/http-client-csharp/emitter/test/Unit/constant-type.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/constant-type.test.ts @@ -33,7 +33,7 @@ describe("Name for constant type", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel); const propertyType = testModel.properties[0].type; @@ -64,7 +64,7 @@ describe("Name for constant type", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const testModel1 = root.models.find((m) => m.name === "TestModel1"); ok(testModel1); const testModel2 = root.models.find((m) => m.name === "TestModel2"); @@ -103,7 +103,7 @@ describe("Constant enum conversion", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel); const propertyType = testModel.properties[0].type; @@ -134,7 +134,7 @@ describe("Constant enum conversion", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel); const propertyType = testModel.properties[0].type; diff --git a/packages/http-client-csharp/emitter/test/Unit/decorator-list.test.ts b/packages/http-client-csharp/emitter/test/Unit/decorator-list.test.ts index 37aaa5ad3fc..53df8aa6e86 100644 --- a/packages/http-client-csharp/emitter/test/Unit/decorator-list.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/decorator-list.test.ts @@ -34,7 +34,7 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const clients = root.clients; strictEqual(clients.length, 1); ok(clients[0].children); @@ -66,7 +66,7 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const methods = root.clients[0].methods; strictEqual(methods.length, 1); const operation = methods[0].operation; @@ -97,7 +97,7 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; strictEqual(models.length, 1); deepStrictEqual(models[0].decorators, [ @@ -127,7 +127,7 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; strictEqual(models.length, 1); deepStrictEqual(models[0].properties[0].decorators, [ @@ -153,7 +153,7 @@ describe("Test emitting decorator list", () => { const sdkContext = await createCSharpSdkContext(context, { additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@clientName"], }); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const methods = root.clients[0].methods; strictEqual(methods.length, 1); const operation = methods[0].operation; diff --git a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts index 6755c79fde1..d3078c9dc59 100644 --- a/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/emitter.test.ts @@ -1,6 +1,6 @@ vi.resetModules(); -import { EmitContext, Program } from "@typespec/compiler"; +import { Diagnostic, EmitContext, Program } from "@typespec/compiler"; import { TestHost } from "@typespec/compiler/testing"; import { strictEqual } from "assert"; import { statSync } from "fs"; @@ -19,6 +19,10 @@ import { describe("$onEmit tests", () => { let program: Program; let $onEmit: (arg0: EmitContext) => any; + let emitCodeModel: ( + context: EmitContext, + updateCodeModel?: (model: CodeModel, context: any) => CodeModel, + ) => any; beforeEach(async () => { // Reset the dynamically imported module to ensure a clean state vi.resetModules(); @@ -58,7 +62,7 @@ describe("$onEmit tests", () => { })); vi.mock("../../src/lib/client-model-builder.js", () => ({ - createModel: vi.fn().mockReturnValue({ Name: "TestNamespace" }), + createModel: vi.fn().mockReturnValue([{ name: "TestNamespace" }, []]), })); program = { @@ -79,18 +83,19 @@ describe("$onEmit tests", () => { reportDiagnostics: vi.fn(), } as unknown as Program; - // dynamically import the module to get the $onEmit function + // dynamically import the module to get the $onEmit and emitCodeModel functions // we avoid importing it at the top to allow mocking of dependencies - $onEmit = (await import("../../src/emitter.js")).$onEmit; + const emitterModule = await import("../../src/emitter.js"); + $onEmit = emitterModule.$onEmit; + emitCodeModel = emitterModule.emitCodeModel; }); - it("should apply the update-code-model callback just once", async () => { + it("should apply the updateCodeModel callback", async () => { const context: EmitContext = createEmitterContext(program); const updateCallback = vi.fn().mockImplementation((model: CodeModel) => { return model; }); - context.options["update-code-model"] = updateCallback; - await $onEmit(context); + await emitCodeModel(context, updateCallback); expect(updateCallback).toHaveBeenCalledTimes(1); }); @@ -179,11 +184,64 @@ describe("$onEmit tests", () => { }); }); +describe("emitCodeModel tests", () => { + let runner: TestHost; + let program: Program; + + beforeEach(async () => { + vi.restoreAllMocks(); + runner = await createEmitterTestHost(); + }); + + it("should return diagnostics array from emitCodeModel", async () => { + program = await typeSpecCompile( + ` + model TestModel { + name: string; + } + + @route("/test") + op test(): TestModel; + `, + runner, + ); + const context = createEmitterContext(program); + const { emitCodeModel } = await import("../../src/emitter.js"); + const [, diagnostics] = await emitCodeModel(context); + + // Verify that diagnostics is an array + expect(Array.isArray(diagnostics)).toBe(true); + // Diagnostics array should be defined (may be empty or have diagnostics) + expect(diagnostics).toBeDefined(); + }); + + it("should collect diagnostics from createModel in emitCodeModel", async () => { + program = await typeSpecCompile( + ` + model TestModel { + name: string; + } + + @route("/test") + op test(): TestModel; + `, + runner, + ); + const context = createEmitterContext(program); + const { emitCodeModel } = await import("../../src/emitter.js"); + const [, diagnostics] = await emitCodeModel(context); + + // The function should return diagnostics even if empty + expect(diagnostics).toBeDefined(); + expect(Array.isArray(diagnostics)).toBe(true); + }); +}); + describe("Test _validateDotNetSdk", () => { let runner: TestHost; let program: Program; const minVersion = 8; - let _validateDotNetSdk: (arg0: any, arg1: number) => Promise; + let _validateDotNetSdk: (arg0: any, arg1: number) => Promise<[boolean, readonly Diagnostic[]]>; beforeEach(async () => { vi.resetModules(); @@ -217,7 +275,9 @@ describe("Test _validateDotNetSdk", () => { (execAsync as Mock).mockRejectedValueOnce(error); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const result = await _validateDotNetSdk(sdkContext, minVersion); + const [result, diagnostics] = await _validateDotNetSdk(sdkContext, minVersion); + // Report collected diagnostics to program + program.reportDiagnostics(diagnostics); expect(result).toBe(false); strictEqual(program.diagnostics.length, 1); strictEqual( @@ -241,10 +301,10 @@ describe("Test _validateDotNetSdk", () => { }); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const result = await _validateDotNetSdk(sdkContext, minVersion); + const [result, diagnostics] = await _validateDotNetSdk(sdkContext, minVersion); expect(result).toBe(true); /* no diagnostics */ - strictEqual(program.diagnostics.length, 0); + strictEqual(diagnostics.length, 0); }); it("should return true for installed SDK version whose major greaters than min supported version", async () => { @@ -258,10 +318,10 @@ describe("Test _validateDotNetSdk", () => { }); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const result = await _validateDotNetSdk(sdkContext, minVersion); + const [result, diagnostics] = await _validateDotNetSdk(sdkContext, minVersion); expect(result).toBe(true); /* no diagnostics */ - strictEqual(program.diagnostics.length, 0); + strictEqual(diagnostics.length, 0); }); it("should return false and report diagnostic for invalid .NET SDK version", async () => { @@ -275,7 +335,9 @@ describe("Test _validateDotNetSdk", () => { }); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const result = await _validateDotNetSdk(sdkContext, minVersion); + const [result, diagnostics] = await _validateDotNetSdk(sdkContext, minVersion); + // Report collected diagnostics to program + program.reportDiagnostics(diagnostics); expect(result).toBe(false); strictEqual(program.diagnostics.length, 1); strictEqual( diff --git a/packages/http-client-csharp/emitter/test/Unit/encode.test.ts b/packages/http-client-csharp/emitter/test/Unit/encode.test.ts index 559d2d54dbb..6344bd3f4ca 100644 --- a/packages/http-client-csharp/emitter/test/Unit/encode.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/encode.test.ts @@ -32,7 +32,7 @@ describe("Test encode duration", () => { // validate method parameter const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const methodParamArray = root.clients[0].methods[0].parameters; strictEqual(1, methodParamArray.length); let type = methodParamArray[0].type; @@ -74,7 +74,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // validate method parameter const methodParamArray = root.clients[0].methods[0].parameters; strictEqual(1, methodParamArray.length); @@ -117,7 +117,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); // validate method parameter const methodParamArray = root.clients[0].methods[0].parameters; strictEqual(1, methodParamArray.length); @@ -161,7 +161,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const codeModel = createModel(sdkContext); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const durationModel = models.find((m) => m.name === "ISO8601DurationProperty"); ok(durationModel); @@ -190,7 +190,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const codeModel = createModel(sdkContext); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const durationModel = models.find((m) => m.name === "Int32SecondsDurationProperty"); ok(durationModel); @@ -219,7 +219,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const codeModel = createModel(sdkContext); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const durationModel = models.find((m) => m.name === "FloatSecondsDurationProperty"); ok(durationModel); diff --git a/packages/http-client-csharp/emitter/test/Unit/input-parameter.test.ts b/packages/http-client-csharp/emitter/test/Unit/input-parameter.test.ts index faabbefec9a..7e4db98cba1 100644 --- a/packages/http-client-csharp/emitter/test/Unit/input-parameter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/input-parameter.test.ts @@ -42,7 +42,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -72,7 +72,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -102,7 +102,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -136,7 +136,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -166,7 +166,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -196,7 +196,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -228,7 +228,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -258,7 +258,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -288,7 +288,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -320,7 +320,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -350,7 +350,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -380,7 +380,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -414,7 +414,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -445,7 +445,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -476,7 +476,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -509,7 +509,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -540,7 +540,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -571,7 +571,7 @@ describe("Test Parameter Explode", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "param", ); @@ -609,8 +609,7 @@ describe("Test Cookie Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const diagnostics = context.program.diagnostics; - createModel(sdkContext); + const [, diagnostics] = createModel(sdkContext); const unsupportedCookie = diagnostics.find( (d) => d.code === "@typespec/http-client-csharp/unsupported-cookie-parameter", @@ -653,8 +652,7 @@ describe("Endpoint parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const diagnostics = context.program.diagnostics; - createModel(sdkContext); + const [, diagnostics] = createModel(sdkContext); const unsupportedCookie = diagnostics.find( (d) => d.code === "@typespec/http-client-csharp/unsupported-endpoint-url", @@ -686,7 +684,7 @@ describe("Endpoint parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const codeModel = createModel(sdkContext); + const [codeModel] = createModel(sdkContext); const client = codeModel.clients[0]; ok(client); ok(client.parameters); @@ -722,7 +720,7 @@ describe("Endpoint parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const codeModel = createModel(sdkContext); + const [codeModel] = createModel(sdkContext); const client = codeModel.clients[0]; ok(client); ok(client.parameters); @@ -759,7 +757,7 @@ describe("Test Spread Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root); // validate service method @@ -802,7 +800,7 @@ describe("Test Spread Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root); // validate service method @@ -855,8 +853,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); - + const [root] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const queryParam = operation.parameters.find((p) => p.name === "queryParam"); @@ -881,8 +878,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); - + const [root] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const pathParam = operation.parameters.find((p) => p.name === "pathParam"); @@ -910,8 +906,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); - + const [root] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const headerParam = operation.parameters.find((p) => p.name === "headerParam"); @@ -940,8 +935,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); - + const [root] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const contentTypeParam = operation.parameters.find((p) => p.name === "contentType"); @@ -970,7 +964,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const metadataParam = operation.parameters.find((p) => p.name === "metadata"); @@ -999,7 +993,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const metadataParam = operation.parameters.find((p) => p.name === "metadata"); @@ -1010,8 +1004,7 @@ describe("Test Operation Parameters", () => { const typedParam = metadataParam as InputHeaderParameter; strictEqual(typedParam.collectionHeaderPrefix, undefined); - const diagnostics = context.program.diagnostics; - const warningDiagnostic = diagnostics.find( + const warningDiagnostic = modelDiagnostics.find( (d) => d.code === "@typespec/http-client-csharp/general-warning" && d.message?.includes("collectionHeaderPrefix"), @@ -1036,7 +1029,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const nameParam = operation.parameters.find((p) => p.name === "name"); @@ -1065,8 +1058,7 @@ describe("Test Operation Parameters", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); - + const [root] = createModel(sdkContext); const operation = root.clients[0].methods[0].operation; const bodyParam = operation.parameters.find((p) => p.name === "bodyParam"); diff --git a/packages/http-client-csharp/emitter/test/Unit/method-parameter-segments.test.ts b/packages/http-client-csharp/emitter/test/Unit/method-parameter-segments.test.ts index de2920346e3..0940caf23e7 100644 --- a/packages/http-client-csharp/emitter/test/Unit/method-parameter-segments.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/method-parameter-segments.test.ts @@ -38,7 +38,7 @@ describe("MethodParameterSegments", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root, "Output model should be generated"); const clients = root.clients; ok(clients && clients.length > 0, "Should have at least one client"); @@ -100,7 +100,7 @@ describe("MethodParameterSegments", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root, "Output model should be generated"); const clients = root.clients; ok(clients && clients.length > 0, "Should have at least one client"); @@ -153,7 +153,7 @@ describe("MethodParameterSegments", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root, "Output model should be generated"); const clients = root.clients; ok(clients && clients.length > 0, "Should have at least one client"); @@ -209,7 +209,7 @@ describe("MethodParameterSegments", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root, "Output model should be generated"); const clients = root.clients; ok(clients && clients.length > 0, "Should have at least one client"); @@ -247,7 +247,7 @@ describe("MethodParameterSegments", () => { const program = await typeSpecCompile(tsp, runner, { IsTCGCNeeded: true }); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root, "Output model should be generated"); const clients = root.clients; @@ -293,7 +293,7 @@ describe("MethodParameterSegments", () => { const program = await typeSpecCompile(tsp, runner, { IsTCGCNeeded: true }); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root, "Output model should be generated"); const clients = root.clients; diff --git a/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts b/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts index 51b9415430b..025e8f5ef91 100644 --- a/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts @@ -51,7 +51,7 @@ op test(@body input: Pet): Pet; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const petModel = models.find((m) => m.name === "Pet"); const catModel = models.find((m) => m.name === "Cat"); @@ -135,7 +135,7 @@ op test(@body input: Pet): Pet; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const codeModel = createModel(sdkContext); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const pet = models.find((m) => m.name === "Pet"); assert(pet !== undefined); @@ -229,7 +229,7 @@ op test(@body input: Pet): Pet; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const codeModel = createModel(sdkContext); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const pet = models.find((m) => m.name === "Pet"); assert(pet !== undefined); @@ -350,7 +350,7 @@ op op5(@body body: ExtendsFooArray): ExtendsFooArray; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const extendsUnknownModel = models.find((m) => m.name === "ExtendsUnknown"); const extendsStringModel = models.find((m) => m.name === "ExtendsString"); @@ -442,7 +442,7 @@ op op5(@body body: IsFooArray): IsFooArray; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const isUnknownModel = models.find((m) => m.name === "IsUnknown"); const isStringModel = models.find((m) => m.name === "IsString"); @@ -493,7 +493,7 @@ op op1(): void; ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const isEmptyModel = models.find((m) => m.name === "Empty"); ok(isEmptyModel); @@ -522,7 +522,7 @@ model Foo { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const model = models.find((m) => m.name === "Foo"); ok(model); @@ -560,7 +560,7 @@ describe("Anonymous models should be included in library", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); ok(root); // validate service method @@ -602,7 +602,7 @@ op testOperation(@bodyRoot body: HeaderModel): void; const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const isEmptyModel = models.find((m) => m.name === "HeaderModel"); ok(isEmptyModel); @@ -647,7 +647,7 @@ op testOperation(@bodyRoot body: HeaderModel): void; const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const isEmptyModel = models.find((m) => m.name === "HeaderModel"); ok(isEmptyModel); @@ -680,7 +680,7 @@ op testOperation(@bodyRoot body: HeaderModel): void; const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const isEmptyModel = models.find((m) => m.name === "HeaderModel"); ok(isEmptyModel); @@ -707,7 +707,7 @@ op testOperation(@bodyRoot body: HeaderModel): void; const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const isEmptyModel = models.find((m) => m.name === "HeaderModel"); ok(isEmptyModel); @@ -757,7 +757,7 @@ describe("typespec-client-generator-core: general decorators list", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; strictEqual(models.length, 1); deepStrictEqual(models[0].decorators, [ @@ -799,7 +799,7 @@ describe("Access decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const enums = root.enums; const colorEnum = enums.find((e) => e.name === "Color"); @@ -830,7 +830,7 @@ describe("Access decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const enums = root.enums; const colorEnum = enums.find((e) => e.name === "Color"); @@ -860,7 +860,7 @@ describe("Access decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const enums = root.enums; const statusEnum = enums.find((e) => e.name === "Status"); @@ -898,7 +898,7 @@ describe("Usage decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const enums = root.enums; const colorEnum = enums.find((e) => e.name === "Color"); @@ -928,7 +928,7 @@ describe("Usage decorator on enums", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const enums = root.enums; const colorEnum = enums.find((e) => e.name === "Color"); @@ -971,7 +971,7 @@ describe("XML serialization options", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const bookModel = models.find((m) => m.name === "Book"); @@ -1035,7 +1035,7 @@ describe("XML serialization options", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const xmlModel = models.find((m) => m.name === "XmlModel"); @@ -1079,7 +1079,7 @@ describe("XML serialization options", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const models = root.models; const containerModel = models.find((m) => m.name === "Container"); diff --git a/packages/http-client-csharp/emitter/test/Unit/namespace-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/namespace-converter.test.ts index 95583fb6fc2..8179f78486b 100644 --- a/packages/http-client-csharp/emitter/test/Unit/namespace-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/namespace-converter.test.ts @@ -50,7 +50,7 @@ describe("Namespace Converter", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); const sdkPackage = sdkContext.sdkPackage; - const parsedNamespaces = fromSdkNamespaces(sdkContext, sdkPackage.namespaces); + const [parsedNamespaces] = fromSdkNamespaces(sdkContext, sdkPackage.namespaces); strictEqual(parsedNamespaces.length, 1); diff --git a/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts index ddaea32c419..af8d27f6fc0 100644 --- a/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/operation-converter.test.ts @@ -35,7 +35,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -106,7 +106,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -176,7 +176,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -231,7 +231,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -275,7 +275,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -314,7 +314,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -346,7 +346,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -382,7 +382,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); @@ -434,7 +434,7 @@ describe("Operation Converter", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); strictEqual(root.clients.length, 1); strictEqual(root.clients[0].methods.length, 1); diff --git a/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts b/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts index d5f5cd66d54..5338c2f7f53 100644 --- a/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts @@ -39,7 +39,7 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -93,7 +93,7 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -138,7 +138,7 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -174,7 +174,7 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -208,7 +208,7 @@ describe("Next link operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -220,13 +220,13 @@ describe("Next link operations", () => { strictEqual(paging.nextLink?.responseSegments.length, 1); strictEqual(paging.nextLink?.responseSegments[0], "next"); - strictEqual(program.diagnostics.length, 1); + strictEqual(modelDiagnostics.length, 1); strictEqual( - program.diagnostics[0].code, + modelDiagnostics[0].code, "@typespec/http-client-csharp/unsupported-continuation-location", ); strictEqual( - program.diagnostics[0].message, + modelDiagnostics[0].message, `Unsupported continuation location for operation ${root.clients[0].methods[0].operation.crossLanguageDefinitionId}.`, ); }); @@ -256,7 +256,7 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -291,7 +291,7 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -327,7 +327,7 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -363,7 +363,7 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -399,7 +399,7 @@ describe("Continuation token operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root, modelDiagnostics] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -415,13 +415,13 @@ describe("Continuation token operations", () => { strictEqual(continuationToken.responseLocation, ResponseLocation.None); strictEqual(continuationToken.responseSegments.length, 1); strictEqual(continuationToken.responseSegments[0], "nextToken"); - strictEqual(program.diagnostics.length, 1); + strictEqual(modelDiagnostics.length, 1); strictEqual( - program.diagnostics[0].code, + modelDiagnostics[0].code, "@typespec/http-client-csharp/unsupported-continuation-location", ); strictEqual( - program.diagnostics[0].message, + modelDiagnostics[0].message, `Unsupported continuation location for operation ${root.clients[0].methods[0].operation.crossLanguageDefinitionId}.`, ); }); @@ -453,7 +453,7 @@ describe("PageSize parameter operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); @@ -491,7 +491,7 @@ describe("PageSize parameter operations", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const method = root.clients[0].methods[0]; strictEqual(method.kind, "paging"); diff --git a/packages/http-client-csharp/emitter/test/Unit/property-type.test.ts b/packages/http-client-csharp/emitter/test/Unit/property-type.test.ts index 5de9f067d45..e3589538bd3 100644 --- a/packages/http-client-csharp/emitter/test/Unit/property-type.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/property-type.test.ts @@ -28,7 +28,7 @@ describe("Test GetInputType for array", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "input", ); @@ -49,7 +49,7 @@ describe("Test GetInputType for array", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const bodyType = root.clients[0].methods[0].operation.responses[0].bodyType; strictEqual(bodyType?.kind, "array"); strictEqual(bodyType.crossLanguageDefinitionId, "TypeSpec.Array"); @@ -86,7 +86,7 @@ describe("Test GetInputType for enum", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "input", ); @@ -130,7 +130,7 @@ describe("Test GetInputType for enum", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "input", ); @@ -169,7 +169,7 @@ describe("Test GetInputType for enum", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "input", ); diff --git a/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts b/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts index 015d4ca3c43..cea35899816 100644 --- a/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts @@ -28,7 +28,7 @@ describe("Test GetInputType for scalar", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "location", ); diff --git a/packages/http-client-csharp/emitter/test/Unit/string-format.test.ts b/packages/http-client-csharp/emitter/test/Unit/string-format.test.ts index 2f86dc8409e..3eea60ca0d1 100644 --- a/packages/http-client-csharp/emitter/test/Unit/string-format.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/string-format.test.ts @@ -27,7 +27,7 @@ describe("Test string format", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const inputParamArray = root.clients[0].methods[0].operation.parameters.filter( (p) => p.name === "sourceUrl", ); @@ -51,7 +51,7 @@ describe("Test string format", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const codeModel = createModel(sdkContext); + const [codeModel] = createModel(sdkContext); const models = codeModel.models; const foo = models.find((m) => m.name === "Foo"); ok(foo); diff --git a/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts index 081708c903f..b6fb88bbdf9 100644 --- a/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts @@ -69,7 +69,7 @@ describe("Enum value references", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const enumType = root.enums.find((e) => e.name === "TestEnum"); ok(enumType, "TestEnum should exist in the enums list"); strictEqual(enumType.values.length, 3, "TestEnum should have 3 values"); @@ -119,7 +119,7 @@ describe("External types", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel, "TestModel should exist"); @@ -164,7 +164,7 @@ describe("External types", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const testModel = root.models.find((m) => m.name === "TestModel"); ok(testModel, "TestModel should exist"); diff --git a/packages/http-client-csharp/emitter/test/Unit/usage.test.ts b/packages/http-client-csharp/emitter/test/Unit/usage.test.ts index a20ee81a401..ee0923b0174 100644 --- a/packages/http-client-csharp/emitter/test/Unit/usage.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/usage.test.ts @@ -33,7 +33,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -54,7 +54,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -75,7 +75,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -97,7 +97,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -124,7 +124,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); const templateModel = root.models.find((model) => model.name === "TemplateModelFoo"); @@ -154,7 +154,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const baseModel = root.models.find((model) => model.name === "BaseModel"); const fooModel = root.models.find((model) => model.name === "Foo"); @@ -191,7 +191,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const baseModel = root.models.find((model) => model.name === "BaseModel"); const fooModel = root.models.find((model) => model.name === "Foo"); const propertyModel = root.models.find((model) => model.name === "PropertyModel"); @@ -218,7 +218,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const fooAlias = root.models.find((model) => model.name === "TestRequest"); ok(fooAlias); @@ -266,7 +266,7 @@ describe("Test Usage", () => { ); const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const fooInfo = root.models.find((model) => model.name === "FooInfo"); const batchCreateFooListItemsRequest = root.models.find( (model) => model.name === "BatchCreateFooListItemsRequest", @@ -312,7 +312,7 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const fooModel = root.models.find((model) => model.name === "Foo"); ok(fooModel); @@ -371,7 +371,7 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const baseModel = root.models.find((model) => model.name === "BaseModelWithDiscriminator"); const derivedModel = root.models.find( (model) => model.name === "DerivedModelWithDiscriminatorA", @@ -441,7 +441,7 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const baseModel = root.models.find((model) => model.name === "BaseModelWithDiscriminator"); const derivedModel = root.models.find( (model) => model.name === "DerivedModelWithDiscriminatorA", @@ -478,7 +478,7 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const simpleEnumRenamed = root.enums.find((enumType) => enumType.name === "SimpleEnumRenamed"); ok(simpleEnumRenamed); @@ -502,7 +502,7 @@ describe("Test Usage", () => { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const renamedModel = root.models.find((model) => model.name === "RenamedModel"); ok(renamedModel); @@ -660,7 +660,7 @@ interface LegacyLro { const context = createEmitterContext(program); const sdkContext = await createCSharpSdkContext(context); - const root = createModel(sdkContext); + const [root] = createModel(sdkContext); const radiologyInsightsInferenceResult = root.models.find( (model) => model.name === "RadiologyInsightsInferenceResult", ); diff --git a/packages/http-client-csharp/readme.md b/packages/http-client-csharp/readme.md index 77f8efc84b9..30e8bea7724 100644 --- a/packages/http-client-csharp/readme.md +++ b/packages/http-client-csharp/readme.md @@ -125,12 +125,6 @@ The name of the generator. By default this is set to `ScmCodeModelGenerator`. Ge Allows emitter authors to specify the path to a custom emitter package, allowing you to extend the emitter behavior. This should be set to `import.meta.url` if you are using a custom emitter. -### `update-code-model` - -**Type:** `object` - -Allows emitter authors to specify a custom function to modify the generated code model before emitting. This is useful for modifying the code model before it is passed to the generator. - ### `license` **Type:** `object` diff --git a/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/emitter.md b/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/emitter.md index 55c52dcc6bf..7a989e85368 100644 --- a/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/emitter.md +++ b/website/src/content/docs/docs/emitters/clients/http-client-csharp/reference/emitter.md @@ -108,12 +108,6 @@ The name of the generator. By default this is set to `ScmCodeModelGenerator`. Ge Allows emitter authors to specify the path to a custom emitter package, allowing you to extend the emitter behavior. This should be set to `import.meta.url` if you are using a custom emitter. -### `update-code-model` - -**Type:** `object` - -Allows emitter authors to specify a custom function to modify the generated code model before emitting. This is useful for modifying the code model before it is passed to the generator. - ### `license` **Type:** `object`