Troisième partie du didacticiel sur le flux de travail Implémentation des activités - Amazon Simple Workflow Service

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Troisième partie du didacticiel sur le flux de travail Implémentation des activités

Nous allons maintenant mettre en œuvre chacune des activités de notre flux de travail, en commençant par une classe de base qui fournit des fonctions courantes pour le code d'activité.

Définition d'un type d'activité de base

Lorsque nous avons conçu le flux de travail, nous avons identifié les activités suivantes :

  • get_contact_activity

  • subscribe_topic_activity

  • wait_for_confirmation_activity

  • send_result_activity

Nous allons à présent mettre en œuvre chacune de ces activités. Comme nos activités partagent certaines fonctions, nous allons débrouiller le chemin et créer un code commun qu'elles pourront toutes utiliser. Nous l'appellerons BasicActivity et le définirons dans un nouveau fichier appelé basic_activity.rb.

Comme avec les autres fichiers sources, nous allons inclure utils.rb pour accéder à la fonction init_domain permettant de configurer l'exemple de domaine.

require_relative 'utils.rb'

Ensuite, nous allons déclarer la classe de l'activité de base et certaines données courantes qui nous intéressent pour chaque activité. Nous enregistrons l'instance AWS::SimpleWorkflow::ActivityType, le nom et les résultats de l'activité dans les attributs de la classe.

class BasicActivity attr_accessor :activity_type attr_accessor :name attr_accessor :results

Ces attributs accèdent aux données d'instance définies dans la classe »initializeméthode, qui prend une activiténom, et une optionversionet carte deoptionsà utiliser lors de l'enregistrement de l'activité auprès d'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

Comme avec l'enregistrement du type de flux de travail, si un type d'activité est déjà enregistré, nous pouvons le récupérer en examinant la collection activity_types du domaine. Si le type d'activité est introuvable, il sera enregistré.

En outre, comme avec les types de flux de travail, vous pouvez définir des options par défaut qui seront stockées avec votre type d'activité lorsque vous l'enregistrerez.

La dernière chose que nécessite notre activité de base est une façon uniforme de l'exécuter. Nous allons définir une méthode do_activity qui utilise une tâche d'activité. Comme illustré ici, nous pouvons utiliser la tâche d'activité transmise pour recevoir les données via l'attribut d'instance input.

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

Voilà ce qui marque la fin de la classe BasicActivity. Maintenant, nous allons nous en server pour définir nos activités de manière simple et uniforme.

Définition de GetContactActivity

La première activité réalisée pendant une exécution de flux de travail est l'activité :get_contact_activity, qui récupère les informations d'abonnement de l'utilisateur à la rubrique Amazon SNS.

Créer un nouveau fichier appeléget_contact_activity.rb, et exigent les deuxyaml, que nous utiliserons pour préparer une chaîne à transmettre à Amazon SWF, etbasic_activity.rb, que nous utiliserons comme base pour celaObtenir l'activité de contactclasse.

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

Dans la mesure où nous avons inclus le code d'enregistrement de l'activité dans BasicActivity, la méthode initialize pour GetContactActivity est assez simple. Il suffit d'appeler le constructeur de la classe de base avec le nom de l'activité, get_contact_activity. C'est tout ce qui est nécessaire pour enregistrer notre activité.

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

Nous allons maintenant définir la méthode do_activity, qui demande l'adresse e-mail et/ou le numéro de téléphone de l'utilisateur.

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

A la fin de do_activity, nous prenons le numéro de téléphone et l'adresse e-mail de l'utilisateur, plaçons ces informations dans une carte, puis utilisons to_yaml pour convertir cette carte entière en chaîne YAML. Il y a une raison importante à cela : tous les résultats que vous transmettez à Amazon SWF lorsque vous terminez une activité doivent êtredonnées chaîne uniquement. La capacité de Ruby à convertir facilement des objets en chaînes YAML et à les reconvertir en objets est particulièrement utile dans ce cas.

Voilà ce qui marque la fin de la mise en œuvre de get_contact_activity. Nous utiliserons ensuite ces données dans la mise en œuvre de subscribe_topic_activity.

Définition de SubscribeTopicActivity

Nous allons maintenant nous plonger dans Amazon SNS et créer une activité qui utilise les informations générées parget_contact_activitypour abonner l'utilisateur à une rubrique Amazon SNS.

Créez un fichier appelé subscribe_topic_activity.rb, ajoutez les mêmes exigences que celles que nous avons utilisées pour get_contact_activity, déclarez votre classe et fournissez sa méthode 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

Maintenant que nous avons préparé le code requis pour configurer et enregistrer l'activité, nous allons ajouter du code pour créer une rubrique Amazon SNS. Pour cela, nous allons utiliser laAWS:: SNS:: Clientl'objetcreate_topicméthode.

Ajout du kitcreate_topicdans votre classe, qui utilise un objet client Amazon SNS transmis.

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

Une fois que nous avons obtenu l'Amazon Resource Name (ARN) de la rubrique, nous pouvons l'utiliser avec l'Amazon SNSattributs set_topic_pour définir le sujetDisplayName, requis pour envoyer des SMS avec Amazon SNS.

Enfin, nous allons définir la méthode do_activity. Nous allons commencer par recueillir les données qui ont été transmises via l'option input lorsque l'activité a été planifiée. Comme nous l'avons mentionné précédemment, elles doivent être transmises sous la forme d'une chaîne que nous avons créée avec to_yaml. Lors de son extraction, nous utiliserons YAML.load pour convertir les données en objets Ruby.

