emarsys 0.1.0

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 (59) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +11 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +171 -0
  6. data/Rakefile +7 -0
  7. data/emarsys.gemspec +25 -0
  8. data/lib/emarsys.rb +60 -0
  9. data/lib/emarsys/client.rb +40 -0
  10. data/lib/emarsys/data_object.rb +78 -0
  11. data/lib/emarsys/data_objects/condition.rb +20 -0
  12. data/lib/emarsys/data_objects/contact.rb +121 -0
  13. data/lib/emarsys/data_objects/contact_list.rb +47 -0
  14. data/lib/emarsys/data_objects/email.rb +133 -0
  15. data/lib/emarsys/data_objects/email_category.rb +20 -0
  16. data/lib/emarsys/data_objects/email_launch_status.rb +38 -0
  17. data/lib/emarsys/data_objects/email_status_code.rb +39 -0
  18. data/lib/emarsys/data_objects/event.rb +44 -0
  19. data/lib/emarsys/data_objects/export.rb +22 -0
  20. data/lib/emarsys/data_objects/field.rb +39 -0
  21. data/lib/emarsys/data_objects/file.rb +39 -0
  22. data/lib/emarsys/data_objects/folder.rb +24 -0
  23. data/lib/emarsys/data_objects/form.rb +21 -0
  24. data/lib/emarsys/data_objects/language.rb +20 -0
  25. data/lib/emarsys/data_objects/segment.rb +21 -0
  26. data/lib/emarsys/data_objects/source.rb +40 -0
  27. data/lib/emarsys/error.rb +24 -0
  28. data/lib/emarsys/extensions.rb +8 -0
  29. data/lib/emarsys/field_mapping.rb +55 -0
  30. data/lib/emarsys/params_converter.rb +29 -0
  31. data/lib/emarsys/request.rb +46 -0
  32. data/lib/emarsys/response.rb +24 -0
  33. data/lib/emarsys/version.rb +3 -0
  34. data/spec/emarsys/client_spec.rb +85 -0
  35. data/spec/emarsys/data_object_spec.rb +55 -0
  36. data/spec/emarsys/data_objects/condition_spec.rb +9 -0
  37. data/spec/emarsys/data_objects/contact_list_spec.rb +9 -0
  38. data/spec/emarsys/data_objects/contact_spec.rb +79 -0
  39. data/spec/emarsys/data_objects/email_category_spec.rb +9 -0
  40. data/spec/emarsys/data_objects/email_launch_status_spec.rb +25 -0
  41. data/spec/emarsys/data_objects/email_spec.rb +84 -0
  42. data/spec/emarsys/data_objects/email_status_code_spec.rb +25 -0
  43. data/spec/emarsys/data_objects/event_spec.rb +23 -0
  44. data/spec/emarsys/data_objects/export_spec.rb +9 -0
  45. data/spec/emarsys/data_objects/field_spec.rb +19 -0
  46. data/spec/emarsys/data_objects/file_spec.rb +29 -0
  47. data/spec/emarsys/data_objects/folder_spec.rb +13 -0
  48. data/spec/emarsys/data_objects/form_spec.rb +9 -0
  49. data/spec/emarsys/data_objects/language_spec.rb +9 -0
  50. data/spec/emarsys/data_objects/segment_spec.rb +9 -0
  51. data/spec/emarsys/data_objects/source_spec.rb +25 -0
  52. data/spec/emarsys/extensions_spec.rb +24 -0
  53. data/spec/emarsys/field_mapping_spec.rb +14 -0
  54. data/spec/emarsys/params_converter_spec.rb +52 -0
  55. data/spec/emarsys/request_spec.rb +27 -0
  56. data/spec/emarsys/response_spec.rb +35 -0
  57. data/spec/emarsys_spec.rb +22 -0
  58. data/spec/spec_helper.rb +28 -0
  59. metadata +178 -0
