Como configurar opções avançadas de API

Esta página descreve como configurar as opções de configuração avançadas para provedores de tipos, como mapeamentos de entrada e propriedades virtuais. Para saber mais sobre tipos, leia Visão geral de tipos. Para saber mais sobre provedores de tipos, leia o Guia de uma página sobre como fazer a integração com o Deployment Manager.

Para integrar uma API que não cumpre os requisitos de API definidos pelo Deployment Manager, use os mapeamentos de entrada e as propriedades virtuais como auxílio para solucionar as inconsistências. Com os mapeamentos de entrada, você pode fornecer mapeamentos explícitos dos parâmetros da API com ambiguidades. As propriedades virtuais, por sua vez, permitem expor propriedades arbitrárias que não existem nas APIs subjacentes. Assim, você pode simplificar a entrada e ocultar as complexidades da API dos usuários.

Implementar as opções de configuração avançadas exige uma grande familiaridade com a API para a qual você está criando o provedor de tipos. Como as APIs podem ser muito diferentes umas das outras, esta página fornece orientações e exemplos gerais, e não para uma API específica.

Antes de começar

Cenários comuns que exigem opções de configuração avançadas

O nome da propriedade é reutilizado com valores diferentes

No caso de determinadas APIs, o mesmo nome de propriedade ou parâmetro é usado novamente em diferentes métodos, mas com valores diferentes. Por exemplo, uma API pode especificar que o parâmetro name para criar um recurso (uma solicitação POST) pode ter o valor foo/bar enquanto o mesmo campo name pode exigir o valor foo/bar/bazpara solicitações de atualização (PATCH ou PUT).

Os valores de propriedade podem ser inferidos a partir da resposta da API

Alguns métodos de API exigem o valor gerado pelo servidor, que é retornado quando é feita uma solicitação GET para o recurso. Por exemplo, uma API pode exigir um parâmetro etag para fazer solicitações de atualização ao alterar um recurso. O valor etag é alterado após cada solicitação mudança. Portanto, você recebe o parâmetro etag atual realizando uma solicitação GET ao recurso antes de fazer a solicitação de atualização do recurso.

Ao usar mapeamentos de entrada, é possível informar ao Deployment Manager que o campo etag pode ser recuperado do recurso da API. O Deployment Manager executa automaticamente uma solicitação GET para conseguir esse valor quando um usuário chama o método que especificado nos mapeamentos de entrada.

Simplificação da entrada de usuários

O Deployment Manager oferece suporte para propriedades virtuais. Essas são propriedades arbitrárias que podem ser expostas aos usuários para diferentes usos por meio do Deployment Manager. Trate as propriedades virtuais como se não existissem propriedades na API de base, mas sim variáveis arbitrárias em que o valor será inserido por você nos mapeamentos de entrada. Por exemplo, imagine que haja uma propriedade da API que precisa ser codificada em base64 antes que o valor seja enviado à API de base. Em vez de pedir a seus usuários que informem o valor na codificação em base64, crie uma propriedade virtual que solicite o valor em texto simples, codifique-o em base64 com mapeamentos de entrada e forneça o resultado à API de base.

Como especificar as opções avançadas

Para especificar opções avançadas, forneça a propriedade collectionOverrides ao criar seu recurso de provedor de tipos e defina mapeamentos de entrada ou propriedades virtuais para cada conjunto de API, conforme necessário.

Por exemplo, com a CLI gcloud, é possível fornecer opções avançadas usando um arquivo YAML que será fornecido com sua solicitação type-providers create. Um exemplo de arquivo YAML pode ser similar ao seguinte:

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]

Essas configurações passam as seguintes instruções ao Deployment Manager:

  • Para o método create, procure o campo denominado emailAddress.displayName no corpo do recurso e defina o respectivo valor como a entrada do usuário para a propriedade displayName na configuração do Deployment Manager. Portanto, se um usuário definir a própria configuração da seguinte forma:

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

    o Deployment Manager definirá o valor de emailAddress.displayName como "John Doe".

  • Para o método update, o campo está no caminho do recurso e não no corpo do recurso, mas é aplicado o mesmo mapeamento de entrada.

Como especificar mapeamentos de entrada

O mapeamento de entrada permite mapear ou inserir informações em determinados campos da API. Desse modo, o Deployment Manager consegue interagir de maneira mais harmoniosa com a API de base. Isso alivia a responsabilidade dos usuários de ter que entender o comportamento sutil da API.

Use mapeamentos de entrada para simplificar a interação dos usuários com a API. Por exemplo, use mapeamentos de entrada para receber automaticamente valores gerados pelo servidor, como impressões digitais, IDs ou ETags. Isso evita que os usuários tenham o trabalho de executar uma solicitação get separada no recurso sempre que quiserem fazer uma atualização.

Da mesma forma, também é possível usar os mapeamentos de entrada para lidar com situações ambíguas ou confusas, em que o mesmo campo da API tem valores distintos para métodos diferentes. Por exemplo, uma solicitação para criar um recurso pode exigir uma propriedade name que o usuário pode especificar, mas a mesma API pode exigir uma propriedade name em um formato diferente para métodos update. Assim, é possível usar mapeamentos de entrada para informar ao Deployment Manager qual valor é apropriado para cada método de API.

