Exemplos de transformação

É possível transformar os dados do CloudEvents escrevendo expressões de transformação usando a CEL. Para mais informações, consulte Transformar eventos recebidos.

Confira a seguir alguns casos de uso e exemplos comuns que mostram como escrever expressões CEL para transformar os dados de eventos.

Casos de uso padrão

Confira a seguir alguns casos de uso padrão ao transformar dados de eventos.

Normalização de dados

É necessário nivelar uma estrutura de dados aninhada na mensagem de evento para facilitar o processamento por um serviço downstream.

Cenário:

Considerando os seguintes dados do CloudEvents:

{
  "data": {
    "orderId": "12345",
    "customer": {
      "firstName": "Alex",
      "lastName": "Taylor",
      "address": {
        "street": "1800 Amphibious Blvd.",
        "city": "Mountain View"
      }
    }
  }
}

Você quer escrever uma expressão CEL que resulte na seguinte saída:

{
  "data": {
    "orderId": "12345",
    "customerFirstName": "Alex",
    "customerLastName": "Taylor",
    "customerStreet": "1800 Amphibious Blvd.",
    "customerCity": "Mountain View"
  }
}
Solução 1:

Formate os dados de saída manualmente. Isso permite listar os nomes dos campos e escolher apenas os elementos necessários na saída. Essa é uma abordagem razoável quando a entrada é previsível e o número de campos é baixo. A função setField adiciona ou substitui um campo do evento por uma determinada chave. Exemplo:

message.setField("data",
{
  "orderId": message.data.orderId,
  "customerFirstName": message.data.customer.firstName,
  "customerLastName": message.data.customer.lastName,
  "customerStreet": message.data.customer.address.street,
  "customerCity": message.data.customer.address.city,
})
Solução 2:

Use uma função na sua expressão. A função setField adiciona ou substitui um campo do evento por uma determinada chave. A função denormalize aplana estruturas profundas para uma lista de pares de chave-valor. Os nomes de campo são delimitados usando um período (.) para segmentar a hierarquia da estrutura. Exemplo:

message.setField("data", message.data.denormalize())

Isso resulta na saída abaixo, que difere um pouco do payload esperado. No entanto, as vantagens incluem uma expressão CEL mais curta, que opera em qualquer entrada e inclui automaticamente qualquer número de campos recebidos.

{
  "data": {
    "orderId": "12345",
    "customer.firstName": "Alex",
    "customer.lastName": "Taylor",
    "customer.address.street": "1800 Amphibious Blvd.",
    "customer.address.city": "Mountain View"
  }
}

Mascaramento de dados

É necessário mascarar dados sensíveis em um payload de evento antes que ele seja enviado para um ambiente menos seguro.

Cenário:

Considerando os seguintes dados do CloudEvents:

{
  "data": {
    "userId": "user123",
    "email": "alex@example.com",
    "creditCardNumber": "1234-5678-9012-3456"
  }
}

Você quer escrever uma expressão CEL que resulte na seguinte saída:

{
  "data": {
    "userId": "user123",
    "email": "a***@example.com",
    "creditCardNumber": "xxxx-xxxx-xxxx-3456"
  }
}
Solução:

Use uma expressão para mascarar informações sensíveis, como o endereço de e-mail e o número do cartão de crédito. A função setField adiciona ou substitui um campo do evento por uma determinada chave. A função de expressão regular extract segue a sintaxe RE2. Exemplo:

message
      .setField("data.email",
          re.extract(message.data.email,
                    "(^.).*@(.*)",
                    "\\1***@\\2"))

      .setField("data.creditCardNumber",
          re.extract(message.data.creditCardNumber,
                    "(\\d{4})\\D*$",
                    "xxxx-xxxx-xxxx-\\1"))

Descarte de dados sensíveis

Você precisa remover campos específicos de um payload de evento com base em determinadas condições.

Cenário:

Considerando os seguintes dados do CloudEvents:

{
  "data": {
    "orderId": "12345",
    "customerType": "gold",
    "discountCode": "VIP"
  }
}

Você quer escrever uma expressão CEL que resulte na seguinte saída:

{
  {
  "orderId": "12345",
  "customerType": "gold"
  }
}
Solução:

Use uma expressão que oculte o campo discountCode se o customerType for "gold". A função removeFields remove campos específicos de um evento. Exemplo:

