设置高级 API 选项

本页面介绍如何为类型提供程序设置高级配置选项,如输入映射和虚拟属性。要详细了解类型,请阅读类型概览。要详细了解类型提供程序,请阅读与 Deployment Manager 集成的单页指南

如果您要集成不满足 Deployment Manager 定义的 API 要求的 API,可以使用输入映射和虚拟属性来帮助解决这些不一致问题。借助输入映射,您可以在有歧义时提供 API 参数的显式映射,而借助虚拟属性,您可以公开底层 API 中不存在的任意属性,以便简化输入并向用户隐藏 API 的复杂性。

要实现高级配置选项,您需要非常熟悉要为其创建类型提供程序的 API。由于各个 API 可能有很大差异,本页面只提供一般指导和示例,不提供具体的 API 指导。

准备工作

需要高级配置选项的常见情况

属性名称以不同的值重复使用

在某些 API 中,相同的属性或参数名称可能在不同的方法中以不同的值重复使用。例如,一个 API 可能会指定用于创建资源(POST 请求)的 name 参数具有值 foo/bar,而一个更新请求(PATCHPUT)的相同 name 字段可能需要值 foo/bar/baz

属性值可以从 API 响应中推断出来

某些 API 方法需要服务器生成的值,当您向资源发出 GET 请求时会返回该值。例如,一个 API 可能需要 etag 参数才能在更改资源时发出更新请求。etag 值在每个更改请求之后发生变更,因此您需要在发出更新资源请求之前,通过对资源执行 GET 请求来获取当前的 etag 参数。

您可以使用输入映射让 Deployment Manager 从 API 资源中检索 etag 字段。当用户调用您在输入映射中指定的方法时,Deployment Manager 会自动执行 GET 请求以获取该值。

简化用户输入

Deployment Manager 支持虚拟属性,您可以针对不同的使用场景通过 Deployment Manager 向用户公开这些任意属性。您可以将虚拟属性视为底层 API 上不存在的属性,但也是可以在输入映射中根据需要注入其值的任意变量。例如,假设有一个 API 属性,必须对它进行 base64 编码,然后才能将它的值发送到底层 API。您可以创建一个提示用户输入纯文本值的虚拟属性,然后用输入映射对该值进行 base64 编码,最后将结果提供给底层 API,而不是要求用户提供 base64 编码值。

指定高级选项

要指定高级选项,请在创建类型提供程序资源时提供 collectionOverrides 属性,并根据需要为每个 API 集合定义输入映射或虚拟属性。

例如,借助 gcloud CLI,您可以使用 YAML 文件提供高级选项,并将 YAML 文件与 type-providers create 请求一起提供。示例 YAML 文件可能如下所示:

collectionOverrides:
- collection: /emailAddresses/v1beta/people
  options:
    inputMappings:
    - methodMatch: ^create$
      fieldName: emailAddress.displayName
      value: $.resource.properties.displayName
      location: BODY
    - methodMatch: ^update$
      fieldName: displayName
      value: $.resource.properties.displayName
      location: PATH
    virtualProperties: |
      schema: http://json-schema.org/draft-04/schema#
      type: object
        properties:
          displayName:
            type: string
credential:
  basicAuth:
    user: [USERNAME]
    password: [PASSWORD]

此配置告诉 Deployment Manager 以下内容:

  • 对于 create 方法,在资源正文中查找名为 emailAddress.displayName 的字段,并将该字段的值设置为用户在 Deployment Manager 配置中输入的 displayName 属性值。因此,如果用户将其配置设置如下:

     resources:
     - name: example
       type: myproject/emailAddress:/emailAddresses/v1beta/people
       properties:
       - displayName: John Doe
         ...
    

    Deployment Manager 会将 emailAddress.displayName 的值设置为 John Doe。

  • 对于 update 方法,该字段位于资源路径而不是资源正文中,但应用了相同的输入映射。

指定输入映射

您可以通过输入映射为某些 API 字段映射或注入信息,让 Deployment Manager 可以更密切地与底层 API 交互,而无需要求用户去理解 API 行为的细微之处。

您还可以利用输入映射来简化用户与 API 交互的方式。例如,您可以使用输入映射自动获取服务器生成的值,例如指纹、ID 或 etag。如此一来,每次要更新时用户就不必对资源执行单独的 get 请求了。

