in packages/autorest.go/src/transform/transform.ts [423:701]
async function processOperationRequests(session: Session<m4.CodeModel>) {
// pre-process multi-request operations as it can add operations to the operations
// collection, and iterating over a modified collection yeilds incorrect results
for (const group of values(session.model.operationGroups)) {
for (const op of values(group.operations).toArray()) {
if (op.language.go!.description) {
op.language.go!.description = parseComments(op.language.go!.description);
}
const normalizeOperationName = await session.getValue('normalize-operation-name', false);
// previous operation naming logic: keep original name if only one body type, and add suffix for operation with non-binary body type if more than one body type
// new normalized operation naming logic: add suffix for operation with unstructured body type and keep original name for operation with structured body type
if (!normalizeOperationName){
if (op.requests!.length > 1) {
// for the non-binary media types we create a new method with the
// media type name as a suffix, e.g. FooAPIWithJSON()
separateOperationByRequestsProtocol(group, op, [KnownMediaType.Binary]);
}
} else {
// add suffix to binary/text, suppose there will be only one structured media type
separateOperationByRequestsProtocol(group, op, [KnownMediaType.Json, KnownMediaType.Xml, KnownMediaType.Form, KnownMediaType.Multipart]);
}
if (!group.language.go!.host) {
// all operations/groups have the same host, so just grab the first one
group.language.go!.host = op.requests?.[0].protocol.http?.uri;
}
}
}
// track any client-level parameterized host params.
const hostParams = new Array<m4.Parameter>();
// track any parameter groups and/or optional parameters
const paramGroups = new Map<string, m4.GroupProperty>();
const singleClient = <boolean>session.model.language.go!.singleClient;
if (singleClient && session.model.operationGroups.length > 1) {
throw new Error('single-client cannot be enabled when there are multiple clients');
}
for (const group of values(session.model.operationGroups)) {
for (const op of values(group.operations)) {
if (op.language.go!.description) {
op.language.go!.description = parseComments(op.language.go!.description);
}
if (op.requests![0].protocol.http!.headers) {
for (const header of values(op.requests![0].protocol.http!.headers)) {
const head = <m4.HttpHeader>header;
head.schema.language.go!.name = schemaTypeToGoType(session.model, head.schema, 'Property');
}
}
const opName = helpers.isLROOperation(op) ? 'Begin' + op.language.go!.name : op.language.go!.name;
const params = values(helpers.aggregateParameters(op));
// create an optional params struct even if the operation contains no optional params.
// this provides version resiliency in case optional params are added in the future.
// don't do this for paging next link operation as this isn't part of the public API
if (!op.language.go!.paging || !op.language.go!.paging.isNextOp) {
// create a type named <OperationGroup><Operation>Options
// if single-client is enabled, omit the <OperationGroup> prefix
let clientPrefix = capitalize(group.language.go!.clientName);
if (singleClient) {
clientPrefix = '';
}
const optionalParamsGroupName = `${clientPrefix}${opName}Options`;
const desc = createOptionsTypeDescription(optionalParamsGroupName, `${group.language.go!.clientName}.${helpers.isPageableOperation(op) && !helpers.isLROOperation(op) ? `New${opName}Pager` : opName}`);
const gp = createGroupProperty(optionalParamsGroupName, desc, false);
gp.language.go!.name = 'options';
// if there's an existing parameter with the name options then pick something else
for (const param of params) {
if (param.language.go!.name === gp.language.go!.name) {
gp.language.go!.name = 'opts';
break;
}
}
gp.required = false;
paramGroups.set(optionalParamsGroupName, gp);
// associate the param group with the operation
op.language.go!.optionalParamGroup = gp;
}
for (const param of params) {
if (param.language.go!.description) {
param.language.go!.description = parseComments(param.language.go!.description);
}
if (param.clientDefaultValue && param.implementation === m4.ImplementationLocation.Method) {
// we treat method params with a client-side default as optional
// since if you don't specify a value, a default is sent and the
// zero-value is ambiguous.
// NOTE: the assumption for client parameters with client-side defaults is that
// the proper default values are being set in the client (hand-written) constructor.
param.required = false;
}
if (!param.required && param.schema.type === m4.SchemaType.Constant && !param.language.go!.amendedDesc) {
if (param.language.go!.description) {
param.language.go!.description += '. ';
}
param.language.go!.description += `Specifying any value will set the value to ${(<m4.ConstantSchema>param.schema).value.value}.`;
param.language.go!.amendedDesc = true;
}
// this is to work around M4 bug #202
// replace the duplicate operation entry in nextLinkOperation with
// the one from our operation group so that things like parameter
// groups/types etc are consistent.
if (op.language.go!.paging && op.language.go!.paging.nextLinkOperation) {
const dupeOp = <m4.Operation>op.language.go!.paging.nextLinkOperation;
for (const internalOp of values(group.operations)) {
if (internalOp.language.default.name === dupeOp.language.default.name) {
op.language.go!.paging.nextLinkOperation = internalOp;
break;
}
}
for (const internalOp of values(group.operations)) {
if (internalOp.language.go!.name === dupeOp.language.default.name) {
if (!internalOp.language.go!.paging.isNextOp) {
internalOp.language.go!.paging.isNextOp = true;
internalOp.language.go!.name = `${uncapitalize(internalOp.language.go!.name)}CreateRequest`;
}
break;
}
}
}
let paramType: 'Property' | 'InBody' | 'HeaderParam' | 'PathParam' | 'QueryParam';
switch (param.protocol.http?.in) {
case 'body':
paramType = 'InBody';
break;
case 'header':
paramType = 'HeaderParam';
break;
case 'path':
paramType = 'PathParam';
break;
case 'query':
paramType = 'QueryParam';
break;
default:
paramType = 'Property';
}
param.schema.language.go!.name = schemaTypeToGoType(session.model, param.schema, paramType);
if (helpers.isTypePassedByValue(param.schema)) {
param.language.go!.byValue = true;
}
param.schema = substitueDiscriminator(param);
// check if this is a header collection
if (param.extensions?.['x-ms-header-collection-prefix']) {
param.schema.language.go!.headerCollectionPrefix = param.extensions['x-ms-header-collection-prefix'];
}
if (param.implementation === m4.ImplementationLocation.Client && (param.schema.type !== m4.SchemaType.Constant || !param.required) && param.language.default.name !== '$host') {
if (param.protocol.http!.in === 'uri') {
// this is a parameterized host param.
// use the param name to avoid reference equality checks.
if (!values(hostParams).where(p => p.language.go!.name === param.language.go!.name).any()) {
hostParams.push(param);
}
// we special-case fully templated host param, e.g. {endpoint}
// as there's no need to do a find/replace in this case, we'd
// just directly use the endpoint param value.
if (!(<string>group.language.go!.host).match(/^\{\w+\}$/)) {
group.language.go!.complexHostParams = true;
}
continue;
}
// add global param info to the operation group
if (group.language.go!.clientParams === undefined) {
group.language.go!.clientParams = new Array<m4.Parameter>();
}
const clientParams = <Array<m4.Parameter>>group.language.go!.clientParams;
// check if this global param has already been added
if (values(clientParams).where(cp => cp.language.go!.name === param.language.go!.name).any()) {
continue;
}
clientParams.push(param);
} else if (param.implementation === m4.ImplementationLocation.Method && param.protocol.http!.in === 'uri') {
// at least one method contains a parameterized host param, bye-bye simple case
group.language.go!.complexHostParams = true;
}
// check for grouping
if (param.extensions?.['x-ms-parameter-grouping'] && <boolean>session.model.language.go!.groupParameters) {
// this param belongs to a param group, init name with default
// if single-client is enabled, omit the <OperationGroup> prefix
let clientPrefix = capitalize(group.language.go!.clientName);
if (singleClient) {
clientPrefix = '';
}
let paramGroupName = `${clientPrefix}${opName}Parameters`;
if (param.extensions['x-ms-parameter-grouping'].name) {
// use the specified name
paramGroupName = <string>param.extensions['x-ms-parameter-grouping'].name;
} else if (param.extensions['x-ms-parameter-grouping'].postfix) {
// use the suffix
paramGroupName = `${clientPrefix}${opName}${<string>param.extensions['x-ms-parameter-grouping'].postfix}`;
}
// create group entry and add the param to it
if (!paramGroups.has(paramGroupName)) {
let subtext = `.${opName} method`;
let groupedClientParams = false;
if (param.implementation === m4.ImplementationLocation.Client) {
subtext = ' client';
groupedClientParams = true;
}
const desc = `${paramGroupName} contains a group of parameters for the ${group.language.go!.clientName}${subtext}.`;
const paramGroup = createGroupProperty(paramGroupName, desc, groupedClientParams);
paramGroups.set(paramGroupName, paramGroup);
}
// associate the group with the param
const paramGroup = paramGroups.get(paramGroupName);
param.language.go!.paramGroup = paramGroup;
// check for a duplicate, if it has the same schema then skip it
const dupe = values(paramGroup!.originalParameter).first((each: m4.Parameter) => { return each.language.go!.name === param.language.go!.name; });
if (!dupe) {
paramGroup!.originalParameter.push(param);
if (param.required) {
// mark the group as required if at least one param in the group is required
paramGroup!.required = true;
}
} else if (dupe.schema !== param.schema) {
throw new Error(`parameter group ${paramGroupName} contains overlapping parameters with different schemas`);
}
// check for groupings of client and method params
for (const otherParam of values(paramGroup?.originalParameter)) {
if (otherParam.implementation !== param.implementation) {
throw new Error(`parameter group ${paramGroupName} contains client and method parameters`);
}
}
} else if (param.implementation === m4.ImplementationLocation.Method && param.required !== true) {
// include all non-required method params in the optional values struct.
(<m4.GroupProperty>op.language.go!.optionalParamGroup).originalParameter.push(param);
// associate the group with the param
param.language.go!.paramGroup = op.language.go!.optionalParamGroup;
}
}
if (helpers.isLROOperation(op)) {
// add the ResumeToken to the optional params type
const tokenParam = newParameter('ResumeToken', '', newString('string', ''));
tokenParam.language.go!.byValue = true;
tokenParam.language.go!.isResumeToken = true;
tokenParam.required = false;
op.parameters?.push(tokenParam);
tokenParam.language.go!.paramGroup = op.language.go!.optionalParamGroup;
(<m4.GroupProperty>op.language.go!.optionalParamGroup).originalParameter.push(tokenParam);
}
// recursively add the marshalling format to the body param if applicable
const marshallingFormat = getMarshallingFormat(op.requests![0].protocol);
if (marshallingFormat !== 'na') {
const bodyParam = values(helpers.aggregateParameters(op)).where((each: m4.Parameter) => { return each.protocol.http?.in === 'body'; }).first();
if (bodyParam) {
recursiveAddMarshallingFormat(bodyParam.schema, marshallingFormat);
if (marshallingFormat === 'xml' && bodyParam.schema.serialization?.xml?.name) {
// mark that this parameter type will need a custom marshaller to handle the XML name
bodyParam.schema.language.go!.xmlWrapperName = bodyParam.schema.serialization?.xml?.name;
} else if (marshallingFormat === 'json' && op.requests![0].protocol.http!.method === 'patch') {
// mark that this type will need a custom marshaller to handle JSON nulls
bodyParam.schema.language.go!.needsPatchMarshaller = true;
}
}
}
}
if (hostParams.length > 0) {
// attach host params to the operation group
group.language.go!.hostParams = hostParams;
} else if (!<boolean>session.model.language.go!.azureARM) {
// if there are no host params and this isn't Azure ARM, check for a swagger-provided host (e.g. test server)
for (const param of values(session.model.globalParameters)) {
if (param.language.default.name === '$host') {
group.language.go!.host = param.clientDefaultValue;
session.model.language.go!.host = group.language.go!.host;
break;
}
}
}
}
// emit any param groups
if (paramGroups.size > 0) {
if (!session.model.language.go!.parameterGroups) {
session.model.language.go!.parameterGroups = new Array<m4.GroupProperty>();
}
const pg = <Array<m4.GroupProperty>>session.model.language.go!.parameterGroups;
for (const items of paramGroups.entries()) {
pg.push(items[1]);
}
}
}