responsys-api 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +1 -0
  3. data/Gemfile +1 -1
  4. data/README.md +82 -10
  5. data/lib/responsys/api/all.rb +17 -0
  6. data/lib/responsys/api/campaign.rb +26 -0
  7. data/lib/responsys/api/client.rb +70 -0
  8. data/lib/responsys/api/folder.rb +25 -0
  9. data/lib/responsys/api/list.rb +39 -0
  10. data/lib/responsys/api/object/all.rb +11 -0
  11. data/lib/responsys/api/object/email_format.rb +23 -0
  12. data/lib/responsys/api/object/field.rb +20 -0
  13. data/lib/responsys/api/object/field_type.rb +23 -0
  14. data/lib/responsys/api/object/interact_object.rb +18 -0
  15. data/lib/responsys/api/object/list_merge_rule.rb +41 -0
  16. data/lib/responsys/api/object/optional_data.rb +23 -0
  17. data/lib/responsys/api/object/query_column.rb +23 -0
  18. data/lib/responsys/api/object/recipient.rb +30 -0
  19. data/lib/responsys/api/object/recipient_data.rb +21 -0
  20. data/lib/responsys/api/object/record.rb +19 -0
  21. data/lib/responsys/api/object/record_data.rb +31 -0
  22. data/lib/responsys/api/session.rb +30 -0
  23. data/lib/responsys/api/table.rb +49 -0
  24. data/lib/responsys/configuration.rb +3 -3
  25. data/lib/responsys/exceptions/all.rb +1 -0
  26. data/lib/responsys/exceptions/parameter_exception.rb +7 -0
  27. data/lib/responsys/helper.rb +78 -2
  28. data/lib/responsys/i18n/en.yml +20 -0
  29. data/lib/responsys/member.rb +80 -0
  30. data/lib/responsys_api.rb +11 -4
  31. data/responsys-api.gemspec +12 -9
  32. data/spec/api/campaign_spec.rb +27 -0
  33. data/spec/api/client_spec.rb +107 -0
  34. data/spec/api/list_spec.rb +71 -0
  35. data/spec/api/object/recipient_data_spec.rb +43 -0
  36. data/spec/api/object/recipient_spec.rb +68 -0
  37. data/spec/api/object/record_data_spec.rb +28 -0
  38. data/spec/api/table_spec.rb +111 -0
  39. data/spec/fixtures/vcr_cassettes/api/campaign/login.yml +940 -0
  40. data/spec/fixtures/vcr_cassettes/api/client/expired_session.yml +1030 -0
  41. data/spec/fixtures/vcr_cassettes/api/list/login.yml +982 -0
  42. data/spec/fixtures/vcr_cassettes/api/list/merge.yml +134 -0
  43. data/spec/fixtures/vcr_cassettes/api/list/retrieve.yml +46 -0
  44. data/spec/fixtures/vcr_cassettes/api/list/retrieve_single.yml +133 -0
  45. data/spec/fixtures/vcr_cassettes/api/list/retrieve_single_single.yml +45 -0
  46. data/spec/fixtures/vcr_cassettes/api/profile_extension/login.yml +982 -0
  47. data/spec/fixtures/vcr_cassettes/api/profile_extension/merge_profile_extension_records.yml +46 -0
  48. data/spec/fixtures/vcr_cassettes/api/profile_extension/retrieve_profile_extension_records.yml +45 -0
  49. data/spec/fixtures/vcr_cassettes/api/table/create.yml +45 -0
  50. data/spec/fixtures/vcr_cassettes/api/table/create_with_pk.yml +45 -0
  51. data/spec/fixtures/vcr_cassettes/api/table/delete.yml +45 -0
  52. data/spec/fixtures/vcr_cassettes/api/table/delete_with_pk.yml +45 -0
  53. data/spec/fixtures/vcr_cassettes/member/present1.yml +982 -0
  54. data/spec/fixtures/vcr_cassettes/member/present2.yml +45 -0
  55. data/spec/fixtures/vcr_cassettes/member/present3.yml +48 -0
  56. data/spec/fixtures/vcr_cassettes/member/present4.yml +48 -0
  57. data/spec/fixtures/vcr_cassettes/member/present5.yml +48 -0
  58. data/spec/fixtures/vcr_cassettes/member/retrieve_profile_extension.yml +87 -0
  59. data/spec/member_spec.rb +136 -0
  60. data/spec/spec_helper.rb +28 -2
  61. metadata +136 -19
  62. data/lib/responsys/responsys_api.rb +0 -41
  63. data/lib/savon/savon_api.rb +0 -22
  64. data/spec/responsys_api_spec.rb +0 -4
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NThmNzcwNWU5NTEwYWJiNjNkMzA4NWE5ZDQ2MjFmOWRiZTMwNjM1Ng==
4
+ YjJjY2JmYjg3NDBmZmQ4NDBhMGM0ZWNiNDIxYzBlZjdmODQzYmQ5YQ==
5
5
  data.tar.gz: !binary |-
