Como usar o Cloud Tasks para acionar o Cloud Functions

Neste tutorial, mostramos como usar o Cloud Tasks em um aplicativo do Google App Engine para acionar uma Função do Cloud e enviar um e-mail programado.

Objetivos

  • Entender o código em cada um dos componentes.
  • Crie uma conta do SendGrid.
  • Fazer o download do código-fonte.
  • Implantar uma Função do Cloud para receber solicitações do Cloud Tasks e enviar um e-mail por meio da API SendGrid.
  • Criar uma fila do Cloud Tasks.
  • Criar uma conta de serviço para autenticar suas solicitações do Cloud Tasks.
  • Implantar o código do cliente que permite que um usuário envie um e-mail.

Custos

O Cloud Tasks, o Cloud Functions e o App Engine têm um nível gratuito. Portanto, desde que você execute o tutorial dentro desse nível dos produtos determinados, isso não resultará em custos adicionais. Para mais informações, consulte Preços.

Antes de começar

  1. Selecione ou crie um projeto do GCP.

    Acessar a página do App Engine

  2. Inicialize um aplicativo do App Engine no seu projeto:

    1. Na página Bem-vindo ao App Engine, clique em Criar aplicativo.

    2. Selecione uma região para o aplicativo. Esse local servirá como o parâmetro LOCATION_ID para suas solicitações do Cloud Tasks. Portanto, anote-o. Observe que dois locais, denominados europe-west e us-central nos comandos do App Engine, são chamados, respectivamente, europe-west1 e us-central1 nos comandos do Cloud Tasks.

    3. Selecione Node.js para a linguagem e Padrão para o ambiente.

    4. Se o pop-up Ativar faturamento for exibido, selecione sua conta de faturamento. Se você não tiver uma conta de faturamento atualmente, clique em Criar conta de faturamento e siga o assistente.

    5. Na página Primeiros passos, clique em Avançar. Você cuidará disso mais tarde.

  3. Ative as APIs Cloud Functions e Cloud Tasks.

    Ativar as APIs

  4. Instale e inicialize o Cloud SDK.

Como entender o código

Esta seção analisa o código do aplicativo e explica como ele funciona.

Como criar a tarefa

A página de índice é veiculada usando gerenciadores no app.yaml. As variáveis necessárias para a criação de tarefas são passadas como variáveis de ambiente.

runtime: nodejs10

env_variables:
  QUEUE_NAME: "my-queue"
  QUEUE_LOCATION: "us-central1"
  FUNCTION_URL: "https://<region>-<project_id>.cloudfunctions.net/sendEmail"
  SERVICE_ACCOUNT_EMAIL: "<member>@<project_id>.iam.gserviceaccount.com"

# Handlers for serving the index page.
handlers:
  - url: /static
    static_dir: static
  - url: /
    static_files: index.html
    upload: index.html

Esse código cria o endpoint /send-email. Esse endpoint processa os envios de formulário da página de índice e passa esses dados para o código de criação de tarefas.

app.post('/send-email', (req, res) => {
  // Set the task payload to the form submission.
  const {to_name, from_name, to_email, date} = req.body;
  const payload = {to_name, from_name, to_email};

  createHttpTaskWithToken(
    process.env.GOOGLE_CLOUD_PROJECT,
    QUEUE_NAME,
    QUEUE_LOCATION,
    FUNCTION_URL,
    SERVICE_ACCOUNT_EMAIL,
    payload,
    date
  );

  res.status(202).send('📫 Your postcard is in the mail! 💌');
});

Na verdade, esse código cria a tarefa e a envia para a fila do Cloud Tasks. O código cria a tarefa por:

  • Especificando o tipo de segmentação como HTTP Request.

  • Especificando o HTTP method a ser usado e o URL do destino.

  • Definir o cabeçalho Content-Type como application/json para que os aplicativos downstream possam analisar o payload estruturado.

  • Adicionar um e-mail da conta de serviço para que o Cloud Tasks possa fornecer credenciais à segmentação da solicitação, o que requer autenticação. A conta de serviço é criada separadamente.

  • Verifique se a entrada do usuário para data está dentro do máximo de 30 dias e adicione-a à solicitação como campo scheduleTime.

const MAX_SCHEDULE_LIMIT = 30 * 60 * 60 * 24; // Represents 30 days in seconds.

const createHttpTaskWithToken = async function (
  project = 'my-project-id', // Your GCP Project id
  queue = 'my-queue', // Name of your Queue
  location = 'us-central1', // The GCP region of your queue
  url = 'https://example.com/taskhandler', // The full url path that the request will be sent to
  email = '<member>@<project-id>.iam.gserviceaccount.com', // Cloud IAM service account
  payload = 'Hello, World!', // The task HTTP request body
  date = new Date() // Intended date to schedule task
) {
  // Imports the Google Cloud Tasks library.
  const {v2beta3} = require('@google-cloud/tasks');

  // Instantiates a client.
  const client = new v2beta3.CloudTasksClient();

  // Construct the fully qualified queue name.
  const parent = client.queuePath(project, location, queue);

  // Convert message to buffer.
  const convertedPayload = JSON.stringify(payload);
  const body = Buffer.from(convertedPayload).toString('base64');

  const task = {
    httpRequest: {
      httpMethod: 'POST',
      url,
      oidcToken: {
        serviceAccountEmail: email,
      },
      headers: {
        'Content-Type': 'application/json',
      },
      body,
    },
  };

  const convertedDate = new Date(date);
  const currentDate = new Date();

  // Schedule time can not be in the past.
  if (convertedDate < currentDate) {
    console.error('Scheduled date in the past.');
  } else if (convertedDate > currentDate) {
    const date_diff_in_seconds = (convertedDate - currentDate) / 1000;
    // Restrict schedule time to the 30 day maximum.
    if (date_diff_in_seconds > MAX_SCHEDULE_LIMIT) {
      console.error('Schedule time is over 30 day maximum.');
    }
    // Construct future date in Unix time.
    const date_in_seconds =
      Math.min(date_diff_in_seconds, MAX_SCHEDULE_LIMIT) + Date.now() / 1000;
    // Add schedule time to request in Unix time using Timestamp structure.
    // https://googleapis.dev/nodejs/tasks/latest/google.protobuf.html#.Timestamp
    task.scheduleTime = {
      seconds: date_in_seconds,
    };
  }

  try {
    // Send create task request.
    const [response] = await client.createTask({parent, task});
    console.log(`Created task ${response.name}`);
    return response.name;
  } catch (error) {
    // Construct error for Stackdriver Error Reporting
    console.error(Error(error.message));
  }
};

module.exports = createHttpTaskWithToken;

Como criar o e-mail

Esse código cria a Função do Cloud, que é o destino da solicitação do Cloud Task. Ele usa o corpo da solicitação para construir um e-mail e enviá-lo por meio da API SendGrid.

const sendgrid = require('@sendgrid/mail');

/**
 * Responds to an HTTP request from Cloud Tasks and sends an email using data
 * from the request body.
 *
 * @param {object} req Cloud Function request context.
 * @param {object} req.body The request payload.
 * @param {string} req.body.to_email Email address of the recipient.
 * @param {string} req.body.to_name Name of the recipient.
 * @param {string} req.body.from_name Name of the sender.
 * @param {object} res Cloud Function response context.
 */
exports.sendEmail = async (req, res) => {
  // Get the SendGrid API key from the environment variable.
  const key = process.env.SENDGRID_API_KEY;
  if (!key) {
    const error = new Error(
      'SENDGRID_API_KEY was not provided as environment variable.'
    );
    error.code = 401;
    throw error;
  }
  sendgrid.setApiKey(key);

  // Get the body from the Cloud Task request.
  const {to_email, to_name, from_name} = req.body;
  if (!to_email) {
    const error = new Error('Email address not provided.');
    error.code = 400;
    throw error;
  } else if (!to_name) {
    const error = new Error('Recipient name not provided.');
    error.code = 400;
    throw error;
  } else if (!from_name) {
    const error = new Error('Sender name not provided.');
    error.code = 400;
    throw error;
  }

  // Construct the email request.
  const msg = {
    to: to_email,
    from: 'postcard@example.com',
    subject: 'A Postcard Just for You!',
    html: postcardHTML(to_name, from_name),
  };

  try {
    await sendgrid.send(msg);
    // Send OK to Cloud Task queue to delete task.
    res.status(200).send('Postcard Sent!');
  } catch (error) {
    // Any status code other than 2xx or 503 will trigger the task to retry.
    res.status(error.code).send(error.message);
  }
};

Como preparar o aplicativo

Como configurar o SendGrid

  1. Crie uma conta do SendGrid.

  2. Crie uma chave de API do SendGrid:

    1. Faça login na sua conta do SendGrid.

    2. Na barra de navegação esquerda, abra Configurações e clique em Chaves de API.

    3. Clique em Criar chave de API e selecione acesso restrito. No cabeçalho Enviar e-mail, selecione Acesso total.

    4. Copie a chave de API quando ela for exibida (você verá essa opção apenas uma vez, cole-a em algum lugar para usá-la mais tarde).

Como fazer o download do código-fonte

  1. Clone o repositório do app de amostra na máquina local:

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git
    
  2. Acesse o diretório que contém o código de amostra:

    cd cloud-tasks/
    

Como implantar o Cloud Function

  1. Navegue até o diretório function/:

    cd function/
    
  2. Implante a função, substituindo sendgrid_api_key pela chave:

    gcloud functions deploy sendEmail --runtime nodejs8 --trigger-http --set-env-vars SENDGRID_API_KEY=sendgrid_api_key
    

    Este comando usa sinalizações:

    • --trigger-http para especificar o tipo de acionador do Cloud Functions.

    • --set-env-var para definir suas credenciais do SendGrid

  3. Defina o controle de acesso da função para permitir apenas usuários autenticados.

    1. Selecione a função sendEmail na IU do Cloud Functions.

    2. Se você não vir informações de permissões para sendEmail, clique em MOSTRAR PAINEL DE INFORMAÇÕES no canto superior direito.

    3. No cabeçalho Invocador do Cloud Functions, exclua o membro allUsers.

    4. Clique no botão Adicionar membros acima.

    5. Defina Novos membros como allAuthenticatedUsers.

    6. Defina o papel como Cloud Function Invoker.

    7. Clique em SAVE.

Como criar uma fila do Cloud Tasks

  1. Crie uma fila usando o seguinte comando gcloud:

    gcloud tasks queues create my-queue
    
  2. Verifique se ele foi criado com sucesso:

    gcloud tasks queues describe my-queue
    

Criar uma conta de serviço

A solicitação do Cloud Tasks deve fornecer credenciais no cabeçalho Authorization do Cloud Function para autenticar a solicitação. Essa conta de serviço permite que o Cloud Tasks crie e adicione um token OIDC para essa finalidade.

  1. Em IAM e administrador | IU das contas de serviço, clique em +CRIAR CONTA DE SERVIÇO.

  2. Adicione um nome de conta de serviço (nome de exibição amigável) e selecione Criar.

  3. Selecione o papel Invocador do Cloud Functions e clique em Continuar.

  4. Selecione Concluído.

Como implantar o endpoint e o criador de tarefas no App Engine

  1. Navegue até o diretório app/:

    cd ../app/
    
  2. Atualize as variáveis no app.yaml com seus valores:

    env_variables:
      QUEUE_NAME: "my-queue"
      QUEUE_LOCATION: "us-central1"
      FUNCTION_URL: "https://<region>-<project_id>.cloudfunctions.net/sendEmail"
      SERVICE_ACCOUNT_EMAIL: "<member>@<project_id>.iam.gserviceaccount.com"

    Para encontrar o local da fila, use o seguinte comando:

    gcloud tasks queues describe my-queue
    

    Para encontrar o URL da função, use o seguinte comando:

    gcloud functions describe sendEmail
    
  3. Implante o aplicativo no ambiente padrão do App Engine usando o seguinte comando:

    gcloud app deploy
    
  4. Abra o aplicativo para enviar um cartão-postal como um e-mail:

    gcloud app browse
    

Limpar

Depois de concluir o tutorial, limpe os recursos criados no Google Cloud para que não consumam sua cota e você não seja cobrado por eles no futuro. Veja como excluir e desativar esses recursos nas seções a seguir.

Como excluir recursos

É possível limpar os recursos criados no GCP para que eles não ocupem a cota e você não seja cobrado por eles no futuro. As próximas seções descrevem como excluir ou desativar esses recursos.

Excluir a Função do Cloud

  1. Acesse a página Cloud Functions no Console do Cloud.

    Acessar a página Cloud Functions.

  2. Clique nas caixas de seleção ao lado das funções.

  3. Clique no botão Excluir na parte superior da página e confirme.

Exclua a fila do Cloud Task

  1. Abra a página de filas do Cloud Tasks no console.

    Acessar a página de filas do Cloud Tasks

  2. Selecione o nome da fila que você quer excluir e clique em Excluir fila.

  3. Confirme a ação.

Excluir o projeto

O jeito mais fácil de evitar cobranças é excluindo o projeto que você criou para o tutorial.

Para excluir o projeto:

  1. No Console do Cloud, acesse a página Gerenciar recursos:

    Acessar a página "Gerenciar recursos"

  2. Na lista de projetos, selecione o projeto que você quer excluir e clique em Excluir .
  3. Na caixa de diálogo, digite o ID do projeto e clique em Encerrar para excluí-lo.

A seguir