responsys-api 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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