

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

# Parte 3 del tutorial acerca del flujo de trabajo de suscripción: implementación de las actividades
<a name="swf-sns-tutorial-implementing-activities"></a>

Ahora implementaremos cada una de las actividades en nuestro flujo de trabajo, empezando con una clase base que proporciona algunas características comunes para el código de actividad.

**Topics**
+ [Definición de un tipo de actividad básica](#defining-a-basic-activity-type)
+ [Definiendo GetContactActivity](#defining-getcontactactivity)
+ [Definir SubscribeTopicActivity](#defining-subscribetopicactivity)
+ [¿Definiendo WaitForConfirmationActivity](#defining-waitforconfirmationactivity)
+ [¿Definiendo SendResultActivity](#defining-sendresultactivity)
+ [Siguientes pasos](#implementing-activities-next-steps)

## Definición de un tipo de actividad básica
<a name="defining-a-basic-activity-type"></a>

Al diseñar el flujo de trabajo, identificamos las siguientes actividades:
+ `get_contact_activity`
+ `subscribe_topic_activity`
+ `wait_for_confirmation_activity`
+ `send_result_activity`

Implementaremos cada una de estas actividades ahora. Como nuestras actividades compartirán algunas características, hagamos un poco de trabajo preliminar y creemos un código común que puedan compartir. Lo llamaremos y **BasicActivity**lo definiremos en un nuevo archivo llamado`basic_activity.rb`.

Al igual que con los demás archivos de código fuente, incluiremos `utils.rb` para obtener acceso a la función `init_domain` para configurar el dominio de ejemplo.

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

A continuación, declararemos la clase de actividad básica y algunos datos comunes que serán de nuestro interés para cada actividad. Guardaremos la [AWS::SimpleWorkflow::ActivityType](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SimpleWorkflow/ActivityType.html)instancia, el *nombre* y *los resultados de la actividad en los* atributos de la clase.

```
   class BasicActivity

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

Estos atributos obtienen acceso a los datos de la instancia que se definen en el método `initialize` de la clase, que toma un *nombre* de actividad, una *versión* opcional y un mapa de *opciones* que se van a utilizar al registrar la actividad en 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
```

Al igual que ocurre con el registro del tipo de flujo de trabajo, si ya se ha registrado un tipo de actividad, podemos recuperarla consultando la colección de [activity\_types](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SimpleWorkflow/Domain.html#activity_types-instance_method) del dominio. Si no se encuentra la actividad, se registrará.

También, al igual que ocurre con los tipos de flujo de trabajo, puede establecer *opciones predeterminadas* que se almacenen con su tipo de actividad al registrarla.

Lo último que obtiene nuestra actividad básica es una forma coherente de ejecutarla. Definiremos un método `do_activity` que tome una tarea de actividad. Tal como se indica, podemos usar la tarea de actividad transmitida para recibir datos a través de su atributo de instancia de `input`.

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

Con esto termina la **BasicActivity**clase. Ahora la usaremos para hacer que la definición de nuestras actividades sea sencilla y coherente.

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

La primera actividad que se ejecuta durante una ejecución de flujo de trabajo es `get_contact_activity`, que recupera la información de suscripción a un tema de Amazon SNS del usuario.

Crea un nuevo archivo llamado `get_contact_activity.rb` y requiere ambos`yaml`, que usaremos para preparar una cadena para pasarla a Amazon SWF y `basic_activity.rb` que usaremos como base para esta **GetContactActivity**clase.

```
   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 hemos introducido el código de registro de la actividad **BasicActivity**, el `initialize` método **GetContactActivity**es bastante sencillo. Nos limitamos a llamar al constructor de clases base con el nombre de actividad, `get_contact_activity`. Esto es todo lo que hace falta para registrar nuestra actividad.

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

Ahora definiremos el `do_activity` método, que solicita el número de and/or teléfono de correo electrónico del usuario. 

```
     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
```

Al final de `do_activity`, tomamos el correo electrónico y el número de teléfono recuperados del usuario, los colocamos en un mapa y, a continuación, usamos `to_yaml` para convertir todo el mapa a una cadena YAML. Existe un motivo importante para esto: los resultados que pase a Amazon SWF al completar una actividad deben ser *datos de cadena únicamente*. La capacidad de Ruby de convertir fácilmente objetos a cadenas YAML y, posteriormente, a objetos una vez más es, por suerte, adecuada para este fin.

Ese es el final de la implementación `get_contact_activity`. Estos datos se usarán a continuación en la implementación de `subscribe_topic_activity`.

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

Ahora profundizaremos en Amazon SNS y crearemos una actividad que utilice la información que genera `get_contact_activity` para suscribir al usuario a un tema de Amazon SNS.

Cree un nuevo archivo llamado `subscribe_topic_activity.rb`, añada los mismos requisitos que usamos para `get_contact_activity`, declare su clase y proporcione su 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
```

Ahora que tenemos el código para que la actividad se configure y registre, añadiremos código para crear un tema de Amazon SNS. Para ello, utilizaremos el método [create\_topic](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Client.html#create_topic-instance_method) del [AWS::SNS::Client](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Client.html)objeto.

Añada el método `create_topic` a la clase, que toma un objeto de cliente de Amazon SNS transmitido.

```
     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
```

Una vez que tengamos el nombre de recurso de Amazon (ARN) del tema, podemos usarlo con el método set\_topic\_attributes [del cliente de](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Client.html#set_topic_attributes-instance_method) Amazon SNS para establecer el tema, que *DisplayName*es necesario para enviar mensajes SMS con Amazon SNS.

Por último, definiremos el método `do_activity`. Comenzaremos recopilando los datos que se han pasado mediante la opción `input` al programarse la actividad. Tal como se ha mencionado anteriormente, estos deben transferirse como cadena, que creamos mediante `to_yaml`. Una vez recuperados, usaremos `YAML.load` para convertir los datos en objetos Ruby.

Este es el principio de `do_activity`, donde recuperamos los datos 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))
```

Si no hemos recibido ninguna entrada, no hay mucho que se pueda hacer, de modo que produciremos un error en la actividad.

Sin embargo, suponiendo que todo vaya bien, seguiremos rellenando nuestro `do_activity` método, buscaremos un cliente de Amazon SNS con el AWS SDK para Ruby y lo pasaremos a nuestro `create_topic` método para crear el tema 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
```

Hay un par de aspectos que merece la pena señalar aquí:
+ Utilizamos [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 establecer la región de nuestro cliente de Amazon SNS. Dado que deseamos enviar mensajes SMS, usaremos la región habilitada para SMS que declaramos en `utils.rb`.
+ Guardamos el ARN del tema en nuestro mapa `activity_data`. Este forma parte de los datos que se pasarán a la *siguiente* actividad de nuestro flujo de trabajo.

Por último, esta actividad suscribe al usuario al tema de Amazon SNS mediante los puntos de conexión transmitidos (correo electrónico y SMS). No exigimos que el usuario escriba *ambos* puntos de conexión, pero necesitamos al menos uno.

```
       # 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) toma el tema ARN, *el* protocolo (que, hábilmente, disfrazamos de clave de mapa para `activity_data` el punto final correspondiente).

Por último, podemos volver a empaquetar la información para la siguiente actividad en formato YAML, de modo que podamos devolverla a 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
```

De ese modo se completa la implementación de `subscribe_topic_activity`. A continuación, definiremos `wait_for_confirmation_activity`.

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

Una vez que un usuario se suscriba a un tema de Amazon SNS, este deberá confirmar la solicitud de suscripción. En este caso, esperaremos la confirmación del usuario por correo electrónico o mediante un mensaje SMS.

La actividad que espera a que el usuario confirme la suscripción se llama `wait_for_confirmation_activity` y la definiremos aquí. Para empezar, cree un nuevo archivo llamado `wait_for_confirmation_activity.rb` y configúrelo cuando hayamos configurado las actividades 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
```

A continuación, empezaremos a definir el método `do_activity` y recuperaremos los datos de entrada de una variable local llamada `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)
```

Ahora que tenemos el tema ARN, podemos recuperarlo creando una nueva instancia de [AWS::SNS::Topic](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Topic.html)y pasándole el ARN.

```
       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
```

Ahora, comprobaremos el tema para ver si el usuario ha confirmado la suscripción mediante uno de los puntos de conexión. Solo exigiremos que se haya confirmado un punto de conexión para considerar la actividad un éxito.

Cualquier tema de Amazon SNS mantiene una lista de las [suscripciones](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Topic.html#subscriptions-instance_method) a dicho tema, y podemos verificar si el usuario ha confirmado o no una suscripción determinada al comprobar si el ARN de la suscripción está establecido en otra opción distinta a `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
```

Si obtenemos un ARN para la suscripción, lo guardaremos en los datos de resultados de la actividad, lo convertiremos a YAML y devolveremos true de `do_activity`, que indica que la actividad se ha completado correctamente.

Como esperar a que se confirme una suscripción puede llevar un tiempo, de vez en cuando suspendemos la tarea de `record_heartbeat` la actividad. Esto indica a Amazon SWF que la actividad se sigue procesando y que puede utilizarse para proporcionar actualizaciones acerca del progreso de la actividad (si realiza alguna tarea, como el procesamiento de archivos, de cuyo progreso pueda informar).

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

Esto pone fin a nuestro bucle `while`. Si de algún modo salimos del bucle while sin éxito, notificaremos el error y pondremos fin al 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
```

Eso pone fin a la implementación de `wait_for_confirmation_activity`. Solo tenemos una actividad más que definir: `send_result_activity`.

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

Si el flujo de trabajo ha progresado hasta este punto, quiere decir que hemos suscrito correctamente al usuario a un tema de Amazon SNS y que el usuario ha confirmado la suscripción.

Nuestra última actividad, `send_result_activity`, envía al usuario una confirmación de la suscripción correcta al tema, mediante el tema al que se ha suscrito al usuario y el punto de conexión con el que el usuario ha confirmado la suscripción.

Cree un nuevo archivo llamado `send_result_activity.rb` y configúrelo cuando hayamos configurado todas las actividades hasta el momento.

```
   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
```

Nuestro `do_activity` método también comienza de manera similar: obtiene los datos de entrada del flujo de trabajo, los convierte de YAML y, a continuación, usa el tema ARN para crear una [AWS::SNS::Topic](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Topic.html)instancia.

```
     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
```

Una vez que tengamos el tema, [publicaremos](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Topic.html#publish-instance_method) un mensaje en él (y también lo repetiremos en la pantalla).

```
       @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
```

Al publicar en un tema de Amazon SNS, se envía el mensaje que el usuario haya proporcionado a *todos* los puntos de conexión suscritos y confirmados que existan para ese tema. Así pues, si el usuario ha confirmado con *ambos* un correo electrónico y un número de SMS, él o ella recibirá dos mensajes de confirmación, uno en cada punto de conexión.

## Siguientes pasos
<a name="implementing-activities-next-steps"></a>

De este modo se completa la implementación de `send_result_activity`. Ahora vinculará todas estas actividades en una aplicación de actividad que controla las tareas de actividad y puede lanzar actividades como respuesta, en [Parte 4 del tutorial acerca del flujo de trabajo de suscripción: implementación del sondeador de tareas de actividades](swf-sns-tutorial-implementing-activities-poller.md).