Atualizações usando máscaras de campo

Na API Google Ads, as atualizações são feitas usando uma máscara de campo. A máscara de campo lista todos os campos que você pretende alterar com a atualização, e todos os campos especificados que não estão na máscara de campo são ignorados, mesmo que sejam enviados ao servidor.

FieldMaskUtil

A maneira recomendada de gerar máscaras de campo é usar o utilitário integrado, que oculta detalhes específicos e permite gerar máscaras de campo automaticamente monitorando as mudanças feitas nos campos da entidade.

O exemplo a seguir mostra como gerar uma máscara de campo para atualizar uma campanha:

campaign = client.resource.campaign
campaign.resource_name = client.path.campaign(customer_id, campaign_id)

mask = client.field_mask.with campaign do
  campaign.status = :PAUSED
  campaign.network_settings = client.resource.network_settings do |ns|
    ns.target_search_network = false
  end
end

O código primeiro cria um objeto Campaign vazio e, em seguida, define o nome do recurso para informar à API sobre a campanha que está sendo atualizada.

Este exemplo usa o método client.field_mask.with na campanha para iniciar o bloco que abrange as atualizações. No final desse bloco, o utilitário compara o estado atual da campanha após o bloco com o estado inicial da campanha antes do bloco e produz automaticamente uma máscara de campo enumerando os campos alterados. É possível fornecer essa máscara de campo à operação ao criá-la para a chamada de mutação da seguinte maneira:

operation = client.operation.campaign
operation.update = campaign
operation.update_mask = mask

Esse método é recomendado quando você está criando uma operação complexa e quer um controle detalhado de cada etapa. No entanto, na maioria dos casos, é possível transmitir o nome do recurso (ou uma instância de recurso atual) para o método de fábrica da biblioteca do Ruby:

campaign_resource_name = client.path.campaign(customer_id, campaign_id)

operation =
  client.operation.update_resource.campaign(campaign_resource_name) do |c|
    c.status = :PAUSED
    c.network_settings = client.resource.network_settings do |ns|
      ns.target_search_network = false
    end
  end

Quando recebe uma string de nome de recurso, esse método cria automaticamente um novo recurso de campanha com resource_name preenchido, constrói a máscara de campo com base nas mudanças feitas no bloco, cria a operação de atualização e retorna a operação final com update e update_mask já preenchidos. Também é possível transmitir uma instância de proto Campaign em vez de uma string de nome de recurso para especificar o estado inicial da campanha. Esse padrão funciona para todos os recursos que aceitam a operação de atualização.

Criar manualmente uma máscara de campo

Para criar uma máscara de campo do zero sem usar utilitários de biblioteca, crie um Google::Protobuf::FieldMask, preencha uma matriz com os nomes de todos os campos que você pretende alterar e atribua a matriz ao campo paths da máscara de campo:

mask = Google::Protobuf::FieldMask.new
mask.paths = ['status', 'name']

Atualizar campos de mensagem e os subcampos deles

Os campos MESSAGE podem ter subcampos (como MaximizeConversions, que tem três: target_cpa_micros, cpc_bid_ceiling_micros e cpc_bid_floor_micros) ou não ter nenhum (como ManualCpm).

Campos de mensagem sem subcampos definidos

Ao atualizar um campo MESSAGE que não está definido com subcampos, use FieldMaskUtil para gerar uma máscara de campo, conforme apresentado anteriormente.

Campos de mensagem com subcampos definidos

Ao atualizar um campo MESSAGE definido com subcampos sem definir explicitamente nenhum dos subcampos nessa mensagem, é necessário adicionar manualmente cada um dos subcampos MESSAGE mutáveis ao FieldMask, de maneira semelhante ao exemplo anterior que criou uma máscara de campo do zero.

Um exemplo comum é atualizar a estratégia de lances de uma campanha sem definir nenhum dos campos na nova estratégia. O exemplo a seguir demonstra como atualizar uma campanha para usar a estratégia de lances MaximizeConversions sem definir nenhum dos subcampos na estratégia de lances.

Neste exemplo, o uso da comparação integrada de FieldMaskUtil não alcança o objetivo pretendido.

O código a seguir gera uma máscara de campo que inclui maximize_conversions. No entanto, a API Google Ads não permite esse comportamento para evitar a limpeza acidental de campos e produz um erro FieldMaskError.FIELD_HAS_SUBFIELDS.

# Creates a campaign with the proper resource name.
campaign = client.resource.campaign do |c|
  c.resource_name = client.path.campaign(customer_id, campaign_id)
end

# Update the maximize conversions field within the update block, so it's
# captured in the field mask.
operation = client.operation.update_resource.campaign(campaign) do |c|
  c.maximize_conversions = client.resource.maximize_conversions
end

# Sends the operation in a mutate request that results in a
# FieldMaskError.FIELD_HAS_SUBFIELDS error because empty MESSAGE fields cannot
# be included in a field mask.
response = client.service.campaign.mutate_campaigns(
  customer_id: customer_id,
  operations: [operation]
)
# Create the operation directly from the campaign's resource name. Don't do
# anything in the block so that the field mask starts empty. You can modify
# other fields in this block, except the message field intended to have a
# blank subfield.
campaign_resource_name = client.path.campaign(customer_id, campaign_id)
operation = client.operation.update_resource.campaign(campaign_resource_name) {}

# Manually add the maximize conversions subfield to the field mask so the API
# knows to clear it.
operation.update_mask.paths << 'maximize_conversions.target_cpa_micros'

# This operation succeeds.
response = client.service.campaign.mutate_campaigns(
  customer_id: customer_id,
  operations: [operation]
)

Limpar campos

Alguns campos podem ser limpos explicitamente. Assim como no exemplo anterior, é preciso adicionar explicitamente esses campos à máscara de campo. Por exemplo, imagine que você tem uma campanha que usa uma estratégia de lances MaximizeConversions e que o campo target_cpa_micros está definido com um valor maior que 0.

No proto3, definir um campo escalar não opcional com o valor padrão (0) é o mesmo que deixá-lo sem definição em uma nova instância de mensagem. Como resultado, FieldMaskUtil adiciona maximize_conversions à máscara de campo em vez de maximize_conversions.target_cpa_micros, o que causa um erro FieldMaskError.FIELD_HAS_SUBFIELDS.

# Create a campaign object representing the campaign you want to change.
campaign = client.resource.campaign do |c|
  c.resource_name = client.path.campaign(customer_id, campaign_id)
end

# The field mask in this operation includes 'maximize_conversions',
# but not 'maximize_conversions.target_cpa_micros', so it results in an
# error.
operation = client.operation.update_resource.campaign(campaign) do |c|
  c.maximize_conversions = client.resource.maximize_conversions do |mc|
    mc.target_cpa_micros = 0
  end
end

# Operation fails because the field mask is invalid.
response = client.service.campaign.mutate_campaigns(
  customer_id: customer_id,
  operations: [operation]
)
# Create a campaign including the maximize conversions fields right away, since
# they are manually added to the field mask.
campaign = client.resource.campaign do |c|
  c.resource_name = client.path.campaign(customer_id, campaign_id)
  c.maximize_conversions = client.resource.maximize_conversions do |mc|
    mc.target_cpa_micros = 0
  end
end

# Create the operation with an empty field mask. You can add a block here with
# other changes that are automatically added to the field mask.
operation = client.operation.update_resource.campaign(campaign) {}

# Add the field to the field mask so the API knows to clear it.
operation.update_mask.paths << 'maximize_conversions.target_cpa_micros'

# Operation succeeds because the correct field mask is specified.
response = client.service.campaign.mutate_campaigns(
  customer_id: customer_id,
  operations: [operation]
)

A abordagem de comparação automática funciona conforme o esperado para campos definidos como optional nos buffers de protocolo da API Google Ads. Como target_cpa_micros não é um campo optional em MaximizeConversions, é necessário anexar explicitamente o caminho a update_mask.paths para limpá-lo.