message.data.customerType == "gold" ?
      message.removeFields(["data.discountCode"]) :
      message

Conversão de dados

Você precisa converter dados de um formato ou tipo para outro.

Cenário:

Considerando os seguintes dados do CloudEvents:

{
  "data": {
    "orderDate": "2024-10-31T12:00:00Z",
    "totalAmount": "1500"
  }
}

Você quer escrever uma expressão CEL que resulte na seguinte saída:

{
  "data": {
    "orderDate": 1704086400,
    "totalAmount": 1500.00
  }
}
Solução:

Use uma expressão que converta orderDate em um carimbo de data/hora do UNIX e o tipo totalAmount de um string em um double (número de ponto flutuante). A função setField adiciona ou substitui um campo do evento por uma determinada chave. Use funções de manipulação de string para converter os resultados de string. Exemplo:

message
      .setField("data.orderDate", int(timestamp(message.data.orderDate)))
      .setField("data.totalAmount", double(message.data.totalAmount))

Roteamento condicional

Você precisa rotear eventos para diferentes destinos com base nos dados do evento.

Cenário:

Considerando os seguintes dados do CloudEvents:

{
  "data": {
    "eventType": "order.created",
    "orderValue": 200
  }
}

Você quer escrever uma expressão CEL que resulte na seguinte saída:

{
  "data": {
    "eventType": "order.created",
    "orderValue": 200,
    "routingKey": "highValue"
  }
}
Solução:

Use uma expressão que adicione um campo routingKey com um "highValue" se o orderValue for maior que 100. Caso contrário, use "normal". O campo routingKey pode ser usado para determinar o caminho de roteamento. A função setField adiciona ou substitui um campo do evento por uma determinada chave. Exemplo:

message.data.orderValue > 100 ?
      message.setField("data.routingKey", "highValue") :
      message.setField("data.routingKey", "normal")

Processamento de valores padrão

É necessário garantir que determinados campos no payload do evento tenham valores padrão se eles não estiverem presentes.

Cenário:

Considerando os seguintes dados do CloudEvents:

{
  "data": {
    "itemName": "Product A"
  }
}

Você quer escrever uma expressão CEL que resulte na seguinte saída:

{
  "data": {
    "itemName": "Product A",
    "quantity": 1
  }
}
Solução:

Use uma expressão que adicione um campo quantity com um valor padrão de 1, se o campo ainda não existir. A macro has testa se um campo está disponível. A função setField adiciona ou substitui um campo do evento por uma determinada chave. Exemplo:

has(message.data.quantity)  ?
    message :
    message.setField("data.quantity", 1)

Manipulação de strings

Você precisa extrair ou modificar partes de um campo de string nos dados do evento.

Cenário:

Considerando os seguintes dados do CloudEvents:

{
  "data": {
    "customerEmail": "alex@example.com"
  }
}

Você quer escrever uma expressão CEL que resulte na seguinte saída:

{
  "data": {
    "customerEmail": "alex@example.com",
    "emailDomain": "example.com"
  }
}
Solução:

Use uma expressão que extrai o nome de domínio ("example.com") do campo customerEmail e o armazena em um novo campo emailDomain. A função setField adiciona ou substitui um campo do evento por uma determinada chave. A função de expressão regular extract segue a sintaxe RE2. Exemplo:

message
  .setField("data.emailDomain",
re.extract(message.data.customerEmail, "(^.*@)(.*)", "\\2"))

Listar e mapear operações

Você precisa trabalhar com listas ou mapas nos dados de eventos.

Cenário:

Considerando os seguintes dados do CloudEvents:

{
  "data": {
    "productIds": [
      "product123",
      "product456"
    ]
  }
}

Você quer escrever uma expressão CEL que resulte na seguinte saída:

{
  "data": {
    "productIds": [
      "product123",
      "product456"
    ],
    "productFound": true
  }
}
Solução:

Use uma expressão que verifique se "product456" existe na lista productIds e armazene o resultado (true ou false) em um novo campo productFound. A função setField adiciona ou substitui um campo do evento por uma determinada chave. A macro exists testa se um predicado é válido para todos os elementos de uma lista e combina os resultados com o operador "or". Exemplo:

message.setField("data.productFound",
        message.data.productIds.exists(id, id == "product123"))

Tratamento de erros

É necessário processar com cuidado possíveis erros ou dados inesperados no payload do evento.

Cenário:

Considerando os seguintes dados do CloudEvents:

{
  "data": {
    "quantity": "abc"
  }
}

Você quer escrever uma expressão CEL que resulte na seguinte saída:

{
  "data": {
    "quantity": 0,
    "error": "Invalid quantity"
  }
}
Solução:

Use uma expressão que tente converter o campo quantity em um número inteiro. Se a conversão falhar, defina o campo quantity como 0 e adicione um novo campo error com o valor "Quantidade inválida".

  • A macro has testa se um campo está disponível.
  • A função type retorna o tipo de um valor.
  • A função de expressão regular matches segue a sintaxe RE2.
  • A função setField adiciona ou substitui um campo do evento por uma determinada chave.

Exemplo:

// Check if data.quantity exists
has(message.data.quantity) &&
// Check if data.quantity is a string
type(message.data.quantity) == string &&
// Check if string consists of digits
message.data.quantity.matches(r'^-?[0-9]+$') ?
  // If data.quantity is valid, use message
  message :
  // If data.quantity is invalid, set to 0 and generate error
  message
    .setField("data.quantity", 0)
    .setField("data.error", "Invalid quantity")

Casos de uso complexos

Confira a seguir alguns casos de uso complexos ao transformar dados de eventos.

Transformação de dados

Você precisa realizar várias transformações em dados de eventos aninhados.

Cenário:

Considerando os seguintes dados do CloudEvents:

{
  "data": {
    "orderId": "12345",
    "customer": {
      "firstName": "Alex",
      "lastName": "Taylor",
      "email": "alex@example.com",
      "address": {
        "street": "1800 Amphibious Blvd.",
        "city": "Mountain View",
        "state": "CA"
      }
    },
    "items": [
      {
        "itemId": "item1",
        "price": 10.00,
        "quantity": 2
      },
      {
        "itemId": "item2",
        "price": 5.00,
        "quantity": 1
      }
    ]
  }
}

Você quer escrever uma expressão CEL que resulte na seguinte saída:

{
  "data": {
    "orderId": "12345",
    "customer.firstName": "Alex",
    "customer.lastName": "Taylor",
    "customer.email": "a***@example.com",
    "customer.address.city": "Mountain View",
    "customer.address.state": "CA"
  }
}
Solução:

Use uma expressão que extraia a cidade e o estado do endereço e que oculte o endereço de e-mail.

  • A função setField adiciona ou substitui um campo do evento por uma determinada chave.
  • A função toMap converte uma lista de mapas CEL em um único mapa CEL.
  • A função de expressão regular extract segue a sintaxe RE2.
  • A função removeFields remove campos específicos de um evento.
  • A função denormalize aplana estruturas profundas para uma lista de pares de chave-valor. Os nomes de campo são delimitados usando um período (.) para segmentar a hierarquia da estrutura.

Exemplo:

message
.setField("data",
  message.data.setField("customer.address",
    message.data.customer.address.map(key, key == "city" || key == "state",
          { key: message.data.customer.address[key] }).toMap())
  .setField("customer.email",
        re.extract(message.data.customer.email, "(^..?).*@(.*)", "\\1***@\\2"))
  .removeFields(["items"])
  .denormalize()
)

Formatação e roteamento de dados

Você precisa formatar os dados do evento, adicionar informações do produto e encaminhar a mensagem do evento.

Cenário:

Considerando os seguintes dados do CloudEvents:

{
  "data": {
    "productId": "p123",
    "productName": "Example Product",
    "category": "electronics"
  }
}

Você quer escrever uma expressão CEL que resulte na seguinte saída:

{
  "data": {
    "productId": "electronics-p123",
    "productName": "EXAMPLE PRODUCT",
    "category": "electronics",
    "routingKey": "electronics"
  }
}
Solução:

Use uma expressão que formata o nome do produto em maiúsculas, adiciona um prefixo ao ID do produto com base na categoria e inclui uma chave de roteamento para processamento posterior. A função setField adiciona ou substitui um campo do evento por uma determinada chave. A função upperAscii retorna uma string com todos os caracteres ASCII convertidos em caracteres maiúsculos correspondentes. Exemplo:

message
.setField("data.productId",
message.data.category + "-" + message.data.productId)
.setField("data.productName", message.data.productName.upperAscii())
.setField("data.routingKey", message.data.category)