@@ -0,0 +1,22 @@
1
+ module Emarsys
2
+
3
+ # Methods for the Export API
4
+ #
5
+ #
6
+ class Export < DataObject
7
+ class << self
8
+
9
+ # Find a specific export
10
+ #
11
+ # @param id [Integer, String] The internal emarsys id
12
+ # @return [Hash] Result data
13
+ # @example
14
+ # Emarsys::Export.resource(2)
15
+ def resource(id)
16
+ get "export/#{id}", {}
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,39 @@
1
+ module Emarsys
2
+
3
+ # Methods for the Field API
4
+ #
5
+ #
6
+ class Field < DataObject
7
+ class << self
8
+
9
+ # List data fields
10
+ #
11
+ # @param params [Hash] optional translation params
12
+ # @option params [Integer, String] :translate translate to another language
13
+ # @return [Hash] List of fields
14
+ # @example
15
+ # Emarsys::Field.collection
16
+ # Emarsys::Field.collection(:translate => 'en')
17
+ def collection(params = {})
18
+ params = params.stringify_keys
19
+ if params['translate']
20
+ get "field/translate/#{params['translate'].to_s}", {}
21
+ else
22
+ get 'field', {}
23
+ end
24
+ end
25
+
26
+ # Query the choice option for a specific field
27
+ #
28
+ # @param id [Integer, String] id of the field
29
+ # @return [Hash] Result Data
30
+ # @example
31
+ # Emarsys::Field.choice(3)
32
+ def choice(id)
33
+ get "field/#{id}/choice", {}
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,39 @@
1
+ module Emarsys
2
+
3
+ # Methods for the File API
4
+ #
5
+ #
6
+ class File < DataObject
7
+ class << self
8
+
9
+ # List files
10
+ #
11
+ # @param params [Hash] optional search params
12
+ # @option params [Integer, String] :folder filter by folder
13
+ # @return [Hash] List of files
14
+ # @example
15
+ # Emarsys::File.collection
16
+ # Emarsys::File.collection(:folder => 3)
17
+ def collection(params = {})
18
+ get 'file', params
19
+ end
20
+
21
+ # Upload a file to the media database
22
+ #
23
+ # @param filename [String] The filename
24
+ # @param base64_encoded_file [String] Base64 encoded version of the file
25
+ # @param folder_id [Integer, String] optinal folder to out te file into
26
+ # @return [Hash] Result data
27
+ # @example
28
+ # Emarsys::File.create("my_file.jpg", "asdhkajsh...")
29
+ # Emarsys::File.create("my_file.jpg", "asdhkajsh...", 3)
30
+ def create(filename, base64_encoded_file, folder_id = nil)
31
+ params = {:filename => filename, :file => base64_encoded_file}
32
+ params.merge!({:folder => folder_id}) unless folder_id.nil?
33
+ post 'file', params
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,24 @@
1
+ module Emarsys
2
+
3
+ # Methods for the Folder API
4
+ #
5
+ #
6
+ class Folder < DataObject
7
+ class << self
8
+
9
+ # List folders
10
+ #
11
+ # @param params [Hash] optional search params
12
+ # @option params [Integer, String] :folder filter by parent folder
13
+ # @return [Hash] List of folders
14
+ # @example
15
+ # Emarsys::Folder.collection
16
+ # Emarsys::Folder.collection(:folder => 3)
17
+ def collection(params = {})
18
+ get 'folder', params
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,21 @@
1
+ module Emarsys
2
+
3
+ # Methods for the Form API
4
+ #
5
+ #
6
+ class Form < DataObject
7
+ class << self
8
+
9
+ # List segments
10
+ #
11
+ # @return [Hash] List of forms
12
+ # @example
13
+ # Emarsys::Form.collection
14
+ def collection
15
+ get 'form', {}
16
+ end
17
+
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,20 @@
1
+ module Emarsys
2
+
3
+ # Methods for the Lanugage API
4
+ #
5
+ #
6
+ class Language < DataObject
7
+ class << self
8
+
9
+ # List languages
10
+ #
11
+ # @return [Hash] List of languages
12
+ # @example
13
+ # Emarsys::Language.collection
14
+ def collection
15
+ get 'language', {}
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ module Emarsys
2
+
3
+ # Methods for the Segment API
4
+ #
5
+ #
6
+ class Segment < DataObject
7
+ class << self
8
+
9
+ # List segments
10
+ #
11
+ # @return [Hash] List of segments
12
+ # @example
13
+ # Emarsys::Segment.collection
14
+ def collection
15
+ get 'filter', {}
16
+ end
17
+
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,40 @@
1
+ module Emarsys
2
+
3
+ # Methods for the Source API
4
+ #
5
+ #
6
+ class Source < DataObject
7
+ class << self
8
+
9
+ # List sources
10
+ #
11
+ # @return [Hash] List of sources
12
+ # @example
13
+ # Emarsys::Source.collection
14
+ def collection
15
+ get 'source', {}
16
+ end
17
+
18
+ # Create a new source
19
+ #
20
+ # @param name [String] Name of the new source
21
+ # @return [Hash] Result data
22
+ # @example
23
+ # Emarsys::Source.create("My new source")
24
+ def create(name)
25
+ post 'source/create', {:name => name}
26
+ end
27
+
28
+ # Destroy a specific source
29
+ #
30
+ # @param id [Integer, String] The internal emarsys id
31
+ # @return [Hash] Result data
32
+ # @example
33
+ # Emarsys::Source.destroy(2)
34
+ def destroy(id)
35
+ delete "source/#{id}", {}
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,24 @@
1
+ module Emarsys
2
+ # Custom error class for rescuing from Emarsys errors
3
+ class Error < StandardError
4
+ def initialize(code, text, status)
5
+ @code = code
6
+ @text = text
7
+ @status = status
8
+ super(build_error_message)
9
+ end
10
+
11
+ def build_error_message
12
+ "HTTP-Code: #{@status}, Emarsys-Code: #{@code} - #{@text}"
13
+ end
14
+ end
15
+
16
+ # Raised when Emarsys returns a 400 HTTP status code
17
+ class BadRequest < Error; end
18
+
19
+ # Raised when Emarsys returns a 401 HTTP status code
20
+ class Unauthorized < Error; end
21
+
22
+ # Raised when Emarsys returns a 500 HTTP status code
23
+ class InternalServerError < Error; end
24
+ end
@@ -0,0 +1,8 @@
1
+ class Hash
2
+ def stringify_keys
3
+ inject({}) do |options, (key, value)|
4
+ options[key.to_s] = value
5
+ options
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,55 @@
1
+ module Emarsys::FieldMapping
2
+
3
+ ATTRIBUTES = [
4
+ {:id => 0, :identifier => '_interests', :name => 'Interests'},
5
+ {:id => 1, :identifier => '_firstname', :name => 'First Name'},
6
+ {:id => 2, :identifier => '_lastname', :name => 'Last Name'},
7
+ {:id => 3, :identifier => '_email', :name => 'E-Mail'},
8
+ {:id => 4, :identifier => '_date_of_birth', :name => 'Date of Birth'},
9
+ {:id => 5, :identifier => '_gender', :name => 'Gender'},
10
+ {:id => 6, :identifier => '_marital_status', :name => 'Marital Status'},
11
+ {:id => 7, :identifier => '_children', :name => 'Children'},
12
+ {:id => 8, :identifier => '_education', :name => 'Education'},
13
+ {:id => 9, :identifier => '_title', :name => 'Title'},
14
+ {:id => 10, :identifier => '_address', :name => 'Address'},
15
+ {:id => 11, :identifier => '_city', :name => 'City'},
16
+ {:id => 12, :identifier => '_region', :name => 'Region'},
17
+ {:id => 13, :identifier => '_zip', :name => 'ZIP Code'},
18
+ {:id => 14, :identifier => '_country', :name => 'Country'},
19
+ {:id => 15, :identifier => '_phone', :name => 'Phone'},
20
+ {:id => 16, :identifier => '_fax', :name => 'Fax'},
21
+ {:id => 17, :identifier => '_job_position', :name => 'Job Position'},
22
+ {:id => 18, :identifier => '_company', :name => 'Company'},
23
+ {:id => 19, :identifier => '_department', :name => 'Department'},
24
+ {:id => 20, :identifier => '_industry', :name => 'Industry'},
25
+ {:id => 21, :identifier => '_phone_office', :name => 'Phone (office)'},
26
+ {:id => 22, :identifier => '_fax_office', :name => 'Fax (office)'},
27
+ {:id => 23, :identifier => '_number_of_employees', :name => 'Number of Employees'},
28
+ {:id => 24, :identifier => '_annual_revenue', :name => 'Annual Revenue (in 000 EUR)'},
29
+ {:id => 25, :identifier => '_url', :name => 'URL'},
30
+ {:id => 26, :identifier => '_preferred_mail_format', :name => 'Preferred Mail Format'},
31
+ {:id => 27, :identifier => '_avg_length_of_visit', :name => 'Avg. length of visit (minutes)'},
32
+ {:id => 28, :identifier => '_page_views_per_day', :name => 'Page views per day'},
33
+ {:id => 29, :identifier => '_days_since_last_email_sent', :name => 'Days since last e-mail sent'},
34
+ {:id => 30, :identifier => '_response_rate', :name => 'Response rate (% of campaigns sent)'},
35
+ {:id => 31, :identifier => '_opt_in', :name => 'Opt-In'},
36
+ {:id => 32, :identifier => '_user_status', :name => 'User status'},
37
+ {:id => 33, :identifier => '_contact_source', :name => 'Contact source'},
38
+ {:id => 34, :identifier => '_contact_form', :name => 'Contact form'},
39
+ {:id => 35, :identifier => '_registration_language', :name => 'Registration Language'},
40
+ {:id => 36, :identifier => '_newsletter', :name => 'Newsletter'},
41
+ {:id => 37, :identifier => '_mobile', :name => 'Mobile'},
42
+ {:id => 38, :identifier => '_first_name_of_partner', :name => 'First Name of Partner'},
43
+ {:id => 39, :identifier => '_birthdate_of_partner', :name => 'Birthdate of Partner'},
44
+ {:id => 40, :identifier => '_anniversary', :name => 'Anniversary'},
45
+ {:id => 41, :identifier => '_company_address', :name => 'Company Address'},
46
+ {:id => 42, :identifier => '_zip_official', :name => 'Zip Code (office)'},
47
+ {:id => 43, :identifier => '_city_official', :name => 'City (office)'},
48
+ {:id => 44, :identifier => '_state_official', :name => 'State (office)'},
49
+ {:id => 45, :identifier => '_country_official', :name => 'Country (office)'},
50
+ {:id => 46, :identifier => '_salutation', :name => 'Salutation'},
51
+ {:id => 47, :identifier => '_email_valid', :name => 'E-Mail valid'},
52
+ {:id => 48, :identifier => '_date_of_first_registration', :name => 'Date of first registration'}
53
+ ]
54
+
55
+ end
@@ -0,0 +1,29 @@
1
+ module Emarsys
2
+ class ParamsConverter
3
+
4
+ attr_accessor :params
5
+
6
+ def initialize(params={})
7
+ self.params = params
8
+ end
9
+
10
+ def convert_to_ids
11
+ new_hash = {}
12
+ params.each do |key, value|
13
+ matching_attributes = Emarsys::FieldMapping::ATTRIBUTES.find{|elem| elem[:identifier] == key.to_s}
14
+ new_hash.merge!({ (matching_attributes.nil? ? key : matching_attributes[:id]) => value })
15
+ end
16
+ new_hash
17
+ end
18
+
19
+ def convert_to_identifiers
20
+ new_hash = {}
21
+ params.each do |key, value|
22
+ matching_attributes = Emarsys::FieldMapping::ATTRIBUTES.find{|elem| elem[:id] == key.to_i && key.to_i != 0}
23
+ new_hash.merge!({ (matching_attributes.nil? ? key : matching_attributes[:identifier]) => value })
24
+ end
25
+ new_hash
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,46 @@
1
+ module Emarsys
2
+
3
+ class Request
4
+ attr_accessor :http_verb, :path, :params
5
+
6
+ def initialize(http_verb, path, params = {})
7
+ self.path = path
8
+ self.http_verb = http_verb
9
+ self.params = params
10
+ end
11
+
12
+ def send_request
13
+ case http_verb.to_sym
14
+ when :post
15
+ RestClient.post(emarsys_uri, converted_params.to_json, :content_type => :json, :x_wsse => client.x_wsse_string) do |response, request, result, &block|
16
+ Emarsys::Response.new(response).result
17
+ end
18
+ when :put
19
+ RestClient.put emarsys_uri, converted_params.to_json, :content_type => :json, :x_wsse => client.x_wsse_string do |response, request, result, &block|
20
+ Emarsys::Response.new(response).result
21
+ end
22
+ when :delete
23
+ RestClient.delete(emarsys_uri, :content_type => :json, :x_wsse => client.x_wsse_string) do |response, request, result, &block|
24
+ Emarsys::Response.new(response).result
25
+ end
26
+ else
27
+ RestClient.get(emarsys_uri, :content_type => :json, :x_wsse => client.x_wsse_string) do |response, request, result, &block|
28
+ Emarsys::Response.new(response).result
29
+ end
30
+ end
31
+ end
32
+
33
+ def client
34
+ Emarsys::Client.new
35
+ end
36
+
37
+ def emarsys_uri
38
+ [Emarsys.api_endpoint, @path].join('/')
39
+ end
40
+
41
+ def converted_params
42
+ Emarsys::ParamsConverter.new(params).convert_to_ids
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1,24 @@
1
+ module Emarsys
2
+ class Response
3
+ attr_accessor :code, :text, :data, :status
4
+
5
+ def initialize(response)
6
+ json = JSON.parse(response)
7
+ self.code = json['replyCode']
8
+ self.text = json['replyText']
9
+ self.data = json['data']
10
+ self.status = response.code if response.respond_to?(:code)
11
+ end
12
+
13
+ def result
14
+ if code == 0
15
+ data
16
+ elsif !status.nil? && status == 401
17
+ raise Emarsys::Unauthorized.new(code, text, status)
18
+ else
19
+ raise Emarsys::BadRequest.new(code, text, status)
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module Emarsys
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+
3
+ describe Emarsys::Client do
4
+
5
+ describe 'configs for username and password' do
6
+ it 'inherits username from module' do
7
+ Emarsys.stub(:api_username).and_return("my_username")
8
+ expect(Emarsys::Client.new.username).to eq("my_username")
9
+ end
10
+
11
+ it 'inherits password from module' do
12
+ Emarsys.stub(:api_password).and_return("my_password")
13
+ expect(Emarsys::Client.new.password).to eq("my_password")
14
+ end
15
+
16
+ it 'raises error if api_username is not set' do
17
+ Emarsys.stub(:api_username).and_return(nil)
18
+ expect{Emarsys::Client.new.username}.to raise_error(ArgumentError, 'Emarsys.api_username is not set')
19
+ end
20
+
21
+ it 'raises error if api_password is not set' do
22
+ Emarsys.stub(:api_password).and_return(nil)
23
+ expect{Emarsys::Client.new.password}.to raise_error(ArgumentError, 'Emarsys.api_password is not set')
24
+ end
25
+ end
26
+
27
+ context 'client authentication' do
28
+ describe '#x_wsse_string' do
29
+ it 'builds x_wsse_string with specific format' do
30
+ Emarsys::Client.any_instance.stub(:username).and_return("my_username")
31
+ Emarsys::Client.any_instance.stub(:header_password_digest).and_return("12345689")
32
+ Emarsys::Client.any_instance.stub(:header_nonce).and_return("some_header_nonce")
33
+ Emarsys::Client.any_instance.stub(:header_created).and_return("2013-01-01")
34
+ expect(Emarsys::Client.new.x_wsse_string).to eq(
35
+ 'UsernameToken Username = "my_username", PasswordDigest = "12345689", Nonce = "some_header_nonce", Created = "2013-01-01"'
36
+ )
37
+ end
38
+ end
39
+
40
+ describe '#header_password_digest' do
41
+ before :each do
42
+ Emarsys::Client.any_instance.stub(:calculated_digest).and_return("something")
43
+ end
44
+
45
+ it 'encodes string with Base64' do
46
+ Base64.should_receive(:encode64).with("something").and_return("something_base64_encoded")
47
+ Emarsys::Client.new.header_password_digest
48
+ end
49
+
50
+ it 'strips of \n character' do
51
+ Base64.should_receive(:encode64).with("something").and_return("something_base64_encoded\n")
52
+ expect(Emarsys::Client.new.header_password_digest).to eq("something_base64_encoded")
53
+ end
54
+ end
55
+
56
+ describe '#header_nonce' do
57
+ it 'uses md5 hash auf current time (as integer)' do
58
+ Time.any_instance.stub(:to_i).and_return(123456789)
59
+ Digest::MD5.should_receive(:hexdigest).with("123456789").and_return("25f9e794323b453885f5181f1b624d0b")
60
+ Emarsys::Client.new.header_nonce
61
+ end
62
+ end
63
+
64
+ describe '#header_created' do
65
+ it 'uses current timestamp format' do
66
+ time = Time.parse("2013-01-01 01:00:01")
67
+ Time.stub(:new).and_return(time)
68
+ expect(Emarsys::Client.new.header_created).to eq("2013-01-01 01:00:01")
69
+ end
70
+ end
71
+
72
+ describe '#calculated_digest' do
73
+ it 'runs sha1 on header_nonce + header_created + password' do
74
+ Emarsys::Client.any_instance.stub(:header_nonce).and_return("some_header_nonce")
75
+ Emarsys::Client.any_instance.stub(:header_created).and_return("12345689")
76
+ Emarsys::Client.any_instance.stub(:password).and_return("my_password")
77
+ Digest::SHA1.should_receive(:hexdigest).with("some_header_nonce12345689my_password")
78
+ Emarsys::Client.new.calculated_digest
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+ end
85
+