6
- ZTdmYTg3YWUwYTExNTI0MGNjNGNhMTAxOTJkZTY0ODgyZjQyMzVhOQ==
6
+ OGIyMTU3ZmY5Mzg3NmIwYmJlMWFhZmViMGNhYWUxMWM5NGQ3MzMwMg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ZTE4MmE0NzZkMTRkY2E5MGE5NGMyZjJkY2FlOTE4ODNmY2UzYmY1YWQ4OWJj
10
- MzQxZTJhODI4YzA2ZDg2NDZhZTBiNmEwMzRhZDcyM2Q5YTIzNzYzNTNmMTU3
11
- YzlkZDRmMzhlOWE0ZWEwMDU1ZmVhMWM5ZWExMTVjMzEyZjJkOWE=
9
+ YjNiZjc5OTAwYjMwMWI2YzZjZjBkYTQyOGI0N2E4YTc5NjgzYjA4MDc5NGVj
10
+ YmFjMmIzYTQwYjQxMTIyNWQxY2NhZjNhZWJjMzY2NzcyZWE5MjcwMmM1ODll
11
+ ZmM1N2Q4MjJlZmZhMTdjMzAyMTA2Mjc0MjZhZWEzNTdiMmE0Nzk=
12
12
  data.tar.gz: !binary |-
13
- NGM4OGYxNzg2MmFkZWI3MWRmZTZiOTliNmFhN2IzZjFkZWEwZTgyZTYxNTYx
14
- ZWMyNjc3YWY0ZDdhNTVhNmRhZjJkNmVlYWZmMmIwNzM4NjM3ZTQ5YTM2M2M2
15
- N2VlMWQyMGFkNzM1NzdmZDgyMWIxMGIxNTVmYzllZWY5NTU3ZTc=
13
+ N2YwNmFhY2RkZTNkZDdmZjA1N2E4ZjJhMDc3NTcxODk3ZmEwNDlmYjU0MDQ2
14
+ NzU4OTdlMTgxODcwMzMyYjIzYzQ1OTc5NjRlOWIyM2E2MGRjMTIzYTM4MDgy
15
+ OGFjNzMwNmUwZDNhM2JiZTExMmQzZmZmMDg1ZTIzZDc1Mjc5MzM=
data/.gitignore CHANGED
@@ -19,3 +19,4 @@ tmp
19
19
  lib/.DS_Store
20
20
  lib/responsys/.DS_Store
21
21
  .rvmrc
22
+ .idea/
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in responsys-api.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -1,29 +1,101 @@
1
- # Responsys::Api
1
+ # ResponsysApi
2
2
 
3
- TODO: Write a gem description
3
+ A gem to help you communicate to the Responsys Interact SOAP API.
4
4
 
5
- ## Installation
5
+ ## Documentation
6
6
 
