emarsys 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+