同样,您也可以使用输入映射来处理同一 API 字段针对不同方法使用不同值等模糊或令人混乱的情况。例如,创建资源的请求可能需要用户可以指定的 name 属性,但是同一 API 可能需要针对 update 方法使用不同格式的 name 属性。您可以使用输入映射来告诉 Deployment Manager 哪个值适用于哪个 API 方法。

要指定类型提供程序的输入映射,请提供 options.inputMappings 属性。您可以定义适用于整个 API 的输入映射,也可以为每个集合明确提供输入映射:

# Input mappings for the entire API
"options": {
  "inputMappings": [
      {
          "fieldName": "[NAME]",
          "location":  "[PATH | BODY | QUERY | HEADER]",
          "methodMatch": "[REGEX_MATCHING_CERTAIN_METHODS]",
          "value": "[VALUE_TO_INJECT]"
      },
      {
          "fieldName": "[NAME]",
          "location":  "[PATH | BODY | QUERY | HEADER]",
          "methodMatch": "[REGEX_MATCHING_CERTAIN_METHODS]",
          "value": "[VALUE_TO_INJECT]"
      }
   ]
},
# Input mappings for specific collections
"collectionOverrides": [
    {
        "collection": "[SPECIFIC_COLLECTION]",
        "options": {
            "inputMappings": [
                {
                    "fieldName": "[NAME]",
                    "location": "[PATH | BODY | QUERY | HEADER]",
                    "methodMatch": "[REGEX_MATCHING_CERTAIN_METHODS]",
                    "value": "[VALUE_TO_INJECT]"
                },
                {
                    "fieldName": "[NAME]",
                    "location": "[PATH | BODY]",
                    "methodMatch": "[REGEX_MATCHING_CERTAIN_METHODS]",
                    "value": "[VALUE_TO_INJECT]"
                },
                ...[additional fields if necessary]...
            ]
        }
    }
]

该语法的各个重要部分如下所述。

集合

[SPECIFIC_COLLECTION] 是此输入映射适用的 API 集合。例如,如果您为 Google Discovery 文档(如 IAM Service Accounts API)提供输入映射,则相关集合为 projects.serviceAccountsprojects.serviceAccountKeys

对于使用 OpenAPI 规范的 API,集合路径可能是 /example-collection/{name}。您可以在 OpenAPI GitHub 代码库中探索功能性 OpenAPI 示例

字段名称

"fieldName" 是要为其指定输入映射的 API 特性 (attribute) 或属性 (property),如 "fieldName": "fingerprint", "fieldName": "etag" 等。

位置

API 属性可以作为网址路径中的参数,也可以作为请求或响应正文的一部分。指定此输入映射应用的位置,例如网址 PATH 或请求 BODY。支持的值包括:

  • PATH
  • BODY
  • QUERY
  • HEADER

方法匹配

指定此输入映射适用的方法。使用正则表达式可指定多个方法。例如:

"methodMatch":"^create$"

对于 OpenAPI 规范,您可以使用以下代码:

"methodMatch: ^(put|get|delete|post)$"

指定 Deployment Manager 应为此字段注入的值。此字段采用 JSONPath 表示法。例如,下面的输入映射表示,对于 name 字段,Deployment Manager 应使用用户提供的值并将其注入到 projects/$.project/topics/$resource.properties.topic 格式中:

"inputMappings":[
{
  "fieldName":"name",
  "location":"PATH",
  "methodMatch":"^post$",
  "value":"concat(\"projects/\", $.project, \"/topics/\", $.resource.properties.topic)"
}...
  • 使用 $.resource.properties.[VARIABLE] 时,您需要将值设置为用户将在配置中设置的属性。例如,对于 $.resource.properties.topic,该值将是用户在配置中为属性 topic 提供的值:

    resources:
    - name: example
      type: example-type-provider:collectionA
      properties:
        topic: history # The value of "history" would be used for the `name` parameter because of the input mapping above
    
  • 要在 get 请求后引用资源本身,请使用 $.resource.self.[VARIABLE]。例如,对于更新请求,如果要获取最新指纹,可以使用此语法让 Deployment Manager 执行 get 并获取值:

    {
      'fieldName': 'fingerprint',
      'location': 'BODY',
      'methodMatch': '^(put)$',
      # self represents the resource by doing a GET on it.
      # This mappings gets latest fingerprint on the request.
      # Final PUT Body will be
      # {
      #   "name": "my-resource-name",
      #   "fingerprint": "<server generated fingerprint>"
      # }
      'value': '$.resource.self.fingerprint'
    }
    

使用虚拟属性

虚拟属性指可以通过 Deployment Manager 向用户公开的任意属性。这些属性不是底层 API 的一部分,而是可用于传递信息或隐藏 API 不一致的任意变量。您也可以在输入映射中引用虚拟属性。

虚拟属性遵循 JSON 4 架构。在特定集合的 options 中提供虚拟属性:

"collection": "[SPECIFIC_COLLECTION]",
  "options": {
   "virtualProperties": "schema: http://json-schema.org/draft-04/schema#\ntype: object\nproperties:\n  [PROPERTY]:\n    type: [DATA_TYPE]\n  [ANOTHER_PROPERTY]:\n    type: [ANOTHER_DATA_TYPE]n"
   "inputMappings": [
    ...
   ]
  }

在 YAML 定义文件中则如下所示:

- collection: projects.serviceAccounts
  options:
    virtualProperties: |
      schema: http://json-schema.org/draft-04/schema#
      type: object
      properties:
        a-property:
          type : string
        b-property:
          type : string
      required:
      - a-property
      - b-property
    inputMappings:
    ...

例如,假设有一个生成电子邮件地址的虚构 API。该 API 有一个用于创建电子邮件的方法,需要 emailAddress.displayName 属性。当用户发出创建电子邮件地址的请求时,他们会提供如下请求:

POST https://example.com/emailAddresses/v1beta/people/

{
  "emailAddress": {
    "displayName": "john"
  }
}

现在,假设该 API 公开了一种更新电子邮件地址的方法,但更新电子邮件的方法只需要 displayName 属性,而不需要 email.displayName 属性:

POST https://example.com/emailAddresses/v1beta/people/john

{
  "displayName": "josh"
}

您希望用户在使用此类型提供程序时如何提供此值?您可以要求他们根据操作以不同方式指定属性:

# Creating an email
resources:
- name: example-config
  type: projects/test-project:emailAddresses
  properties:
    emailAddress:
      displayName: john

# Updating an email
resources:
- name: example-config
  type: projects/test-project:emailAddresses
  properties:
    displayName: john

或者,您可以创建一个无论操作如何都接受同一值的虚拟属性,然后使用输入映射将虚拟属性映射到相应的 API 参数。在此示例中,假设您定义了一个名为 displayName 的虚拟属性。您的输入映射可能如下所示:

{
    "collectionOverrides":[
      {
        "collection":"emailAddresses",
        "options":{
          "inputMappings":[
            {
              "fieldName":"emailAddress.displayName",
              "location":"BODY",
              "methodMatch":"^create$",
              "value":"$.resource.properties.displayName"
            },
            {
              "fieldName":"displayName",
              "location":"BODY",
              "methodMatch":"^update$",
              "value":"$.resource.properties.displayName"
            }
          ],
          "virtualProperties":"schema: http://json-schema.org/draft-04/schema#\ntype: object\nproperties:\n  displayName:\n    type: string\nrequired:\n- displayName\n"
        }
      }
    ],
    "descriptorUrl":"https://example.com/emailAddresses/v1beta/",
    "options":{
      "nameProperty":""
    }
}

具体来说,虚拟属性是在此处定义的:

"virtualProperties":"schema: http://json-schema.org/draft-04/schema#\ntype: object\nproperties:\n  displayName:\n    type: string\nrequired:\n- displayName\n"

以直观易懂的格式:

"virtualProperties":
  "schema: http://json-schema.org/draft-04/schema#\n
   type: object\n
   properties:\n
     displayName:\n
     - type: string\n
   required:\n
   - displayName\n"

现在,您的用户可以将 displayName 指定为更新和创建请求的顶层属性,Deployment Manager 将知道如何正确映射值。

# Creating an email
resources:
- name: example-config
  type: projects/test-project:emailAddresses
  properties:
    displayName: john

# Updating an email
resources:
- name: example-config
  type: projects/test-project:emailAddresses
  properties:
    displayName: john

后续步骤