code: def generate_function(
      api: str, phase: Phase, function: swagger_parser.Function
  ) -> Code:
      indent_level = 1 if phase == "api" else 2
      code = IndentedLines(indent_level)
      function_name = camel_case(function.name)
  
      params_by_location: typing.Dict[
          str, typing.List[swagger_parser.Parameter]
      ] = {}
      for param in function.params.values():
          params_by_location[param.where] = params_by_location.get(
              param.where, []
          )
          params_by_location[param.where].append(param)
  
      params_in_order = (
          params_by_location.get("path", [])
          + params_by_location.get("query", [])
          + params_by_location.get("body", [])
      )
      # we might want to sort params by name or field id as well so that they're positioning is not
      # reliant on the schema's but this will need followup client code update.
      params_in_order = sorted(
          params_in_order, key=lambda param: not param.required
      )
  
      function_args = ", ".join(
          f"{param.name}{'' if param.required else '?'}: {annotation(param.type)}"
          for param in params_in_order
      )
      call_list = ", ".join(param.name for param in params_in_order)
      if function_args:
          function_args += ", "
          call_list += ", "
  
      jsdoc: typing.List[str] = []
      if function.summary is not None:
          summary = function.summary.replace("\n", " ")
          jsdoc += [f"@summary {summary}"]
      for param in params_in_order:
          param_name = (
              param.name if param.required else f"[{param.name}]"
          )
          jsdoc += [
              f"@param {{{annotation(param.type)}}} {param_name} {param.title or ''}".replace(
                  "\n", " "
              ).strip()
          ]
      jsdoc += ["@param {*} [options] Override http request option."]
      jsdoc += [
          "@throws {RequiredError}"
      ]  # does the code generator throw anything else?
  
      if phase == "params":
          code.start_comment()
          code += ""  # something might be missing here
          code += jsdoc
          code.end_comment()
  
          code += f"{function_name}({function_args}options: any = {{}}): FetchArgs {{"
          code.indent()
          for param in params_in_order:
              if param.required:
                  code += f"// verify required parameter '{param.name}' is not null or undefined"
                  code += f"if ({param.name} === null || {param.name} === undefined) {{"
                  code.indent()
                  code += f"throw new RequiredError('{param.name}','Required parameter {param.name} was null or undefined when calling {function_name}.');"
                  code.dedent()
                  code += "}"
          path_params = params_by_location.get("path")
          fixed_path = re.sub(
              r"{(\w*)}",
              lambda match: f"{{{camel_case(match.group(1))}}}",
              function.path,
          )
          code += f"const localVarPath = `{fixed_path}`{'' if path_params else ';'}"
          if path_params:
              code.indent()
              for n, param in enumerate(path_params, start=1):
                  line = f'.replace(`{{${{"{param.name}"}}}}`, encodeURIComponent(String({param.name})))'
                  code += (
                      f"{line}{';' if n == len(path_params) else ''}"
                  )
              code.dedent()
          code += (
              "const localVarUrlObj = new URL(localVarPath, BASE_PATH);"
          )
          code += f"const localVarRequestOptions = {{ method: '{function.method.upper()}', ...options }};"
          code += "const localVarHeaderParameter = {} as any;"
          code += "const localVarQueryParameter = {} as any;"
          code += ""
  
          # should only need bearer token auth -- if this breaks, handle parsing auth types
          if function.needs_auth:
              code += "// authentication BearerToken required"
              code += "if (configuration && configuration.apiKey) {"
              code.indent()
              code += "const localVarApiKeyValue = typeof configuration.apiKey === 'function'"
              code.indent()
              code += '? configuration.apiKey("Authorization")'
              code += ": configuration.apiKey;"
              code.dedent()
              code += 'localVarHeaderParameter["Authorization"] = localVarApiKeyValue;'
              code.dedent()
              code += "}"
              code += ""
  
          for param in params_by_location.get("query", []):
              null_check = (
                  ""
                  if isinstance(
                      param.type,
                      (
                          swagger_parser.Sequence,
                          swagger_parser.DateTime,
                      ),
                  )
                  else " !== undefined"
              )
              code += f"if ({param.name}{null_check}) {{"
              code.indent()
              if isinstance(param.type, swagger_parser.DateTime):
                  code += f"localVarQueryParameter['{param.serialized_name}'] = {param.name}.toISOString()"
              else:
                  code += f"localVarQueryParameter['{param.serialized_name}'] = {param.name}"
              code.dedent()
              code += "}"
              code += ""
  
          # should only be one required body parameter that's application/json? if
          # this breaks, handle parsing consumption types
          body_param = next(
              iter(params_by_location.get("body", [])), None
          )
          if body_param:
              code += "localVarHeaderParameter['Content-Type'] = 'application/json';"
              code += ""
  
          # original code for queryparameters used the node querystrings module --
          # it treats objects and arrays differently.
          code += "objToSearchParams(localVarQueryParameter, localVarUrlObj.searchParams);"
          code += "objToSearchParams(options.query || {}, localVarUrlObj.searchParams);"
          code += "localVarRequestOptions.headers = { ...localVarHeaderParameter, ...options.headers };"
  
          if body_param:
              if not isinstance(body_param.type, swagger_parser.String):
                  code += f"localVarRequestOptions.body = JSON.stringify({body_param.name})"
              else:
                  code += "const needsSerialization = localVarRequestOptions.headers['Content-Type'] === 'application/json';"
                  code += f"localVarRequestOptions.body = needsSerialization ? JSON.stringify({body_param.name}) : {body_param.name}"
  
          code += ""
          code += "return {"
          code.indent()
          code += "url: `${localVarUrlObj.pathname}${localVarUrlObj.search}`,"
          code += "options: localVarRequestOptions,"
          code.dedent()
          code += "};"
          code.dedent()
          code += "},"
  
          return str(code)
      if phase == "fp":
          code.start_comment()
          code += ""
          code += jsdoc
          code.end_comment()
  
          success_response = function.responses.get("200")
          assert success_response, function
  
          code += f"{function_name}({function_args}options?: any): (fetch?: FetchAPI, basePath?: string) => Promise<{annotation(success_response)}> {{"
          code.indent()
          code += f"const localVarFetchArgs = {api}FetchParamCreator(configuration).{function_name}({call_list}options);"
          code += "return (fetch: FetchAPI = window.fetch, basePath: string = BASE_PATH) => {"
          code.indent()
          code += "return fetch(basePath + localVarFetchArgs.url, localVarFetchArgs.options).then((response) => {"
          code.indent()
          code += (
              "if (response.status >= 200 && response.status < 300) {"
          )
          code.indent()
          code += "return response.json();"
          code.dedent()
          code += "} else {"
          code.indent()
          code += "throw response;"
          code.dedent()
          code += "}"
          code.dedent()
          code += "});"
          code.dedent()
          code += "};"
          code.dedent()
          code += "},"
          return str(code)
      if phase == "factory":
          code.start_comment()
          code += ""
          code += jsdoc
          code.end_comment()
  
          code += f"{function_name}({function_args}options?: any) {{"
          code.indent()
          code += f"return {api}Fp(configuration).{function_name}({call_list}options)(fetch, basePath);"
          code.dedent()
          code += "},"
          return str(code)
      if phase == "api":
          code.start_comment()
          code += ""
          code += jsdoc
          code += f"@memberof {api}"
          code.end_comment()
  
          code += (
              f"public {function_name}({function_args}options?: any) {{"
          )
          code.indent()
          code += f"return {api}Fp(this.configuration).{function_name}({call_list}options)(this.fetch, this.basePath)"
          code.dedent()
          code += "}"
          code += ""
          return str(code)
  
      assert_never(phase)
  
imports: import os
  import re
  import typing
  from dataclasses import dataclass, field
  from shutil import copy, rmtree
  import swagger_parser
  from typing_extensions import Literal, assert_never
label_desc: Use image node_id 7 for calling a global function with example usage: generate_function(api, phase, function) and returns: str, str, str, str