

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

# Tutorial de fluxo de trabalho de inscrição - Parte 3: Implementar as atividades
<a name="swf-sns-tutorial-implementing-activities"></a>

Agora, implementaremos cada uma das atividades no nosso fluxo de trabalho, começando com uma classe base que fornece alguns recursos comuns para o código de atividade.

**Topics**
+ [Definir um tipo de atividade básica](#defining-a-basic-activity-type)
+ [Definindo GetContactActivity](#defining-getcontactactivity)
+ [Definindo SubscribeTopicActivity](#defining-subscribetopicactivity)
+ [Definindo WaitForConfirmationActivity](#defining-waitforconfirmationactivity)
+ [Definindo SendResultActivity](#defining-sendresultactivity)
+ [Próximas etapas](#implementing-activities-next-steps)

## Definir um tipo de atividade básica
<a name="defining-a-basic-activity-type"></a>

Ao projetar o fluxo de trabalho, identificamos as seguintes atividades:
+ `get_contact_activity`
+ `subscribe_topic_activity`
+ `wait_for_confirmation_activity`
+ `send_result_activity`

Implementaremos cada uma dessas atividades agora. Como nossas atividades compartilharão alguns recursos, vamos fazer um pequeno trabalho de base e criar um código comum que eles possam compartilhar. Vamos chamá-lo **BasicActivity**e defini-lo em um novo arquivo chamado`basic_activity.rb`.

Como acontece com os outros arquivos de origem, incluiremos `utils.rb` para acessar a função `init_domain` e configurar o domínio da amostra.

```
   require_relative 'utils.rb' 
```

Em seguida, declararemos a classe de atividade básica e alguns dados comuns que serão de nosso interesse para cada atividade. Salvaremos a [AWS::SimpleWorkflow::ActivityType](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SimpleWorkflow/ActivityType.html)instância, o *nome* e *os resultados* da atividade nos atributos da classe.

```
   class BasicActivity

     attr_accessor :activity_type
     attr_accessor :name
     attr_accessor :results
```

Esses atributos acessam dados de instância definidos no método `initialize` da classe, que recebe um *nome* de atividade e uma *versão* opcional e um mapa de *opções* a serem usados ao registrar a atividade com o Amazon SWF.

```
     def initialize(name, version = 'v1', options = nil)

       @activity_type = nil
       @name = name
       @results = nil

       # get the domain to use for activity tasks.
       @domain = init_domain

       # Check to see if this activity type already exists.
       @domain.activity_types.each do | a |
         if (a.name == @name) && (a.version == version)
           @activity_type = a
         end
       end

       if @activity_type.nil?
         # If no options were specified, use some reasonable defaults.
         if options.nil?
           options = {
             # All timeouts are in seconds.
             :default_task_heartbeat_timeout => 900,
             :default_task_schedule_to_start_timeout => 120,
             :default_task_schedule_to_close_timeout => 3800,
             :default_task_start_to_close_timeout => 3600 }
         end
         @activity_type = @domain.activity_types.register(@name, version, options)
       end
     end
```

Como acontece com o registro do tipo de fluxo de trabalho, se um tipo de atividade já estiver registrado, poderemos recuperá-lo observando a coleção [activity\_types](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SimpleWorkflow/Domain.html#activity_types-instance_method) do domínio. Se a atividade não puder ser encontrada, ela será registrada.

Além disso, como em tipos de fluxo de trabalho, você pode definir *opções padrão* que são armazenadas com seu tipo de atividade quando você o registra.

A última coisa que nossa atividade básica obtém é uma maneira consistente de executá-la. Definiremos um método `do_activity` que usa uma tarefa de atividade. Conforme mostrado, podemos usar a tarefa de atividade transmitida para receber dados através de seu atributo de instância `input`.

```
     def do_activity(task)
       @results = task.input # may be nil
       return true
     end
   end
```

Isso encerra a **BasicActivity**aula. Agora, vamos usá-la para tornar a definição das nossas atividades simples e consistente.

## Definindo GetContactActivity
<a name="defining-getcontactactivity"></a>

A primeira atividade que é executada durante a execução de um fluxo de trabalho é `get_contact_activity`, que recupera as informações de assinatura do tópico do Amazon SNS do usuário.

Crie um novo arquivo chamado`get_contact_activity.rb`, e exija ambos`yaml`, que usaremos para preparar uma string para passar para o Amazon SWF e`basic_activity.rb`, que usaremos como base para essa **GetContactActivity**classe.

```
   require 'yaml'
   require_relative 'basic_activity.rb'

   # **GetContactActivity** provides a prompt for the user to enter contact
   # information. When the user successfully enters contact information, the
   # activity is complete.
   class GetContactActivity < BasicActivity
```

Como inserimos o código de registro da atividade **BasicActivity**, o `initialize` método **GetContactActivity**é bem simples. Simplesmente chamamos o construtor da classe base com o nome da atividade, `get_contact_activity`. Isso é tudo o que é necessário para registrar nossa atividade.

```
     # initialize the activity
     def initialize
       super('get_contact_activity')
     end
```

Agora definiremos o `do_activity` método, que solicita o número de and/or telefone do e-mail do usuário. 

```
     def do_activity(task)
       puts ""
       puts "Please enter either an email address or SMS message (mobile phone) number to"
       puts "receive SNS notifications. You can also enter both to use both address types."
       puts ""
       puts "If you enter a phone number, it must be able to receive SMS messages, and must"
       puts "be 11 digits (such as 12065550101 to represent the number 1-206-555-0101)."

       input_confirmed = false
       while !input_confirmed
         puts ""
         print "Email: "
         email = $stdin.gets.strip

         print "Phone: "
         phone = $stdin.gets.strip

         puts ""
         if (email == '') && (phone == '')
           print "You provided no subscription information. Quit? (y/n)"
            confirmation = $stdin.gets.strip.downcase
            if confirmation == 'y'
              return false
            end
         else
            puts "You entered:"
            puts "  email: #{email}"
            puts "  phone: #{phone}"
            print "\nIs this correct? (y/n): "
            confirmation = $stdin.gets.strip.downcase
            if confirmation == 'y'
              input_confirmed = true
            end
         end
       end

       # make sure that @results is a single string. YAML makes this easy.
       @results = { :email => email, :sms => phone }.to_yaml
       return true
     end
   end
```

No final de `do_activity`, usamos o e-mail e o número de telefone recuperados do usuário, colocamos esses dados em um mapa e, em seguida, usamos `to_yaml` para converter o mapa inteiro em uma string YAML. Há um motivo importante para isso: todos os resultados que você passar para o Amazon SWF ao concluir uma atividade devem ser *apenas dados de string de caracteres*. A habilidade do Ruby de converter objetos facilmente em strings YAML e depois novamente em objetos é, felizmente, bem adaptada para esse propósito.

Esse é o fim da implementação de `get_contact_activity`. Esses dados serão usados em seguida na implementação de `subscribe_topic_activity`.

## Definindo SubscribeTopicActivity
<a name="defining-subscribetopicactivity"></a>

Agora, vamos nos aprofundar no Amazon SNS e criar uma atividade que use as informações geradas por `get_contact_activity` para inscrever o usuário em um tópico do Amazon SNS.

Crie um novo arquivo chamado `subscribe_topic_activity.rb`, adicione os mesmos requisitos que usamos para `get_contact_activity`, declare sua classe e forneça seu método `initialize`.

```
   require 'yaml'
   require_relative 'basic_activity.rb'

   # **SubscribeTopicActivity** sends an SMS / email message to the user, asking for
   # confirmation.  When this action has been taken, the activity is complete.
   class SubscribeTopicActivity < BasicActivity

     def initialize
       super('subscribe_topic_activity')
     end
```

Agora que já temos o código para configurar e registrar a atividade, adicionaremos algum código para criar um tópico do Amazon SNS. Para fazer isso, usaremos o método [create\_topic](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Client.html#create_topic-instance_method) do [AWS::SNS::Client](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Client.html)objeto.

Adicione o método `create_topic` à sua classe, que recebe um objeto de cliente Amazon SNS passado.

```
     def create_topic(sns_client)
       topic_arn = sns_client.create_topic(:name => 'SWF_Sample_Topic')[:topic_arn]

       if topic_arn != nil
         # For an SMS notification, setting `DisplayName` is *required*. Note that
         # only the *first 10 characters* of the DisplayName will be shown on the
         # SMS message sent to the user, so choose your DisplayName wisely!
         sns_client.set_topic_attributes( {
           :topic_arn => topic_arn,
           :attribute_name => 'DisplayName',
           :attribute_value => 'SWFSample' } )
       else
         @results = {
           :reason => "Couldn't create SNS topic", :detail => "" }.to_yaml
         return nil
       end

       return topic_arn
     end
```

Depois de termos o Amazon Resource Name (ARN) do tópico, podemos usá-lo com o método set\_topic\_attributes [do](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Client.html#set_topic_attributes-instance_method) cliente Amazon SNS para definir o tópico *DisplayName*, que é necessário para enviar mensagens SMS com o Amazon SNS.

Por fim, definiremos o método `do_activity`. Começaremos coletando quaisquer dados que tenham sido transmitidos por meio da opção `input` quando a atividade foi agendada. Conforme mencionado anteriormente, isso deve ser transmitido como uma string, que nós criamos usando `to_yaml`. Ao recuperá-lo, usaremos `YAML.load` para transformar os dados em objetos Ruby.

Este é o início de `do_activity`, no qual recuperamos os dados de entrada.

```
     def do_activity(task)
       activity_data = {
         :topic_arn => nil,
         :email => { :endpoint => nil, :subscription_arn => nil },
         :sms => { :endpoint => nil, :subscription_arn => nil },
       }

       if task.input != nil
         input = YAML.load(task.input)
         activity_data[:email][:endpoint] = input[:email]
         activity_data[:sms][:endpoint] = input[:sms]
       else
         @results = { :reason => "Didn't receive any input!", :detail => "" }.to_yaml
         puts("  #{@results.inspect}")
         return false
       end

       # Create an SNS client. This is used to interact with the service. Set the
       # region to $SMS_REGION, which is a region that supports SMS notifications
       # (defined in the file `utils.rb`).
       sns_client = AWS::SNS::Client.new(
         :config => AWS.config.with(:region => $SMS_REGION))
```

Se não recebermos nenhuma entrada, não haverá muito a fazer, então vamos simplesmente marcar a atividade como falha.

No entanto, supondo que tudo esteja bem, continuaremos preenchendo nosso `do_activity` método, obteremos um cliente Amazon SNS com AWS SDK para Ruby o e o passaremos para `create_topic` nosso método para criar o tópico do Amazon SNS.

```
       # Create the topic and get the ARN
       activity_data[:topic_arn] = create_topic(sns_client)

       if activity_data[:topic_arn].nil?
         return false
       end
```

Há algumas coisas que merecem destaque aqui:
+ Usamos [https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/Core/Configuration.html#with-instance_method](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/Core/Configuration.html#with-instance_method) para definir a região do nosso cliente Amazon SNS. Como queremos enviar mensagens SMS, usamos a região habilitada para SMS que declaramos em `utils.rb`.
+ Salvamos o ARN do tópico em nosso mapa `activity_data`. Isso faz parte dos dados que serão transmitidos à *próxima* atividade no nosso fluxo de trabalho.

Por fim, essa atividade inscreve o usuário no tópico do Amazon SNS, usando os endpoints passados (e-mail e SMS). Não exigimos que o usuário insira *ambos* os endpoints, mas precisamos de pelo menos um.

```
       # Subscribe the user to the topic, using either or both endpoints.
       [:email, :sms].each do | x |
         ep = activity_data[x][:endpoint]
         # don't try to subscribe an empty endpoint
         if (ep != nil && ep != "")
           response = sns_client.subscribe( {
             :topic_arn => activity_data[:topic_arn],
             :protocol => x.to_s, :endpoint => ep } )
           activity_data[x][:subscription_arn] = response[:subscription_arn]
         end
       end
```

[AWS::SNS::Client.subscribe](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Client.html#subscribe-instance_method) usa o tópico ARN, *o* protocolo (que, de forma inteligente, disfarçamos como a chave do mapa para `activity_data` o endpoint correspondente).

Por fim, reempacotamos as informações para a próxima atividade no formato YAML, para que possamos enviá-las de volta ao Amazon SWF.

```
       # if at least one subscription arn is set, consider this a success.
       if (activity_data[:email][:subscription_arn] != nil) or (activity_data[:sms][:subscription_arn] != nil)
         @results = activity_data.to_yaml
       else
         @results = { :reason => "Couldn't subscribe to SNS topic", :detail => "" }.to_yaml
         puts("  #{@results.inspect}")
         return false
       end
       return true
     end
   end
```

Isso completa a implementação do `subscribe_topic_activity`. Em seguida, definiremos `wait_for_confirmation_activity`.

## Definindo WaitForConfirmationActivity
<a name="defining-waitforconfirmationactivity"></a>

Depois que um usuário se inscrever em um tópico do Amazon SNS, ele ainda precisará confirmar a solicitação de inscrição. Nesse caso, aguardaremos que o usuário confirme por e-mail ou mensagem SMS.

A atividade que aguarda a confirmação da inscrição pelo usuário é chamada de `wait_for_confirmation_activity`, e nós a definiremos aqui. Para começar, crie um novo arquivo chamado `wait_for_confirmation_activity.rb` e configure-o como fizemos nas atividades anteriores.

```
   require 'yaml'
   require_relative 'basic_activity.rb'

   # **WaitForConfirmationActivity** waits for the user to confirm the SNS
   # subscription.  When this action has been taken, the activity is complete. It
   # might also time out...
   class WaitForConfirmationActivity < BasicActivity

     # Initialize the class
     def initialize
       super('wait_for_confirmation_activity')
     end
```

Em seguida, vamos começar a definir o método `do_activity` e a recuperar todos os dados de entrada em uma variável local chamada de `subscription_data`.

```
     def do_activity(task)
       if task.input.nil?
         @results = { :reason => "Didn't receive any input!", :detail => "" }.to_yaml
         return false
       end

       subscription_data = YAML.load(task.input)
```

Agora que temos o ARN do tópico, podemos recuperar o tópico criando uma nova instância do [AWS::SNS::Topic](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Topic.html)e passando o ARN para ela.

```
       topic = AWS::SNS::Topic.new(subscription_data[:topic_arn])

       if topic.nil?
         @results = {
           :reason => "Couldn't get SWF topic ARN",
           :detail => "Topic ARN: #{topic.arn}" }.to_yaml
         return false
       end
```

Agora, verificamos o tópico para ver se o usuário confirmou a assinatura usando um dos endpoints. Só exigiremos que um endpoint tenha sido confirmado para considerar a atividade como bem-sucedida.

Um tópico do Amazon SNS mantém uma lista das [assinaturas](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Topic.html#subscriptions-instance_method) desse tópico, e podemos verificar se o usuário confirmou ou não uma assinatura específica, verificando se o ARN da assinatura está definido como algo diferente de `PendingConfirmation`.

```
       # loop until we get some indication that a subscription was confirmed.
       subscription_confirmed = false
       while(!subscription_confirmed)
         topic.subscriptions.each do | sub |
           if subscription_data[sub.protocol.to_sym][:endpoint] == sub.endpoint
             # this is one of the endpoints we're interested in. Is it subscribed?
             if sub.arn != 'PendingConfirmation'
               subscription_data[sub.protocol.to_sym][:subscription_arn] = sub.arn
               puts "Topic subscription confirmed for (#{sub.protocol}: #{sub.endpoint})"
               @results = subscription_data.to_yaml
               return true
             else
               puts "Topic subscription still pending for (#{sub.protocol}: #{sub.endpoint})"
             end
           end
         end
```

Se obtivermos um ARN para a assinatura, vamos salvá-lo nos dados do resultado da atividade, convertê-lo em YAML e retornar "true" de `do_activity`, que indica que a atividade foi concluída com êxito.

Como esperar pela confirmação de uma assinatura pode demorar um pouco, ocasionalmente solicitamos `record_heartbeat` a tarefa da atividade. Isso sinaliza para o Amazon SWF que a atividade ainda está sendo processada e também pode ser usado para fornecer atualizações sobre o progresso da atividade (se você estiver fazendo algo, como processar arquivos, para o qual possa relatar o progresso).

```
         task.record_heartbeat!(
           { :details => "#{topic.num_subscriptions_confirmed} confirmed, #{topic.num_subscriptions_pending} pending" })
         # sleep a bit.
         sleep(4.0)
       end
```

Isso finaliza nosso loop `while`. Se, de alguma forma, sairmos do loop sem sucesso, informaremos a falha e terminamos o método `do_activity`.

```
       if (subscription_confirmed == false)
         @results = {
           :reason => "No subscriptions could be confirmed",
           :detail => "#{topic.num_subscriptions_confirmed} confirmed, #{topic.num_subscriptions_pending} pending" }.to_yaml
         return false
       end
     end
   end
```

Isso acaba com a implementação de `wait_for_confirmation_activity`. Temos apenas mais uma atividade para definir: `send_result_activity`.

## Definindo SendResultActivity
<a name="defining-sendresultactivity"></a>

Se o fluxo de trabalho tiver progredido até aqui, teremos inscrito com êxito o usuário em um tópico do Amazon SNS e o usuário terá confirmado a inscrição.

Nossa última atividade, `send_result_activity`, envia ao usuário uma confirmação da inscrição bem-sucedida no tópico, usando o tópico no qual o usuário se inscreveu e o endpoint com o qual o usuário confirmou a assinatura.

Crie um novo arquivo chamado `send_result_activity.rb` e configure-o como configuramos todas as atividades até agora.

```
   require 'yaml'
   require_relative 'basic_activity.rb'

   # **SendResultActivity** sends the result of the activity to the screen, and, if
   # the user successfully registered using SNS, to the user using the SNS contact
   # information collected.
   class SendResultActivity < BasicActivity

     def initialize
       super('send_result_activity')
     end
```

Nosso `do_activity` método também começa da mesma forma, obtendo os dados de entrada do fluxo de trabalho, convertendo-os do YAML e, em seguida, usando o tópico ARN para criar uma instância. [AWS::SNS::Topic](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Topic.html)

```
     def do_activity(task)
       if task.input.nil?
         @results = { :reason => "Didn't receive any input!", :detail => "" }
         return false
       end

       input = YAML.load(task.input)

       # get the topic, so we publish a message to it.
       topic = AWS::SNS::Topic.new(input[:topic_arn])

       if topic.nil?
         @results = {
           :reason => "Couldn't get SWF topic",
           :detail => "Topic ARN: #{topic.arn}" }
         return false
       end
```

Quando tivermos o tópico, [publicaremos](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Topic.html#publish-instance_method) uma mensagem nele (e também o ecoaremos na tela).

```
       @results = "Thanks, you've successfully confirmed registration, and your workflow is complete!"

       # send the message via SNS, and also print it on the screen.
       topic.publish(@results)
       puts(@results)

       return true
     end
   end
```

A publicação em um tópico do Amazon SNS envia a mensagem que você forneceu a *todos* os endpoints inscritos e confirmados que existem para esse tópico. Portanto, se o usuário confirmar com um e-mail e *também* com um número de SMS, ele receberá duas mensagens de confirmação, uma em cada endpoint.

## Próximas etapas
<a name="implementing-activities-next-steps"></a>

Isso completa a implementação de `send_result_activity`. Agora, você combinará todas essas atividades em um aplicativo de atividade que lida com as tarefas de atividades e que pode iniciar atividades em resposta, em [Tutorial de fluxo de trabalho de inscrição - Parte 4: Implementar o agente de sondagem de tarefas de atividades](swf-sns-tutorial-implementing-activities-poller.md).