async function processOperationRequests()

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]);
    }
  }  
}