code: def generate_proxy(classname, endpoints):
      """
      Generate a Retrofit Proxy class.
  
      Retrofit interfaces look like this:
          public interface GitHubService {
              @GET("/users/{user}/repos")
              Call<List<Repo>> listRepos(@Path("user") String user);
          }
        :param classname: name of the class
        :param endpoints: list of endpoints served by this class
      """
      # Replace path vars like (?<schemaname>.*) with {schemaname} for Retrofit's annotation
      var_pattern = re.compile(r"\{(\w+)\}")
  
      helper_class = []
      found_key_array_parameter = False
  
      yield "/*"
      yield " * This file is auto-generated by h2o-3/h2o-bindings/bin/gen_java.py"
      yield " * Copyright 2016 H2O.ai;  Apache License Version 2.0 (see LICENSE for details)"
      yield " */"
      yield "package water.bindings.proxies.retrofit;"
      yield ""
      yield "import water.bindings.pojos.*;"
      yield "import retrofit2.*;"
      yield "import retrofit2.http.*;"
      yield "import java.util.Map;" if classname == "Grid" or classname == "ModelBuilders" else None
      yield ""
      yield "public interface " + classname + " {"
      yield ""
  
      for e in endpoints:
          method = e["handler_method"]
          # should we always use e.api_name ?
          if method == "exec":
              method = e["api_name"]
  
          param_strs = []
          required_param_strs = []
          for field in e["input_params"]:
              fname = field["name"]
              if field["is_path_param"]:
                  ftype = "Path"
              else:
                  if e["http_method"] == "GET":
                      ftype = "Query"
                  else:
                      ftype = "Field"
              ptype = translate_type(
                  field["type"], field["schema_name"]
              )
              if ptype.endswith("KeyV3") or ptype == "ColSpecifierV3":
                  ptype = "String"
              if ptype.endswith("KeyV3[]"):
                  ptype = "String[]"
              param_str = '@{ftype}("{fname}") {ptype} {fname}'.format(
                  **locals()
              )
              param_strs.append(param_str)
              if field["required"]:
                  required_param_strs.append(param_str)
          if len(param_strs) == len(required_param_strs):
              required_param_strs = None
  
          yield "  /** "
          yield bi.wrap(e["summary"], indent="   * ")
          for field in e["input_params"]:
              s = "   *   @param %s " % field["name"]
              yield s + bi.wrap(
                  field["help"],
                  indent="   *" + " " * (len(s) - 4),
                  indent_first=False,
              )
          yield "   */"
          # Create 2 versions of each call: first with all input parameters present, and then only with required params
          for params in [param_strs, required_param_strs]:
              if params is None:
                  continue
              yield "  @FormUrlEncoded" if e[
                  "http_method"
              ] == "POST" else None
              yield '  @{method}("{path}")'.format(
                  method=e["http_method"], path=e["url_pattern"]
              )
              if len(params) <= 1:
                  args = params[0] if params else ""
                  yield "  Call<{schema}> {method}({args});".format(
                      schema=e["output_schema"],
                      method=method,
                      args=args,
                  )
              else:
                  yield "  Call<{schema}> {method}(".format(
                      schema=e["output_schema"], method=method
                  )
                  for arg in params:
                      yield "    " + arg + (
                          "" if arg == params[-1] else ","
                      )
                  yield "  );"
              yield ""
  
          # Make special static Helper class for Grid and ModelBuilders.
          if "algo" in e:
              # We make two train_ and validate_ methods.  One (built here) takes the parameters schema, the other
              # (built above) takes each parameter.
              helper_class.append("    /**")
              helper_class.append(
                  bi.wrap(e["summary"], indent="     * ")
              )
              helper_class.append("     */")
              helper_class.append(
                  "    public static Call<{oschema}> {method}({outer_class} z, {ischema} p) {{".format(
                      ischema=e["input_schema"],
                      oschema=e["output_schema"],
                      method=method,
                      outer_class=classname,
                  )
              )
              helper_class.append(
                  "      return z.{method}(".format(method=method)
              )
              for field in e["input_params"]:
                  ptype = translate_type(
                      field["type"], field["schema_name"]
                  )
                  pname = translate_name(field["name"])
                  if ptype.endswith("KeyV3"):
                      s = "(p.{parm} == null? null : p.{parm}.name)".format(
                          parm=pname
                      )
                  elif ptype.endswith("KeyV3[]"):
                      found_key_array_parameter = True
                      s = "(p.{parm} == null? null : keyArrayToStringArray(p.{parm}))".format(
                          parm=pname
                      )
                  elif ptype == "ColSpecifierV3":
                      s = "(p.{parm} == null? null : p.{parm}.columnName)".format(
                          parm=pname
                      )
                  else:
                      s = "p." + pname
                  if field != e["input_params"][-1]:
                      s += ","
                  helper_class.append("        " + s)
              helper_class.append("      );")
              helper_class.append("    }")
              helper_class.append("")
  
      if helper_class:
          yield ""
          yield '  @SuppressWarnings("unused")'
          yield "  class Helper {"
          for line in helper_class:
              yield line
          if found_key_array_parameter:
              yield "    /**"
              yield "     * Return an array of Strings for an array of keys."
              yield "     */"
              yield "    public static String[] keyArrayToStringArray(KeyV3[] keys) {"
              yield "      if (keys == null) return null;"
              yield "      String[] ids = new String[keys.length];"
              yield "      int i = 0;"
              yield "      for (KeyV3 key : keys) ids[i++] = key.name;"
              yield "      return ids;"
              yield "    }"
          yield "  }"
          yield ""
  
      yield "}"
  
file_path: h2o-3/h2o-bindings/bin/gen_java.py
imports: import re
  import textwrap
  import bindings
label_desc: Use image node_id 7 for calling a global function with example usage: generate_proxy(classname, endpoints) without return types