7
- Add this line to your application's Gemfile:
7
+ Have a look at the [wiki](https://github.com/dandemeyere/responsys-api/wiki) to understand how it works as well as special tips specially prepared for you ! If you have any questions or if you want to report a bug please create an [issue](https://github.com/dandemeyere/responsys-api/issues).
8
8
 
9
- gem 'responsys-api'
9
+ ## Installation
10
10
 
11
- And then execute:
11
+ Add this line to your application"s Gemfile:
12
12
 
13
- $ bundle
13
+ gem "responsys-api", "~> 0.0.4"
14
14
 
15
- Or install it yourself as:
15
+ Or install it locally with:
16
16
 
17
17
  $ gem install responsys-api
18
18
 
19
19
  ## Usage
20
+ ### Configuration
21
+
22
+ ```ruby
23
+ # Configure ResponsysApi in your initializers (config/initializers/responsys_api.rb):
24
+ Responsys.configure do |config|
25
+ config.settings = {
26
+ username: "your_responsys_username",
27
+ password: "your_responsys_password",
28
+ wsdl: "https://wsXXXX.responsys.net/webservices/wsdl/ResponsysWS_Level1.wsdl",
29
+ debug: false
30
+ }
31
+ end
32
+ ```
33
+
34
+ Note that the debug option is optional and is set to false by default.
35
+
36
+ ### Example
37
+ ```ruby
38
+ #!/usr/bin/env ruby
39
+
40
+ ## Scenario : subscribe a user to a newsletter
41
+ ## Details : the user exists in the list of your users in Responsys @ "the_folder_containing_the_list/my_customers_list". He just decided to subscribe so let's update his status !
42
+
43
+ # Require the gem
44
+ require 'responsys_api'
20
45
 
21
- TODO: Write usage instructions here
46
+ Responsys.configure do |config|
47
+ config.settings = {
48
+ username: "your_responsys_username",
49
+ password: "your_responsys_password",
50
+ wsdl: "https://wsXXXX.responsys.net/webservices/wsdl/ResponsysWS_Level1.wsdl"
51
+ }
52
+ end
53
+
54
+ # A list is an "InteractObject" according to the official API documentation
55
+ list = Responsys::Api::Object::InteractObject.new("the_folder_containing_the_list", "my_customers_list")
56
+
57
+ # The Member (or "user" for the example) record to update
58
+ member = Responsys::Member.new('user@email.com')
59
+
60
+ # Add the user to the list if he is not present.
61
+ unless member.present_in_list?(list)
62
+ puts "New user. Let's add it"
63
+ member.add_to_list(list)
64
+ else
65
+ puts "The user is in the list"
66
+ end
67
+
68
+ # Subscribe the user if he hasn't subscribed yet
69
+ unless member.subscribed?(list)
70
+ puts "Subscribing the user to the list"
71
+ member.subscribe(list)
72
+ else
73
+ puts "The user already subscribed"
74
+ end
75
+
76
+ # Check the member has a subscribed status
77
+ puts member.subscribed?(list) ? "#{member.email} has subscribed to #{list.object_name}" : "An error happened"
78
+ ```
79
+ ### Session
80
+ The API client used by the gem logs in as soon as a the first method is called. The same session is used as it is valid. If you want to close the API session you can manually call the log out action :
81
+
82
+ ```ruby
83
+ Responsys::Api::Client.instance.logout
84
+ ```
22
85
 
23
86
  ## Contributing
24
87
 
25
88
  1. Fork it
26
89
  2. Create your feature branch (`git checkout -b my-new-feature`)
27
- 3. Commit your changes (`git commit -am 'Add some feature'`)
90
+ 3. Commit your changes (`git commit -am "Add some feature"`)
28
91
  4. Push to the branch (`git push origin my-new-feature`)
29
92
  5. Create new Pull Request
93
+
94
+ ## To Do
95
+ * Add thorough tests with properly mocked out API responses
96
+ * Build out API functionality
97
+ * Member profile extension
98
+ * CRUD operations (create new key/value, update value by key, delete key/value by key)
99
+ * Tables
100
+ * CRUD operations (create new key/value, update value by key, delete key/value by key)
101
+ * Batch member profile updates
@@ -0,0 +1,17 @@
1
+ require "responsys/api/folder"
2
+ require "responsys/api/list"
3
+ require "responsys/api/session"
4
+ require "responsys/api/table"
5
+ require "responsys/api/campaign"
6
+
7
+ module Responsys
8
+ module Api
9
+ module All
10
+ include Responsys::Api::Folder
11
+ include Responsys::Api::List
12
+ include Responsys::Api::Session
13
+ include Responsys::Api::Table
14
+ include Responsys::Api::Campaign
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ require "responsys/api/object/all"
2
+
3
+ module Responsys
4
+ module Api
5
+ module Campaign
6
+ include Responsys::Exceptions
7
+
8
+ def trigger_message(campaign, recipients)
9
+ raise ParameterException, I18n.t("api.campaign.incorrect_recipients_type") unless recipients.is_a? Array
10
+ message = {
11
+ campaign: campaign.to_api,
12
+ recipientData: recipients.map(&:to_api)
13
+ }
14
+ api_method(:trigger_campaign_message, message)
15
+ end
16
+
17
+ def check_failures(outcome, recipients)
18
+ if outcome.respond_to?(:each_index)
19
+ outcome.each_index { |i| puts "failed:\n" + recipients[i][:recipient].to_s unless outcome[i][:success] }
20
+ else
21
+ puts "failed:\n" + recipients[:recipient].to_s unless outcome[:success]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,70 @@
1
+ require "responsys/configuration"
2
+ require "savon"
3
+ require "responsys/helper"
4
+ require "responsys/api/all"
5
+ require "responsys/api/object/all"
6
+ require "singleton"
7
+
8
+ module Responsys
9
+ module Api
10
+ class Client
11
+ include Singleton
12
+ include Responsys::Api::All
13
+
14
+ attr_accessor :credentials, :client, :session_id, :jsession_id, :header
15
+
16
+ def initialize
17
+ settings = Responsys.configuration.settings
18
+ @credentials = {
19
+ username: settings[:username],
20
+ password: settings[:password]
21
+ }
22
+
23
+ if settings[:debug]
24
+ @client = Savon.client(wsdl: settings[:wsdl], element_form_default: :qualified, log_level: :debug, log: true, pretty_print_xml: true)
25
+ else
26
+ @client = Savon.client(wsdl: settings[:wsdl], element_form_default: :qualified)
27
+ end
28
+
29
+ login
30
+ end
31
+
32
+ def api_method(action, message = nil, response_type = :hash)
33
+ begin
34
+ response = run_with_credentials(action, message, jsession_id, header)
35
+
36
+ case response_type
37
+ when :result
38
+ Responsys::Helper.format_response_result(response, action)
39
+ when :hash
40
+ Responsys::Helper.format_response_hash(response, action)
41
+ end
42
+
43
+ rescue Exception => e
44
+ error_response = Responsys::Helper.format_response_with_errors(e)
45
+
46
+ if error_response[:error][:code] == "INVALID_SESSION_ID"
47
+ login
48
+ api_method(action, message, response_type)
49
+ else
50
+ error_response
51
+ end
52
+ end
53
+ end
54
+
55
+ def available_operations
56
+ @client.operations
57
+ end
58
+
59
+ private
60
+
61
+ def run(method, message)
62
+ @client.call(method.to_sym, message: message)
63
+ end
64
+
65
+ def run_with_credentials(method, message, cookie, header)
66
+ @client.call(method.to_sym, message: message, cookies: cookie, soap_header: header)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,25 @@
1
+ module Responsys
2
+ module Api
3
+ module Folder
4
+ def list_folders
5
+ api_method(:list_folders)
6
+ end
7
+
8
+ def create_folder(name)
9
+ api_method(:create_folder, { :folderName => name })
10
+ end
11
+
12
+ def delete_folder(name)
13
+ api_method(:delete_folder, { :folderName => name })
14
+ end
15
+
16
+ def folder_exists?(name)
17
+ all = list_folders
18
+
19
+ all.select! { |curr_folder| curr_folder[:name] == name }
20
+
21
+ !all.empty?
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,39 @@
1
+ require "responsys/api/object/all"
2
+
3
+ module Responsys
4
+ module Api
5
+ module List
6
+ include Responsys::Api::Object
7
+ def retrieve_list_members(interact_object, query_column, field_list, ids_to_retrieve)
8
+ message = {
9
+ list: interact_object.to_api,
10
+ queryColumn: query_column.to_api,
11
+ fieldList: field_list,
12
+ idsToRetrieve: ids_to_retrieve
13
+ }
14
+
15
+ api_method(:retrieve_list_members, message)
16
+ end
17
+
18
+ def merge_list_members(interact_object, record_data, merge_rule = ListMergeRule.new)
19
+ message = {
20
+ list: interact_object.to_api,
21
+ recordData: record_data.to_api,
22
+ mergeRule: merge_rule.to_api
23
+ }
24
+
25
+ api_method(:merge_list_members, message)
26
+ end
27
+
28
+ def merge_list_members_riid(interact_object, record_data, merge_rule = ListMergeRule.new)
29
+ message = {
30
+ list: interact_object.to_api,
31
+ recordData: record_data.to_api,
32
+ mergeRule: merge_rule.to_api
33
+ }
34
+
35
+ api_method(:merge_list_members_riid, message)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,11 @@
1
+ require "responsys/api/object/interact_object"
2
+ require "responsys/api/object/list_merge_rule"
3
+ require "responsys/api/object/record"
4
+ require "responsys/api/object/record_data"
5
+ require "responsys/api/object/field"
6
+ require "responsys/api/object/field_type"
7
+ require "responsys/api/object/query_column"
8
+ require "responsys/api/object/recipient"
9
+ require "responsys/api/object/recipient_data"
10
+ require "responsys/api/object/email_format"
11
+ require "responsys/api/object/optional_data"
@@ -0,0 +1,23 @@
1
+ module Responsys
2
+ module Api
3
+ module Object
4
+ class EmailFormat
5
+ include Responsys::Api::Object
6
+ attr_accessor :email_format_string
7
+ AVAILABLE_EMAIL_FORMAT = %w(TEXT_FORMAT HTML_FORMAT NO_FORMAT MULTIPART_FORMAT)
8
+
9
+ def initialize(email_format = "NO_FORMAT")
10
+ if AVAILABLE_EMAIL_FORMAT.include? email_format
11
+ @email_format_string = email_format
12
+ else
13
+ raise ParameterException, I18n.t("api.object.email_format.incorrect_email_format")
14
+ end
15
+ end
16
+
17
+ def to_api
18
+ @email_format_string
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ module Responsys
2
+ module Api
3
+ module Object
4
+ class Field
5
+ attr_accessor :field_name, :field_type, :custom, :data_extraction_key
6
+
7
+ def initialize(field_name, field_type, custom = false, data_extraction_key = false)
8
+ @field_name = field_name
9
+ @field_type = field_type
10
+ @custom = custom
11
+ @data_extraction_key = data_extraction_key
12
+ end
13
+
14
+ def to_api
15
+ { fieldName: @field_name, fieldType: @field_type.to_api, custom: @custom, dataExtractionKey: @data_extraction_key }
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ module Responsys
2
+ module Api
3
+ module Object
4
+ class FieldType
5
+ include Responsys::Exceptions
6
+ attr_accessor :field_type_string
7
+ FIELD_TYPES = %w(STR500 STR4000 NUMBER TIMESTAMP INTEGER)
8
+
9
+ def initialize(field_type)
10
+ if FIELD_TYPES.include? field_type
11
+ @field_type_string = field_type
12
+ else
13
+ raise ParameterException, I18n.t("api.object.field_type.incorrect_field_type")
14
+ end
15
+ end
16
+
17
+ def to_api
18
+ @field_type_string
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ module Responsys
2
+ module Api
3
+ module Object
4
+ class InteractObject
5
+ attr_accessor :folder_name, :object_name
6
+
7
+ def initialize(folder_name, object_name)
8
+ @folder_name = folder_name
9
+ @object_name = object_name
10
+ end
11
+
12
+ def to_api
13
+ { folderName: @folder_name, objectName: @object_name }
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,41 @@
1
+ module Responsys
2
+ module Api
3
+ module Object
4
+ class ListMergeRule
5
+ attr_accessor :insert_on_no_match, :update_on_match, :match_column_name1, :match_column_name2, :match_column_name3, :match_operator, :optin_value, :optout_value, :html_value, :text_value, :reject_record_if_channel_empty, :default_permission_status
6
+
7
+ def initialize(options = {})
8
+ @insert_on_no_match = options[:insertOnNoMatch].nil? ? false : options[:insertOnNoMatch]
9
+ @update_on_match = options[:updateOnMatch] || "REPLACE_ALL"
10
+ @match_column_name1 = options[:matchColumnName1] || "EMAIL_ADDRESS_"
11
+ @match_column_name2 = options[:matchColumnName2] || ""
12
+ @match_column_name3 = options[:matchColumnName3] || ""
13
+ @match_operator = options[:matchOperator] || "AND"
14
+ @optin_value = options[:optinValue] || "I"
15
+ @optout_value = options[:optoutValue] || "O"
16
+ @html_value = options[:htmlValue] || "H"
17
+ @text_value = options[:textValue] || "T"
18
+ @reject_record_if_channel_empty = options[:rejectRecordIfChannelEmpty] || ""
19
+ @default_permission_status = options[:defaultPermissionStatus] || "OPTOUT"
20
+ end
21
+
22
+ def to_api
23
+ {
24
+ insertOnNoMatch: @insert_on_no_match,
25
+ updateOnMatch: @update_on_match,
26
+ matchColumnName1: @match_column_name1,
27
+ matchColumnName2: @match_column_name2,
28
+ matchColumnName3: @match_column_name3,
29
+ matchOperator: @match_operator,
30
+ optinValue: @optin_value,
31
+ optoutValue: @optout_value,
32
+ htmlValue: @html_value,
33
+ textValue: @text_value,
34
+ rejectRecordIfChannelEmpty: @reject_record_if_channel_empty,
35
+ defaultPermissionStatus: @default_permission_status
36
+ }
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end