Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save beccasaurus/d3d4923ef061b342409b3b4b2d8ebc29 to your computer and use it in GitHub Desktop.
Save beccasaurus/d3d4923ef061b342409b3b4b2d8ebc29 to your computer and use it in GitHub Desktop.
πŸš€ GAPIC Code Samples (syntax v2) .yaml

GAPIC Code Samples

πŸ‘“ Structuring code samples for GAPIC client libraries

This shows examples of code samples configured to demonstrate calls to 1 rpc call for a given API service.


type: com.google.api.codegen.SampleConfiguration
config_schema_version: 2.0.0

imports:
-

id_config:
  prefix:
  suffix:

code_samples:
- id:
  type: SINGLE_RPC
  api_method:
  request_config:
  on_success:
  on_error:
  return_value:
  language_options:
  test_cases:

sample_templates:
- id:

common_parameters:
- id:

common_statements:
- id:

πŸ““ Sample Details

type: com.google.api.codegen.SampleConfiguration
config_schema_version: 2.0.0

code_samples:

- id: my_unique_sample_id
  type: SINGLE_RPC
  title: List Foo Items
  description: "This sample demonstrates calling the Foo API"
ID string
(required)
Sample identifier. Recommended to be globally unique across APIs.
Type SampleType
(required)
Indicates the type of sample, e.g. SINGLE_RPC
Title string
(required)
Short description of the sample. May be used in various ways, e.g. to name generated class/function or simply shown in a comment.
Description List<string>
(optional)
Description of sample. Intended to be output in source code as comment.

πŸ›° API Call Configuration

code_samples:

- identifier: my_sample_id
  type: SINGLE_RPC
  
  # SINGLE_RPC code samples invoke one API method.
  #
  # The API method is defined here, including the API, version, etc.
  #
  api_method:
    api: google.cloud.foo
    version: v1
    default_version: true
    service: FooService
    method: ListFoos
    signature: [parent, filter_options]
    comment: ["Note: May take up to 5000ms to respond with value."]
API Name string
(required)
Name of API, e.g. google.cloud.language
API Version string
(required)
Version of the API to call, e.g. v1beta1
Default Version bool
default: false
Some generators output different code for the default version of the API, usually the latest stable version. For example, explicit version numbers may not be used in imports.
Service Name string
(required)
Name of service object of API, e.g. LanguageService
Method Name string
(required)
Name of rpc method on service, e.g. ClassifyText
Method Signature List<string>
(optional)
Defines the method signature to use. Each item provides is the name of a field on the request message. A matching signature must be defined on the rpc via option(google.api.method_signature).
If not specified, default signature is used (usually passing the whole request object).
Comment List<string>
(optional)
Describes this API invocation, useful for non-trivial or obvious method signatures. Intended to be output in source code as comment.

πŸš€ API Request Message Configuration

code_samples:

- id: my_unique_sample_id
  type: SINGLE_RPC
  description: "This sample demonstrates calling the Foo API"

  request_config:
    fields:
    
    # Basic configuration of field on request message
    # with literal value.
    - field: max_results
      value: 10
      
    # Add description to field, intended to be displayed
    # as a comment on the field assignment.
    - field: filter
      description: |
          Provide a filter for Foo objects (optional)
          e.g. name~Awesome
      value: "[FILTER]"
      
    # Provide an input_parameter name.
    # Intended to add a parameter to the sample method/function
    # and allow users to provide their own values, e.g. via CLI.
    - field: parent.project
      value: "[PROJECT]"
      input_parameter: project_id
      description: "Your Google Cloud Project ID"
    
    # Define literal value for repeated string field
    - field: categories
      value:
      - Technology
      - Science
      description: "List of categories to search within"
    
    # Define literal value for map<string,int> field
    - field: limit_options
      value:
        min: 0
        max: 100
        
    # Read local file into a text field
    - field: filters.csv_filter.file_content
      input_parameter: text_file_path
      input_type: LOCAL_FILE
    
    # Read local file into a bytes field
    - field: filters.audio_query.content
      input_parameter: audio_file_path
      description: "Path to local audio file, e.g. dir/audio.wav"
      input_type: LOCAL_FILE
Field Name string Name of field on request message. May reference a deeply nested field, e.g. config.query_options.limit.
Description string Description of this field and what it is being used for. Typically used as parameter description comment.
Value Any
string, bool, int
List<string>
Map<string,int>
Represents the default value to configure this sample.
For simple types (string, bool, numerics), this is a native value.
For enum types, this is either an int or string reference to enum value.
For repeated types, this is a List of values.
For map types, this is a Dictionary of values.
For message types, this is a List of request fields for that message.
Input Parameter string (Optional) Name to use for developer to provide this value as input.
If provided, the sample should be parameterized to allow for input.
Value often used as the variable name for generated method or CLI.
Input Type RequestFieldInputType Used to denote special input handling, e.g. byte fields on messages can be populated from a provided string to a local file path using RequestFieldInputType.LOCAL_FILE

