

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

# Terza parte del tutorial sul flusso di lavoro di sottoscrizione: implementazione delle attività
<a name="swf-sns-tutorial-implementing-activities"></a>

In questa parte del tutorial, implementeremo ogni attività nel nostro flusso di lavoro, cominciando con una classe di base che fornisce alcune funzionalità comuni per il codice delle attività.

**Topics**
+ [Definizione di un tipo di attività di base](#defining-a-basic-activity-type)
+ [Definendo GetContactActivity](#defining-getcontactactivity)
+ [Definizione SubscribeTopicActivity](#defining-subscribetopicactivity)
+ [Definizione WaitForConfirmationActivity](#defining-waitforconfirmationactivity)
+ [Definizione SendResultActivity](#defining-sendresultactivity)
+ [Fasi successive](#implementing-activities-next-steps)

## Definizione di un tipo di attività di base
<a name="defining-a-basic-activity-type"></a>

Quando abbiamo progettato il flusso di lavoro, abbiamo identificato le seguenti attività:
+ `get_contact_activity`
+ `subscribe_topic_activity`
+ `wait_for_confirmation_activity`
+ `send_result_activity`

Ora implementeremo ognuna di queste attività. Poiché le nostre attività condivideranno alcune funzionalità, facciamo un po' di lavoro di base e creiamo del codice comune che possano condividere. Lo chiameremo e **BasicActivity**lo definiremo in un nuovo file chiamato`basic_activity.rb`.

Come con gli altri file di origine, includeremo `utils.rb` per accedere alla funzione `init_domain` con cui configureremo l'esempio di dominio.

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

Successivamente, dichiareremo la classe di attività di base e alcuni dati comuni che ci interessano per ogni attività. Salveremo l'[AWS::SimpleWorkflow::ActivityType](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SimpleWorkflow/ActivityType.html)istanza, *il nome* e *i risultati* dell'attività negli attributi della classe.

```
   class BasicActivity

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

Questi attributi accedono ai dati dell'istanza definiti nel `initialize` metodo della classe, che richiede un *nome* di attività e una *versione* e una mappa opzionali di *opzioni* da utilizzare per la registrazione dell'attività con 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
```

Come con la registrazione del tipo di flusso di lavoro, se un tipo di attività è già registrato, possiamo recuperarlo esaminando la raccolta [workflow\_types](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SimpleWorkflow/Domain.html#activity_types-instance_method) del dominio. Se il tipo di attività è introvabile, verrà registrato.

Inoltre, come con i tipi di flusso di lavoro, puoi impostare delle *opzioni di default* che sono archiviate insieme al tipo di attività alla registrazione dello stesso.

L'ultima cosa di cui necessita la nostra attività di base è un modo di esecuzione coerente. A questo proposito, definiremo un metodo `do_activity` che accetta un task di attività Come mostrato di seguito, possiamo utilizzare il task di attività passato per ricevere dati via il relativo attributo di istanza `input`.

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

Questo conclude la lezione. **BasicActivity** Ora la utilizzeremo per definire le attività in modo semplice e coerente.

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

La prima attività che viene eseguita durante l'esecuzione di un flusso di lavoro è `get_contact_activity` quella di recuperare le informazioni sull'abbonamento all'argomento Amazon SNS dell'utente.

Crea un nuovo file chiamato `get_contact_activity.rb` e richiedi entrambi`yaml`, che useremo per preparare una stringa da passare ad Amazon SWF e `basic_activity.rb` che useremo come base per questa **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
```

Poiché abbiamo inserito il codice di registrazione dell'attività **BasicActivity**, il `initialize` metodo **GetContactActivity**è piuttosto semplice. È sufficiente chiamare il costruttore della classe di base con il nome dell'attività, ovvero `get_contact_activity`. Questo è tutto quello che dobbiamo fare per registrare la nostra attività.

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

Definiremo ora il `do_activity` metodo, che richiede il numero di and/or telefono e-mail dell'utente. 

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

Alla fine di `do_activity`, inseriamo l'indirizzo e-mail e il numero di telefono dell'utente in una mappa e utilizziamo `to_yaml` per convertire l'intera mappa in una stringa YAML. C'è una ragione importante per questo: tutti i risultati che passi ad Amazon SWF quando completi un'attività devono essere solo *dati di stringa*. La capacità di Ruby di convertire facilmente oggetti in stringhe YAML e quindi di riconvertirle in oggetti è particolarmente utile in questo caso.

L'implementazione di `get_contact_activity` è terminata. Utilizzeremo questi dati in seguito nell'implementazione di `subscribe_topic_activity`.

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

Ora approfondiremo Amazon SNS e creeremo un'attività che utilizza le informazioni generate `get_contact_activity` da per iscrivere l'utente a un argomento di Amazon SNS.

Crea un nuovo file denominato `subscribe_topic_activity.rb`, aggiungi gli stessi requisiti utilizzati per `get_contact_activity`, dichiara la classe e fornisci il relativo metodo `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
```

Ora che abbiamo il codice per configurare e registrare l'attività, aggiungeremo del codice per creare un argomento Amazon SNS. Per farlo, useremo il metodo [AWS::SNS::Client](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Client.html)[create\_topic](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Client.html#create_topic-instance_method) dell'oggetto.

Aggiungi il `create_topic` metodo alla tua classe, che accetta un oggetto client Amazon SNS passato.

```
     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 volta ottenuto l'Amazon Resource Name (ARN) dell'argomento, possiamo utilizzarlo con il metodo set\_topic\_attributes [del](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Client.html#set_topic_attributes-instance_method) client Amazon SNS per impostare l'argomento, necessario per l'invio *DisplayName*di messaggi SMS con Amazon SNS.

Infine, definiremo il metodo `do_activity`. Inizieremo con il raccogliere tutti i dati passati via l'opzione `input` durante la pianificazione dell'attività. Come menzionato in precedenza, questi dati devono essere passati come stringa creata utilizzando `to_yaml`. Dopo il recupero dei dati, utilizzeremo `YAML.load` per convertirli in oggetti Ruby.

Di seguito è illustrato l'inizio di `do_activity`, che ci consente di recuperare i dati di input.

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

Nel caso non ricevessimo alcun input, non ci sarebbe molto da fare. Dovremmo semplicemente generare un errore nell'attività.

Supponendo che tutto vada bene, tuttavia, continueremo a compilare il nostro `do_activity` metodo, procureremo un client Amazon SNS con e lo passeremo al `create_topic` nostro metodo per creare AWS SDK per Ruby l'argomento 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
```

A questo punto, è importante considerare quanto segue:
+ Utilizziamo [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)per impostare la regione per il nostro client Amazon SNS. Poiché intendiamo inviare messaggi SMS, utilizziamo la regione che supporta il servizio SMS dichiarata in `utils.rb`.
+ Salviamo l'ARN dell'argomento nella mappa `activity_data`. Questi sono alcuni dei dati che saranno passati all'attività *successiva* nel flusso di lavoro.

Infine, questa attività iscrive l'utente all'argomento Amazon SNS, utilizzando gli endpoint trasmessi (e-mail e SMS). L'utente non deve immettere *entrambi* gli endpoint, ma almeno 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) tratta l'argomento ARN, *il* protocollo (che, abilmente, abbiamo mascherato da chiave `activity_data` della mappa per l'endpoint corrispondente).

Infine, reimpacchettiamo le informazioni per l'attività successiva in formato YAML, in modo da poterle rispedire ad 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
```

L'implementazione di `subscribe_topic_activity` è terminata. Nella sezione successiva, definiremo `wait_for_confirmation_activity`.

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

Una volta che un utente si è iscritto a un argomento di Amazon SNS, dovrà comunque confermare la richiesta di abbonamento. In questo caso, attendiamo la conferma dell'utente via e-mail o SMS.

L'attività che attende la conferma della sottoscrizione da parte dell'utente è denominata `wait_for_confirmation_activity` e la definiremo in questa sezione. Per prima cosa, crea un nuovo file denominato `wait_for_confirmation_activity.rb` e configuralo come nelle attività precedenti.

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

Successivamente, inizieremo a definire il metodo `do_activity` e a recuperare i dati di input in una variabile locale denominata `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)
```

Ora che abbiamo l'argomento ARN, possiamo recuperare l'argomento creando una nuova istanza di [AWS::SNS::Topic](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Topic.html)e passandole l'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
```

A questo punto, verificheremo l'argomento per determinare se l'utente ha confermato la sottoscrizione utilizzando uno degli endpoint. Per considerare l'attività come riuscita, è sufficiente che venga confermato un solo endpoint.

Un argomento di Amazon SNS contiene un elenco degli [abbonamenti](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Topic.html#subscriptions-instance_method) per quell'argomento e possiamo verificare se l'utente ha confermato o meno un determinato abbonamento controllando se l'ARN dell'abbonamento è impostato su qualcosa di diverso. `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 otteniamo un ARN per la sottoscrizione, lo registreremo nei dati di risultato dell'attività, lo convertiremo in YAML e restituiremo true da `do_activity`, a indicare il completamento senza errori dell'attività.

Poiché attendere la conferma di un abbonamento potrebbe richiedere del tempo, occasionalmente ci occuperemo dell'attività da `record_heartbeat` svolgere. Questo segnala ad Amazon SWF che l'attività è ancora in fase di elaborazione e può essere utilizzato anche per fornire aggiornamenti sullo stato di avanzamento dell'attività (se stai facendo qualcosa, come l'elaborazione di file, per cui puoi segnalare lo stato di avanzamento).

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

Qui si conclude il ciclo `while`. Se per un qualsiasi motivo, il ciclo while non riesce, segnaleremo un errore e termineremo il metodo `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
```

L'implementazione di `wait_for_confirmation_activity` risulta completata. Abbiamo quindi una sola attività da definire, ovvero `send_result_activity`.

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

Se il flusso di lavoro è progredito fino a questo punto, abbiamo correttamente iscritto l'utente a un argomento di Amazon SNS e l'utente ha confermato l'abbonamento.

La nostra ultima attività, `send_result_activity`, invia all'utente la conferma dell'avvenuta sottoscrizione all'argomento, utilizzando l'argomento in questione e l'endpoint con il quale l'utente ha confermato la sottoscrizione.

Crea un nuovo file denominato `send_result_activity.rb` e configuralo come per le altre attività.

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

Anche il nostro `do_activity` metodo inizia in modo simile, ottenendo i dati di input dal flusso di lavoro, convertendoli da YAML e quindi utilizzando l'argomento ARN per creare un'istanza. [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
```

Una volta che abbiamo l'argomento, vi [pubblicheremo](https://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SNS/Topic.html#publish-instance_method) un messaggio (che riprodurremo anche sullo schermo).

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

La pubblicazione su un argomento Amazon SNS invia il messaggio fornito a *tutti gli* endpoint sottoscritti e confermati esistenti per quell'argomento. Di conseguenza, se l'utente ha confermato la sottoscrizione con un'e-mail *e* un numero SMS, riceverà due messaggi di conferma, uno a ogni endpoint.

## Fasi successive
<a name="implementing-activities-next-steps"></a>

L'implementazione di `send_result_activity` risulta completata. Nella fase successiva, combinerai tutte queste attività in un'applicazione di attività che gestisce i task di attività e che può avviare attività in risposta, come descritto nella sezione [Quarta parte del tutorial sul flusso di lavoro di sottoscrizione: implementazione del poller dei task di attività](swf-sns-tutorial-implementing-activities-poller.md).