Voici le début de do_activity, qui nous permet de récupérer les données d'entrée.

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 nous n'avions reçu aucune entrée, il n'y aurait pas eu beaucoup à faire. Nous aurions tout simplement arrêter l'activité.

Toutefois, en supposant que tout va bien, nous allons continuer à renseigner notredo_activity, obtenez un client Amazon SNS avec l'optionAWS SDK for Ruby, et transmettez-le à notrecreate_topicpour créer la rubrique 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

Deux éléments sont à ici à prendre en compte :

  • Nous utilisonsAWS.config.withpour définir la région pour notre client Amazon SNS. Comme nous voulons envoyer des messages SMS, nous utilisons la région SMS que nous avons déclarée dans utils.rb.

  • Nous enregistrons l'ARN de la rubrique dans notre carte activity_data. Ces données seront transmises à l'activité suivante dans notre flux de travail.

Enfin, cette activité abonne l'utilisateur à la rubrique Amazon SNS, à l'aide des points de terminaison transmis (e-mail et SMS). L'utilisateur n'est pas obligé de renseigner les deux points de terminaison, mais au moins un.

# 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 utilise la rubrique ARN et le protocole (que nous avons intelligemment déguisé sous la forme de clé activity_data pour le point de terminaison correspondant).

Enfin, nous re-regroupons les informations nécessaires à la prochaine activité au format YAML, afin de les renvoyer à 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

Nous avons terminé la mise en œuvre de l'activité subscribe_topic_activity. Ensuite, nous allons définir wait_for_confirmation_activity.

Définition de WaitForConfirmationActivity

Une fois qu'un utilisateur est abonné à une rubrique Amazon SNS, il lui reste à confirmer la demande d'abonnement. Dans ce cas, nous allons attendre que l'utilisateur confirme l'abonnement par e-mail ou par SMS.

L'activité qui attend que l'utilisateur confirme l'abonnement s'appelle wait_for_confirmation_activity, et nous allons la définir ici. Pour commencer, créez un fichier appelé wait_for_confirmation_activity.rb et configurez-le comme nous l'avons fait dans les activités précédentes.

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

Ensuite, nous allons commencer par définir la méthode do_activity et nous allons extraire les données d'entrée dans une variable locale appelée 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)

Maintenant que nous avons l'ARN de la rubrique, nous pouvons créer une instance AWS::SNS::Topic et lui transmettre l'ARN pour récupérer cette rubrique.

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

Nous allons maintenant vérifier la rubrique afin de déterminer si l'utilisateur a confirmé l'abonnement à l'aide de l'un points de terminaison. Pour que l'activité soit considérée comme ayant réussi, seul un point de terminaison doit être confirmé.

Une rubrique Amazon SNS gère une liste desabonnementspour cette rubrique. Nous pouvons donc déterminer si l'utilisateur a confirmé un abonnement particulier ou non en vérifiant si l'ARN de l'abonnement ne correspond pas à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 nous obtenons un ARN pour l'abonnement, nous l'enregistrerons dans les données de résultat de l'activité, les convertirons au format YAML et renverrons la valeur true à partir de do_activity, qui indique que l'activité s'est correctement déroulée.

Dans la mesure où il faut parfois patienter longtemps pour savoir si un abonnement a été confirmé, nous appellerons parfois record_heartbeat au niveau de la tâche d'activité. Ce paramètre indique à Amazon SWF que l'activité est toujours en cours de traitement. Il permet également de savoir où en est l'activité (si vous faites quelque chose, comme le traitement de fichiers, dont vous pouvez déterminer la progression).

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

Cela termine la boucle while. Si, pour une raison ou une autre, nous sortons de la boucle while sans succès, nous indiquerons un échec et mettrons fin à la méthode 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

Nous arrivons maintenant au terme de la mise en œuvre de wait_for_confirmation_activity. Il ne reste plus qu'une seule activité à définir : send_result_activity.

Définition de SendResultActivity

Si le flux de travail a progressé jusque-là, cela veut dire que nous avons abonné l'utilisateur à une rubrique Amazon SNS et qu'il a confirmé l'abonnement.

Notre dernière activité, send_result_activity, envoie à l'utilisateur une confirmation de l'abonnement à la rubrique, à l'aide de la rubrique à laquelle il s'est abonné et du point de terminaison avec lequel il a confirmé l'abonnement.

Créez un fichier appelé send_result_activity.rb et configurez-le comme nous l'avons fait pour toutes les autres activités.

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

La méthode do_activity commence également de la même manière : elle récupère les données d'entrée à partir du le flux de travail, les convertit au format YAM, puis utilise l'ARN de la rubrique pour créer une instance AWS::SNS::Topic.

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

Une fois que nous aurons la rubrique, nous allons y publier un message (et le reproduire à l'écran).

@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

Toute publication dans une rubrique Amazon SNS envoie le message que vous fournissez àtousdes points de terminaison souscrits et confirmés qui existent pour cette rubrique. Donc, si l'utilisateur a confirmé l'abonnement avec à la fois un e-mail et un numéro de SMS, il reçoit deux messages de confirmation, un à chaque point de terminaison.

Étapes suivantes

Nous avons terminé la mise en œuvre de l'activité send_result_activity. A présent, vous allez relier toutes ces activités dans une application d'activité qui gère les tâches d'activité et qui peut lancer les activités en réponse, comme décrit dans la section Quatrième partie du didacticiel sur le flux de Mise en œuvre de l'animateur de tâches d'activité.