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 097035f7c0..20468746fe 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 @@ -2,271 +2,200 @@ // Licensed under the MIT License. See License.txt in the project root for license information. import { - SdkClient, + SdkClientType, SdkContext, - SdkOperationGroup, + SdkEndpointParameter, + SdkHttpOperation, + SdkServiceMethod, getAllModels, - getHttpOperationWithCache, - getLibraryName, - listClients, - listOperationGroups, - listOperationsInOperationGroup, } from "@azure-tools/typespec-client-generator-core"; -import { - EmitContext, - NoTarget, - Service, - getDoc, - getNamespaceFullName, - listServices, -} from "@typespec/compiler"; -import { HttpOperation, getAllHttpServices, getAuthentication, getServers } from "@typespec/http"; -import { getVersions } from "@typespec/versioning"; +import { getDoc } from "@typespec/compiler"; import { NetEmitterOptions, resolveOptions } from "../options.js"; -import { ClientKind } from "../type/client-kind.js"; import { CodeModel } from "../type/code-model.js"; import { InputClient } from "../type/input-client.js"; -import { InputConstant } from "../type/input-constant.js"; import { InputOperationParameterKind } from "../type/input-operation-parameter-kind.js"; -import { InputOperation } from "../type/input-operation.js"; import { InputParameter } from "../type/input-parameter.js"; -import { InputEnumType, InputModelType } from "../type/input-type.js"; +import { InputEnumType, InputModelType, InputPrimitiveType } from "../type/input-type.js"; import { RequestLocation } from "../type/request-location.js"; -import { reportDiagnostic } from "./lib.js"; +import { fromSdkType } from "./converter.js"; import { Logger } from "./logger.js"; import { navigateModels } from "./model.js"; -import { loadOperation } from "./operation.js"; +import { fromSdkServiceMethod, getParameterDefaultValue } from "./operation.js"; import { processServiceAuthentication } from "./service-authentication.js"; -import { resolveServers } from "./typespec-server.js"; -import { createContentTypeOrAcceptParameter } from "./utils.js"; export function createModel(sdkContext: SdkContext): CodeModel { // initialize tcgc model if (!sdkContext.operationModelsMap) getAllModels(sdkContext); - const services = listServices(sdkContext.emitContext.program); - if (services.length === 0) { - services.push({ - type: sdkContext.emitContext.program.getGlobalNamespaceType(), - }); - } - - // TODO: support multiple service. Current only chose the first service. - const service = services[0]; - const serviceNamespaceType = service.type; - if (serviceNamespaceType === undefined) { - throw Error("Can not emit yaml for a namespace that doesn't exist."); - } - - return createModelForService(sdkContext, service); -} - -export function createModelForService( - sdkContext: SdkContext, - service: Service -): CodeModel { - const program = sdkContext.emitContext.program; - const serviceNamespaceType = service.type; - - const apiVersions: Set | undefined = new Set(); - let defaultApiVersion: string | undefined = undefined; - let versions = getVersions(program, service.type)[1] - ?.getVersions() - .map((v) => v.value); - const targetApiVersion = sdkContext.emitContext.options["api-version"]; - if ( - versions !== undefined && - targetApiVersion !== undefined && - targetApiVersion !== "all" && - targetApiVersion !== "latest" - ) { - const targetApiVersionIndex = versions.findIndex((v) => v === targetApiVersion); - versions = versions.slice(0, targetApiVersionIndex + 1); - } - if (versions && versions.length > 0) { - for (const ver of versions) { - apiVersions.add(ver); - } - defaultApiVersion = versions[versions.length - 1]; - } - const defaultApiVersionConstant: InputConstant | undefined = defaultApiVersion - ? { - Type: { - Kind: "string", - }, - Value: defaultApiVersion, - } - : undefined; - - const servers = getServers(program, serviceNamespaceType); - const namespace = getNamespaceFullName(serviceNamespaceType) || "client"; - const authentication = getAuthentication(program, serviceNamespaceType); - let auth = undefined; - if (authentication) { - auth = processServiceAuthentication(authentication); - } + const sdkPackage = sdkContext.sdkPackage; const modelMap = new Map(); const enumMap = new Map(); - let urlParameters: InputParameter[] | undefined = undefined; - let url: string = ""; - const convenienceOperations: HttpOperation[] = []; - - //create endpoint parameter from servers - if (servers !== undefined) { - const typespecServers = resolveServers(sdkContext, servers, modelMap, enumMap); - if (typespecServers.length > 0) { - /* choose the first server as endpoint. */ - url = typespecServers[0].url; - urlParameters = typespecServers[0].parameters; - } - } - const [services] = getAllHttpServices(program); - const routes = services[0].operations; - if (routes.length === 0) { - reportDiagnostic(program, { - code: "no-route", - format: { service: services[0].namespace.name }, - target: NoTarget, - }); - } - Logger.getInstance().info("routes:" + routes.length); - - const clients: InputClient[] = []; - const dpgClients = listClients(sdkContext); - for (const client of dpgClients) { - clients.push(emitClient(client)); - addChildClients(sdkContext.emitContext, client, clients); - } navigateModels(sdkContext, modelMap, enumMap); - for (const client of clients) { - for (const op of client.Operations) { - const apiVersionIndex = op.Parameters.findIndex( - (value: InputParameter) => value.IsApiVersion - ); - if (apiVersionIndex === -1) { - continue; - } - const apiVersionInOperation = op.Parameters[apiVersionIndex]; - if (defaultApiVersionConstant !== undefined) { - if (!apiVersionInOperation.DefaultValue?.Value) { - apiVersionInOperation.DefaultValue = defaultApiVersionConstant; - } - } else { - apiVersionInOperation.Kind = InputOperationParameterKind.Method; - } - } - } + const rootApiVersions = getRootApiVersions(sdkPackage.clients); + + const inputClients: InputClient[] = []; + fromSdkClients( + sdkPackage.clients.filter((c) => c.initialization.access === "public"), + inputClients, + [] + ); const clientModel = { - Name: namespace, - ApiVersions: Array.from(apiVersions.values()), + Name: sdkPackage.rootNamespace, + ApiVersions: rootApiVersions, Enums: Array.from(enumMap.values()), Models: Array.from(modelMap.values()), - Clients: clients, - Auth: auth, + Clients: inputClients, + Auth: processServiceAuthentication(sdkPackage), } as CodeModel; return clientModel; - function addChildClients( - context: EmitContext, - client: SdkClient | SdkOperationGroup, - clients: InputClient[] + function fromSdkClients( + clients: SdkClientType[], + inputClients: InputClient[], + parentClientNames: string[] ) { - const dpgOperationGroups = listOperationGroups(sdkContext, client as SdkClient); - for (const dpgGroup of dpgOperationGroups) { - const subClient = emitClient(dpgGroup, client); - clients.push(subClient); - addChildClients(context, dpgGroup, clients); + for (const client of clients) { + const inputClient = emitClient(client, parentClientNames); + inputClients.push(inputClient); + const subClients = client.methods + .filter((m) => m.kind === "clientaccessor") + .map((m) => m.response as SdkClientType); + parentClientNames.push(inputClient.Name); + fromSdkClients(subClients, inputClients, parentClientNames); + parentClientNames.pop(); } } - function getClientName(client: SdkClient | SdkOperationGroup): string { - if (client.kind === ClientKind.SdkClient) { - return client.name; - } + function emitClient(client: SdkClientType, parentNames: string[]): InputClient { + const endpointParameter = client.initialization.properties.find( + (p) => p.kind === "endpoint" + ) as SdkEndpointParameter; + const uri = getMethodUri(endpointParameter); + const clientParameters = fromSdkEndpointParameter(endpointParameter); + return { + Name: getClientName(client, parentNames), + Description: client.description, + Operations: client.methods + .filter((m) => m.kind !== "clientaccessor") + .map((m) => + fromSdkServiceMethod( + m as SdkServiceMethod, + uri, + clientParameters, + rootApiVersions, + sdkContext, + modelMap, + enumMap + ) + ), + Protocol: {}, + Parent: parentNames.length > 0 ? parentNames[parentNames.length - 1] : undefined, + Parameters: clientParameters, + }; + } - const pathParts = client.groupPath.split("."); - if (pathParts?.length >= 3) { - return pathParts.slice(pathParts.length - 2).join(""); - } + function getClientName( + client: SdkClientType, + parentClientNames: string[] + ): string { + const clientName = client.name; + + if (parentClientNames.length === 0) return clientName; + if (parentClientNames.length >= 2) + return `${parentClientNames.slice(parentClientNames.length - 1).join("")}${clientName}`; - const clientName = getLibraryName(sdkContext, client.type); if ( clientName === "Models" && resolveOptions(sdkContext.emitContext)["model-namespace"] !== false ) { - reportDiagnostic(program, { - code: "invalid-name", - format: { name: clientName }, - target: client.type, - }); + Logger.getInstance().warn(`Invalid client name "${clientName}"`); return "ModelsOps"; } + return clientName; } - function emitClient( - client: SdkClient | SdkOperationGroup, - parent?: SdkClient | SdkOperationGroup - ): InputClient { - const operations = listOperationsInOperationGroup(sdkContext, client); - let clientDesc = ""; - if (operations.length > 0) { - const container = getHttpOperationWithCache(sdkContext, operations[0]).container; - clientDesc = getDoc(program, container) ?? ""; + function fromSdkEndpointParameter(p: SdkEndpointParameter): InputParameter[] { + if (p.type.templateArguments.length === 0) + return [ + { + Name: p.name, + NameInRequest: p.serializedName ?? p.name, + Type: fromSdkType(p.type, sdkContext, modelMap, enumMap), + Location: RequestLocation.Uri, + IsApiVersion: false, + IsResourceParameter: false, + IsContentType: false, + IsRequired: true, + IsEndpoint: true, + SkipUrlEncoding: false, + Explode: false, + Kind: InputOperationParameterKind.Client, + DefaultValue: { + Type: { + Kind: "string", + }, + Value: p.type.serverUrl, + }, + }, + ]; + + // TODO: support free-style endpoint url with multiple parameters + const endpointExpr = p.type.serverUrl + .replace("https://", "") + .replace("http://", "") + .split("/")[0]; + if (!/^\{\w+\}$/.test(endpointExpr)) + throw new Error(`Unsupported server url "${p.type.serverUrl}"`); + const endpointVariableName = endpointExpr.substring(1, endpointExpr.length - 1); + + const parameters: InputParameter[] = []; + for (const parameter of p.type.templateArguments) { + const isEndpoint = parameter.name === endpointVariableName; + const parameterType = isEndpoint + ? ({ + Kind: "uri", + } as InputPrimitiveType) + : fromSdkType(parameter.type, sdkContext, modelMap, enumMap); + parameters.push({ + Name: parameter.name, + NameInRequest: parameter.serializedName, + // TODO: remove this workaround after https://github.com/Azure/typespec-azure/issues/1212 is fixed + Description: parameter.__raw ? getDoc(sdkContext.program, parameter.__raw) : undefined, + // TODO: we should do the magic in generator + Type: parameterType, + Location: RequestLocation.Uri, + IsApiVersion: parameter.isApiVersionParam, + IsResourceParameter: false, + IsContentType: false, + IsRequired: !parameter.optional, + IsEndpoint: isEndpoint, + SkipUrlEncoding: false, + Explode: false, + Kind: InputOperationParameterKind.Client, + DefaultValue: getParameterDefaultValue(parameter.clientDefaultValue, parameterType), + }); } + return parameters; + } +} - const inputClient: InputClient = { - Name: getClientName(client), - Description: clientDesc, - Operations: [], - Protocol: {}, - Parent: parent === undefined ? undefined : getClientName(parent), - Parameters: urlParameters, - }; - for (const op of operations) { - const httpOperation = getHttpOperationWithCache(sdkContext, op); - const inputOperation: InputOperation = loadOperation( - sdkContext, - httpOperation, - url, - urlParameters, - serviceNamespaceType, - modelMap, - enumMap - ); +function getRootApiVersions(clients: SdkClientType[]): string[] { + // find any root client since they should have the same api versions + const oneRootClient = clients.find((c) => c.initialization.access === "public"); + if (!oneRootClient) throw new Error("Root client not found"); - applyDefaultContentTypeAndAcceptParameter(inputOperation); - inputClient.Operations.push(inputOperation); - if (inputOperation.GenerateConvenienceMethod) convenienceOperations.push(httpOperation); - } - return inputClient; - } + return oneRootClient.apiVersions; } -function applyDefaultContentTypeAndAcceptParameter(operation: InputOperation): void { - const defaultValue: string = "application/json"; - if ( - operation.Parameters.some((value) => value.Location === RequestLocation.Body) && - !operation.Parameters.some((value) => value.IsContentType === true) - ) { - operation.Parameters.push( - createContentTypeOrAcceptParameter([defaultValue], "contentType", "Content-Type") - ); - operation.RequestMediaTypes = [defaultValue]; - } +function getMethodUri(p: SdkEndpointParameter | undefined): string { + if (!p) return ""; - if ( - !operation.Parameters.some( - (value) => - value.Location === RequestLocation.Header && value.NameInRequest.toLowerCase() === "accept" - ) - ) { - operation.Parameters.push( - createContentTypeOrAcceptParameter([defaultValue], "accept", "Accept") - ); - } + if (p.type.templateArguments.length > 0) return p.type.serverUrl; + + return `{${p.name}}`; } diff --git a/packages/http-client-csharp/emitter/src/lib/converter.ts b/packages/http-client-csharp/emitter/src/lib/converter.ts index a6c0c8acb2..6f4930b0b0 100644 --- a/packages/http-client-csharp/emitter/src/lib/converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/converter.ts @@ -52,6 +52,7 @@ export function fromSdkType( } as InputNullableType; } if (sdkType.kind === "model") return fromSdkModelType(sdkType, context, models, enums); + if (sdkType.kind === "endpoint") return fromSdkEndpointType(); if (sdkType.kind === "enum") return fromSdkEnumType(sdkType, context, enums); if (sdkType.kind === "enumvalue") return fromSdkEnumValueTypeToConstantType(sdkType, context, enums, literalTypeContext); @@ -64,10 +65,9 @@ export function fromSdkType( return fromSdkDateTimeType(sdkType); if (sdkType.kind === "duration") return fromSdkDurationType(sdkType as SdkDurationType); if (sdkType.kind === "tuple") return fromTupleType(); - // TODO -- only in operations we could have these types, considering we did not adopt getAllOperations from TCGC yet, this should be fine. - // we need to resolve these conversions when we adopt getAllOperations + // TODO -- endpoint and credential are handled separately in emitter, since we have specific locations for them in input model. + // We can handle unify the way we handle them in the future, probably by chaning the input model schema and do the conversion in generator. if (sdkType.kind === "credential") throw new Error("Credential type is not supported yet."); - if (sdkType.kind === "endpoint") throw new Error("Endpoint type is not supported yet."); return fromSdkBuiltInType(sdkType); } @@ -368,3 +368,9 @@ function fromSdkArrayType( CrossLanguageDefinitionId: arrayType.crossLanguageDefinitionId, }; } + +function fromSdkEndpointType(): InputPrimitiveType { + return { + Kind: "string", + }; +} diff --git a/packages/http-client-csharp/emitter/src/lib/operation.ts b/packages/http-client-csharp/emitter/src/lib/operation.ts index e8d13bbf81..ae124d322b 100644 --- a/packages/http-client-csharp/emitter/src/lib/operation.ts +++ b/packages/http-client-csharp/emitter/src/lib/operation.ts @@ -1,31 +1,28 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -import { getLroMetadata } from "@azure-tools/typespec-azure-core"; import { + SdkBodyParameter, + SdkBuiltInKinds, + SdkBuiltInType, SdkContext, - UsageFlags, - getAccess, - isApiVersion, + SdkHeaderParameter, + SdkHttpOperation, + SdkHttpResponse, + SdkPathParameter, + SdkQueryParameter, + SdkServiceMethod, + SdkServiceResponseHeader, + SdkType, shouldGenerateConvenient, shouldGenerateProtocol, + UsageFlags, } from "@azure-tools/typespec-client-generator-core"; -import { - Model, - ModelProperty, - Namespace, - Operation, - Type, - getDeprecated, - getDoc, - getSummary, - isErrorModel, -} from "@typespec/compiler"; -import { HttpOperation, HttpOperationParameter, HttpOperationResponse } from "@typespec/http"; -import { getExtensions } from "@typespec/openapi"; +import { getDeprecated, getDoc, getSummary, isErrorModel } from "@typespec/compiler"; +import { HttpStatusCodeRange } from "@typespec/http"; import { getResourceOperation } from "@typespec/rest"; import { NetEmitterOptions } from "../options.js"; -import { BodyMediaType, typeToBodyMediaType } from "../type/body-media-type.js"; +import { BodyMediaType } from "../type/body-media-type.js"; import { collectionFormatToDelimMap } from "../type/collection-format.js"; import { HttpResponseHeader } from "../type/http-response-header.js"; import { InputConstant } from "../type/input-constant.js"; @@ -33,334 +30,364 @@ import { InputOperationParameterKind } from "../type/input-operation-parameter-k import { InputOperation } from "../type/input-operation.js"; import { InputParameter } from "../type/input-parameter.js"; import { - InputArrayType, InputEnumType, InputModelType, + InputPrimitiveType, InputType, - isInputEnumType, - isInputLiteralType, - isInputUnionType, } from "../type/input-type.js"; import { convertLroFinalStateVia } from "../type/operation-final-state-via.js"; -import { OperationLongRunning } from "../type/operation-long-running.js"; import { OperationPaging } from "../type/operation-paging.js"; import { OperationResponse } from "../type/operation-response.js"; -import { RequestLocation, requestLocationMap } from "../type/request-location.js"; -import { RequestMethod, parseHttpRequestMethod } from "../type/request-method.js"; -import { getExternalDocs, getOperationId, hasDecorator } from "./decorators.js"; +import { RequestLocation } from "../type/request-location.js"; +import { parseHttpRequestMethod } from "../type/request-method.js"; +import { fromSdkType } from "./converter.js"; +import { getExternalDocs, getOperationId } from "./decorators.js"; import { Logger } from "./logger.js"; -import { getDefaultValue, getEffectiveSchemaType, getInputType } from "./model.js"; -import { createContentTypeOrAcceptParameter, getTypeName } from "./utils.js"; +import { getInputType } from "./model.js"; -export function loadOperation( - sdkContext: SdkContext, - operation: HttpOperation, +export function fromSdkServiceMethod( + method: SdkServiceMethod, uri: string, - urlParameters: InputParameter[] | undefined = undefined, - serviceNamespaceType: Namespace, - models: Map, - enums: Map + clientParameters: InputParameter[], + rootApiVersions: string[], + sdkContext: SdkContext, + modelMap: Map, + enumMap: Map ): InputOperation { - const { path: fullPath, operation: op, verb, parameters: typespecParameters } = operation; - const program = sdkContext.program; - Logger.getInstance().info(`load operation: ${op.name}, path:${fullPath} `); - const resourceOperation = getResourceOperation(program, op); - const desc = getDoc(program, op); - const summary = getSummary(program, op); - const externalDocs = getExternalDocs(sdkContext, op); - - const parameters: InputParameter[] = []; - if (urlParameters) { - for (const param of urlParameters) { - parameters.push(param); - } - } - for (const p of typespecParameters.parameters) { - parameters.push(loadOperationParameter(sdkContext, p)); + let generateConvenience = shouldGenerateConvenient(sdkContext, method.operation.__raw.operation); + if (method.operation.verb === "patch" && generateConvenience) { + Logger.getInstance().warn( + `Convenience method is not supported for PATCH method, it will be automatically turned off. Please set the '@convenientAPI' to false for operation ${method.operation.__raw.operation.name}.` + ); + generateConvenience = false; } - if (typespecParameters.body?.property && !isVoidType(typespecParameters.body.type)) { - parameters.push(loadBodyParameter(sdkContext, typespecParameters.body?.property)); - } else if (typespecParameters.body?.type && !isVoidType(typespecParameters.body.type)) { - const rawBodyType = typespecParameters.body.type; - const bodyParameter = loadBodyParameter(sdkContext, rawBodyType as Model); - const bodyType = bodyParameter.Type; - if (bodyType.Kind === "model" && (bodyType.Usage & UsageFlags.Spread) !== 0) { - bodyParameter.Kind = InputOperationParameterKind.Spread; - } - parameters.push(bodyParameter); - } + return { + Name: method.name, + ResourceName: + getResourceOperation(sdkContext.program, method.operation.__raw.operation)?.resourceType + .name ?? + getOperationGroupName(sdkContext, method.operation, sdkContext.sdkPackage.rootNamespace), + Deprecated: getDeprecated(sdkContext.program, method.__raw!), + // TODO: we need to figure out how we want to handle summary and description + // Right now, we generate garbage for some APIs like `Platform-OpenAI-TypeSpec` + Summary: getSummary(sdkContext.program, method.__raw!), + Description: getDoc(sdkContext.program, method.__raw!), + Accessibility: method.access, + Parameters: fromSdkOperationParameters( + method.operation, + clientParameters, + rootApiVersions, + sdkContext, + modelMap, + enumMap + ), + Responses: fromSdkHttpOperationResponses( + method.operation.responses, + sdkContext, + modelMap, + enumMap + ), + HttpMethod: parseHttpRequestMethod(method.operation.verb), + RequestBodyMediaType: getBodyMediaType(method.operation.bodyParam?.type), + Uri: uri, + Path: method.operation.path, + ExternalDocsUrl: getExternalDocs(sdkContext, method.operation.__raw.operation)?.url, + RequestMediaTypes: getRequestMediaTypes(method.operation), + BufferResponse: true, + LongRunning: loadLongRunningOperation(method, sdkContext, modelMap, enumMap), + Paging: loadOperationPaging(method), + GenerateProtocolMethod: shouldGenerateProtocol(sdkContext, method.operation.__raw.operation), + GenerateConvenienceMethod: generateConvenience, + }; +} - const responses: OperationResponse[] = []; - for (const res of operation.responses) { - const operationResponse = loadOperationResponse(sdkContext, res); - if (operationResponse) { - responses.push(operationResponse); - } - if (operationResponse?.ContentTypes && operationResponse.ContentTypes.length > 0) { - const acceptParameter = createContentTypeOrAcceptParameter( - [operationResponse.ContentTypes[0]], // We currently only support one content type per response - "accept", - "Accept" - ); - const acceptIndex = parameters.findIndex((p) => p.NameInRequest.toLowerCase() === "accept"); - if (acceptIndex > -1) { - parameters.splice(acceptIndex, 1, acceptParameter); - } else { - parameters.push(acceptParameter); - } - } +export function getParameterDefaultValue( + clientDefaultValue: any, + parameterType: InputType +): InputConstant | undefined { + if ( + clientDefaultValue === undefined || + // a constant parameter should overwrite client default value + parameterType.Kind === "constant" + ) { + return undefined; } - const mediaTypes: string[] = []; - const contentTypeParameter = parameters.find((value) => value.IsContentType); - if (contentTypeParameter) { - if (isInputLiteralType(contentTypeParameter.Type)) { - mediaTypes.push(contentTypeParameter.DefaultValue?.Value); - } else if (isInputUnionType(contentTypeParameter.Type)) { - for (const unionItem of contentTypeParameter.Type.VariantTypes) { - if (isInputLiteralType(unionItem)) { - mediaTypes.push(unionItem.Value as string); - } else { - throw "Media type of content type should be string."; - } - } - } else if (isInputEnumType(contentTypeParameter.Type)) { - const mediaTypeValues = contentTypeParameter.Type.Values.map((value) => value.Value); - if (mediaTypeValues.some((item) => item === undefined)) { - throw "Media type of content type should be string."; - } - mediaTypes.push(...mediaTypeValues); - } + return { + Type: { + Kind: getValueType(clientDefaultValue), + }, + Value: clientDefaultValue, + }; +} + +function getValueType(value: any): SdkBuiltInKinds { + switch (typeof value) { + case "string": + return "string"; + case "number": + return "int32"; + case "boolean": + return "boolean"; + case "bigint": + return "int64"; + default: + throw new Error(`Unsupported default value type: ${typeof value}`); } +} - const requestMethod = parseHttpRequestMethod(verb); - const generateProtocol: boolean = shouldGenerateProtocol(sdkContext, op); - let generateConvenience: boolean = shouldGenerateConvenient(sdkContext, op); +function fromSdkOperationParameters( + operation: SdkHttpOperation, + clientParameters: InputParameter[], + rootApiVersions: string[], + sdkContext: SdkContext, + modelMap: Map, + enumMap: Map +): InputParameter[] { + const params = clientParameters.concat( + operation.parameters.map((p) => + fromSdkHttpOperationParameter(p, rootApiVersions, sdkContext, modelMap, enumMap) + ) + ); + return operation.bodyParam + ? params.concat( + fromSdkHttpOperationParameter( + operation.bodyParam, + rootApiVersions, + sdkContext, + modelMap, + enumMap + ) + ) + : params; +} - if (requestMethod === RequestMethod.PATCH && generateConvenience) { - Logger.getInstance().warn( - `Convenience method is not supported for PATCH method, it will be automatically turned off. Please set the '@convenientAPI' to false for operation ${op.name}.` - ); - generateConvenience = false; +// TODO: roll back to SdkMethodParameter when we figure out how to represent the parameter location +// https://github.com/Azure/typespec-azure/issues/981 +function fromSdkHttpOperationParameter( + p: SdkPathParameter | SdkQueryParameter | SdkHeaderParameter | SdkBodyParameter, + rootApiVersions: string[], + sdkContext: SdkContext, + modelMap: Map, + enumMap: Map +): InputParameter { + const isContentType = + p.kind === "header" && p.serializedName.toLocaleLowerCase() === "content-type"; + const parameterType = fromSdkType(p.type, sdkContext, modelMap, enumMap); + // remove this after: https://github.com/Azure/typespec-azure/issues/1084 + if (p.type.kind === "bytes") { + (parameterType as InputPrimitiveType).Encode = ( + p.correspondingMethodParams[0].type as SdkBuiltInType + ).encode; } + const format = p.kind === "header" || p.kind === "query" ? p.collectionFormat : undefined; + const serializedName = p.kind !== "body" ? p.serializedName : p.name; - /* handle lro */ - /* handle paging. */ - let paging: OperationPaging | undefined = undefined; - for (const res of operation.responses) { - const body = res.responses[0]?.body; - if (body?.type) { - const bodyType = getEffectiveSchemaType(sdkContext, body.type); - if (bodyType.kind === "Model" && hasDecorator(bodyType, "$pagedResult")) { - const itemsProperty = Array.from(bodyType.properties.values()).find((it) => - hasDecorator(it, "$items") - ); - const nextLinkProperty = Array.from(bodyType.properties.values()).find((it) => - hasDecorator(it, "$nextLink") - ); - paging = { - NextLinkName: nextLinkProperty?.name, - ItemName: itemsProperty?.name, - } as OperationPaging; - } - } + return { + Name: p.name, + NameInRequest: p.kind === "header" ? normalizeHeadername(serializedName) : serializedName, + Description: p.description, + Type: parameterType, + Location: getParameterLocation(p), + IsApiVersion: + p.name.toLocaleLowerCase() === "apiversion" || p.name.toLocaleLowerCase() === "api-version", + IsContentType: isContentType, + IsEndpoint: false, + Explode: parameterType.Kind === "array" && format === "multi" ? true : false, + ArraySerializationDelimiter: format ? collectionFormatToDelimMap[format] : undefined, + IsRequired: !p.optional, + Kind: getParameterKind(p, parameterType, isContentType, rootApiVersions.length > 0), + DefaultValue: getParameterDefaultValue(p.clientDefaultValue, parameterType), + } as InputParameter; +} + +function loadLongRunningOperation( + method: SdkServiceMethod, + sdkContext: SdkContext, + modelMap: Map, + enumMap: Map +): import("../type/operation-long-running.js").OperationLongRunning | undefined { + if (method.kind !== "lro") { + return undefined; } - /* TODO: handle lro */ return { - Name: getTypeName(sdkContext, op), - ResourceName: - resourceOperation?.resourceType.name ?? - getOperationGroupName(sdkContext, op, serviceNamespaceType), - Summary: summary, - Deprecated: getDeprecated(program, op), - Description: desc, - Accessibility: getAccess(sdkContext, op), - Parameters: parameters, - Responses: responses, - HttpMethod: requestMethod, - RequestBodyMediaType: typeToBodyMediaType(typespecParameters.body?.type), - Uri: uri, - Path: fullPath, - ExternalDocsUrl: externalDocs?.url, - RequestMediaTypes: mediaTypes.length > 0 ? mediaTypes : undefined, - BufferResponse: true, - LongRunning: loadLongRunningOperation(sdkContext, operation), - Paging: paging, - GenerateProtocolMethod: generateProtocol, - GenerateConvenienceMethod: generateConvenience, - } as InputOperation; + FinalStateVia: convertLroFinalStateVia(method.__raw_lro_metadata.finalStateVia), + FinalResponse: { + // in swagger, we allow delete to return some meaningful body content + // for now, let assume we don't allow return type + StatusCodes: method.operation.verb === "delete" ? [204] : [200], + BodyType: + method.__raw_lro_metadata.finalEnvelopeResult && + method.__raw_lro_metadata.finalEnvelopeResult !== "void" + ? getInputType( + sdkContext, + method.__raw_lro_metadata.finalEnvelopeResult, + modelMap, + enumMap, + method.operation.__raw.operation + ) + : undefined, + BodyMediaType: BodyMediaType.Json, + } as OperationResponse, + ResultPath: method.__raw_lro_metadata.finalResultPath, + }; +} - function isVoidType(type: Type): boolean { - return type.kind === "Intrinsic" && type.name === "void"; +function fromSdkHttpOperationResponses( + operationResponses: Map, + sdkContext: SdkContext, + modelMap: Map, + enumMap: Map +): OperationResponse[] { + const responses: OperationResponse[] = []; + operationResponses.forEach((r, range) => { + responses.push({ + StatusCodes: toStatusCodesArray(range), + BodyType: r.type ? fromSdkType(r.type, sdkContext, modelMap, enumMap) : undefined, + BodyMediaType: BodyMediaType.Json, + Headers: fromSdkServiceResponseHeaders(r.headers, sdkContext, modelMap, enumMap), + IsErrorResponse: r.type !== undefined && isErrorModel(sdkContext.program, r.type.__raw!), + ContentTypes: r.contentTypes, + }); + }); + return responses; +} + +function fromSdkServiceResponseHeaders( + headers: SdkServiceResponseHeader[], + sdkContext: SdkContext, + modelMap: Map, + enumMap: Map +): HttpResponseHeader[] { + return headers.map( + (h) => + ({ + Name: h.__raw!.name, + NameInResponse: h.serializedName, + Description: h.description, + Type: fromSdkType(h.type, sdkContext, modelMap, enumMap), + }) as HttpResponseHeader + ); +} + +function toStatusCodesArray(range: number | HttpStatusCodeRange): number[] { + if (typeof range === "number") return [range]; + + const statusCodes: number[] = [range.end - range.start + 1]; + for (let i = range.start; i <= range.end; i++) { + statusCodes.push(i); } + return statusCodes; +} - function loadOperationParameter( - context: SdkContext, - parameter: HttpOperationParameter - ): InputParameter { - const { type: location, name, param } = parameter; - const format = parameter.type === "path" ? undefined : parameter.format; - const typespecType = param.type; - const inputType: InputType = getInputType(context, param, models, enums, operation.operation); - let defaultValue: InputConstant | undefined = undefined; - const value = getDefaultValue(typespecType); - if (value) { - defaultValue = { - Type: inputType, - Value: value, - }; - } - const requestLocation = requestLocationMap[location]; - const isApiVer: boolean = isApiVersion(sdkContext, parameter); - const isContentType: boolean = - requestLocation === RequestLocation.Header && name.toLowerCase() === "content-type"; - const kind: InputOperationParameterKind = - isContentType || inputType.Kind === "constant" - ? InputOperationParameterKind.Constant - : isApiVer - ? defaultValue - ? InputOperationParameterKind.Constant - : InputOperationParameterKind.Client - : InputOperationParameterKind.Method; - return { - Name: getTypeName(sdkContext, param), - NameInRequest: name, - Description: getDoc(program, param), - Type: inputType, - Location: requestLocation, - DefaultValue: defaultValue, - IsRequired: !param.optional, - IsApiVersion: isApiVer, - IsResourceParameter: false, - IsContentType: isContentType, - IsEndpoint: false, - SkipUrlEncoding: - // TODO: update this when https://github.com/Azure/typespec-azure/issues/1022 is resolved - getExtensions(program, param).get("x-ms-skip-url-encoding") === true, - Explode: (inputType as InputArrayType).ValueType && format === "multi" ? true : false, - Kind: kind, - ArraySerializationDelimiter: format ? collectionFormatToDelimMap[format] : undefined, - } as InputParameter; +function getBodyMediaType(type: SdkType | undefined) { + if (type === undefined) { + return BodyMediaType.None; } - function loadBodyParameter( - context: SdkContext, - body: ModelProperty | Model - ): InputParameter { - const inputType: InputType = getInputType(context, body, models, enums, operation.operation); - const requestLocation = RequestLocation.Body; - const kind: InputOperationParameterKind = InputOperationParameterKind.Method; - return { - Name: getTypeName(context, body), - NameInRequest: body.name, - Description: getDoc(program, body), - Type: inputType, - Location: requestLocation, - IsRequired: body.kind === "Model" ? true : !body.optional, - IsApiVersion: false, - IsResourceParameter: false, - IsContentType: false, - IsEndpoint: false, - SkipUrlEncoding: false, - Explode: false, - Kind: kind, - } as InputParameter; + if (type.kind === "model") { + return BodyMediaType.Json; + } else if (type.kind === "string") { + return BodyMediaType.Text; + } else if (type.kind === "bytes") { + return BodyMediaType.Binary; } + return BodyMediaType.None; +} - function loadOperationResponse( - context: SdkContext, - response: HttpOperationResponse - ): OperationResponse | undefined { - if (!response.statusCode || response.statusCode === "*") { - return undefined; - } - const status: number[] = []; - status.push(Number(response.statusCode)); - //TODO: what to do if more than 1 response? - const body = response.responses[0]?.body; +function getRequestMediaTypes(op: SdkHttpOperation): string[] | undefined { + const contentTypes = op.parameters.filter( + (p) => p.kind === "header" && p.serializedName.toLocaleLowerCase() === "content-type" + ); + if (contentTypes.length === 0) return undefined; + return contentTypes.map((p) => getMediaTypes(p.type)).flat(); +} - let type: InputType | undefined = undefined; - if (body?.type) { - const typespecType = getEffectiveSchemaType(context, body.type); - const inputType: InputType = getInputType( - context, - typespecType, - models, - enums, - operation.operation - ); - type = inputType; +function getMediaTypes(type: SdkType): string[] { + if (type.kind === "constant") { + if (type.valueType.kind !== "string") { + throw `Media type in "content-type" should be string. But get ${type.valueType.kind}.`; } - - const headers = response.responses[0]?.headers; - const responseHeaders: HttpResponseHeader[] = []; - if (headers) { - for (const key of Object.keys(headers)) { - responseHeaders.push({ - Name: key, - NameInResponse: headers[key].name, - Description: getDoc(program, headers[key]) ?? "", - Type: getInputType(context, headers[key].type, models, enums, operation.operation), - } as HttpResponseHeader); + return [type.value as string]; + } else if (type.kind === "union") { + const mediaTypes: string[] = []; + for (const unionItem of type.values) { + if (unionItem.kind === "constant" && unionItem.valueType.kind === "string") { + mediaTypes.push(unionItem.value as string); + } else { + throw `Media type in "content-type" should be string. But get ${unionItem.kind}.`; } } + return mediaTypes; + } else if (type.kind === "enum") { + if (type.valueType.kind !== "string") { + throw `Media type in "content-type" should be string. But get ${type.valueType.kind}.`; + } + return type.values.map((v) => v.value as string); + } + return []; +} - return { - StatusCodes: status, - BodyType: type, - BodyMediaType: BodyMediaType.Json, - Headers: responseHeaders, - IsErrorResponse: isErrorModel(program, response.type), - ContentTypes: body?.contentTypes, - } as OperationResponse; +function loadOperationPaging( + method: SdkServiceMethod +): OperationPaging | undefined { + if (method.kind !== "paging") { + return undefined; } - function loadLongRunningOperation( - context: SdkContext, - op: HttpOperation - ): OperationLongRunning | undefined { - const metadata = getLroMetadata(program, op.operation); - if (metadata === undefined) { - return undefined; - } + return { + ItemName: method.__raw_paged_metadata.itemsProperty?.name, + NextLinkName: method.__raw_paged_metadata.nextLinkProperty?.name, + }; +} - let bodyType = undefined; - if ( - op.verb !== "delete" && - metadata.finalResult !== undefined && - metadata.finalResult !== "void" - ) { - bodyType = getInputType( - context, - metadata.finalEnvelopeResult as Model, - models, - enums, - op.operation - ); - } +// TODO: https://github.com/Azure/typespec-azure/issues/981 +function getParameterLocation( + p: SdkPathParameter | SdkQueryParameter | SdkHeaderParameter | SdkBodyParameter | undefined +): RequestLocation { + switch (p?.kind) { + case "path": + return RequestLocation.Path; + case "header": + return RequestLocation.Header; + case "query": + return RequestLocation.Query; + case "body": + return RequestLocation.Body; + default: + return RequestLocation.None; + } +} - return { - FinalStateVia: convertLroFinalStateVia(metadata.finalStateVia), - FinalResponse: { - // in swagger, we allow delete to return some meaningful body content - // for now, let assume we don't allow return type - StatusCodes: op.verb === "delete" ? [204] : [200], - BodyType: bodyType, - BodyMediaType: BodyMediaType.Json, - } as OperationResponse, - ResultPath: metadata.finalResultPath, - } as OperationLongRunning; +function getParameterKind( + p: SdkPathParameter | SdkQueryParameter | SdkHeaderParameter | SdkBodyParameter, + type: InputType, + isContentType: boolean, + hasGlobalApiVersion: boolean +): InputOperationParameterKind { + if (p.kind === "body") { + if (type.Kind === "model" && (type.Usage & UsageFlags.Spread) !== 0) { + return InputOperationParameterKind.Spread; + } + return InputOperationParameterKind.Method; } + return isContentType || type.Kind === "constant" + ? InputOperationParameterKind.Constant + : p.isApiVersionParam + ? hasGlobalApiVersion + ? InputOperationParameterKind.Client + : InputOperationParameterKind.Method + : InputOperationParameterKind.Method; } function getOperationGroupName( context: SdkContext, - operation: Operation, - serviceNamespaceType: Namespace + operation: SdkHttpOperation, + namespace: string ): string { - const explicitOperationId = getOperationId(context, operation); + const explicitOperationId = getOperationId(context, operation.__raw.operation); if (explicitOperationId) { const ids: string[] = explicitOperationId.split("_"); if (ids.length > 1) { @@ -368,14 +395,23 @@ function getOperationGroupName( } } - if (operation.interface) { - return operation.interface.name; + if (operation.__raw.operation.interface) { + return operation.__raw.operation.interface.name; } - let namespace = operation.namespace; - if (!namespace) { - namespace = context.program.checker.getGlobalNamespaceType() ?? serviceNamespaceType; + if (operation.__raw.operation.namespace) { + return operation.__raw.operation.namespace.name; } + return namespace; +} - if (namespace) return namespace.name; - else return ""; +// TODO: remove after https://github.com/Azure/typespec-azure/issues/1227 is fixed +function normalizeHeadername(name: string): string { + switch (name.toLocaleLowerCase()) { + case "accept": + return "Accept"; + case "content-type": + return "Content-Type"; + default: + return name; + } } 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 51e85900df..2def6f3e9c 100644 --- a/packages/http-client-csharp/emitter/src/lib/service-authentication.ts +++ b/packages/http-client-csharp/emitter/src/lib/service-authentication.ts @@ -1,61 +1,100 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -import { Authentication } from "@typespec/http"; -import { InputApiKeyAuth } from "../type/input-api-key-auth.js"; +import { + SdkCredentialParameter, + SdkCredentialType, + SdkHttpOperation, + SdkPackage, +} from "@azure-tools/typespec-client-generator-core"; +import { Oauth2Auth, OAuth2Flow } from "@typespec/http"; import { InputAuth } from "../type/input-auth.js"; -import { InputOAuth2Auth } from "../type/input-oauth2-auth.js"; import { Logger } from "./logger.js"; -export function processServiceAuthentication(authentication: Authentication): InputAuth { - const auth = {} as InputAuth; - let scopes: Set | undefined; +export function processServiceAuthentication( + sdkPackage: SdkPackage +): InputAuth | undefined { + let authClientParameter: SdkCredentialParameter | undefined = undefined; + for (const client of sdkPackage.clients) { + for (const parameter of client.initialization.properties) { + if (parameter.kind === "credential") { + authClientParameter = parameter; + break; + } + } + } - for (const option of authentication.options) { - for (const scheme of option.schemes) { - switch (scheme.type) { - case "apiKey": - auth.ApiKey = { Name: scheme.name } as InputApiKeyAuth; - break; - case "oauth2": - for (const flow of scheme.flows) { - if (flow.scopes) { - scopes ??= new Set(); - for (const scope of flow.scopes) { - scopes.add(scope.value); - } - } - } - break; - case "http": - const schemeOrApiKeyPrefix = scheme.scheme; - if (schemeOrApiKeyPrefix === "basic") { + if (!authClientParameter) { + return undefined; + } + if (authClientParameter.type.kind === "credential") { + return processAuthType(authClientParameter.type); + } + const inputAuth: InputAuth = {}; + for (const authType of authClientParameter.type.values) { + // TODO: TCGC might change to []SdkCredentialType + const auth = processAuthType(authType as SdkCredentialType); + if (auth?.ApiKey) { + inputAuth.ApiKey = auth.ApiKey; + } + if (auth?.OAuth2) { + inputAuth.OAuth2 = auth.OAuth2; + } + } + return inputAuth; +} + +function processAuthType(credentialType: SdkCredentialType): InputAuth | undefined { + const scheme = credentialType.scheme; + switch (scheme.type) { + case "apiKey": + return { ApiKey: { Name: scheme.name } } as InputAuth; + case "oauth2": + return processOAuth2(scheme); + case "http": + { + const schemeOrApiKeyPrefix = scheme.scheme; + switch (schemeOrApiKeyPrefix) { + case "basic": Logger.getInstance().warn( - `{schemeOrApiKeyPrefix} auth method is currently not supported.` + `${schemeOrApiKeyPrefix} auth method is currently not supported.` ); - } else if (schemeOrApiKeyPrefix === "bearer") { - auth.ApiKey = { - Name: "Authorization", - Prefix: "Bearer", - } as InputApiKeyAuth; - } else { - auth.ApiKey = { - Name: "Authorization", - Prefix: schemeOrApiKeyPrefix, - } as InputApiKeyAuth; - } - break; - default: - throw new Error("Not supported authentication."); + return undefined; + case "bearer": + return { + ApiKey: { + Name: "Authorization", + Prefix: "Bearer", + }, + } as InputAuth; + default: + return { + ApiKey: { + Name: "Authorization", + Prefix: schemeOrApiKeyPrefix, + }, + } as InputAuth; + } } - } + break; + default: + throw new Error(`un-supported authentication scheme ${scheme.type}`); } +} - if (scopes) { - auth.OAuth2 = { - Scopes: Array.from(scopes.values()), - } as InputOAuth2Auth; +function processOAuth2(scheme: Oauth2Auth): InputAuth | undefined { + let scopes: Set | undefined = undefined; + for (const flow of scheme.flows) { + if (flow.scopes) { + scopes ??= new Set(); + for (const scope of flow.scopes) { + scopes.add(scope.value); + } + } } - - return auth; + return scopes + ? ({ + OAuth2: { Scopes: Array.from(scopes.values()) }, + } as InputAuth) + : undefined; } diff --git a/packages/http-client-csharp/emitter/src/lib/utils.ts b/packages/http-client-csharp/emitter/src/lib/utils.ts index 05cf2d680c..aec553beaf 100644 --- a/packages/http-client-csharp/emitter/src/lib/utils.ts +++ b/packages/http-client-csharp/emitter/src/lib/utils.ts @@ -68,5 +68,5 @@ export function createContentTypeOrAcceptParameter( Value: mediaTypes[0], } as InputConstant) : undefined, - } as InputParameter; + }; } 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 3eeb9d02a7..b2bc4dbdc5 100644 --- a/packages/http-client-csharp/emitter/test/Unit/encode.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/encode.test.ts @@ -2,7 +2,7 @@ import { TestHost } from "@typespec/compiler/testing"; import { getAllHttpServices } from "@typespec/http"; import assert, { deepStrictEqual } from "assert"; import { beforeEach, describe, it } from "vitest"; -import { loadOperation } from "../../src/lib/operation.js"; +import { createModel } from "../../src/lib/client-model-builder.js"; import { InputDurationType, InputEnumType, InputModelType } from "../../src/type/input-type.js"; import { createEmitterContext, @@ -32,18 +32,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = createNetSdkContext(context); - const [services] = getAllHttpServices(program); - const modelMap = new Map(); - const enumMap = new Map(); - const operation = loadOperation( - sdkContext, - services[0].operations[0], - "", - [], - services[0].namespace, - modelMap, - enumMap - ); + const root = createModel(sdkContext); deepStrictEqual( { Kind: "duration", @@ -53,7 +42,7 @@ describe("Test encode duration", () => { Encode: undefined, }, } as InputDurationType, - operation.Parameters[0].Type + root.Clients[0].Operations[0].Parameters[1].Type ); }); @@ -70,18 +59,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = createNetSdkContext(context); - const [services] = getAllHttpServices(program); - const modelMap = new Map(); - const enumMap = new Map(); - const operation = loadOperation( - sdkContext, - services[0].operations[0], - "", - [], - services[0].namespace, - modelMap, - enumMap - ); + const root = createModel(sdkContext); deepStrictEqual( { Kind: "duration", @@ -91,7 +69,7 @@ describe("Test encode duration", () => { Encode: undefined, }, } as InputDurationType, - operation.Parameters[0].Type + root.Clients[0].Operations[0].Parameters[1].Type ); }); @@ -108,18 +86,7 @@ describe("Test encode duration", () => { ); const context = createEmitterContext(program); const sdkContext = createNetSdkContext(context); - const [services] = getAllHttpServices(program); - const modelMap = new Map(); - const enumMap = new Map(); - const operation = loadOperation( - sdkContext, - services[0].operations[0], - "", - [], - services[0].namespace, - modelMap, - enumMap - ); + const root = createModel(sdkContext); deepStrictEqual( { Kind: "duration", @@ -129,7 +96,7 @@ describe("Test encode duration", () => { Encode: undefined, }, } as InputDurationType, - operation.Parameters[0].Type + root.Clients[0].Operations[0].Parameters[1].Type ); }); 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 f904b1cb3b..fdd99e58af 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 = createNetSdkContext(context); const root = createModel(sdkContext); - const type = root.Clients[0].Operations[0].Parameters[0].Type; + const type = root.Clients[0].Operations[0].Parameters[3].Type; strictEqual(type.Kind, "array"); strictEqual(type.CrossLanguageDefinitionId, "TypeSpec.Array"); strictEqual(type.ValueType.Kind, "string"); @@ -82,7 +82,7 @@ describe("Test GetInputType for enum", () => { const context = createEmitterContext(program); const sdkContext = createNetSdkContext(context); const root = createModel(sdkContext); - const type = root.Clients[0].Operations[0].Parameters[0].Type; + const type = root.Clients[0].Operations[0].Parameters[3].Type; strictEqual(type.Kind, "enum"); strictEqual(type.Name, "SimpleEnum"); strictEqual(type.IsExtensible, false); @@ -124,7 +124,7 @@ describe("Test GetInputType for enum", () => { const context = createEmitterContext(program); const sdkContext = createNetSdkContext(context); const root = createModel(sdkContext); - const type = root.Clients[0].Operations[0].Parameters[0].Type; + const type = root.Clients[0].Operations[0].Parameters[3].Type; strictEqual(type.Kind, "enum"); strictEqual(type.Name, "FixedIntEnum"); strictEqual(type.CrossLanguageDefinitionId, "Azure.Csharp.Testing.FixedIntEnum"); @@ -158,7 +158,7 @@ describe("Test GetInputType for enum", () => { const context = createEmitterContext(program); const sdkContext = createNetSdkContext(context); const root = createModel(sdkContext); - const type = root.Clients[0].Operations[0].Parameters[0].Type; + const type = root.Clients[0].Operations[0].Parameters[3].Type; strictEqual(type.Kind, "enum"); strictEqual(type.Name, "FixedEnum"); strictEqual(type.CrossLanguageDefinitionId, "Azure.Csharp.Testing.FixedEnum"); 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 ccbdfbd667..2c14f5f8a2 100644 --- a/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/scalar.test.ts @@ -28,13 +28,13 @@ describe("Test GetInputType for scalar", () => { const context = createEmitterContext(program); const sdkContext = createNetSdkContext(context); const root = createModel(sdkContext); - deepStrictEqual(root.Clients[0].Operations[0].Parameters[0].Type.Kind, "azureLocation"); + deepStrictEqual(root.Clients[0].Operations[0].Parameters[1].Type.Kind, "azureLocation"); deepStrictEqual( { Kind: "azureLocation", Encode: "string", }, - root.Clients[0].Operations[0].Parameters[0].Type + root.Clients[0].Operations[0].Parameters[1].Type ); }); }); 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 a31cd0d4ff..2f8f8347b6 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 @@ -2,7 +2,7 @@ import { TestHost } from "@typespec/compiler/testing"; import { getAllHttpServices } from "@typespec/http"; import assert, { deepStrictEqual } from "assert"; import { beforeEach, describe, it } from "vitest"; -import { loadOperation } from "../../src/lib/operation.js"; +import { createModel } from "../../src/lib/client-model-builder.js"; import { InputEnumType, InputModelType, InputPrimitiveType } from "../../src/type/input-type.js"; import { createEmitterContext, @@ -28,24 +28,13 @@ describe("Test string format", () => { ); const context = createEmitterContext(program); const sdkContext = createNetSdkContext(context); - const [services] = getAllHttpServices(program); - const modelMap = new Map(); - const enumMap = new Map(); - const operation = loadOperation( - sdkContext, - services[0].operations[0], - "", - [], - services[0].namespace, - modelMap, - enumMap - ); + const root = createModel(sdkContext); deepStrictEqual( { Kind: "url", Encode: undefined, } as InputPrimitiveType, - operation.Parameters[0].Type + root.Clients[0].Operations[0].Parameters[1].Type ); }); diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/InputLiteralType.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/InputLiteralType.cs index 7db00875d8..5f2b455b9a 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/InputLiteralType.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/InputLiteralType.cs @@ -13,5 +13,7 @@ public InputLiteralType(InputType valueType, object value) : base("Literal") public InputType ValueType { get; } public object Value { get; } + + public static implicit operator InputConstant(InputLiteralType literal) => new(literal.Value, literal.ValueType); } } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/Serialization/TypeSpecInputParameterConverter.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/Serialization/TypeSpecInputParameterConverter.cs index bdbf16fd30..f7121c0a99 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/Serialization/TypeSpecInputParameterConverter.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/Serialization/TypeSpecInputParameterConverter.cs @@ -91,7 +91,8 @@ public static InputParameter CreateInputParameter(ref Utf8JsonReader reader, str description: description, type: parameterType, location: requestLocation, - defaultValue: defaultValue, + // TODO: not use default value for constant value https://github.com/microsoft/typespec/issues/4028 + defaultValue: parameterType is InputLiteralType { Value: not null } literalType ? literalType : defaultValue, kind: parameterKind, isRequired: isRequired, isApiVersion: isApiVersion, diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/Serialization/TypeSpecInputTypeConverter.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/Serialization/TypeSpecInputTypeConverter.cs index fbadc5f4ea..04f7fdbf30 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/Serialization/TypeSpecInputTypeConverter.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/Serialization/TypeSpecInputTypeConverter.cs @@ -62,7 +62,7 @@ private InputType CreateObject(ref Utf8JsonReader reader, JsonSerializerOptions private InputType CreateDerivedType(ref Utf8JsonReader reader, string? id, string? kind, string? name, JsonSerializerOptions options) => kind switch { - null => throw new JsonException("InputType must have a 'Kind' property"), + null => throw new JsonException($"InputType (id: '{id}', name: '{name}') must have a 'Kind' property"), LiteralKind => TypeSpecInputLiteralTypeConverter.CreateInputLiteralType(ref reader, id, name, options, _referenceHandler.CurrentResolver), UnionKind => TypeSpecInputUnionTypeConverter.CreateInputUnionType(ref reader, id, name, options, _referenceHandler.CurrentResolver), ModelKind => TypeSpecInputModelTypeConverter.CreateModelType(ref reader, id, name, options, _referenceHandler.CurrentResolver), diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/authentication/api-key/tspCodeModel.json b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/authentication/api-key/tspCodeModel.json index a884cf4336..07cd5099b8 100644 --- a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/authentication/api-key/tspCodeModel.json +++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/authentication/api-key/tspCodeModel.json @@ -41,9 +41,8 @@ "Parameters": [ { "$id": "7", - "Name": "host", - "NameInRequest": "host", - "Description": "TestServer endpoint", + "Name": "endpoint", + "NameInRequest": "endpoint", "Type": { "$id": "8", "Kind": "string" @@ -65,36 +64,11 @@ }, "Value": "http://localhost:3000" } - }, - { - "$id": "11", - "Name": "accept", - "NameInRequest": "Accept", - "Type": { - "$id": "12", - "Kind": "string" - }, - "Location": "Header", - "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": false, - "IsRequired": true, - "IsEndpoint": false, - "SkipUrlEncoding": false, - "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "13", - "Type": { - "$ref": "12" - }, - "Value": "application/json" - } } ], "Responses": [ { - "$id": "14", + "$id": "11", "StatusCodes": [ 204 ], @@ -105,14 +79,14 @@ ], "HttpMethod": "GET", "RequestBodyMediaType": "None", - "Uri": "{host}", + "Uri": "{endpoint}", "Path": "/authentication/api-key/valid", "BufferResponse": true, "GenerateProtocolMethod": true, "GenerateConvenienceMethod": true }, { - "$id": "15", + "$id": "12", "Name": "invalid", "ResourceName": "ApiKey", "Description": "Check whether client is authenticated.", @@ -122,60 +96,41 @@ "$ref": "7" }, { - "$id": "16", + "$id": "13", "Name": "accept", "NameInRequest": "Accept", "Type": { - "$id": "17", - "Kind": "string" + "$id": "14", + "Kind": "constant", + "ValueType": { + "$id": "15", + "Kind": "string" + }, + "Value": "application/json" }, "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "18", - "Type": { - "$ref": "17" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Constant" } ], "Responses": [ { - "$id": "19", + "$id": "16", "StatusCodes": [ 204 ], "BodyMediaType": "Json", "Headers": [], "IsErrorResponse": false - }, - { - "$id": "20", - "StatusCodes": [ - 403 - ], - "BodyType": { - "$ref": "2" - }, - "BodyMediaType": "Json", - "Headers": [], - "IsErrorResponse": true, - "ContentTypes": [ - "application/json" - ] } ], "HttpMethod": "GET", "RequestBodyMediaType": "None", - "Uri": "{host}", + "Uri": "{endpoint}", "Path": "/authentication/api-key/invalid", "BufferResponse": true, "GenerateProtocolMethod": true, @@ -183,7 +138,7 @@ } ], "Protocol": { - "$id": "21" + "$id": "17" }, "Parameters": [ { @@ -193,9 +148,9 @@ } ], "Auth": { - "$id": "22", + "$id": "18", "ApiKey": { - "$id": "23", + "$id": "19", "Name": "x-ms-api-key" } } diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/authentication/http/custom/tspCodeModel.json b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/authentication/http/custom/tspCodeModel.json index e82b79c317..336a0c648a 100644 --- a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/authentication/http/custom/tspCodeModel.json +++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/authentication/http/custom/tspCodeModel.json @@ -41,9 +41,8 @@ "Parameters": [ { "$id": "7", - "Name": "host", - "NameInRequest": "host", - "Description": "TestServer endpoint", + "Name": "endpoint", + "NameInRequest": "endpoint", "Type": { "$id": "8", "Kind": "string" @@ -65,36 +64,11 @@ }, "Value": "http://localhost:3000" } - }, - { - "$id": "11", - "Name": "accept", - "NameInRequest": "Accept", - "Type": { - "$id": "12", - "Kind": "string" - }, - "Location": "Header", - "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": false, - "IsRequired": true, - "IsEndpoint": false, - "SkipUrlEncoding": false, - "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "13", - "Type": { - "$ref": "12" - }, - "Value": "application/json" - } } ], "Responses": [ { - "$id": "14", + "$id": "11", "StatusCodes": [ 204 ], @@ -105,14 +79,14 @@ ], "HttpMethod": "GET", "RequestBodyMediaType": "None", - "Uri": "{host}", + "Uri": "{endpoint}", "Path": "/authentication/http/custom/valid", "BufferResponse": true, "GenerateProtocolMethod": true, "GenerateConvenienceMethod": true }, { - "$id": "15", + "$id": "12", "Name": "invalid", "ResourceName": "Custom", "Description": "Check whether client is authenticated.", @@ -122,60 +96,41 @@ "$ref": "7" }, { - "$id": "16", + "$id": "13", "Name": "accept", "NameInRequest": "Accept", "Type": { - "$id": "17", - "Kind": "string" + "$id": "14", + "Kind": "constant", + "ValueType": { + "$id": "15", + "Kind": "string" + }, + "Value": "application/json" }, "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "18", - "Type": { - "$ref": "17" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Constant" } ], "Responses": [ { - "$id": "19", + "$id": "16", "StatusCodes": [ 204 ], "BodyMediaType": "Json", "Headers": [], "IsErrorResponse": false - }, - { - "$id": "20", - "StatusCodes": [ - 403 - ], - "BodyType": { - "$ref": "2" - }, - "BodyMediaType": "Json", - "Headers": [], - "IsErrorResponse": true, - "ContentTypes": [ - "application/json" - ] } ], "HttpMethod": "GET", "RequestBodyMediaType": "None", - "Uri": "{host}", + "Uri": "{endpoint}", "Path": "/authentication/http/custom/invalid", "BufferResponse": true, "GenerateProtocolMethod": true, @@ -183,7 +138,7 @@ } ], "Protocol": { - "$id": "21" + "$id": "17" }, "Parameters": [ { @@ -193,9 +148,9 @@ } ], "Auth": { - "$id": "22", + "$id": "18", "ApiKey": { - "$id": "23", + "$id": "19", "Name": "Authorization", "Prefix": "SharedAccessKey" } diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/authentication/oauth2/tspCodeModel.json b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/authentication/oauth2/tspCodeModel.json index d2fed49fd0..40bd57d8d5 100644 --- a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/authentication/oauth2/tspCodeModel.json +++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/authentication/oauth2/tspCodeModel.json @@ -41,9 +41,8 @@ "Parameters": [ { "$id": "7", - "Name": "host", - "NameInRequest": "host", - "Description": "TestServer endpoint", + "Name": "endpoint", + "NameInRequest": "endpoint", "Type": { "$id": "8", "Kind": "string" @@ -65,36 +64,11 @@ }, "Value": "http://localhost:3000" } - }, - { - "$id": "11", - "Name": "accept", - "NameInRequest": "Accept", - "Type": { - "$id": "12", - "Kind": "string" - }, - "Location": "Header", - "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": false, - "IsRequired": true, - "IsEndpoint": false, - "SkipUrlEncoding": false, - "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "13", - "Type": { - "$ref": "12" - }, - "Value": "application/json" - } } ], "Responses": [ { - "$id": "14", + "$id": "11", "StatusCodes": [ 204 ], @@ -105,14 +79,14 @@ ], "HttpMethod": "GET", "RequestBodyMediaType": "None", - "Uri": "{host}", + "Uri": "{endpoint}", "Path": "/authentication/oauth2/valid", "BufferResponse": true, "GenerateProtocolMethod": true, "GenerateConvenienceMethod": true }, { - "$id": "15", + "$id": "12", "Name": "invalid", "ResourceName": "OAuth2", "Description": "Check whether client is authenticated. Will return an invalid bearer error.", @@ -122,60 +96,41 @@ "$ref": "7" }, { - "$id": "16", + "$id": "13", "Name": "accept", "NameInRequest": "Accept", "Type": { - "$id": "17", - "Kind": "string" + "$id": "14", + "Kind": "constant", + "ValueType": { + "$id": "15", + "Kind": "string" + }, + "Value": "application/json" }, "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "18", - "Type": { - "$ref": "17" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Constant" } ], "Responses": [ { - "$id": "19", + "$id": "16", "StatusCodes": [ 204 ], "BodyMediaType": "Json", "Headers": [], "IsErrorResponse": false - }, - { - "$id": "20", - "StatusCodes": [ - 403 - ], - "BodyType": { - "$ref": "2" - }, - "BodyMediaType": "Json", - "Headers": [], - "IsErrorResponse": true, - "ContentTypes": [ - "application/json" - ] } ], "HttpMethod": "GET", "RequestBodyMediaType": "None", - "Uri": "{host}", + "Uri": "{endpoint}", "Path": "/authentication/oauth2/invalid", "BufferResponse": true, "GenerateProtocolMethod": true, @@ -183,7 +138,7 @@ } ], "Protocol": { - "$id": "21" + "$id": "17" }, "Parameters": [ { @@ -193,9 +148,9 @@ } ], "Auth": { - "$id": "22", + "$id": "18", "OAuth2": { - "$id": "23", + "$id": "19", "Scopes": [ "https://security.microsoft.com/.default" ] diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/authentication/union/tspCodeModel.json b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/authentication/union/tspCodeModel.json index 2126391d3b..dc046df299 100644 --- a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/authentication/union/tspCodeModel.json +++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/authentication/union/tspCodeModel.json @@ -19,9 +19,8 @@ "Parameters": [ { "$id": "4", - "Name": "host", - "NameInRequest": "host", - "Description": "TestServer endpoint", + "Name": "endpoint", + "NameInRequest": "endpoint", "Type": { "$id": "5", "Kind": "string" @@ -43,36 +42,11 @@ }, "Value": "http://localhost:3000" } - }, - { - "$id": "8", - "Name": "accept", - "NameInRequest": "Accept", - "Type": { - "$id": "9", - "Kind": "string" - }, - "Location": "Header", - "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": false, - "IsRequired": true, - "IsEndpoint": false, - "SkipUrlEncoding": false, - "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "10", - "Type": { - "$ref": "9" - }, - "Value": "application/json" - } } ], "Responses": [ { - "$id": "11", + "$id": "8", "StatusCodes": [ 204 ], @@ -83,14 +57,14 @@ ], "HttpMethod": "GET", "RequestBodyMediaType": "None", - "Uri": "{host}", + "Uri": "{endpoint}", "Path": "/authentication/union/validkey", "BufferResponse": true, "GenerateProtocolMethod": true, "GenerateConvenienceMethod": true }, { - "$id": "12", + "$id": "9", "Name": "validToken", "ResourceName": "Union", "Description": "Check whether client is authenticated", @@ -98,36 +72,11 @@ "Parameters": [ { "$ref": "4" - }, - { - "$id": "13", - "Name": "accept", - "NameInRequest": "Accept", - "Type": { - "$id": "14", - "Kind": "string" - }, - "Location": "Header", - "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": false, - "IsRequired": true, - "IsEndpoint": false, - "SkipUrlEncoding": false, - "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "15", - "Type": { - "$ref": "14" - }, - "Value": "application/json" - } } ], "Responses": [ { - "$id": "16", + "$id": "10", "StatusCodes": [ 204 ], @@ -138,7 +87,7 @@ ], "HttpMethod": "GET", "RequestBodyMediaType": "None", - "Uri": "{host}", + "Uri": "{endpoint}", "Path": "/authentication/union/validtoken", "BufferResponse": true, "GenerateProtocolMethod": true, @@ -146,7 +95,7 @@ } ], "Protocol": { - "$id": "17" + "$id": "11" }, "Parameters": [ { @@ -156,13 +105,13 @@ } ], "Auth": { - "$id": "18", + "$id": "12", "ApiKey": { - "$id": "19", + "$id": "13", "Name": "x-ms-api-key" }, "OAuth2": { - "$id": "20", + "$id": "14", "Scopes": [ "https://security.microsoft.com/.default" ] diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/src/Generated/UnbrandedTypeSpecClient.RestClient.cs b/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/src/Generated/UnbrandedTypeSpecClient.RestClient.cs index b2ac019e6d..585535af4d 100644 --- a/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/src/Generated/UnbrandedTypeSpecClient.RestClient.cs +++ b/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/src/Generated/UnbrandedTypeSpecClient.RestClient.cs @@ -75,8 +75,8 @@ internal PipelineMessage CreateNoContentTypeRequest(string p1, string p2, Binary uri.AppendPath(p2, true); request.Uri = uri.ToUri(); request.Headers.Set("p1", p1); - request.Headers.Set("Accept", "application/json"); request.Headers.Set("Content-Type", "application/json"); + request.Headers.Set("Accept", "application/json"); message.Apply(options); return message; } @@ -106,8 +106,8 @@ internal PipelineMessage CreateCreateLiteralRequest(BinaryContent content, Reque uri.Reset(_endpoint); uri.AppendPath("/literal", false); request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); request.Headers.Set("Content-Type", "application/json"); + request.Headers.Set("Accept", "application/json"); message.Apply(options); return message; } @@ -171,8 +171,8 @@ internal PipelineMessage CreatePatchActionRequest(BinaryContent content, Request uri.Reset(_endpoint); uri.AppendPath("/patch", false); request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); request.Headers.Set("Content-Type", "application/json"); + request.Headers.Set("Accept", "application/json"); message.Apply(options); return message; } @@ -187,8 +187,8 @@ internal PipelineMessage CreateAnonymousBodyRequest(RequestOptions options) uri.Reset(_endpoint); uri.AppendPath("/anonymousBody", false); request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); request.Headers.Set("Content-Type", "application/json"); + request.Headers.Set("Accept", "application/json"); message.Apply(options); return message; } @@ -203,8 +203,8 @@ internal PipelineMessage CreateFriendlyModelRequest(RequestOptions options) uri.Reset(_endpoint); uri.AppendPath("/friendlyName", false); request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); request.Headers.Set("Content-Type", "application/json"); + request.Headers.Set("Accept", "application/json"); message.Apply(options); return message; } @@ -220,7 +220,6 @@ internal PipelineMessage CreateAddTimeHeaderRequest(DateTimeOffset repeatability uri.AppendPath("/", false); request.Uri = uri.ToUri(); request.Headers.Set("Repeatability-First-Sent", repeatabilityFirstSent.ToString("R")); - request.Headers.Set("Accept", "application/json"); message.Apply(options); return message; } @@ -235,8 +234,8 @@ internal PipelineMessage CreateProjectedNameModelRequest(RequestOptions options) uri.Reset(_endpoint); uri.AppendPath("/projectedName", false); request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); request.Headers.Set("Content-Type", "application/json"); + request.Headers.Set("Accept", "application/json"); message.Apply(options); return message; } @@ -256,7 +255,7 @@ internal PipelineMessage CreateReturnsAnonymousModelRequest(RequestOptions optio return message; } - internal PipelineMessage CreateGetUnknownValueRequest(RequestOptions options) + internal PipelineMessage CreateGetUnknownValueRequest(string accept, RequestOptions options) { PipelineMessage message = Pipeline.CreateMessage(); message.ResponseClassifier = PipelineMessageClassifier200; @@ -266,7 +265,7 @@ internal PipelineMessage CreateGetUnknownValueRequest(RequestOptions options) uri.Reset(_endpoint); uri.AppendPath("/unknown-value", false); request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); + request.Headers.Set("Accept", accept); message.Apply(options); return message; } @@ -281,8 +280,8 @@ internal PipelineMessage CreateInternalProtocolRequest(BinaryContent content, Re uri.Reset(_endpoint); uri.AppendPath("/internalProtocol", false); request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); request.Headers.Set("Content-Type", "application/json"); + request.Headers.Set("Accept", "application/json"); message.Apply(options); return message; } @@ -297,7 +296,6 @@ internal PipelineMessage CreateStillConvenientRequest(RequestOptions options) uri.Reset(_endpoint); uri.AppendPath("/stillConvenient", false); request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); message.Apply(options); return message; } @@ -313,7 +311,6 @@ internal PipelineMessage CreateHeadAsBooleanRequest(string id, RequestOptions op uri.AppendPath("/headAsBoolean/", false); uri.AppendPath(id, true); request.Uri = uri.ToUri(); - request.Headers.Set("Accept", "application/json"); message.Apply(options); return message; } diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/src/Generated/UnbrandedTypeSpecClient.cs b/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/src/Generated/UnbrandedTypeSpecClient.cs index e6c54bf666..217631cb21 100644 --- a/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/src/Generated/UnbrandedTypeSpecClient.cs +++ b/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/src/Generated/UnbrandedTypeSpecClient.cs @@ -885,12 +885,16 @@ public virtual async Task> ReturnsAn /// /// /// + /// /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// is null. /// Service returned a non-success status code. /// The response returned from the service. - public virtual ClientResult GetUnknownValue(RequestOptions options) + public virtual ClientResult GetUnknownValue(string accept, RequestOptions options) { - using PipelineMessage message = CreateGetUnknownValueRequest(options); + Argument.AssertNotNull(accept, nameof(accept)); + + using PipelineMessage message = CreateGetUnknownValueRequest(accept, options); return ClientResult.FromResponse(Pipeline.ProcessMessage(message, options)); } @@ -902,28 +906,40 @@ public virtual ClientResult GetUnknownValue(RequestOptions options) /// /// /// + /// /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// is null. /// Service returned a non-success status code. /// The response returned from the service. - public virtual async Task GetUnknownValueAsync(RequestOptions options) + public virtual async Task GetUnknownValueAsync(string accept, RequestOptions options) { - using PipelineMessage message = CreateGetUnknownValueRequest(options); + Argument.AssertNotNull(accept, nameof(accept)); + + using PipelineMessage message = CreateGetUnknownValueRequest(accept, options); return ClientResult.FromResponse(await Pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); } /// get extensible enum. + /// + /// is null. /// Service returned a non-success status code. - public virtual ClientResult GetUnknownValue() + public virtual ClientResult GetUnknownValue(string accept) { - ClientResult result = GetUnknownValue(null); + Argument.AssertNotNull(accept, nameof(accept)); + + ClientResult result = GetUnknownValue(accept, null); return ClientResult.FromValue(result.GetRawResponse().Content.ToString(), result.GetRawResponse()); } /// get extensible enum. + /// + /// is null. /// Service returned a non-success status code. - public virtual async Task> GetUnknownValueAsync() + public virtual async Task> GetUnknownValueAsync(string accept) { - ClientResult result = await GetUnknownValueAsync(null).ConfigureAwait(false); + Argument.AssertNotNull(accept, nameof(accept)); + + ClientResult result = await GetUnknownValueAsync(accept, null).ConfigureAwait(false); return ClientResult.FromValue(result.GetRawResponse().Content.ToString(), result.GetRawResponse()); } diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/tspCodeModel.json b/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/tspCodeModel.json index 71c5947d1b..5cf777b9e3 100644 --- a/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/tspCodeModel.json +++ b/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/tspCodeModel.json @@ -1502,7 +1502,7 @@ "NameInRequest": "unbrandedTypeSpecUrl", "Type": { "$id": "225", - "Kind": "url" + "Kind": "uri" }, "Location": "Uri", "IsApiVersion": false, @@ -1523,13 +1523,11 @@ "Kind": "string" }, "Location": "Header", - "IsRequired": true, "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, + "IsRequired": true, "Kind": "Method" }, { @@ -1541,13 +1539,11 @@ "Kind": "string" }, "Location": "Query", - "IsRequired": true, "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, + "IsRequired": true, "Kind": "Method" }, { @@ -1559,13 +1555,11 @@ "Kind": "string" }, "Location": "Query", - "IsRequired": false, "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, + "IsRequired": false, "Kind": "Method" }, { @@ -1574,24 +1568,20 @@ "NameInRequest": "Accept", "Type": { "$id": "233", - "Kind": "string" + "Kind": "constant", + "ValueType": { + "$id": "234", + "Kind": "string" + }, + "Value": "application/json" }, "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "234", - "Type": { - "$ref": "233" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Constant" } ], "Responses": [ @@ -1638,13 +1628,11 @@ "Kind": "string" }, "Location": "Header", - "IsRequired": true, "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, + "IsRequired": true, "Kind": "Method" }, { @@ -1661,86 +1649,69 @@ "Value": "text/plain" }, "Location": "Header", - "DefaultValue": { - "$id": "242", - "Type": { - "$ref": "240" - }, - "Value": "text/plain" - }, - "IsRequired": true, "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, + "IsRequired": true, "Kind": "Constant" }, { - "$id": "243", + "$id": "242", "Name": "p2", "NameInRequest": "p2", "Type": { - "$id": "244", + "$id": "243", "Kind": "string" }, "Location": "Path", - "IsRequired": true, "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, + "IsRequired": true, "Kind": "Method" }, { - "$id": "245", - "Name": "action", - "NameInRequest": "action", + "$id": "244", + "Name": "accept", + "NameInRequest": "Accept", "Type": { - "$ref": "115" + "$id": "245", + "Kind": "constant", + "ValueType": { + "$id": "246", + "Kind": "string" + }, + "Value": "application/json" }, - "Location": "Body", - "IsRequired": true, + "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Method" + "IsRequired": true, + "Kind": "Constant" }, { - "$id": "246", - "Name": "accept", - "NameInRequest": "Accept", + "$id": "247", + "Name": "action", + "NameInRequest": "action", "Type": { - "$id": "247", - "Kind": "string" + "$ref": "115" }, - "Location": "Header", + "Location": "Body", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "248", - "Type": { - "$ref": "247" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Method" } ], "Responses": [ { - "$id": "249", + "$id": "248", "StatusCodes": [ 200 ], @@ -1767,7 +1738,7 @@ "GenerateConvenienceMethod": true }, { - "$id": "250", + "$id": "249", "Name": "noContentType", "ResourceName": "UnbrandedTypeSpec", "Description": "Return hi again", @@ -1777,112 +1748,99 @@ "$ref": "224" }, { - "$id": "251", + "$id": "250", "Name": "p1", "NameInRequest": "p1", "Type": { - "$id": "252", + "$id": "251", "Kind": "string" }, "Location": "Header", - "IsRequired": true, "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, + "IsRequired": true, "Kind": "Method" }, { - "$id": "253", + "$id": "252", "Name": "p2", "NameInRequest": "p2", "Type": { - "$id": "254", + "$id": "253", "Kind": "string" }, "Location": "Path", - "IsRequired": true, "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, + "IsRequired": true, "Kind": "Method" }, { - "$id": "255", - "Name": "action", - "NameInRequest": "action", + "$id": "254", + "Name": "contentType", + "NameInRequest": "Content-Type", + "Description": "Body parameter's content type. Known values are application/json", "Type": { - "$ref": "115" + "$id": "255", + "Kind": "constant", + "ValueType": { + "$id": "256", + "Kind": "string" + }, + "Value": "application/json" }, - "Location": "Body", - "IsRequired": true, + "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": false, + "IsContentType": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Method" + "IsRequired": true, + "Kind": "Constant" }, { - "$id": "256", + "$id": "257", "Name": "accept", "NameInRequest": "Accept", "Type": { - "$id": "257", - "Kind": "string" + "$id": "258", + "Kind": "constant", + "ValueType": { + "$id": "259", + "Kind": "string" + }, + "Value": "application/json" }, "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "258", - "Type": { - "$ref": "257" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Constant" }, { - "$id": "259", - "Name": "contentType", - "NameInRequest": "Content-Type", + "$id": "260", + "Name": "action", + "NameInRequest": "action", "Type": { - "$id": "260", - "Kind": "string" + "$ref": "115" }, - "Location": "Header", + "Location": "Body", "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": true, - "IsRequired": true, + "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "261", - "Type": { - "$ref": "260" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Method" } ], "Responses": [ { - "$id": "262", + "$id": "261", "StatusCodes": [ 200 ], @@ -1909,7 +1867,7 @@ "GenerateConvenienceMethod": false }, { - "$id": "263", + "$id": "262", "Name": "helloDemo2", "ResourceName": "UnbrandedTypeSpec", "Description": "Return hi in demo2", @@ -1919,34 +1877,30 @@ "$ref": "224" }, { - "$id": "264", + "$id": "263", "Name": "accept", "NameInRequest": "Accept", "Type": { - "$id": "265", - "Kind": "string" + "$id": "264", + "Kind": "constant", + "ValueType": { + "$id": "265", + "Kind": "string" + }, + "Value": "application/json" }, "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "266", - "Type": { - "$ref": "265" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Constant" } ], "Responses": [ { - "$id": "267", + "$id": "266", "StatusCodes": [ 200 ], @@ -1970,7 +1924,7 @@ "GenerateConvenienceMethod": true }, { - "$id": "268", + "$id": "267", "Name": "createLiteral", "ResourceName": "UnbrandedTypeSpec", "Description": "Create with literal value", @@ -1980,76 +1934,67 @@ "$ref": "224" }, { - "$id": "269", - "Name": "body", - "NameInRequest": "body", + "$id": "268", + "Name": "contentType", + "NameInRequest": "Content-Type", + "Description": "Body parameter's content type. Known values are application/json", "Type": { - "$ref": "78" + "$id": "269", + "Kind": "constant", + "ValueType": { + "$id": "270", + "Kind": "string" + }, + "Value": "application/json" }, - "Location": "Body", - "IsRequired": true, + "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": false, + "IsContentType": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Method" + "IsRequired": true, + "Kind": "Constant" }, { - "$id": "270", + "$id": "271", "Name": "accept", "NameInRequest": "Accept", "Type": { - "$id": "271", - "Kind": "string" + "$id": "272", + "Kind": "constant", + "ValueType": { + "$id": "273", + "Kind": "string" + }, + "Value": "application/json" }, "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "272", - "Type": { - "$ref": "271" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Constant" }, { - "$id": "273", - "Name": "contentType", - "NameInRequest": "Content-Type", + "$id": "274", + "Name": "body", + "NameInRequest": "body", "Type": { - "$id": "274", - "Kind": "string" + "$ref": "78" }, - "Location": "Header", + "Location": "Body", "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": true, - "IsRequired": true, + "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "275", - "Type": { - "$ref": "274" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Method" } ], "Responses": [ { - "$id": "276", + "$id": "275", "StatusCodes": [ 200 ], @@ -2076,7 +2021,7 @@ "GenerateConvenienceMethod": true }, { - "$id": "277", + "$id": "276", "Name": "helloLiteral", "ResourceName": "UnbrandedTypeSpec", "Description": "Send literal parameters", @@ -2086,124 +2031,93 @@ "$ref": "224" }, { - "$id": "278", + "$id": "277", "Name": "p1", "NameInRequest": "p1", "Type": { - "$id": "279", + "$id": "278", "Kind": "constant", "ValueType": { - "$id": "280", + "$id": "279", "Kind": "string" }, "Value": "test" }, "Location": "Header", - "DefaultValue": { - "$id": "281", - "Type": { - "$ref": "279" - }, - "Value": "test" - }, - "IsRequired": true, "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, + "IsRequired": true, "Kind": "Constant" }, { - "$id": "282", + "$id": "280", "Name": "p2", "NameInRequest": "p2", "Type": { - "$id": "283", + "$id": "281", "Kind": "constant", "ValueType": { - "$id": "284", + "$id": "282", "Kind": "int32" }, "Value": 123 }, "Location": "Path", - "DefaultValue": { - "$id": "285", - "Type": { - "$ref": "283" - }, - "Value": 123 - }, - "IsRequired": true, "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, + "IsRequired": true, "Kind": "Constant" }, { - "$id": "286", + "$id": "283", "Name": "p3", "NameInRequest": "p3", "Type": { - "$id": "287", + "$id": "284", "Kind": "constant", "ValueType": { - "$id": "288", + "$id": "285", "Kind": "boolean" }, "Value": true }, "Location": "Query", - "DefaultValue": { - "$id": "289", - "Type": { - "$ref": "287" - }, - "Value": true - }, - "IsRequired": true, "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, + "IsRequired": true, "Kind": "Constant" }, { - "$id": "290", + "$id": "286", "Name": "accept", "NameInRequest": "Accept", "Type": { - "$id": "291", - "Kind": "string" + "$id": "287", + "Kind": "constant", + "ValueType": { + "$id": "288", + "Kind": "string" + }, + "Value": "application/json" }, "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "292", - "Type": { - "$ref": "291" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Constant" } ], "Responses": [ { - "$id": "293", + "$id": "289", "StatusCodes": [ 200 ], @@ -2227,7 +2141,7 @@ "GenerateConvenienceMethod": true }, { - "$id": "294", + "$id": "290", "Name": "topAction", "ResourceName": "UnbrandedTypeSpec", "Description": "top level method", @@ -2237,57 +2151,51 @@ "$ref": "224" }, { - "$id": "295", + "$id": "291", "Name": "action", "NameInRequest": "action", "Type": { - "$id": "296", + "$id": "292", "Kind": "utcDateTime", "Encode": "rfc3339", "WireType": { - "$id": "297", + "$id": "293", "Kind": "string" } }, "Location": "Path", - "IsRequired": true, "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, + "IsRequired": true, "Kind": "Method" }, { - "$id": "298", + "$id": "294", "Name": "accept", "NameInRequest": "Accept", "Type": { - "$id": "299", - "Kind": "string" + "$id": "295", + "Kind": "constant", + "ValueType": { + "$id": "296", + "Kind": "string" + }, + "Value": "application/json" }, "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "300", - "Type": { - "$ref": "299" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Constant" } ], "Responses": [ { - "$id": "301", + "$id": "297", "StatusCodes": [ 200 ], @@ -2311,7 +2219,7 @@ "GenerateConvenienceMethod": true }, { - "$id": "302", + "$id": "298", "Name": "topAction2", "ResourceName": "UnbrandedTypeSpec", "Description": "top level method2", @@ -2321,34 +2229,30 @@ "$ref": "224" }, { - "$id": "303", + "$id": "299", "Name": "accept", "NameInRequest": "Accept", "Type": { - "$id": "304", - "Kind": "string" + "$id": "300", + "Kind": "constant", + "ValueType": { + "$id": "301", + "Kind": "string" + }, + "Value": "application/json" }, "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "305", - "Type": { - "$ref": "304" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Constant" } ], "Responses": [ { - "$id": "306", + "$id": "302", "StatusCodes": [ 200 ], @@ -2372,7 +2276,7 @@ "GenerateConvenienceMethod": false }, { - "$id": "307", + "$id": "303", "Name": "patchAction", "ResourceName": "UnbrandedTypeSpec", "Description": "top level patch", @@ -2382,76 +2286,67 @@ "$ref": "224" }, { - "$id": "308", - "Name": "body", - "NameInRequest": "body", + "$id": "304", + "Name": "contentType", + "NameInRequest": "Content-Type", + "Description": "Body parameter's content type. Known values are application/json", "Type": { - "$ref": "78" + "$id": "305", + "Kind": "constant", + "ValueType": { + "$id": "306", + "Kind": "string" + }, + "Value": "application/json" }, - "Location": "Body", - "IsRequired": true, + "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": false, + "IsContentType": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Method" + "IsRequired": true, + "Kind": "Constant" }, { - "$id": "309", + "$id": "307", "Name": "accept", "NameInRequest": "Accept", "Type": { - "$id": "310", - "Kind": "string" + "$id": "308", + "Kind": "constant", + "ValueType": { + "$id": "309", + "Kind": "string" + }, + "Value": "application/json" }, "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "311", - "Type": { - "$ref": "310" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Constant" }, { - "$id": "312", - "Name": "contentType", - "NameInRequest": "Content-Type", + "$id": "310", + "Name": "body", + "NameInRequest": "body", "Type": { - "$id": "313", - "Kind": "string" + "$ref": "78" }, - "Location": "Header", + "Location": "Body", "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": true, - "IsRequired": true, + "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "314", - "Type": { - "$ref": "313" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Method" } ], "Responses": [ { - "$id": "315", + "$id": "311", "StatusCodes": [ 200 ], @@ -2478,7 +2373,7 @@ "GenerateConvenienceMethod": false }, { - "$id": "316", + "$id": "312", "Name": "anonymousBody", "ResourceName": "UnbrandedTypeSpec", "Description": "body parameter without body decorator", @@ -2488,76 +2383,67 @@ "$ref": "224" }, { - "$id": "317", - "Name": "AnonymousBodyRequest", - "NameInRequest": "", + "$id": "313", + "Name": "contentType", + "NameInRequest": "Content-Type", + "Description": "Body parameter's content type. Known values are application/json", "Type": { - "$ref": "172" + "$id": "314", + "Kind": "constant", + "ValueType": { + "$id": "315", + "Kind": "string" + }, + "Value": "application/json" }, - "Location": "Body", - "IsRequired": true, + "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": false, + "IsContentType": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Spread" + "IsRequired": true, + "Kind": "Constant" }, { - "$id": "318", + "$id": "316", "Name": "accept", "NameInRequest": "Accept", "Type": { - "$id": "319", - "Kind": "string" + "$id": "317", + "Kind": "constant", + "ValueType": { + "$id": "318", + "Kind": "string" + }, + "Value": "application/json" }, "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "320", - "Type": { - "$ref": "319" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Constant" }, { - "$id": "321", - "Name": "contentType", - "NameInRequest": "Content-Type", + "$id": "319", + "Name": "anonymousBodyRequest", + "NameInRequest": "anonymousBodyRequest", "Type": { - "$id": "322", - "Kind": "string" + "$ref": "172" }, - "Location": "Header", + "Location": "Body", "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": true, - "IsRequired": true, + "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "323", - "Type": { - "$ref": "322" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Spread" } ], "Responses": [ { - "$id": "324", + "$id": "320", "StatusCodes": [ 200 ], @@ -2584,7 +2470,7 @@ "GenerateConvenienceMethod": true }, { - "$id": "325", + "$id": "321", "Name": "friendlyModel", "ResourceName": "UnbrandedTypeSpec", "Description": "Model can have its friendly name", @@ -2594,76 +2480,67 @@ "$ref": "224" }, { - "$id": "326", - "Name": "FriendlyModelRequest", - "NameInRequest": "", + "$id": "322", + "Name": "contentType", + "NameInRequest": "Content-Type", + "Description": "Body parameter's content type. Known values are application/json", "Type": { - "$ref": "209" + "$id": "323", + "Kind": "constant", + "ValueType": { + "$id": "324", + "Kind": "string" + }, + "Value": "application/json" }, - "Location": "Body", - "IsRequired": true, + "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": false, + "IsContentType": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Spread" + "IsRequired": true, + "Kind": "Constant" }, { - "$id": "327", + "$id": "325", "Name": "accept", "NameInRequest": "Accept", "Type": { - "$id": "328", - "Kind": "string" + "$id": "326", + "Kind": "constant", + "ValueType": { + "$id": "327", + "Kind": "string" + }, + "Value": "application/json" }, "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "329", - "Type": { - "$ref": "328" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Constant" }, { - "$id": "330", - "Name": "contentType", - "NameInRequest": "Content-Type", + "$id": "328", + "Name": "friendlyModelRequest", + "NameInRequest": "friendlyModelRequest", "Type": { - "$id": "331", - "Kind": "string" + "$ref": "209" }, - "Location": "Header", + "Location": "Body", "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": true, - "IsRequired": true, + "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "332", - "Type": { - "$ref": "331" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Spread" } ], "Responses": [ { - "$id": "333", + "$id": "329", "StatusCodes": [ 200 ], @@ -2690,7 +2567,7 @@ "GenerateConvenienceMethod": true }, { - "$id": "334", + "$id": "330", "Name": "addTimeHeader", "ResourceName": "UnbrandedTypeSpec", "Accessibility": "public", @@ -2699,57 +2576,30 @@ "$ref": "224" }, { - "$id": "335", + "$id": "331", "Name": "repeatabilityFirstSent", "NameInRequest": "Repeatability-First-Sent", "Type": { - "$id": "336", + "$id": "332", "Kind": "utcDateTime", "Encode": "rfc7231", "WireType": { - "$id": "337", + "$id": "333", "Kind": "string" } }, "Location": "Header", - "IsRequired": false, "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, + "IsRequired": false, "Kind": "Method" - }, - { - "$id": "338", - "Name": "accept", - "NameInRequest": "Accept", - "Type": { - "$id": "339", - "Kind": "string" - }, - "Location": "Header", - "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": false, - "IsRequired": true, - "IsEndpoint": false, - "SkipUrlEncoding": false, - "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "340", - "Type": { - "$ref": "339" - }, - "Value": "application/json" - } } ], "Responses": [ { - "$id": "341", + "$id": "334", "StatusCodes": [ 204 ], @@ -2767,7 +2617,7 @@ "GenerateConvenienceMethod": true }, { - "$id": "342", + "$id": "335", "Name": "projectedNameModel", "ResourceName": "UnbrandedTypeSpec", "Description": "Model can have its projected name", @@ -2777,76 +2627,67 @@ "$ref": "224" }, { - "$id": "343", - "Name": "ProjectedNameModelRequest", - "NameInRequest": "", + "$id": "336", + "Name": "contentType", + "NameInRequest": "Content-Type", + "Description": "Body parameter's content type. Known values are application/json", "Type": { - "$ref": "215" + "$id": "337", + "Kind": "constant", + "ValueType": { + "$id": "338", + "Kind": "string" + }, + "Value": "application/json" }, - "Location": "Body", - "IsRequired": true, + "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": false, + "IsContentType": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Spread" + "IsRequired": true, + "Kind": "Constant" }, { - "$id": "344", + "$id": "339", "Name": "accept", "NameInRequest": "Accept", "Type": { - "$id": "345", - "Kind": "string" + "$id": "340", + "Kind": "constant", + "ValueType": { + "$id": "341", + "Kind": "string" + }, + "Value": "application/json" }, "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "346", - "Type": { - "$ref": "345" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Constant" }, { - "$id": "347", - "Name": "contentType", - "NameInRequest": "Content-Type", + "$id": "342", + "Name": "projectedNameModelRequest", + "NameInRequest": "projectedNameModelRequest", "Type": { - "$id": "348", - "Kind": "string" + "$ref": "215" }, - "Location": "Header", + "Location": "Body", "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": true, - "IsRequired": true, + "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "349", - "Type": { - "$ref": "348" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Spread" } ], "Responses": [ { - "$id": "350", + "$id": "343", "StatusCodes": [ 200 ], @@ -2873,7 +2714,7 @@ "GenerateConvenienceMethod": true }, { - "$id": "351", + "$id": "344", "Name": "returnsAnonymousModel", "ResourceName": "UnbrandedTypeSpec", "Description": "return anonymous model", @@ -2883,34 +2724,30 @@ "$ref": "224" }, { - "$id": "352", + "$id": "345", "Name": "accept", "NameInRequest": "Accept", "Type": { - "$id": "353", - "Kind": "string" + "$id": "346", + "Kind": "constant", + "ValueType": { + "$id": "347", + "Kind": "string" + }, + "Value": "application/json" }, "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "354", - "Type": { - "$ref": "353" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Constant" } ], "Responses": [ { - "$id": "355", + "$id": "348", "StatusCodes": [ 200 ], @@ -2934,7 +2771,7 @@ "GenerateConvenienceMethod": true }, { - "$id": "356", + "$id": "349", "Name": "getUnknownValue", "ResourceName": "UnbrandedTypeSpec", "Description": "get extensible enum", @@ -2944,45 +2781,48 @@ "$ref": "224" }, { - "$id": "357", + "$id": "350", "Name": "accept", "NameInRequest": "Accept", "Type": { - "$id": "358", + "$id": "351", "Kind": "string" }, "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "359", - "Type": { - "$ref": "358" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Method" } ], "Responses": [ { - "$id": "360", + "$id": "352", "StatusCodes": [ 200 ], "BodyType": { - "$id": "361", - "Kind": "string" + "$id": "353", + "Kind": "constant", + "ValueType": { + "$id": "354", + "Kind": "string" + }, + "Value": "Sunday" }, "BodyMediaType": "Json", "Headers": [], "IsErrorResponse": false, "ContentTypes": [ + "application/json", + "application/json", + "application/json", + "application/json", + "application/json", + "application/json", + "application/json", "application/json" ] } @@ -2996,7 +2836,7 @@ "GenerateConvenienceMethod": true }, { - "$id": "362", + "$id": "355", "Name": "internalProtocol", "ResourceName": "UnbrandedTypeSpec", "Description": "When set protocol false and convenient true, then the protocol method should be internal", @@ -3006,76 +2846,67 @@ "$ref": "224" }, { - "$id": "363", - "Name": "body", - "NameInRequest": "body", + "$id": "356", + "Name": "contentType", + "NameInRequest": "Content-Type", + "Description": "Body parameter's content type. Known values are application/json", "Type": { - "$ref": "78" + "$id": "357", + "Kind": "constant", + "ValueType": { + "$id": "358", + "Kind": "string" + }, + "Value": "application/json" }, - "Location": "Body", - "IsRequired": true, + "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": false, + "IsContentType": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Method" + "IsRequired": true, + "Kind": "Constant" }, { - "$id": "364", + "$id": "359", "Name": "accept", "NameInRequest": "Accept", "Type": { - "$id": "365", - "Kind": "string" + "$id": "360", + "Kind": "constant", + "ValueType": { + "$id": "361", + "Kind": "string" + }, + "Value": "application/json" }, "Location": "Header", "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, - "IsRequired": true, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "366", - "Type": { - "$ref": "365" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Constant" }, { - "$id": "367", - "Name": "contentType", - "NameInRequest": "Content-Type", + "$id": "362", + "Name": "body", + "NameInRequest": "body", "Type": { - "$id": "368", - "Kind": "string" + "$ref": "78" }, - "Location": "Header", + "Location": "Body", "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": true, - "IsRequired": true, + "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "369", - "Type": { - "$ref": "368" - }, - "Value": "application/json" - } + "IsRequired": true, + "Kind": "Method" } ], "Responses": [ { - "$id": "370", + "$id": "363", "StatusCodes": [ 200 ], @@ -3102,7 +2933,7 @@ "GenerateConvenienceMethod": true }, { - "$id": "371", + "$id": "364", "Name": "stillConvenient", "ResourceName": "UnbrandedTypeSpec", "Description": "When set protocol false and convenient true, the convenient method should be generated even it has the same signature as protocol one", @@ -3110,36 +2941,11 @@ "Parameters": [ { "$ref": "224" - }, - { - "$id": "372", - "Name": "accept", - "NameInRequest": "Accept", - "Type": { - "$id": "373", - "Kind": "string" - }, - "Location": "Header", - "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": false, - "IsRequired": true, - "IsEndpoint": false, - "SkipUrlEncoding": false, - "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "374", - "Type": { - "$ref": "373" - }, - "Value": "application/json" - } } ], "Responses": [ { - "$id": "375", + "$id": "365", "StatusCodes": [ 204 ], @@ -3157,7 +2963,7 @@ "GenerateConvenienceMethod": true }, { - "$id": "376", + "$id": "366", "Name": "headAsBoolean", "ResourceName": "UnbrandedTypeSpec", "Description": "head as boolean.", @@ -3167,52 +2973,25 @@ "$ref": "224" }, { - "$id": "377", + "$id": "367", "Name": "id", "NameInRequest": "id", "Type": { - "$id": "378", + "$id": "368", "Kind": "string" }, "Location": "Path", - "IsRequired": true, "IsApiVersion": false, - "IsResourceParameter": false, "IsContentType": false, "IsEndpoint": false, - "SkipUrlEncoding": false, "Explode": false, - "Kind": "Method" - }, - { - "$id": "379", - "Name": "accept", - "NameInRequest": "Accept", - "Type": { - "$id": "380", - "Kind": "string" - }, - "Location": "Header", - "IsApiVersion": false, - "IsResourceParameter": false, - "IsContentType": false, "IsRequired": true, - "IsEndpoint": false, - "SkipUrlEncoding": false, - "Explode": false, - "Kind": "Constant", - "DefaultValue": { - "$id": "381", - "Type": { - "$ref": "380" - }, - "Value": "application/json" - } + "Kind": "Method" } ], "Responses": [ { - "$id": "382", + "$id": "369", "StatusCodes": [ 204 ], @@ -3231,7 +3010,7 @@ } ], "Protocol": { - "$id": "383" + "$id": "370" }, "Parameters": [ { @@ -3241,9 +3020,9 @@ } ], "Auth": { - "$id": "384", + "$id": "371", "ApiKey": { - "$id": "385", + "$id": "372", "Name": "my-api-key" } }