πŸ“‘ Response Handling Configuration

code_samples:

- id: my_unique_sample_id
  type: SINGLE_RPC
  description: "This sample demonstrates calling the Foo API"
  
  # (Optional) define a return value
  return_value: $response.foo_entities
  
  # (Optional) define statements for error handling
  on_error:
  - print: ["API Error {}", $response.error.message]
  
  # Define statements to generate which run after a successful API response.
  on_success:
  
  # Define a code comment.
  # Arguments are used to include variable names in comment
  # using the correct casing idiomatic to the language.
  # This will print ListFoo in C#, listFoo in Node.js, list_foo in Python.
  # The variable is an arbitrary snake_case_string and does not need to be defined.
  - comment:
    - "Listing out the entities returned by {}"
    - list_foo

  # Define a variable. Value is an expression referring to a field on the response.
  - define:
      variable: foo_entities
      value: $response.foo_entities

  # Print. String formatting of {} will generate code which uses idiomatic
  # string formatting or interpolation, e.g. ${x} in Node.js or ''.format(x) in Python.
  - print:
    - "Total entities returned: {}"
    - foo_entities.total_count

  # Loop over a List collection.
  - loop:
      collection: foo_entities.foos
      variable: foo
      body:
      - print: ["Foo name: {}", foo.name]

      # Loop over a map.
      # Either key or value is required (or both).
      # If only key or value is defined for the loop, languages may generate
      # idiomatic code which loops over just keys or just values (versus both).
      - loop:
          map: foo.custom_attributes
          key: attr_name
          value: attr_value
          body:
          - print: ["Custom attribute {} => {}", attr_key, attr_value]

      # Wrap a code section in embed codes for inclusion in docs
      # e.g. [START foo_api_save_logs_to_disk_v1]
      # Note: the sample `id:` is used as an embed code which wraps the entire sample code.
      code_section:
        id: save_logs_to_disk
        description: "Save the log files locally"
        body:

        # Write string or bytes to local file.
        # Depending on if the field is a string or bytes, the generated code
        # will be idiomatic for saving bytes or text to a local file. 
        define: { variable: log, value: foo.latest_log }
        - write_file:
            contents: log.text
            file_name:
            - "Foo Log {}-{}-{}"
            - log.created_date.year
            - log.created_date.month
            - log.created_date.day
On Success List<Statement> Procedural list of statements representing success handling code after API request.
On Error List<Statement>
(optional)
Procedural list of statements representing error handling code after API request. If not provides, either default error handling or no error handling will be generated, depending on the language.
Return Value Any
(optional)
Value from response to return from the sample. Definition of "return" may differ between languages, this may not be a method return value in all cases.

ℹ️ New idea. Not yet tested in practice.


♻️ Reusability and Templates

Template Provider: `id:` – (`sample_templates:`, `common_parameters:`, `common_statements:`)
Template Consumer: `include:`

Implementation note: templates are nothing more than structures matching those found in samples which are merged into samples. For simplicity, samples cannot themselves be reused as template. Templates are explicitly defined as separate structures. Selecting sections of existing samples to be used as templates hasn't worked well.

This design is simply compile-time data composition via append-only deeply merging structures.


πŸ“Ž Defining Templates

Reusable types:

  • Sample Template (Full sample structure)
  • Common Parameter (Request message input field)
  • Common Statements (Response statement list)

Templates and common items are identified and used via their unique id:.

sample_templates:
- id: my_sample_template

common_parameters:
- id: my_common_parameter

common_statements:
- id: my_common_statement_block
  statements:
  -

Template IDs are scoped type, i.e. it is OK for a sample template to have the same ID as a parameter template.

To help easily author templates with unique IDs, it can be useful to configure your id_config.

id_config:
  prefix: cool_api_
  suffix: _v1

sample_templates:
- id: my_sample_template
# id: cool_api_my_sample_template_v1  # <= fully qualified ID defined

common_parameters:
- id: my_common_parameter
# id: cool_api_my_common_parameter_v1  # <= fully qualified ID defined

common_statements:
- id: my_common_statement_block
# id: cool_api_my_common_statement_block_v1  # <= fully qualified ID defined
  statements:
  -

Reminder: ID configuration affects all - id: defined in the YAML document.

πŸ“‹ Using Templates

Templates and common items are used via include:.

Include shared sample templates

sample_templates:
- id: base_sample
  rpc_method:
    api: google.cloud.foo
    version: v1
    service: FooService

code_samples:
- id: list_foos
  include: [base_sample]
  rpc_method:
    rpc: ListFoos
  # api: google.cloud.foo    These values are merged
  # version: v1              into the sample via the
  # service: FooService      template.

Include shared parameters