Para especificar mapeamentos de entrada para um provedor de tipos, forneça a propriedade options.inputMappings. É possível definir os mapeamentos de entrada que se aplicam à API por inteiro ou fornecer explicitamente aqueles aplicáveis a cada coleção:

# 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]...
            ]
        }
    }
]

Todas as partes importantes dessa sintaxe estão descritas abaixo.

Coleta

[SPECIFIC_COLLECTION] é a coleção da API à que o mapeamento de entrada se aplica. Por exemplo, se você estiver fornecendo mapeamentos de entrada para um documento do Google Discovery, como a API IAM Service Accounts, os conjuntos relevantes são projects.serviceAccounts e projects.serviceAccountKeys.

Para uma API que usa a especificação OpenAPI, o caminho da coleção pode ser /example-collection/{name}. Explore um exemplo de OpenAPI funcional no repositório da OpenAPI no GitHub.

Nome do campo

"fieldName" é o atributo da API ou a propriedade para que você quer especificar o mapeamento de entrada. Por exemplo, "fieldName": "fingerprint", "fieldName": "etag" e assim por diante.

Local

As propriedades da API podem aparecer como parâmetros no caminho do URL ou como parte do corpo da solicitação ou da resposta. Especifique como local onde o mapeamento de entrada se aplica, como o PATH do URL ou o BODY da solicitação. Os valores aceitos são:

  • PATH
  • BODY
  • QUERY
  • HEADER

Correspondência de métodos

Especifique a quais métodos o mapeamento de entrada se aplica. Use regex para especificar vários métodos. Por exemplo:

"methodMatch":"^create$"

Para as especificações da OpenAPI, você pode fazer:

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

Value

Especifique o valor que o Deployment Manager deve inserir nesse campo. O campo usa a notação JSONPath. Por exemplo, neste mapeamento de entrada está definido que, para o campo name, o Deployment Manager deve receber o valor fornecido pelo usuário e injetá-lo no formato projects/$.project/topics/$resource.properties.topic:

"inputMappings":[
{
  "fieldName":"name",
  "location":"PATH",
  "methodMatch":"^post$",
  "value":"concat(\"projects/\", $.project, \"/topics/\", $.resource.properties.topic)"
}...
  • Ao usar $.resource.properties.[VARIABLE], você define o valor de uma propriedade que o usuário definirá na respectiva configuração. Por exemplo, para $.resource.properties.topic, o valor será o que for informado pelo usuário para a propriedade topic na configuração dele:

    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
    
  • Para se referir ao próprio recurso após uma solicitação get, use $.resource.self.[VARIABLE]. Por exemplo, para solicitações de atualização, se você quiser a impressão digital mais recente, poderá usar esta sintaxe para instruir o Deployment Manager a executar uma get e reter o valor:

    {
      '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'
    }
    

Como usar propriedades virtuais

Propriedades virtuais são propriedades arbitrárias que podem ser expostas aos usuários por meio do Deployment Manager. Essas propriedades não fazem parte da API subjacente. Elas são variáveis arbitrárias que podem ser usadas para transmitir informações ou ocultar dos usuários as inconsistências na API. Você também pode fazer referência a propriedades virtuais nos mapeamentos de entrada.

As propriedades virtuais seguem o esquema JSON 4. Forneça as propriedades virtuais como parte de options para uma coleção específica:

"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": [
    ...
   ]
  }

Em um arquivo de definição de YAML, a aparência fica assim:

- 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:
    ...

Por exemplo, imagine uma API falsa que gera endereços de e-mail. Suponhamos que a API tenha um método para criar um e-mail que aceite uma propriedade emailAddress.displayName. Quando um usuário quer criar um endereço de e-mail, ele envia a seguinte solicitação:

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

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

Digamos que a API exiba uma maneira de atualizar o endereço de e-mail, mas o método para atualizar um e-mail requeira apenas a propriedade displayName em vez da propriedade email.displayName:

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

{
  "displayName": "josh"
}

Como os usuários podem fornecer o valor ao usar esse provedor de tipos? Você pode pedir a eles que especifiquem a propriedade de uma maneira diferente, dependendo da operação:

# 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

Como alternativa, é possível criar uma propriedade virtual que use o mesmo valor, independentemente da operação. Depois, use os mapeamentos de entrada para mapear a propriedade virtual para o parâmetro da API apropriado. Para este exemplo, supõem-se que você tenha definido uma propriedade virtual chamada displayName. Os mapeamentos de entrada podem ter a seguinte aparência:

{
    "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":""
    }
}

Especificamente, a propriedade virtual está definida aqui:

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

Em formato legível por humanos:

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

Agora, seus usuários podem especificar displayName como a propriedade de nível superior para solicitações de atualização e criação, e o Deployment Manager saberá como mapear o valor corretamente.

# 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

A seguir