kampainer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 03a41e771943f773f67b6e0ac09039ee33852554beb8de46657357dba397b48f
4
+ data.tar.gz: b04481efe285529989e41f5ed1add04cacd7670811b4c9641ccd80122a62d282
5
+ SHA512:
6
+ metadata.gz: c74bade2fc8fc314545e4d0d133bd4ec26fe704452d6873fdaca7c22376a87748e05d6ffc046e5621a5fb3791de61132e8e17b31ac3116c219b5675fd244c532
7
+ data.tar.gz: 0a88c11d58fce7d2bf25a5944701dcff1d45dc8a420fc6f5ba6188226734bef911f6b1f0e6683045c883a3a7bd76b94a15d09e2b4e7721be055d113bb5eacd79
@@ -0,0 +1,53 @@
1
+ # Ruby CircleCI 2.0 configuration file
2
+ #
3
+ # Check https://circleci.com/docs/2.0/language-ruby/ for more details
4
+ #
5
+ version: 2
6
+ jobs:
7
+ build:
8
+ docker:
9
+ # specify the version you desire here
10
+ - image: circleci/ruby:2.5
11
+
12
+ working_directory: ~/repo
13
+
14
+ steps:
15
+ - checkout
16
+
17
+ # Download and cache dependencies
18
+ - restore_cache:
19
+ keys:
20
+ - v1-dependencies-{{ checksum "Gemfile.lock" }}
21
+ # fallback to using the latest cache if no exact match is found
22
+ - v1-dependencies-
23
+
24
+ - run:
25
+ name: install dependencies
26
+ command: |
27
+ bundle install --jobs=4 --retry=3 --path vendor/bundle
28
+
29
+ - save_cache:
30
+ paths:
31
+ - ./vendor/bundle
32
+ key: v1-dependencies-{{ checksum "Gemfile.lock" }}
33
+
34
+ # run tests!
35
+ - run:
36
+ name: run tests
37
+ command: |
38
+ mkdir /tmp/test-results
39
+ TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
40
+ circleci tests split --split-by=timings)"
41
+
42
+ bundle exec rspec \
43
+ --format progress \
44
+ --out /tmp/test-results/rspec.xml \
45
+ --format progress \
46
+ $TEST_FILES
47
+
48
+ # collect reports
49
+ - store_test_results:
50
+ path: /tmp/test-results
51
+ - store_artifacts:
52
+ path: /tmp/test-results
53
+ destination: test-results
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in kampainer.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Piers Chambers
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # Kampainer
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/kampainer.svg)](https://badge.fury.io/rb/kampainer)
4
+ [![CircleCI](https://circleci.com/gh/varyonic/kampainer.svg?style=svg)](https://circleci.com/gh/varyonic/kampainer)
5
+
6
+ Unofficial Ruby wrapper for the Campaigner Elements API
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'kampainer'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ ## Usage
21
+
22
+ See specs for sample usage.
23
+
24
+ ## Development
25
+
26
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
27
+
28
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
29
+
30
+ ## Contributing
31
+
32
+ Bug reports and pull requests are welcome on GitHub at https://github.com/varyonic/kampainer.
33
+
34
+ ## License
35
+
36
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "kampainer"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/kampainer.gemspec ADDED
@@ -0,0 +1,31 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "kampainer/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "kampainer"
8
+ spec.version = Kampainer::VERSION
9
+ spec.authors = ["Piers Chambers"]
10
+ spec.email = ["piers@varyonic.com"]
11
+
12
+ spec.summary = %q{Unofficial Ruby wrapper for the Campaigner Elements API.}
13
+ spec.homepage = "https://github.com/varyonic/kampainer"
14
+ spec.license = "MIT"
15
+
16
+ # Specify which files should be added to the gem when it is released.
17
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
19
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
20
+ end
21
+ spec.bindir = "exe"
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ["lib"]
24
+
25
+ spec.add_dependency "nokogiri"
26
+ spec.add_dependency "roxml"
27
+
28
+ spec.add_development_dependency "bundler", "~> 1.17"
29
+ spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "rspec", "~> 3.0"
31
+ end
@@ -0,0 +1,40 @@
1
+ module Kampainer
2
+ # See https://ws.campaigner.com/2013/01/campaignmanagement.asmx?WSDL
3
+
4
+ # GetCampaignSummary
5
+ class CampaignData < SchemaObject
6
+ xml_name 'campaignData'
7
+ xml_accessor :id, as: Integer
8
+ xml_accessor :campaign_name
9
+ xml_accessor :campaign_subject
10
+ xml_accessor :campaign_format
11
+ xml_accessor :campaign_status
12
+ xml_accessor :campaign_type
13
+ xml_accessor :html_content
14
+ xml_accessor :txt_content
15
+ xml_accessor :from_name
16
+ xml_accessor :from_email_id, as: Integer
17
+ xml_accessor :reply_email_id, as: Integer
18
+ xml_accessor :track_replies?
19
+ xml_accessor :auto_reply_message?
20
+ xml_accessor :project_id, as: Integer
21
+ xml_accessor :is_welcome_campaign?
22
+ xml_accessor :date_modified
23
+ end
24
+
25
+ # GetCampaignSummary
26
+ class CampaignRecipientsData < SchemaObject
27
+ xml_reader :send_to_all_contacts?
28
+ # TODO
29
+ end
30
+
31
+ class CampaignScheduleData < SchemaObject
32
+ # TODO
33
+ end
34
+
35
+ class FromEmailDescription < SchemaObject
36
+ xml_reader :id, as: Integer
37
+ xml_reader :email_address
38
+ xml_reader :from_email_status
39
+ end
40
+ end
@@ -0,0 +1,32 @@
1
+ module Kampainer
2
+ # See https://ws.campaigner.com/2013/01/campaignmanagement.asmx?WSDL
3
+ class CampaignManager
4
+ attr_reader :session
5
+
6
+ def initialize(session)
7
+ @session = session
8
+ end
9
+
10
+ def create_update_campaign(campaign)
11
+ call('CreateUpdateCampaign', campaign.to_xml)[0].to_i
12
+ end
13
+
14
+ def delete_campaign(id)
15
+ call('DeleteCampaign', campaignId: id)
16
+ end
17
+
18
+ def get_campaign_summary(id)
19
+ call('GetCampaignSummary', campaignId: id)
20
+ end
21
+
22
+ def list_from_emails
23
+ call('ListFromEmails')
24
+ end
25
+
26
+ protected
27
+
28
+ def call(action_name, *params)
29
+ session.call("#{session.base_url}2013/01/campaignmanagement.asmx", action_name, *params)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,149 @@
1
+ module Kampainer
2
+
3
+ class ContactKey < SchemaObject
4
+ xml_name 'ContactKey'
5
+ xml_accessor :id, as: Integer, from: 'ContactId'
6
+ xml_accessor :unique_identifier, from: 'ContactUniqueIdentifier'
7
+ end
8
+
9
+ class ArrayOfInt < SchemaCollection
10
+ xml_accessor :collection, as: [Integer], from: 'int'
11
+ end
12
+
13
+ class Contact < SchemaObject
14
+ class Key < ContactKey; end
15
+
16
+ class CustomAttribute < SchemaObject
17
+ xml_name 'CustomAttribute'
18
+ xml_accessor :id, as: Integer, from: :attr
19
+ xml_accessor :value, from: :content
20
+ end
21
+
22
+ class CustomAttributes < SchemaCollection
23
+ xml_accessor :collection, as: [CustomAttribute]
24
+ end
25
+
26
+ xml_name 'ContactData'
27
+ xml_accessor :key, as: Key, from: 'ContactKey'
28
+ xml_accessor :first_name
29
+ xml_accessor :last_name
30
+ xml_accessor :email_address
31
+ xml_accessor :phone
32
+ xml_accessor :email_format
33
+ xml_accessor :status
34
+ xml_accessor :is_test_contact
35
+ xml_accessor :custom_attributes, as: CustomAttributes
36
+ xml_accessor :add_to_groups, as: ArrayOfInt
37
+
38
+ def custom_attributes=(custom_attributes)
39
+ custom_attributes = CustomAttributes.new(custom_attributes) if custom_attributes.is_a?(Array)
40
+ @custom_attributes = custom_attributes
41
+ end
42
+
43
+ def add_to_groups=(group_ids)
44
+ group_ids = ArrayOfInt.new(group_ids) unless group_ids.is_a?(SchemaObject)
45
+ @add_to_groups = group_ids
46
+ end
47
+ end
48
+
49
+ class ContactKeys < SchemaCollection
50
+ xml_reader :collection, as: [Contact::Key]
51
+
52
+ def initialize(keys = [])
53
+ @collection = keys.map do |key|
54
+ case key
55
+ when Contact::Key then key
56
+ when Integer then Contact::Key.new(id: key)
57
+ when String then Contact::Key.new(unique_identifier: key)
58
+ else Contact::Key.new(key)
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ class ContactsDataFilter < SchemaObject
65
+ xml_name 'contactFilter'
66
+ xml_accessor :keys, as: ContactKeys, from: 'ContactKeys'
67
+ end
68
+
69
+ class ContactInformationFilter < SchemaObject
70
+ xml_name 'contactInformationFilter' # GetContacts
71
+ xml_accessor :include_static_attributes
72
+ xml_accessor :include_custom_attributes
73
+ xml_accessor :include_system_attributes
74
+ xml_accessor :include_group_membership_data
75
+
76
+ def initialize(options)
77
+ options.each do |k, v|
78
+ @include_static_attributes = !!v if k.to_s =~ /static/ || v.to_s =~ /static/
79
+ @include_custom_attributes = !!v if k.to_s =~ /custom/ || v.to_s =~ /custom/
80
+ @include_system_attributes = !!v if k.to_s =~ /system/ || v.to_s =~ /system/
81
+ @include_group_membership_data = !!v if k.to_s =~ /group|member/ || v.to_s =~ /group|member/
82
+ end
83
+ end
84
+ end
85
+
86
+ # GetContacts
87
+ class ContactDetailData < SchemaObject
88
+ class StaticAttributes < SchemaObject
89
+ xml_reader :first_name
90
+ xml_reader :last_name
91
+ xml_reader :email
92
+ xml_reader :phone
93
+ xml_reader :email_format
94
+ xml_reader :status
95
+ xml_reader :is_test_contact
96
+ end
97
+
98
+ class AttributeDetails < SchemaObject
99
+ xml_name 'AttributeDetails'
100
+ xml_reader :id, as: Integer
101
+ xml_reader :name
102
+ xml_reader :value
103
+ xml_reader :default_value
104
+ end
105
+
106
+ class ArrayOfAttributeDetails < SchemaCollection
107
+ xml_name 'CustomAttributes'
108
+ xml_reader :collection, as: [AttributeDetails]
109
+ end
110
+
111
+ class ArrayOfContactGroupDescription < SchemaCollection
112
+ xml_reader :collection, as: [ContactGroupDescription]
113
+ end
114
+
115
+ xml_name 'ContactDetailData'
116
+ xml_reader :key, as: Contact::Key, from: 'ContactKey'
117
+ xml_reader :static_attributes, as: StaticAttributes
118
+ xml_reader :custom_attributes, as: ArrayOfAttributeDetails, from: 'CustomAttributes'
119
+ xml_reader :contact_groups, as: ArrayOfContactGroupDescription, from: 'group_membership_data'
120
+ end
121
+
122
+ # GetContacts
123
+ class ContactData < SchemaCollection
124
+ xml_accessor :collection, as: [ContactDetailData]
125
+ end
126
+
127
+ # ImmediateUpload
128
+ class Contacts < SchemaCollection
129
+ xml_name 'contacts'
130
+ xml_accessor :collection, as: [Contact]
131
+ end
132
+
133
+ # ImmediateUpload
134
+ class UploadResultData < SchemaObject
135
+ xml_accessor :index, as: Integer
136
+ xml_accessor :key, as: ContactKey, from: 'ContactKey'
137
+ xml_accessor :result_description
138
+ end
139
+
140
+ # ListTestContacts
141
+ class TestContact < Contact
142
+ xml_accessor :email
143
+ end
144
+
145
+ # DeleteContacts
146
+ class ArrayOfContactKey < ContactKeys
147
+ xml_name 'contactKeys'
148
+ end
149
+ end
@@ -0,0 +1,31 @@
1
+ module Kampainer
2
+ class Attribute < SchemaObject
3
+ class Filter < SchemaObject
4
+ xml_name 'filter'
5
+ xml_accessor :include_all_default_attributes?
6
+ xml_accessor :include_all_custom_attributes?
7
+ xml_accessor :include_all_system_attributes?
8
+
9
+ def initialize(options)
10
+ @include_all_default_attributes = false
11
+ @include_all_custom_attributes = false
12
+ @include_all_system_attributes = false
13
+ options.each do |k, v|
14
+ @include_all_default_attributes = !!v if k.to_s =~ /default/ || v.to_s =~ /default/
15
+ @include_all_custom_attributes = !!v if k.to_s =~ /custom/ || v.to_s =~ /custom/
16
+ @include_all_system_attributes = !!v if k.to_s =~ /system/ || v.to_s =~ /system/
17
+ end
18
+ end
19
+ end
20
+
21
+ xml_accessor :id, as: Integer
22
+ xml_accessor :name
23
+ xml_accessor :static_attribute_id, as: Integer
24
+ xml_accessor :is_key?
25
+ xml_accessor :attribute_type
26
+ xml_accessor :data_type
27
+ xml_accessor :last_modified_date # was LastUpdatedDate
28
+ end
29
+
30
+ class AttributeDescription < Attribute; end
31
+ end
@@ -0,0 +1,26 @@
1
+ module Kampainer
2
+
3
+ # ListContactGroups
4
+ class ContactGroupDescription < SchemaObject
5
+ xml_reader :type
6
+ xml_reader :id
7
+ xml_reader :name
8
+ xml_reader :description
9
+ xml_reader :status
10
+ xml_reader :last_modified_date
11
+ xml_reader :project_id
12
+ end
13
+
14
+ # CreateContactGroups
15
+ class ContactGroup < ContactGroupDescription
16
+ attr_accessor :type, :id, :name, :description
17
+ end
18
+
19
+ class ContactGroupId < SchemaObject
20
+ xml_reader :id, from: :content
21
+ end
22
+
23
+ class ContactGroupIds < SchemaCollection
24
+ xml_accessor :collection, as: [Integer], from: 'int'
25
+ end
26
+ end
@@ -0,0 +1,61 @@
1
+ module Kampainer
2
+ # See https://ws.campaigner.com/2013/01/contactmanagement.asmx?WSDL
3
+ class ContactManager
4
+ attr_reader :session
5
+
6
+ def initialize(session)
7
+ @session = session
8
+ end
9
+
10
+ # @option params [String] attribute_name
11
+ # @option params [String] attribute_type
12
+ # @option params [String|??] default_value
13
+ # @return [Integer] attribute id
14
+ def create_update_attribute(params)
15
+ params.transform_keys! { |key| key.to_s.camelcase(:lower) }
16
+ call('CreateUpdateAttribute', params)[0].to_i
17
+ end
18
+
19
+ def delete_attribute(id)
20
+ call('DeleteAttribute', id: id)
21
+ end
22
+
23
+ # @param *keys One or more keys of contacts to delete.
24
+ def delete_contacts(*keys)
25
+ contact_keys = ArrayOfContactKey[keys]
26
+ call('DeleteContacts', contact_keys.to_xml)[0].to_a
27
+ end
28
+
29
+ # @param *keys One or more contact keys.
30
+ def get_contacts(*keys)
31
+ filter = ContactsDataFilter.new(keys: ContactKeys[keys])
32
+ attribute_filter = ContactInformationFilter.new(include: 'static,custom,groups')
33
+ call('GetContacts', filter.to_xml, attribute_filter.to_xml)[0].to_a
34
+ end
35
+
36
+ # @return [ContactKey]
37
+ def immediate_upload(contact)
38
+ contact.key ||= ContactKey.new(unique_identifier: contact.email_address, id: 0)
39
+ contacts = Contacts.new(Array(contact))
40
+ call('ImmediateUpload', contacts.to_xml)[0].key
41
+ end
42
+
43
+ # @option filters [Boolean] include_all_default_attributes
44
+ # @option filters [Boolean] include_all_custom_attributes
45
+ # @option filters [Boolean] include_all_system_attributes
46
+ def list_attributes(filters)
47
+ filter_xml = Attribute::Filter.new(filters).to_xml
48
+ call('ListAttributes', filter_xml)
49
+ end
50
+
51
+ def list_test_contacts
52
+ call('ListTestContacts')
53
+ end
54
+
55
+ protected
56
+
57
+ def call(action_name, *params)
58
+ session.call("#{session.base_url}2013/01/contactmanagement.asmx", action_name, *params)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,75 @@
1
+ require 'benchmark'
2
+ require 'net/http'
3
+
4
+ module Kampainer
5
+
6
+ # Generic Net::HTTP wrapper that logs the request and response.
7
+ class HttpRequest
8
+ attr_reader :uri, :request
9
+ attr_reader :headers
10
+ attr_reader :logger
11
+
12
+ def initialize(method, path, headers, logger)
13
+ @headers = headers
14
+ @logger = logger
15
+ @uri = URI(path)
16
+ @request = Net::HTTP.const_get(method.capitalize).new(uri)
17
+ end
18
+
19
+ # Create HTTP request with provided headers.
20
+ # Invoke request over HTTPS.
21
+ # Return response on success or log failure and throw error.
22
+ def send(action_name, data)
23
+ headers.each { |k, v| request[k] = v }
24
+ request.body = data
25
+ response = log_request_response(data) do
26
+ https(uri).request(request)
27
+ end
28
+ fail_unless_expected_response response, Net::HTTPSuccess
29
+ response.body
30
+ end
31
+
32
+ def https(uri)
33
+ Net::HTTP.new(uri.host, uri.port).tap do |http|
34
+ http.use_ssl = true
35
+ end
36
+ end
37
+
38
+ # Log URI, method, data
39
+ # Start timer.
40
+ # Yield URI, method, data.
41
+ # Log response and time taken.
42
+ def log_request_response(data = nil)
43
+ logger.info "[#{self.class.name}] request = #{request.method} #{uri}"
44
+ logger.info "[#{self.class.name}] headers = #{headers.inspect}"
45
+ logger.info "[#{self.class.name}] data = #{data}"
46
+ response = nil
47
+ tms = Benchmark.measure do
48
+ response = yield
49
+ end
50
+ logger.info("[#{self.class.name}] response (#{ms(tms)}ms): #{response.inspect} #{response.body}")
51
+ response
52
+ end
53
+
54
+ def ms(tms)
55
+ (tms.real*1000).round(3)
56
+ end
57
+
58
+ class UnexpectedHttpResponse < StandardError
59
+ attr_reader :response
60
+
61
+ def initialize(response)
62
+ @response = response
63
+ super "#{response.message} (#{response.code}): #{response.body}"
64
+ end
65
+ end
66
+
67
+ def fail_unless_expected_response(response, *allowed_responses)
68
+ unless allowed_responses.any? { |allowed| response.is_a?(allowed) }
69
+ logger.error "#{response.inspect}: #{response.body}"
70
+ raise UnexpectedHttpResponse, response
71
+ end
72
+ response
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,36 @@
1
+ module Kampainer
2
+ # See https://ws.campaigner.com/2013/01/listmanagement.asmx?WSDL
3
+ class ListManager
4
+ attr_reader :session
5
+
6
+ def initialize(session)
7
+ @session = session
8
+ end
9
+
10
+ # @param [Hash] contact_group
11
+ def create_update_contact_group(contact_group)
12
+ params = [
13
+ { contactGroupType: contact_group.type },
14
+ { contactGroupId: contact_group.id || 0 },
15
+ { name: contact_group.name },
16
+ { description: contact_group.description },
17
+ { isTempGroup: 'false' },
18
+ ]
19
+ call('CreateUpdateContactGroups', *params)[0].id
20
+ end
21
+
22
+ def delete_contact_groups(*ids)
23
+ call('DeleteContactGroups', ContactGroupIds.new(ids).to_xml)
24
+ end
25
+
26
+ def list_contact_groups
27
+ call('ListContactGroups')
28
+ end
29
+
30
+ protected
31
+
32
+ def call(action_name, *params)
33
+ session.call("#{session.base_url}2013/01/listmanagement.asmx", action_name, *params)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,24 @@
1
+ class RequestBody < Nokogiri::XML::Builder
2
+ def initialize(action_name, username, password, request)
3
+ super()
4
+ __send__('soap12:Envelope', {'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
5
+ 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
6
+ 'xmlns:soap12' => 'http://www.w3.org/2003/05/soap-envelope'}) do |root|
7
+ root.__send__('soap12:Body') do |body|
8
+ body.__send__(action_name, xmlns: 'https://ws.campaigner.com/2013/01') do |doc|
9
+ doc.authentication do
10
+ doc.Username(username)
11
+ doc.Password(password)
12
+ end
13
+ request.each do |node|
14
+ if node.is_a?(Hash)
15
+ node.each { |key, val| body.send(key, val) }
16
+ else
17
+ body.parent << node
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ module Kampainer
2
+ class ResponseHeader < SchemaObject
3
+ xml_accessor :error_flag?
4
+ xml_accessor :return_code
5
+ xml_accessor :return_message
6
+
7
+ def message
8
+ "#{return_code}: #{return_message}"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,33 @@
1
+ require 'forwardable'
2
+ require 'roxml'
3
+
4
+ module Kampainer
5
+ class SchemaObject
6
+ include ROXML
7
+ xml_convention :camelcase
8
+
9
+ def initialize(options = {})
10
+ options.each_pair { |k, v| send("#{k}=", v) }
11
+ end
12
+
13
+ def inspect
14
+ variables = instance_variables - [:@roxml_references]
15
+ s = variables.map { |iv| "#{iv}: #{instance_variable_get(iv).inspect}" }.join(', ')
16
+ "<#{self.class.name}: #{s} >"
17
+ end
18
+ end
19
+
20
+ # https://stackoverflow.com/questions/2844106/ruby-roxml-how-to-get-an-array-to-render-its-xml
21
+ class SchemaCollection < SchemaObject
22
+ extend Forwardable
23
+
24
+ class << self
25
+ alias_method :[], :new
26
+ end
27
+
28
+ def initialize(collection = [])
29
+ @collection = collection
30
+ end
31
+ def_delegators :collection, :length, :<<, :to_a
32
+ end
33
+ end
@@ -0,0 +1,62 @@
1
+ require 'logger'
2
+ require 'nokogiri'
3
+
4
+ module Kampainer
5
+ class Session
6
+ attr_reader :base_url
7
+ attr_reader :username, :password
8
+ attr_accessor :logger
9
+
10
+ XMLNS = 'https://ws.campaigner.com/2013/01'.freeze
11
+
12
+ def initialize(username:, password:, base_url: nil, logger: nil)
13
+ @base_url = base_url || 'https://ws.campaigner.com/'
14
+ @username = username
15
+ @password = password
16
+ @logger = logger || Logger.new('/dev/null')
17
+ end
18
+
19
+ def call(url, action_name, *params)
20
+ xml_request = build_xml_request(action_name, *params)
21
+ commit(url, action_name, xml_request)
22
+ end
23
+
24
+ protected
25
+
26
+ def build_xml_request(action_name, *nodes)
27
+ RequestBody.new(action_name, username, password, nodes).to_xml
28
+ end
29
+
30
+ def commit(url, action_name, xml_request)
31
+ headers = {
32
+ 'SOAPAction' => "#{XMLNS}/#{action_name}",
33
+ 'Content-Type' => 'text/xml; charset=utf-8',
34
+ 'Content-Length' => xml_request.size.to_s
35
+ }
36
+ request = HttpRequest.new('POST', url, headers, logger)
37
+ parse request.send("#{XMLNS}/#{action_name}", xml_request)
38
+ end
39
+
40
+ def parse(xml)
41
+ xmldoc = Nokogiri::XML(xml)
42
+
43
+ header = xmldoc.xpath('//soap:Header/*')
44
+ response_header = Kampainer.const_get(header[0].name).from_xml(header[0].to_s)
45
+ raise Error, response_header.message if response_header.error_flag?
46
+
47
+ body = xmldoc.xpath('//soap:Body/*[1]')[0]
48
+
49
+ response = []
50
+
51
+ body.xpath('*')&.each do |node|
52
+ node.elements.each do |childnode|
53
+ response << Kampainer.const_get(childnode.name).from_xml(childnode.to_s)
54
+ rescue
55
+ response << childnode.inner_text
56
+ end
57
+ end
58
+
59
+ response
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,3 @@
1
+ module Kampainer
2
+ VERSION = "0.1.0"
3
+ end
data/lib/kampainer.rb ADDED
@@ -0,0 +1,19 @@
1
+ require "kampainer/version"
2
+
3
+ require 'kampainer/schema_object'
4
+ require 'kampainer/contact_attribute'
5
+ require 'kampainer/contact_group'
6
+ require 'kampainer/contact'
7
+ require 'kampainer/contact_manager'
8
+ require 'kampainer/campaign'
9
+ require 'kampainer/campaign_manager'
10
+ require 'kampainer/list_manager'
11
+
12
+ require 'kampainer/http_request'
13
+ require "kampainer/session"
14
+ require 'kampainer/request_body'
15
+ require 'kampainer/response'
16
+
17
+ module Kampainer
18
+ class Error < StandardError; end
19
+ end
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kampainer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Piers Chambers
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-03-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: roxml
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.17'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.17'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ description:
84
+ email:
85
+ - piers@varyonic.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".circleci/config.yml"
91
+ - ".gitignore"
92
+ - ".rspec"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - bin/console
98
+ - bin/setup
99
+ - kampainer.gemspec
100
+ - lib/kampainer.rb
101
+ - lib/kampainer/campaign.rb
102
+ - lib/kampainer/campaign_manager.rb
103
+ - lib/kampainer/contact.rb
104
+ - lib/kampainer/contact_attribute.rb
105
+ - lib/kampainer/contact_group.rb
106
+ - lib/kampainer/contact_manager.rb
107
+ - lib/kampainer/http_request.rb
108
+ - lib/kampainer/list_manager.rb
109
+ - lib/kampainer/request_body.rb
110
+ - lib/kampainer/response.rb
111
+ - lib/kampainer/schema_object.rb
112
+ - lib/kampainer/session.rb
113
+ - lib/kampainer/version.rb
114
+ homepage: https://github.com/varyonic/kampainer
115
+ licenses:
116
+ - MIT
117
+ metadata: {}
118
+ post_install_message:
119
+ rdoc_options: []
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubyforge_project:
134
+ rubygems_version: 2.7.6
135
+ signing_key:
136
+ specification_version: 4
137
+ summary: Unofficial Ruby wrapper for the Campaigner Elements API.
138
+ test_files: []