UNPKG

@autorest/go

Version:
554 lines 27.8 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as m4 from '@autorest/codemodel'; import { KnownMediaType } from '@azure-tools/codegen'; import { values } from '@azure-tools/linq'; import { adaptXMLInfo } from './types.js'; import { adaptPossibleType, hasDescription } from './types.js'; import * as go from '../../../codemodel.go/src/index.js'; import * as helpers from '../transform/helpers.js'; // track all of the client and parameter group params across all operations // as not every option might contain them, and parameter groups can be shared // across multiple operations const clientParams = new Map(); const paramGroups = new Map(); export function adaptClients(m4CodeModel, codeModel) { var _a, _b, _c, _d; for (const group of values(m4CodeModel.operationGroups)) { const client = adaptClient(codeModel.type, group); for (const op of values(group.operations)) { const httpPath = op.requests[0].protocol.http.path; const httpMethod = op.requests[0].protocol.http.method; let method; const naming = adaptMethodNaming(op); if (helpers.isLROOperation(op) && helpers.isPageableOperation(op)) { method = new go.LROPageableMethod(op.language.go.name, client, httpPath, httpMethod, getStatusCodes(op), naming); method.finalStateVia = ((_b = (_a = op.extensions) === null || _a === void 0 ? void 0 : _a['x-ms-long-running-operation-options']) === null || _b === void 0 ? void 0 : _b['final-state-via']); method.nextLinkName = op.language.go.paging.nextLinkName; if (op.language.go.paging.nextLinkOperation) { // adapt the next link operation const nextPageMethod = new go.NextPageMethod(op.language.go.paging.nextLinkOperation.language.go.name, client, httpPath, httpMethod, getStatusCodes(op.language.go.paging.nextLinkOperation)); populateMethod(op.language.go.paging.nextLinkOperation, nextPageMethod, m4CodeModel, codeModel); method.nextPageMethod = nextPageMethod; } } else if (helpers.isLROOperation(op)) { method = new go.LROMethod(op.language.go.name, client, httpPath, httpMethod, getStatusCodes(op), naming); method.finalStateVia = ((_d = (_c = op.extensions) === null || _c === void 0 ? void 0 : _c['x-ms-long-running-operation-options']) === null || _d === void 0 ? void 0 : _d['final-state-via']); } else if (helpers.isPageableOperation(op)) { if (op.language.go.paging.isNextOp) { continue; } method = new go.PageableMethod(op.language.go.name, client, httpPath, httpMethod, getStatusCodes(op), naming); method.nextLinkName = op.language.go.paging.nextLinkName; if (op.language.go.paging.nextLinkOperation) { // adapt the next link operation const nextPageMethod = adaptNextPageMethod(op, m4CodeModel, client, codeModel); method.nextPageMethod = nextPageMethod; } } else { method = new go.Method(op.language.go.name, client, httpPath, httpMethod, getStatusCodes(op), naming); } populateMethod(op, method, m4CodeModel, codeModel); client.methods.push(method); } // if any client parameters were adapted, add them to the client if (group.language.go.clientParams) { for (const param of group.language.go.clientParams) { const adaptedParam = clientParams.get(param.language.go.name); if (!adaptedParam) { throw new Error(`missing adapted client parameter ${param.language.go.name}`); } client.parameters.push(adaptedParam); } } codeModel.clients.push(client); } } // used to ensure unique instances of go.NextPageMethod since they can be shared across operations // only adaptNextPageMethod should touch this! const adaptedNextPageMethods = new Map(); function adaptNextPageMethod(op, m4CodeModel, client, codeModel) { const nextPageMethodName = op.language.go.paging.nextLinkOperation.language.go.name; let nextPageMethod = adaptedNextPageMethods.get(nextPageMethodName); if (!nextPageMethod) { const httpPath = op.language.go.paging.nextLinkOperation.requests[0].protocol.http.path; const httpMethod = op.language.go.paging.nextLinkOperation.requests[0].protocol.http.method; nextPageMethod = new go.NextPageMethod(nextPageMethodName, client, httpPath, httpMethod, getStatusCodes(op.language.go.paging.nextLinkOperation)); populateMethod(op.language.go.paging.nextLinkOperation, nextPageMethod, m4CodeModel, codeModel); adaptedNextPageMethods.set(nextPageMethodName, nextPageMethod); } return nextPageMethod; } function populateMethod(op, method, m4CodeModel, codeModel) { if (go.isMethod(method)) { if (hasDescription(op.language.go)) { method.docs.description = op.language.go.description; } let optionalParamsGroup = paramGroups.get(op.language.go.optionalParamGroup.schema.language.go.name); if (!optionalParamsGroup) { optionalParamsGroup = adaptParameterGroup('method', op.language.go.optionalParamGroup); paramGroups.set(op.language.go.optionalParamGroup.schema.language.go.name, optionalParamsGroup); } method.optionalParamsGroup = optionalParamsGroup; method.responseEnvelope = adaptResponseEnvelope(m4CodeModel, codeModel, op, method); } adaptMethodParameters(op, method); for (const apiver of values(op.apiVersions)) { method.apiVersions.push(apiver.version); } } function adaptHeaderType(schema, forParam) { // for header params, we never pass the element type by pointer const type = adaptPossibleType(schema, forParam); if (go.isInterfaceType(type) || go.isMapType(type) || go.isModelType(type) || go.isPolymorphicType(type) || go.isSliceType(type) || go.isQualifiedType(type)) { throw new Error(`unexpected header parameter type ${schema.type}`); } return type; } function adaptPathParameterType(schema) { const type = adaptPossibleType(schema); if (go.isMapType(type) || go.isInterfaceType(type) || go.isModelType(type) || go.isPolymorphicType(type) || go.isSliceType(type) || go.isQualifiedType(type)) { throw new Error(`unexpected path parameter type ${schema.type}`); } return type; } function adaptQueryParameterType(schema) { const type = adaptPossibleType(schema); if (go.isMapType(type) || go.isInterfaceType(type) || go.isModelType(type) || go.isPolymorphicType(type) || go.isSliceType(type) || go.isQualifiedType(type)) { throw new Error(`unexpected query parameter type ${schema.type}`); } else if (go.isSliceType(type)) { type.elementTypeByValue = true; } return type; } function adaptURIPrameterType(schema) { const type = adaptPossibleType(schema); if (!go.isConstantType(type) && !go.isPrimitiveType(type)) { throw new Error(`unexpected URI parameter type ${schema.type}`); } return type; } function adaptClient(type, group) { const description = `${group.language.go.clientName} contains the methods for the ${group.language.go.name} group.`; const client = new go.Client(group.language.go.clientName, { description: description }, go.newClientOptions(type, group.language.go.clientName)); client.host = group.language.go.host; if (group.language.go.complexHostParams) { client.templatedHost = true; } if (group.language.go.hostParams) { for (const hostParam of values(group.language.go.hostParams)) { const uriParam = new go.URIParameter(hostParam.language.go.name, hostParam.language.go.serializedName, adaptURIPrameterType(hostParam.schema), adaptParameterKind(hostParam), hostParam.language.go.byValue, adaptMethodLocation(hostParam.implementation)); client.parameters.push(uriParam); } } return client; } function adaptMethodLocation(location) { if (!location) { return 'method'; } switch (location) { case m4.ImplementationLocation.Client: return 'client'; case m4.ImplementationLocation.Method: return 'method'; default: throw new Error(`unhandled parameter location type ${location}`); } } function adaptMethodParameters(op, method) { if (!op.parameters) { return; } for (const param of values(helpers.aggregateParameters(op))) { const methodParam = adaptMethodParameter(op, param); method.parameters.push(methodParam); } } function adaptResponseEnvelope(m4CodeModel, codeModel, op, forMethod) { const respEnvSchema = op.language.go.responseEnv; const respEnv = new go.ResponseEnvelope(respEnvSchema.language.go.name, { description: respEnvSchema.language.go.description }, forMethod); // add any headers for (const prop of values(respEnvSchema.properties)) { if (prop.language.go.fromHeader) { let headerResp; if (prop.schema.language.go.headerCollectionPrefix) { const headerType = adaptPossibleType(prop.schema, false); if (!go.isMapType(headerType)) { throw new Error(`unexpected type ${go.getTypeDeclaration(headerType)} for HeaderMapResponse ${prop.language.go.name}`); } headerResp = new go.HeaderMapResponse(prop.language.go.name, headerType, prop.schema.language.go.headerCollectionPrefix, prop.language.go.fromHeader, prop.language.go.byValue); } else { headerResp = new go.HeaderResponse(prop.language.go.name, adaptHeaderType(prop.schema, false), prop.language.go.fromHeader, prop.language.go.byValue); } if (hasDescription(prop.language.go)) { headerResp.docs.description = prop.language.go.description; } respEnv.headers.push(headerResp); } } if (!respEnvSchema.language.go.resultProp) { return respEnv; } // now add the result field const resultProp = respEnvSchema.language.go.resultProp; if (helpers.isMultiRespOperation(op)) { respEnv.result = adaptAnyResult(op); } else if (resultProp.schema.type === m4.SchemaType.Binary) { respEnv.result = new go.BinaryResult(resultProp.language.go.name, 'binary'); } else if (m4CodeModel.language.go.headAsBoolean && op.requests[0].protocol.http.method === 'head') { respEnv.result = new go.HeadAsBooleanResult(resultProp.language.go.name); } else if (!resultProp.language.go.embeddedType) { const resultType = adaptPossibleType(resultProp.schema); if (go.isInterfaceType(resultType) || go.isLiteralValue(resultType) || go.isModelType(resultType) || go.isPolymorphicType(resultType) || go.isQualifiedType(resultType)) { throw new Error(`invalid monomorphic result type ${resultType}`); } respEnv.result = new go.MonomorphicResult(resultProp.language.go.name, adaptResultFormat(helpers.getSchemaResponse(op).protocol), resultType, resultProp.language.go.byValue); respEnv.result.xml = adaptXMLInfo(resultProp.schema); } else if (resultProp.isDiscriminator) { let ifaceResult; for (const iface of values(codeModel.interfaceTypes)) { if (iface.name === resultProp.schema.language.go.name) { ifaceResult = iface; break; } } if (!ifaceResult) { throw new Error(`didn't find InterfaceType for result property ${resultProp.schema.language.go.name}`); } respEnv.result = new go.PolymorphicResult(ifaceResult); } else if (helpers.getSchemaResponse(op)) { let modelType; for (const model of codeModel.models) { if (model.name === resultProp.schema.language.go.name) { modelType = model; break; } } if (!modelType) { throw new Error(`didn't find type name ${resultProp.schema.language.go.name} for response envelope ${respEnv.name}`); } const resultFormat = adaptResultFormat(helpers.getSchemaResponse(op).protocol); if (resultFormat !== 'JSON' && resultFormat !== 'XML') { throw new Error(`unexpected result format ${resultFormat} for model ${modelType.name}`); } respEnv.result = new go.ModelResult(modelType, resultFormat); } else { throw new Error(`unhandled result type for operation ${op.language.go.name}`); } if (hasDescription(resultProp.language.go)) { respEnv.result.docs.description = resultProp.language.go.description; } return respEnv; } function adaptMethodNaming(op) { const info = op.language.go; return new go.MethodNaming(info.protocolNaming.internalMethod, info.protocolNaming.requestMethod, info.protocolNaming.responseMethod); } function getStatusCodes(op) { var _a; // concat all status codes that return the same schema into one array. // this is to support operations that specify multiple response codes // that return the same schema (or no schema). let statusCodes = new Array(); for (const resp of values(op.responses)) { statusCodes = statusCodes.concat(parseInt((_a = resp.protocol.http) === null || _a === void 0 ? void 0 : _a.statusCodes)); } if (statusCodes.length === 0) { // if the operation defines no status codes (which is non-conformant) // then add 200, 201, 202, and 204 to the list. this is to accomodate // some quirky tests in the test server. // TODO: https://github.com/Azure/autorest.go/issues/659 statusCodes = [200, 201, 202, 204]; } return statusCodes; } function adaptMethodParameter(op, param) { var _a, _b; let adaptedParam; let location = 'method'; if (param.implementation === m4.ImplementationLocation.Client) { // check if we've already adapted this client parameter // TODO: grouped client params const clientParam = clientParams.get(param.language.go.name); if (clientParam) { return clientParam; } location = 'client'; } switch ((_a = param.protocol.http) === null || _a === void 0 ? void 0 : _a.in) { case 'body': { if (!op.requests[0].protocol.http.mediaTypes) { throw new Error(`no media types defined for operation ${op.operationId}`); } let contentType = `"${op.requests[0].protocol.http.mediaTypes[0]}"`; if (op.requests[0].protocol.http.mediaTypes.length > 1) { for (const param of values(op.requests[0].parameters)) { // If a request defined more than one possible media type, then the param is expected to be synthesized from modelerfour // and should be a SealedChoice schema type that account for the acceptable media types defined in the swagger. if (param.origin === 'modelerfour:synthesized/content-type' && param.schema.type === m4.SchemaType.SealedChoice) { contentType = `string(${param.language.go.name})`; } } } const bodyType = adaptPossibleType(param.schema); const placement = adaptParameterKind(param); if (op.requests[0].protocol.http.knownMediaType === KnownMediaType.Form) { const collectionFormat = adaptCollectionFormat(param); if (collectionFormat) { if (!go.isSliceType(bodyType)) { throw new Error(`unexpected type ${go.getTypeDeclaration(bodyType)} for FormBodyCollectionParameter ${param.language.go.name}`); } adaptedParam = new go.FormBodyCollectionParameter(param.language.go.name, param.language.go.serializedName, bodyType, collectionFormat, placement, param.language.go.byValue); } else { adaptedParam = new go.FormBodyParameter(param.language.go.name, param.language.go.serializedName, bodyType, placement, param.language.go.byValue); } } else if (op.requests[0].protocol.http.knownMediaType === KnownMediaType.Multipart) { adaptedParam = new go.MultipartFormBodyParameter(param.language.go.name, bodyType, placement, param.language.go.byValue); } else { const format = adaptBodyFormat(op.requests[0].protocol); adaptedParam = new go.BodyParameter(param.language.go.name, format, contentType, bodyType, placement, param.language.go.byValue); adaptedParam.xml = adaptXMLInfo(param.schema); } break; } case 'header': { const collectionFormat = adaptCollectionFormat(param); if (param.schema.language.go.headerCollectionPrefix) { const headerType = adaptPossibleType(param.schema, true); if (!go.isMapType(headerType)) { throw new Error(`unexpected type ${go.getTypeDeclaration(headerType)} for HeaderMapParameter ${param.language.go.name}`); } adaptedParam = new go.HeaderMapParameter(param.language.go.name, param.language.go.serializedName, headerType, param.schema.language.go.headerCollectionPrefix, adaptParameterKind(param), param.language.go.byValue, location); } else if (collectionFormat) { const headerType = adaptPossibleType(param.schema, true); if (!go.isSliceType(headerType)) { throw new Error(`unexpected type ${go.getTypeDeclaration(headerType)} for HeaderCollectionParameter ${param.language.go.name}`); } adaptedParam = new go.HeaderCollectionParameter(param.language.go.name, param.language.go.serializedName, headerType, collectionFormat, adaptParameterKind(param), param.language.go.byValue, location); } else { adaptedParam = new go.HeaderParameter(param.language.go.name, param.language.go.serializedName, adaptHeaderType(param.schema, true), adaptParameterKind(param), param.language.go.byValue, location); } break; } case 'path': { const collectionFormat = adaptCollectionFormat(param); if (collectionFormat) { const pathType = adaptPossibleType(param.schema); if (!go.isSliceType(pathType)) { throw new Error(`unexpected type ${go.getTypeDeclaration(pathType)} for PathCollectionParameter ${param.language.go.name}`); } adaptedParam = new go.PathCollectionParameter(param.language.go.name, param.language.go.serializedName, !skipURLEncoding(param), pathType, collectionFormat, adaptParameterKind(param), param.language.go.byValue, location); } else { adaptedParam = new go.PathParameter(param.language.go.name, param.language.go.serializedName, !skipURLEncoding(param), adaptPathParameterType(param.schema), adaptParameterKind(param), param.language.go.byValue, location); } break; } case 'query': { const collectionFormat = adaptExtendedCollectionFormat(param); if (collectionFormat) { const queryType = adaptPossibleType(param.schema); if (!go.isSliceType(queryType)) { throw new Error(`unexpected type ${go.getTypeDeclaration(queryType)} for QueryCollectionParameter ${param.language.go.name}`); } adaptedParam = new go.QueryCollectionParameter(param.language.go.name, param.language.go.serializedName, !skipURLEncoding(param), queryType, collectionFormat, adaptParameterKind(param), param.language.go.byValue, location); } else { adaptedParam = new go.QueryParameter(param.language.go.name, param.language.go.serializedName, !skipURLEncoding(param), adaptQueryParameterType(param.schema), adaptParameterKind(param), param.language.go.byValue, location); } break; } case 'uri': adaptedParam = new go.URIParameter(param.language.go.name, param.language.go.serializedName, adaptURIPrameterType(param.schema), adaptParameterKind(param), param.language.go.byValue, adaptParameterlocation(param)); break; default: { if ((_b = param.protocol.http) === null || _b === void 0 ? void 0 : _b.in) { throw new Error(`unhandled parameter location type ${param.protocol.http.in}`); } // this is a synthesized parameter (e.g. ResumeToken) if (param.language.go.isResumeToken) { adaptedParam = new go.ResumeTokenParameter(); } else { const type = adaptPossibleType(param.schema); const placement = adaptParameterKind(param); const paramLoc = adaptParameterlocation(param); adaptedParam = new go.Parameter(param.language.go.name, type, placement, param.language.go.byValue, paramLoc); } } } if (hasDescription(param.language.go)) { adaptedParam.docs.description = param.language.go.description; } // track client parameter for later use if (adaptedParam.location === 'client') { clientParams.set(param.language.go.name, adaptedParam); } if (param.language.go.paramGroup) { const paramGroup = findOrAdaptParamsGroup(param); // parameter groups can be shared across methods so don't add any duplicate parameters if (values(paramGroup.params).where((each) => { return each.name === adaptedParam.name; }).count() === 0) { paramGroup.params.push(adaptedParam); } if (adaptedParam.kind === 'required') { // if at least one param within a group is required then the group must be required. // however, it's possible that the param group was initially created from a non-required // param. so we need to be sure to update it as required. paramGroup.required = true; } adaptedParam.group = paramGroup; } return adaptedParam; } function adaptCollectionFormat(param) { var _a; switch ((_a = param.protocol.http) === null || _a === void 0 ? void 0 : _a.style) { case m4.SerializationStyle.PipeDelimited: return 'pipes'; case m4.SerializationStyle.Simple: return 'csv'; case m4.SerializationStyle.SpaceDelimited: return 'ssv'; case m4.SerializationStyle.TabDelimited: return 'tsv'; default: return undefined; } } function adaptExtendedCollectionFormat(param) { var _a, _b; switch ((_a = param.protocol.http) === null || _a === void 0 ? void 0 : _a.style) { case m4.SerializationStyle.Form: if (((_b = param.protocol.http) === null || _b === void 0 ? void 0 : _b.explode) === true) { return 'multi'; } return 'csv'; case m4.SerializationStyle.PipeDelimited: return 'pipes'; case m4.SerializationStyle.Simple: return 'csv'; case m4.SerializationStyle.SpaceDelimited: return 'ssv'; case m4.SerializationStyle.TabDelimited: return 'tsv'; default: return undefined; } } // returns true if the parameter should not be URL encoded function skipURLEncoding(param) { if (param.extensions) { return param.extensions['x-ms-skip-url-encoding'] === true; } return false; } function adaptParameterlocation(param) { if (!param.implementation) { return 'method'; } switch (param.implementation) { case m4.ImplementationLocation.Client: return 'client'; case m4.ImplementationLocation.Method: return 'method'; default: throw new Error(`unhandled parameter location ${param.implementation}`); } } function adaptParameterKind(param) { if (param.clientDefaultValue) { const adaptedType = adaptPossibleType(param.schema); if (!go.isLiteralValueType(adaptedType)) { throw new Error(`unsupported client side default type ${go.getTypeDeclaration(adaptedType)} for parameter ${param.language.go.name}`); } return new go.ClientSideDefault(new go.LiteralValue(adaptedType, param.clientDefaultValue)); } else if (param.schema.type === m4.SchemaType.Constant) { if (param.required) { return 'literal'; } return 'flag'; } else if (param.required === true) { return 'required'; } else { return 'optional'; } } function findOrAdaptParamsGroup(param) { const groupProp = param.language.go.paramGroup; let paramGroup = paramGroups.get(groupProp.schema.language.go.name); if (!paramGroup) { paramGroup = adaptParameterGroup(adaptParameterlocation(param), groupProp); paramGroups.set(groupProp.schema.language.go.name, paramGroup); } return paramGroup; } function adaptParameterGroup(location, groupProp) { const paramGroup = new go.ParameterGroup(groupProp.language.go.name, groupProp.schema.language.go.name, groupProp.required === true, location); paramGroup.docs.description = groupProp.language.go.description; return paramGroup; } // returns the media type used by the protocol function adaptBodyFormat(protocol) { switch (protocol.http.knownMediaType) { case KnownMediaType.Json: return 'JSON'; case KnownMediaType.Xml: return 'XML'; case KnownMediaType.Binary: return 'binary'; case KnownMediaType.Text: return 'Text'; default: throw new Error(`unhandled body protocol format ${protocol.http.knownMediaType}`); } } function adaptAnyResult(op) { const resultTypes = {}; for (const resp of values(op.responses)) { let possibleType; if (helpers.isSchemaResponse(resp)) { possibleType = adaptPossibleType(resp.schema); } else { // the operation contains a mix of schemas and non-schema responses continue; } resultTypes[parseInt(resp.protocol.http.statusCodes)] = possibleType; } return new go.AnyResult('Value', 'JSON', resultTypes); } function adaptResultFormat(protocol) { switch (protocol.http.knownMediaType) { case KnownMediaType.Json: return 'JSON'; case KnownMediaType.Xml: return 'XML'; case KnownMediaType.Text: return 'Text'; default: throw new Error(`unhandled result protocol format ${protocol.http.knownMediaType}`); } } //# sourceMappingURL=clients.js.map