common_parameters:
- id: local_file_parameter
  input_type: LOCAL_FILE
  input_parameter: file_path
  value: "[FILE PATH]"
  description: "Path to a local file"

samples:
- id: list_foos
  request_config:
    fields:
    - field: input_config.foo_file.content
      include: local_file_parameter
    # input_type: LOCAL_FILE              These values are merged
    # input_parameter: file_path          into the sample via the
    # value: "[FILE PATH]"                template.
    # description: "Path to a local file"

Include shared code statements

common_statements:
- id: define_results_and_print_names
  code:
  - define:
      variable: results
      value: $response.result_sets[0]
  - loop:
      collection: results
      variable: result
      body:
      - print: ["Name: {}", result.name]

code_samples:
- id: list_foos
  on_success:
  - print: ["Let's print out the result names:"]
  - include: { id: define_results_and_print_names }
# - define:
#     variable: results
#     value: $response.result_sets[0]
# - loop:                               These values are merged
#     collection: results               into the sample via the
#     variable: result                  template.
#     body:
#     - print: ["Name: {}", result.name]
  - print: ["Done printing out result names."]
# If a template loops over items and you need to add additional code
# statements into that loop, you can add statements into the `code:` block.
# These statements are added into the same scope as the last statement included.

- id: list_foos_with_status_codes
  on_success:
  - print: ["Let's print out the result names:"]
  - include:
      id: define_results_and_print_names
      code:
  # - define:
  #     variable: results
  #     value: $response.result_sets[0]
  # - loop:                               These values are merged
  #     collection: results               into the sample via the
  #     variable: result                  template.
  #     body:
  #     - print: ["Name: {}", result.name]
        - print: ["Result status code: {}", result.status_code]
  - print: ["Done printing out result names."]

↩️ Importing Files

Templates can be imported from YAML files.

imports: [my_api.templates.yaml]

code_samples:
- id: call_my_api
  include: [my_api.base]
  rpc_method:
    rpc: MyApi
  on_success:
  - print: ["Got response: {}", $response]
    
# my_api.templates.yaml

imports: [../../base.templates.yaml]

id_config:
  prefix: my_api.

sample_templates:
- id: my_api_base
  api_method:
    api: api.myapi.com
    version: v2
    service: MyApiService
  api_request:
    fields:
    - include: base_templates.display_name
# base.templates.yaml

id_config:
  prefix: base_templates.

common_parameters:
- id: display_name
  field: display_name
  input_parameter: display_name
  description: "Display name"
  value: "[DISPLAY NAME]"

Any defined code_samples: in imported files are ignored.

When a YAML file is imported, its sample templates and common parameters/statements become available for use in the current YAML document.


Implementation note: Imported YAML files may have their own id_config: which must be taken into account when resolving IDs to include:. The id_config: of the YAML document being imported must not affect the scope of the document with the imports: statement.

πŸ†” Identifier Configuration

Use id_config: if you want to add a common prefix and/or suffix to your sample and/or template identifiers or document embed codes.

id_config:
  prefix: AAA_
  suffix: ZZZ_

code_samples:
- id: my_code_sample
# id: AAA_my_sample_sample_ZZZ  # <= fully qualified ID defined
  on_success:
  - code_section:
      id: part_of_this_sample
    # id: AAA_part_of_this_sample_ZZZ  # <= fully qualified ID defined
      body:
      -
  
sample_templates:
- id: my_sample_template
# id: AAA_my_sample_template_ZZZ  # <= fully qualified ID defined

common_parameters:
- id: my_common_parameter
# id: AAA_my_common_parameter_ZZZ  # <= fully qualified ID defined

common_statements:
- id: my_common_statement_block
# id: AAA_my_common_statement_block_ZZZ  # <= fully qualified ID defined
  statements:
  -

If an id_config: prefix or suffix is configured for the YAML document, every - id: is affected.
There is no way to define an - id: without the prefix and/or suffix being added in this case.

βš™οΈ Language Options

code_samples:

- id: my_unique_sample_id
  type: SINGLE_RPC
  description: "This sample demonstrates calling the Foo API"
  
  language_options:
    nodejs:
      calling_form: promise_callbacks
    csharp:
      class_name: DemoListFoo
Language Options map<LanguageName,map<Any,Any>> Options passed through to language-specific generators.

Language options allow for configuration options to be defined which are specific to certain languages.

As much as possible, sample configurations are generalized to be language agnostic.

When it makes sense to, however, options for generating a sample can be configured in language_options.

The value is a Map keyed by language name (string) with Map<Any,Any> values which are handled by the individual language generators.

ℹ️ Unsupported Functionality

Known functionality which is currently done in hand-written samples but generated samples do not (currently) support.

  • Adding START/END embed codes around:

    • imports*
    • client library instantiation
    • individual request variables
    • groups of request variables
    • api invocation
  • (TBD)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment