mrkt 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3002468b0ce903416cfff2fa51ef03940cf35252
4
+ data.tar.gz: 410bed015b4ecc50ef564c63a72aafdb528fff68
5
+ SHA512:
6
+ metadata.gz: ccfddbff9633674fd87973063e3cff57fda750cb28d2087b334c2d17a536f9fb73032cd69edcf3f0be6e0fbb264cf4e2f8376b6f6cfa2fc2f2ad0d699f97816c
7
+ data.tar.gz: d4ff3f1a12d64609f3dab730c30f7bfc8b6e6767e6e527524c0cfc6441033c2c3ae5bee757182c92d26f5d0fb56b9f25ba865b7a320c4c63ebed6560a41e72d4
@@ -0,0 +1,9 @@
1
+ /.rspec-local
2
+ /.bundle/
3
+ /.yardoc
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.5
5
+ - 2.2.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mkto_rest.gemspec
4
+ gemspec
@@ -0,0 +1,68 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ mrkt (0.4.0)
5
+ faraday_middleware (~> 0.9.1)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ addressable (2.3.8)
11
+ byebug (4.0.5)
12
+ columnize (= 0.9.0)
13
+ coderay (1.1.0)
14
+ columnize (0.9.0)
15
+ crack (0.4.2)
16
+ safe_yaml (~> 1.0.0)
17
+ diff-lcs (1.2.5)
18
+ docile (1.1.5)
19
+ faraday (0.9.1)
20
+ multipart-post (>= 1.2, < 3)
21
+ faraday_middleware (0.9.1)
22
+ faraday (>= 0.7.4, < 0.10)
23
+ json (1.8.2)
24
+ method_source (0.8.2)
25
+ multipart-post (2.0.0)
26
+ pry (0.10.1)
27
+ coderay (~> 1.1.0)
28
+ method_source (~> 0.8.1)
29
+ slop (~> 3.4)
30
+ pry-byebug (3.1.0)
31
+ byebug (~> 4.0)
32
+ pry (~> 0.10)
33
+ rake (10.1.1)
34
+ rspec (3.2.0)
35
+ rspec-core (~> 3.2.0)
36
+ rspec-expectations (~> 3.2.0)
37
+ rspec-mocks (~> 3.2.0)
38
+ rspec-core (3.2.2)
39
+ rspec-support (~> 3.2.0)
40
+ rspec-expectations (3.2.0)
41
+ diff-lcs (>= 1.2.0, < 2.0)
42
+ rspec-support (~> 3.2.0)
43
+ rspec-mocks (3.2.1)
44
+ diff-lcs (>= 1.2.0, < 2.0)
45
+ rspec-support (~> 3.2.0)
46
+ rspec-support (3.2.2)
47
+ safe_yaml (1.0.4)
48
+ simplecov (0.10.0)
49
+ docile (~> 1.1.0)
50
+ json (~> 1.8)
51
+ simplecov-html (~> 0.10.0)
52
+ simplecov-html (0.10.0)
53
+ slop (3.6.0)
54
+ webmock (1.21.0)
55
+ addressable (>= 2.3.6)
56
+ crack (>= 0.3.2)
57
+
58
+ PLATFORMS
59
+ ruby
60
+
61
+ DEPENDENCIES
62
+ bundler (~> 1.3)
63
+ mrkt!
64
+ pry-byebug (~> 3.1.0)
65
+ rake (~> 10.0)
66
+ rspec (~> 3.2)
67
+ simplecov (~> 0.10.0)
68
+ webmock (~> 1.21.0)
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 jalemieux@gmail.com
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,87 @@
1
+ # Mrkt
2
+
3
+ [![Build Status](https://secure.travis-ci.org/raszi/mrkt.png)](http://travis-ci.org/raszi/mrkt)
4
+ [![Gem Version](https://badge.fury.io/rb/mrkt.png)](http://badge.fury.io/rb/mrkt)
5
+
6
+ This gem provides some level of abstraction to Marketo REST APIs. Please note that this gem is alpha quality.
7
+
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'mrkt'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install mrkt
24
+
25
+
26
+ ## Prerequisites
27
+
28
+ Get the following from your Marketo admin:
29
+
30
+ * hostname, i.e. `'123-abc-123.mktorest.com'`
31
+ * client id, e.g. `'4567e1cdf-0fae-4685-a914-5be45043f2d8'`
32
+ * client secret, e.g. `'7Gn0tuiHZiDHnzeu9P14uDQcSx9xIPPt'`
33
+
34
+
35
+ ## Usage
36
+
37
+ ### Create a client and authenticate
38
+
39
+ ```ruby
40
+ client = Mrkt::Client.new(
41
+ host: '123-abc-123.mktorest.com',
42
+ client_id: '4567e1cdf-0fae-4685-a914-5be45043f2d8',
43
+ client_secret: '7Gn0tuiHZiDHnzeu9P14uDQcSx9xIPPt')
44
+ ```
45
+
46
+ If you need verbosity during troubleshooting, set the client to debug mode
47
+
48
+ ```ruby
49
+ client.debug = true
50
+ ```
51
+
52
+ ### Get leads matching an email, print their id and email
53
+
54
+ ```ruby
55
+ response = client.get_leads :email, 'sammy@acme.com'
56
+ response.body[:result].each do |result|
57
+ p "id: #{result[:id]}, email: #{result[:email]}"
58
+ end
59
+ ```
60
+
61
+ ### Create/Update leads
62
+
63
+ ```ruby
64
+ response = client.createupdate_leads([ email: 'sample@example.com', firstName: 'John' ], lookup_field: :email)
65
+ response.body[:result].each do |result|
66
+ p "id: #{result[:id]}, email: #{result[:email]}"
67
+ end
68
+ ```
69
+
70
+
71
+ ## Run Tests
72
+
73
+ $ bundle exec rspec
74
+
75
+
76
+ ## Examples
77
+
78
+ Examples are in the `spec/` directory.
79
+
80
+
81
+ ## Contributing
82
+
83
+ 1. Fork it ( https://github.com/raszi/mrkt/fork )
84
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
85
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
86
+ 4. Push to the branch (`git push origin my-new-feature`)
87
+ 5. Create a new Pull Request
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |s|
5
+ s.rspec_opts = '-f d -c'
6
+ s.pattern = 'spec/**/*_spec.rb'
7
+ end
8
+
9
+ task default: :spec
@@ -0,0 +1,38 @@
1
+ require 'mrkt/version'
2
+ require 'mrkt/errors'
3
+
4
+ require 'mrkt/concerns/connection'
5
+ require 'mrkt/concerns/authentication'
6
+ require 'mrkt/concerns/crud_leads'
7
+ require 'mrkt/concerns/import_leads'
8
+
9
+ module Mrkt
10
+ class Client
11
+ include Connection
12
+ include Authentication
13
+ include CrudLeads
14
+ include ImportLeads
15
+
16
+ attr_accessor :debug
17
+
18
+ def initialize(options = {})
19
+ @host = options.fetch(:host)
20
+
21
+ @client_id = options.fetch(:client_id)
22
+ @client_secret = options.fetch(:client_secret)
23
+
24
+ @options = options
25
+ end
26
+
27
+ %i(get post delete).each do |http_method|
28
+ define_method(http_method) do |path, payload = {}, &block|
29
+ authenticate!
30
+
31
+ connection.send(http_method, path, payload) do |req|
32
+ add_authorization(req)
33
+ block.call(req) unless block.nil?
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,37 @@
1
+ module Mrkt
2
+ module Authentication
3
+ def authenticate!
4
+ authenticate unless authenticated?
5
+ fail Mrkt::Errors::AuthorizationError, 'Client not authenticated' unless authenticated?
6
+ end
7
+
8
+ def authenticated?
9
+ @token && valid_token?
10
+ end
11
+
12
+ def valid_token?
13
+ @valid_until && Time.now < @valid_until
14
+ end
15
+
16
+ def authenticate
17
+ params = {
18
+ grant_type: 'client_credentials',
19
+ client_id: @client_id,
20
+ client_secret: @client_secret
21
+ }
22
+
23
+ connection.get('/identity/oauth/token', params).tap do |response|
24
+ data = response.body
25
+
26
+ @token = data.fetch(:access_token)
27
+ @token_type = data.fetch(:token_type)
28
+ @valid_until = Time.now + data.fetch(:expires_in)
29
+ @scope = data.fetch(:scope)
30
+ end
31
+ end
32
+
33
+ def add_authorization(req)
34
+ req.headers[:authorization] = "Bearer #{@token}"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,24 @@
1
+ require 'mrkt/faraday_middleware'
2
+
3
+ module Mrkt
4
+ module Connection
5
+ def connection
6
+ @connection ||= init_connection
7
+ end
8
+
9
+ def init_connection
10
+ Faraday.new(url: "https://#{@host}") do |conn|
11
+ conn.request :multipart
12
+ conn.request :url_encoded
13
+
14
+ conn.response :logger if @debug
15
+ conn.response :mkto, content_type: /\bjson$/
16
+
17
+ conn.options.timeout = @options[:read_timeout] if @options.key?(:read_timeout)
18
+ conn.options.open_timeout = @options[:open_timeout] if @options.key?(:open_timeout)
19
+
20
+ conn.adapter Faraday.default_adapter
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,48 @@
1
+ module Mrkt
2
+ module CrudLeads
3
+ def get_leads(filter_type, filter_values, fields: nil, batch_size: nil, next_page_token: nil)
4
+ params = {
5
+ filterType: filter_type,
6
+ filterValues: filter_values.join(',')
7
+ }
8
+ params[:fields] = fields if fields
9
+ params[:batchSize] = batch_size if batch_size
10
+ params[:nextPageToken] = next_page_token if next_page_token
11
+
12
+ get('/rest/v1/leads.json', params)
13
+ end
14
+
15
+ def createupdate_leads(leads, action: 'createOrUpdate', lookup_field: nil, partition_name: nil)
16
+ post('/rest/v1/leads.json') do |req|
17
+ params = {
18
+ action: action,
19
+ input: leads
20
+ }
21
+ params[:lookupField] = lookup_field if lookup_field
22
+ params[:partitionName] = partition_name if partition_name
23
+
24
+ json_payload(req, params)
25
+ end
26
+ end
27
+
28
+ def delete_leads(leads)
29
+ delete('/rest/v1/leads.json') do |req|
30
+ json_payload(req, input: leads.map { |lead_id| { id: lead_id } })
31
+ end
32
+ end
33
+
34
+ def json_payload(req, payload)
35
+ req.headers[:content_type] = 'application/json'
36
+ req.body = JSON.generate(payload)
37
+ end
38
+
39
+ def associate_lead(id, cookie)
40
+ params = Faraday::Utils::ParamsHash.new
41
+ params[:cookie] = cookie
42
+
43
+ post("/rest/v1/leads/#{id}/associate.json?#{params.to_query}") do |req|
44
+ json_payload(req, {})
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,27 @@
1
+ module Mrkt
2
+ module ImportLeads
3
+ def import_lead(file, format = 'csv', lookup_field = nil, list_id: nil, partition_name: nil)
4
+ params = {
5
+ format: format,
6
+ file: Faraday::UploadIO.new(file, 'text/csv')
7
+ }
8
+ params[:lookupField] = lookup_field if lookup_field
9
+ params[:listId] = list_id if list_id
10
+ params[:partitionName] = partition_name if partition_name
11
+
12
+ post('/bulk/v1/leads.json', params)
13
+ end
14
+
15
+ def import_lead_status(id)
16
+ get("/bulk/v1/leads/batch/#{id}.json")
17
+ end
18
+
19
+ def import_lead_failures(id)
20
+ get("/bulk/v1/leads/batch/#{id}/failures.json")
21
+ end
22
+
23
+ def import_lead_warnings(id)
24
+ get("/bulk/v1/leads/batch/#{id}/warnings.json")
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,52 @@
1
+ class Mrkt::Errors
2
+ class Error < StandardError
3
+ end
4
+
5
+ def self.create_class
6
+ Class.new(Error)
7
+ end
8
+
9
+ Unknown = create_class
10
+ EmptyResponse = create_class
11
+ AuthorizationError = create_class
12
+
13
+ RESPONSE_CODE_TO_ERROR = {
14
+ 413 => 'RequestEntityTooLarge',
15
+ 600 => 'EmptyAccessToken',
16
+ 601 => 'AccessTokenInvalid',
17
+ 602 => 'AccessTokenExpired',
18
+ 603 => 'AccessDenied',
19
+ 604 => 'RequestTimedOut',
20
+ 605 => 'HTTPMethodNotSupported',
21
+ 606 => 'MaxRateLimit',
22
+ 607 => 'DailyQuotaReached',
23
+ 608 => 'APITemporarilyUnavailable',
24
+ 609 => 'InvalidJSON',
25
+ 610 => 'RequestedResourceNotFound',
26
+ 611 => 'System',
27
+ 612 => 'InvalidContentType',
28
+ 703 => 'DisabledFeature',
29
+ 1001 => 'TypeMismatch',
30
+ 1002 => 'MissingParamater',
31
+ 1003 => 'UnspecifiedAction',
32
+ 1004 => 'LeadNotFound',
33
+ 1005 => 'LeadAlreadyExists',
34
+ 1006 => 'FieldNotFound',
35
+ 1007 => 'MultipleLeadsMatching',
36
+ 1008 => 'PartitionAccessDenied',
37
+ 1009 => 'PartitionNameUnspecified',
38
+ 1010 => 'PartitionUpdateDenied',
39
+ 1011 => 'FieldNotSupported',
40
+ 1012 => 'InvalidCookieValue',
41
+ 1013 => 'ObjectNotFound',
42
+ 1014 => 'FailedToCreateObject'
43
+ }
44
+
45
+ RESPONSE_CODE_TO_ERROR.values.each do |class_name|
46
+ const_set(class_name, create_class)
47
+ end
48
+
49
+ def self.find_by_response_code(response_code)
50
+ const_get(RESPONSE_CODE_TO_ERROR.fetch(response_code, 'Error'))
51
+ end
52
+ end
@@ -0,0 +1,11 @@
1
+ require 'faraday'
2
+
3
+ module Mrkt
4
+ module FaradayMiddleware
5
+ autoload :Response, 'mrkt/faraday_middleware/response'
6
+ end
7
+
8
+ if Faraday::Middleware.respond_to? :register_middleware
9
+ Faraday::Response.register_middleware mkto: -> { Mrkt::FaradayMiddleware::Response }
10
+ end
11
+ end
@@ -0,0 +1,58 @@
1
+ require 'faraday_middleware'
2
+
3
+ module Mrkt
4
+ module FaradayMiddleware
5
+ class Request < Faraday::Middleware
6
+ def initialize(app, client_id, client_secret)
7
+ super(app)
8
+
9
+ @client_id = client_id
10
+ @client_secret = client_secret
11
+ end
12
+
13
+ def call(env)
14
+ authenticate(env) unless authenticated? || env[:authentication]
15
+
16
+ env.request_headers[:authorization] = "Bearer #{@token}"
17
+ @app.call(env)
18
+ end
19
+
20
+ def authenticated?
21
+ @token && token_valid?
22
+ end
23
+
24
+ def token_valid?
25
+ @valid_until && Time.now < @valid_until
26
+ end
27
+
28
+ def authenticate(orig_env)
29
+ env = Faraday::Env.from(orig_env)
30
+
31
+ body = env[:body]
32
+ params = Faraday::Utils::ParamsHash.new
33
+ params.update(
34
+ grant_type: 'client_credentials',
35
+ client_id: @client_id,
36
+ client_secret: @client_secret
37
+ )
38
+ env[:authentication] = true
39
+ env[:method] = :get
40
+
41
+ env[:url] = env[:url].dup
42
+ env[:url].path = '/identity/oauth/token'
43
+ env[:url].query = params.to_query
44
+
45
+ orig_env[:body] = body
46
+
47
+ response = @app.call(env)
48
+ data = response.body
49
+
50
+ @token = data[:access_token]
51
+ @token_type = data[:token_type]
52
+ @expires_in = data[:expires_in]
53
+ @valid_until = Time.now + data[:expires_in]
54
+ @scope = data[:scope]
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,29 @@
1
+ require 'faraday_middleware'
2
+
3
+ module Mrkt
4
+ module FaradayMiddleware
5
+ class Response < ::FaradayMiddleware::ParseJson
6
+ define_parser do |body|
7
+ JSON.parse(body, symbolize_names: true) unless body.strip.empty?
8
+ end
9
+
10
+ def process_response(env)
11
+ super
12
+
13
+ data = env[:body]
14
+
15
+ fail Mrkt::Errors::EmptyResponse if data.nil?
16
+ fail Mrkt::Errors::Error, data[:error_description] if data.key?(:error)
17
+
18
+ handle_errors!(data[:errors]) unless data.fetch(:success, true)
19
+ end
20
+
21
+ def handle_errors!(errors)
22
+ error = errors.first
23
+
24
+ fail Mrkt::Errors::Unknown if error.nil?
25
+ fail Mrkt::Errors.find_by_response_code(error[:code].to_i), error[:message]
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module Mrkt
2
+ VERSION = '0.4.0'
3
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mrkt/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'mrkt'
8
+ spec.version = Mrkt::VERSION
9
+ spec.authors = ['KARASZI István', 'Jacques Lemieux']
10
+ spec.email = ['github@spam.raszi.hu', 'jalemieux@gmail.com']
11
+ spec.description = 'Marketo REST API Facade'
12
+ spec.summary = ''
13
+ spec.homepage = ''
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = %w(lib)
20
+
21
+ spec.required_ruby_version = '~> 2.0'
22
+
23
+ spec.add_dependency 'faraday_middleware', '~> 0.9.1'
24
+
25
+ spec.add_development_dependency 'bundler', '~> 1.3'
26
+ spec.add_development_dependency 'rake', '~> 10.0'
27
+ spec.add_development_dependency 'rspec', '~> 3.2'
28
+ spec.add_development_dependency 'webmock', '~> 1.21.0'
29
+ spec.add_development_dependency 'simplecov', '~> 0.10.0'
30
+ spec.add_development_dependency 'pry-byebug', '~> 3.1.0'
31
+ end
@@ -0,0 +1,38 @@
1
+ describe Mrkt::Authentication do
2
+ include_context 'initialized client'
3
+
4
+ describe '#authenticate' do
5
+ subject { client.authenticate }
6
+
7
+ context 'on a successful response' do
8
+ it { is_expected.to be_success }
9
+ end
10
+
11
+ context 'when the token is invalid' do
12
+ let(:authentication_stub) do
13
+ {
14
+ error: 'invalid_client',
15
+ error_description: 'Bad client credentials'
16
+ }
17
+ end
18
+
19
+ it 'should raise an Error' do
20
+ expect { subject }.to raise_error(Mrkt::Errors::Error, 'Bad client credentials')
21
+ end
22
+ end
23
+ end
24
+
25
+ describe '#authenticated?' do
26
+ subject { client.authenticated? }
27
+
28
+ context 'before authentication' do
29
+ it { is_expected.to be_falsey }
30
+ end
31
+
32
+ context 'after authentication' do
33
+ before { client.authenticate }
34
+
35
+ it { is_expected.to be_truthy }
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,151 @@
1
+ describe Mrkt::CrudLeads do
2
+ include_context 'initialized client'
3
+
4
+ describe '#get_leads' do
5
+ let(:filter_type) { 'email' }
6
+ let(:filter_values) { %w(user@example.com) }
7
+ let(:response_stub) do
8
+ {
9
+ requestId: 'c245#14cd6830ae2',
10
+ result: [
11
+ {
12
+ id: 1,
13
+ firstName: 'John',
14
+ lastName: 'Snow',
15
+ email: 'sample@exmaple.com',
16
+ updatedAt: '2015-04-20 05:46:13',
17
+ createdAt: '2015-04-20 05:46:13'
18
+ }
19
+ ],
20
+ success: true
21
+ }
22
+ end
23
+ subject { client.get_leads(filter_type, filter_values) }
24
+
25
+ before do
26
+ stub_request(:get, "https://#{host}/rest/v1/leads.json")
27
+ .with(query: { filterType: filter_type, filterValues: filter_values.join(',') })
28
+ .to_return(json_stub(response_stub))
29
+ end
30
+
31
+ it { is_expected.to be_success }
32
+ end
33
+
34
+ describe '#createupdate_leads' do
35
+ let(:leads) do
36
+ [
37
+ firstName: 'John',
38
+ lastName: 'Snow',
39
+ email: 'sample@example.com'
40
+ ]
41
+ end
42
+ let(:request_body) do
43
+ {
44
+ action: 'createOrUpdate',
45
+ input: [
46
+ {
47
+ firstName: 'John',
48
+ lastName: 'Snow',
49
+ email: 'sample@example.com'
50
+ }
51
+ ],
52
+ lookupField: 'email'
53
+ }
54
+ end
55
+ let(:response_stub) do
56
+ {
57
+ requestId: 'c245#14cd6830ae2',
58
+ success: true,
59
+ result: [
60
+ {
61
+ id: 1,
62
+ status: 'created'
63
+ }
64
+ ]
65
+ }
66
+ end
67
+ subject { client.createupdate_leads(leads, lookup_field: :email) }
68
+
69
+ before do
70
+ stub_request(:post, "https://#{host}/rest/v1/leads.json")
71
+ .with(json_stub(request_body))
72
+ .to_return(json_stub(response_stub))
73
+ end
74
+
75
+ it { is_expected.to be_success }
76
+ end
77
+
78
+ describe '#delete_leads' do
79
+ let(:leads) { [1] }
80
+ let(:request_body) do
81
+ {
82
+ input: [
83
+ { id: 1 }
84
+ ]
85
+ }
86
+ end
87
+ let(:response_stub) do
88
+ {
89
+ requestId: 'c245#14cd6830ae2',
90
+ result: [
91
+ { id: 4098, status: 'deleted' }
92
+ ],
93
+ success: true
94
+ }
95
+ end
96
+ subject { client.delete_leads(leads) }
97
+
98
+ before do
99
+ stub_request(:delete, "https://#{host}/rest/v1/leads.json")
100
+ .with(json_stub(request_body))
101
+ .to_return(json_stub(response_stub))
102
+ end
103
+
104
+ it { is_expected.to be_success }
105
+ end
106
+
107
+ describe '#associate_lead' do
108
+ let(:id) { 1 }
109
+ let(:cookie) { 'id:561-HYG-937&token:_mch-marketo.com-1427205775289-40768' }
110
+ let(:request_stub) { {} }
111
+
112
+ subject { client.associate_lead(id, cookie) }
113
+
114
+ before do
115
+ stub_request(:post, "https://#{host}/rest/v1/leads/#{id}/associate.json?#{URI.encode_www_form(cookie: cookie)}")
116
+ .with(json_stub(request_stub))
117
+ .to_return(json_stub(response_stub))
118
+ end
119
+
120
+ context 'with an existing lead id' do
121
+ let(:response_stub) do
122
+ {
123
+ requestId: 'c245#14cd6830ae2',
124
+ result: [],
125
+ success: true
126
+ }
127
+ end
128
+
129
+ it { is_expected.to be_success }
130
+ end
131
+
132
+ context 'with a non-existing lead id' do
133
+ let(:response_stub) do
134
+ {
135
+ requestId: 'c245#14cd6830ae2',
136
+ success: false,
137
+ errors: [
138
+ {
139
+ code: "1004",
140
+ message: "Lead '1' not found"
141
+ }
142
+ ]
143
+ }
144
+ end
145
+
146
+ it 'should raise an Error' do
147
+ expect { subject }.to raise_error(Mrkt::Errors::LeadNotFound)
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,98 @@
1
+ require 'csv'
2
+ require 'tempfile'
3
+
4
+ describe Mrkt::ImportLeads do
5
+ include_context 'initialized client'
6
+
7
+ describe '#import_lead' do
8
+ let(:tempfile) { Tempfile.new(['import-leads', 'csv']) }
9
+ let(:response_stub) do
10
+ {
11
+ requestId: 'c245#14cd6830ae2',
12
+ success: true,
13
+ result: [
14
+ {
15
+ batchId: 1,
16
+ status: 'Importing'
17
+ }
18
+ ]
19
+ }
20
+ end
21
+ subject { client.import_lead(tempfile) }
22
+
23
+ before do
24
+ CSV.open(tempfile, 'wb') do |csv|
25
+ csv << %w(email firstName lastName)
26
+ csv << %w(sample@example.com John Snow)
27
+ end
28
+
29
+ stub_request(:post, "https://#{host}/bulk/v1/leads.json")
30
+ .with(headers: { content_type: %r{multipart/form-data; boundary=\S+} })
31
+ .to_return(json_stub(response_stub))
32
+ end
33
+
34
+ after do
35
+ tempfile.unlink
36
+ end
37
+
38
+ it { is_expected.to be_success }
39
+ end
40
+
41
+ describe '#import_lead_status' do
42
+ let(:id) { 1 }
43
+ let(:response_stub) do
44
+ {
45
+ requestId: 'c245#14cd6830ae2',
46
+ result: [
47
+ {
48
+ batchId: id,
49
+ status: 'Importing',
50
+ numOfLeadsProcessed: 4,
51
+ numOfRowsFailed: 1,
52
+ numOfRowsWithWarning: 0,
53
+ message: 'Import completed with errors, 4 records imported (4 members), 1 failed'
54
+ }
55
+ ],
56
+ success: true
57
+ }
58
+ end
59
+ subject { client.import_lead_status(1) }
60
+
61
+ before do
62
+ stub_request(:get, "https://#{host}/bulk/v1/leads/batch/#{id}.json")
63
+ .to_return(json_stub(response_stub))
64
+ end
65
+
66
+ it { is_expected.to be_success }
67
+ end
68
+
69
+ describe '#import_lead_failures' do
70
+ let(:id) { 1 }
71
+ let(:respose_stub) do
72
+
73
+ end
74
+ subject { client.import_lead_failures(1) }
75
+
76
+ before do
77
+ stub_request(:get, "https://#{host}/bulk/v1/leads/batch/#{id}/failures.json")
78
+ .to_return(headers: { content_length: 0 })
79
+ end
80
+
81
+ it { is_expected.to be_success }
82
+ end
83
+
84
+ describe '#import_lead_warnings' do
85
+ let(:id) { 1 }
86
+ let(:respose_stub) do
87
+
88
+ end
89
+ subject { client.import_lead_warnings(1) }
90
+
91
+ before do
92
+ stub_request(:get, "https://#{host}/bulk/v1/leads/batch/#{id}/warnings.json")
93
+ .to_return(headers: { content_length: 0 })
94
+ end
95
+
96
+ it { is_expected.to be_success }
97
+ end
98
+ end
@@ -0,0 +1,21 @@
1
+ describe Mrkt::Errors do
2
+ describe '.find_by_response_code' do
3
+ subject { described_class.find_by_response_code(code) }
4
+
5
+ context 'when the code is' do
6
+ context 'known' do
7
+ let(:code) { 413 }
8
+
9
+ it 'should return the mapped error class' do
10
+ is_expected.to eq(Mrkt::Errors::RequestEntityTooLarge)
11
+ end
12
+ end
13
+
14
+ context 'unknown' do
15
+ let(:code) { 7331 }
16
+
17
+ it { is_expected.to eq(Mrkt::Errors::Error) }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ describe Mrkt do
2
+ include_context 'initialized client'
3
+
4
+ it { is_expected.to respond_to(:get, :post, :delete) }
5
+ end
@@ -0,0 +1,31 @@
1
+ if ENV['COVERAGE']
2
+ require 'simplecov'
3
+ SimpleCov.start
4
+ end
5
+
6
+ require 'bundler/setup'
7
+ Bundler.setup
8
+
9
+ require 'mrkt'
10
+
11
+ Dir[File.expand_path('spec/support/**/*.rb')].each { |f| require f }
12
+
13
+ RSpec.configure do |config|
14
+ config.default_formatter = :doc if config.files_to_run.one?
15
+
16
+ config.expect_with :rspec do |expectations|
17
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
18
+ end
19
+
20
+ config.order = :random
21
+ Kernel.srand config.seed
22
+
23
+ config.expect_with :rspec do |expectations|
24
+ expectations.syntax = :expect
25
+ end
26
+
27
+ config.mock_with :rspec do |mocks|
28
+ mocks.syntax = :expect
29
+ mocks.verify_partial_doubles = true
30
+ end
31
+ end
@@ -0,0 +1,19 @@
1
+ require 'securerandom'
2
+ require 'json'
3
+
4
+ shared_context 'initialized client' do
5
+ let(:host) { '0-KBZ-0.mktorest.com' }
6
+ let(:client_id) { SecureRandom.uuid }
7
+ let(:client_secret) { SecureRandom.hex }
8
+ let(:authentication_stub) do
9
+ { access_token: SecureRandom.uuid, token_type: 'bearer', expires_in: 2241, scope: 'RestClient' }
10
+ end
11
+
12
+ subject(:client) { Mrkt::Client.new(host: host, client_id: client_id, client_secret: client_secret) }
13
+
14
+ before do
15
+ stub_request(:get, "https://#{host}/identity/oauth/token")
16
+ .with(query: { client_id: client_id, client_secret: client_secret, grant_type: 'client_credentials' })
17
+ .to_return(json_stub(authentication_stub))
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ require 'webmock/rspec'
2
+
3
+ RSpec.configure do
4
+ def json_stub(content_stub)
5
+ {
6
+ headers: { content_type: 'application/json' },
7
+ body: JSON.generate(content_stub)
8
+ }
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,179 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mrkt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - KARASZI István
8
+ - Jacques Lemieux
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-04-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: faraday_middleware
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 0.9.1
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 0.9.1
28
+ - !ruby/object:Gem::Dependency
29
+ name: bundler
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.3'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.3'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '10.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '10.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rspec
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '3.2'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '3.2'
70
+ - !ruby/object:Gem::Dependency
71
+ name: webmock
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: 1.21.0
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: 1.21.0
84
+ - !ruby/object:Gem::Dependency
85
+ name: simplecov
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: 0.10.0
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: 0.10.0
98
+ - !ruby/object:Gem::Dependency
99
+ name: pry-byebug
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: 3.1.0
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: 3.1.0
112
+ description: Marketo REST API Facade
113
+ email:
114
+ - github@spam.raszi.hu
115
+ - jalemieux@gmail.com
116
+ executables: []
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - ".gitignore"
121
+ - ".rspec"
122
+ - ".travis.yml"
123
+ - Gemfile
124
+ - Gemfile.lock
125
+ - LICENSE.txt
126
+ - README.md
127
+ - Rakefile
128
+ - lib/mrkt.rb
129
+ - lib/mrkt/concerns/authentication.rb
130
+ - lib/mrkt/concerns/connection.rb
131
+ - lib/mrkt/concerns/crud_leads.rb
132
+ - lib/mrkt/concerns/import_leads.rb
133
+ - lib/mrkt/errors.rb
134
+ - lib/mrkt/faraday_middleware.rb
135
+ - lib/mrkt/faraday_middleware/request.rb
136
+ - lib/mrkt/faraday_middleware/response.rb
137
+ - lib/mrkt/version.rb
138
+ - mrkt.gemspec
139
+ - spec/concerns/authentication_spec.rb
140
+ - spec/concerns/crud_leads_spec.rb
141
+ - spec/concerns/import_leads_spec.rb
142
+ - spec/errors_spec.rb
143
+ - spec/mkto_rest_spec.rb
144
+ - spec/spec_helper.rb
145
+ - spec/support/initialized_client.rb
146
+ - spec/support/webmock.rb
147
+ homepage: ''
148
+ licenses:
149
+ - MIT
150
+ metadata: {}
151
+ post_install_message:
152
+ rdoc_options: []
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '2.0'
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ requirements: []
166
+ rubyforge_project:
167
+ rubygems_version: 2.4.3
168
+ signing_key:
169
+ specification_version: 4
170
+ summary: ''
171
+ test_files:
172
+ - spec/concerns/authentication_spec.rb
173
+ - spec/concerns/crud_leads_spec.rb
174
+ - spec/concerns/import_leads_spec.rb
175
+ - spec/errors_spec.rb
176
+ - spec/mkto_rest_spec.rb
177
+ - spec/spec_helper.rb
178
+ - spec/support/initialized_client.rb
179
+ - spec/support/webmock.rb