dimelo_ccp_api 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.ruby-version +1 -0
- data/.travis.yml +15 -0
- data/CHANGELOG.md +29 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +59 -0
- data/Rakefile +23 -0
- data/VERSION +1 -0
- data/dimelo_ccp_api.gemspec +26 -0
- data/examples/dimelo_api_test +34 -0
- data/gemfiles/Gemfile.activesupport-3.2.x +5 -0
- data/gemfiles/Gemfile.activesupport-4.0.x +5 -0
- data/gemfiles/Gemfile.activesupport-4.1.x +5 -0
- data/gemfiles/Gemfile.activesupport-head +5 -0
- data/lib/dimelo/ccp/api/basic_object.rb +17 -0
- data/lib/dimelo/ccp/api/client.rb +46 -0
- data/lib/dimelo/ccp/api/common/openable.rb +23 -0
- data/lib/dimelo/ccp/api/common/publishable.rb +23 -0
- data/lib/dimelo/ccp/api/common/starrable.rb +23 -0
- data/lib/dimelo/ccp/api/connection.rb +86 -0
- data/lib/dimelo/ccp/api/error.rb +71 -0
- data/lib/dimelo/ccp/api/lazzy_collection.rb +114 -0
- data/lib/dimelo/ccp/api/model/answer.rb +39 -0
- data/lib/dimelo/ccp/api/model/attachment.rb +44 -0
- data/lib/dimelo/ccp/api/model/category.rb +12 -0
- data/lib/dimelo/ccp/api/model/category_group.rb +12 -0
- data/lib/dimelo/ccp/api/model/feedback.rb +20 -0
- data/lib/dimelo/ccp/api/model/feedback_comment.rb +31 -0
- data/lib/dimelo/ccp/api/model/membership.rb +12 -0
- data/lib/dimelo/ccp/api/model/private_message.rb +8 -0
- data/lib/dimelo/ccp/api/model/question.rb +20 -0
- data/lib/dimelo/ccp/api/model/role.rb +8 -0
- data/lib/dimelo/ccp/api/model/user.rb +37 -0
- data/lib/dimelo/ccp/api/model/webhook.rb +9 -0
- data/lib/dimelo/ccp/api/model.rb +209 -0
- data/lib/dimelo/ccp/api/version.rb +7 -0
- data/lib/dimelo/ccp/api.rb +62 -0
- data/lib/dimelo_ccp_api.rb +1 -0
- data/spec/examples/openable_examples.rb +37 -0
- data/spec/examples/starrable_example.rb +57 -0
- data/spec/fixtures/files/logo.jpg +0 -0
- data/spec/lib/dimelo/ccp/api/client_spec.rb +111 -0
- data/spec/lib/dimelo/ccp/api/connection_spec.rb +129 -0
- data/spec/lib/dimelo/ccp/api/model/attachment_spec.rb +26 -0
- data/spec/lib/dimelo/ccp/api/model/feedback_spec.rb +10 -0
- data/spec/lib/dimelo/ccp/api/model/question_spec.rb +10 -0
- data/spec/lib/dimelo/ccp/api/model_spec.rb +162 -0
- data/spec/spec_helper.rb +7 -0
- metadata +174 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4962e1ddaa81b7ef970c119cbd677c7fc3a59fc1
|
4
|
+
data.tar.gz: 34a481ab52fcea68f15dce8208b0c77b24ae51f2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0ed9b3e676d8ad3f03370e33099b8d93e12f04f6768e49fb067db1689073da37db7deed45f0a9e9fd7af7e3fc4d924bf94baefb1dc479c2349b2a3b337efa233
|
7
|
+
data.tar.gz: c6db38d30585780a991826a384532b8f89a049ed4c8a5f56cfc507b8034c63f1a61df1aebbc8782fe9efb92a3c0cc92bf4a4366a4931924614eb1bdb8d7698ad
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.1
|
data/.travis.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.2
|
4
|
+
- 2.1
|
5
|
+
- 2.0
|
6
|
+
- ruby-head
|
7
|
+
- jruby-head
|
8
|
+
gemfile:
|
9
|
+
- gemfiles/Gemfile.activesupport-3.2.x
|
10
|
+
- gemfiles/Gemfile.activesupport-4.0.x
|
11
|
+
- gemfiles/Gemfile.activesupport-4.1.x
|
12
|
+
- gemfiles/Gemfile.activesupport-edge
|
13
|
+
matrix:
|
14
|
+
allow_failures:
|
15
|
+
- gemfile: gemfiles/Gemfile.activesupport-edge
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# 0.3.3
|
2
|
+
|
3
|
+
Remove warning when attribute is present in api but not defined in model
|
4
|
+
|
5
|
+
# 0.3.2
|
6
|
+
|
7
|
+
Implement webhook as a standard resource instead of crappy built-in stuff
|
8
|
+
|
9
|
+
# 0.3.1
|
10
|
+
|
11
|
+
Add a feature to push a webhook API configuration through API
|
12
|
+
|
13
|
+
# 0.2.2
|
14
|
+
|
15
|
+
Fix handling of validation errors, they now return status 422 and were incorrectely raise.
|
16
|
+
This fix restores the previous behaviour: returning a Dimelo record with AR errors.
|
17
|
+
|
18
|
+
# 0.2.1
|
19
|
+
|
20
|
+
Attempt to fix segfault in model.rb:116 (#inspect)
|
21
|
+
Fix HTTPS spec with github
|
22
|
+
|
23
|
+
# 0.2.0
|
24
|
+
|
25
|
+
Added support for Role + upgrade to Rspec 3
|
26
|
+
|
27
|
+
# 0.0.4
|
28
|
+
|
29
|
+
Support http_options for Client.new to be able to set dedicated timeout
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2006-2014 Dimelo SA
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Dimelo CCP API [![Build Status](https://travis-ci.org/dimelo/dimelo_ccp_api.svg?branch=master)](https://travis-ci.org/dimelo/dimelo_ccp_api) [![Code Climate](https://codeclimate.com/github/dimelo/dimelo_ccp_api.png)](https://codeclimate.com/github/dimelo/dimelo_ccp_api)
|
2
|
+
|
3
|
+
Ruby client for the Dimelo Customer Community Platform
|
4
|
+
|
5
|
+
This client support most of Dimelo CCP resources, can read and write them, paginates with cursor like interface, supports attachments and supports proper validation and error format.
|
6
|
+
|
7
|
+
This is heavily used internaly at Dimelo.
|
8
|
+
|
9
|
+
# Compatibility
|
10
|
+
|
11
|
+
Compatible and tested with:
|
12
|
+
|
13
|
+
- Ruby 2.0, 2.1, 2.2 and Jruby-head
|
14
|
+
- ActiveSupport 3.0+, 4.0.x and 4.1.x
|
15
|
+
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Gemfile:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
gem 'dimelo_ccp_api'
|
23
|
+
```
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
require 'dimelo_ccp_api'
|
29
|
+
|
30
|
+
|
31
|
+
users_client = Dimelo::CCP::API::Client.new('https://domain-test.api.users.dimelo.com/1.0', 'access_token' => ENV['DIMELO_CCP_TOKEN'])
|
32
|
+
answers_client = Dimelo::CCP::API::Client.new('https://domain-test.api.answers.dimelo.com/1.0', 'access_token' => ENV['DIMELO_CCP_TOKEN'])
|
33
|
+
feedbacks_client = Dimelo::CCP::API::Client.new('https://domain-test.api.ideas.dimelo.com/1.0', 'access_token' => ENV['DIMELO_CCP_TOKEN'])
|
34
|
+
|
35
|
+
user = Dimelo::CCP::User.find(1, users_client)
|
36
|
+
questions = user.questions(answers_client)
|
37
|
+
puts "question count: #{questions.count}"
|
38
|
+
|
39
|
+
questions.each do |question, i|
|
40
|
+
answers = question.answers
|
41
|
+
puts "#{i} of #{questions.count} => answer count: #{answers.count}"
|
42
|
+
answers.each do |answer|
|
43
|
+
answer.question_flow_state = "lol"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
feedbacks = Dimelo::CCP::Feedback.find({ :order => 'updated_at.desc' }, feedbacks_client)
|
48
|
+
puts "feedbacks count: #{feedbacks.count}"
|
49
|
+
puts "feedbacks not by anonymous and superadmin: #{feedbacks.select{|f| f.user_id.present?}.count}"
|
50
|
+
|
51
|
+
```
|
52
|
+
|
53
|
+
## Contributing
|
54
|
+
|
55
|
+
1. Fork it ( http://github.com/dimelo/dimelo_ccp_api/fork )
|
56
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
57
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
58
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
59
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "spec/rake/spectask" # RSpec 1.3
|
6
|
+
|
7
|
+
desc 'Run all specs in spec directory.'
|
8
|
+
Spec::Rake::SpecTask.new(:spec) do |task|
|
9
|
+
task.libs = ['lib', 'spec']
|
10
|
+
task.spec_files = FileList['spec/**/*_spec.rb']
|
11
|
+
end
|
12
|
+
rescue LoadError
|
13
|
+
require "rspec/core/rake_task" # RSpec 2.0
|
14
|
+
|
15
|
+
desc 'Run all specs in spec directory.'
|
16
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
17
|
+
t.rspec_opts = %w{--colour --format progress}
|
18
|
+
t.pattern = 'spec/**/*_spec.rb'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'Default: runs specs.'
|
23
|
+
task :default => :spec
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.4.1
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "dimelo/ccp/api/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "dimelo_ccp_api"
|
7
|
+
s.version = Dimelo::CCP::API::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Jean Boussier", "Renaud Morvan"]
|
10
|
+
s.email = ["jean.boussier@dimelo.com", "nel@w3fu.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Dimelo CCP v2 API client}
|
13
|
+
s.description = %q{Rest API client for Dimelo CCP v2 plateform}
|
14
|
+
|
15
|
+
s.rubyforge_project = "dimelo_ccp_api"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
s.add_dependency('activesupport', '>= 3.0.0')
|
22
|
+
s.add_dependency('activemodel', '>= 3.0.0')
|
23
|
+
s.add_dependency('faraday')
|
24
|
+
s.add_development_dependency('rake')
|
25
|
+
s.add_development_dependency('rspec', '~> 3.0')
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
|
6
|
+
require 'dimelo_api'
|
7
|
+
require 'pp'
|
8
|
+
|
9
|
+
unless ENV['DIMELO_API_KEY'].present?
|
10
|
+
puts "Don't forget to set your api key"
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
users_client = Dimelo::CCP::API::Client.new('https://domain-test.api.users.dimelo.com/1.0', 'access_token' => ENV['DIMELO_API_KEY'])
|
16
|
+
answers_client = Dimelo::CCP::API::Client.new('https://domain-test.api.answers.dimelo.com/1.0', 'access_token' => ENV['DIMELO_API_KEY'])
|
17
|
+
feedbacks_client = Dimelo::CCP::API::Client.new('https://domain-test.api.ideas.dimelo.com/1.0', 'access_token' => ENV['DIMELO_API_KEY'])
|
18
|
+
|
19
|
+
user = Dimelo::API::User.find(1, users_client)
|
20
|
+
questions = user.questions(answers_client)
|
21
|
+
puts "question count: #{questions.count}"
|
22
|
+
|
23
|
+
questions.each do |question, i|
|
24
|
+
answers = question.answers
|
25
|
+
puts "#{i} of #{questions.count} => answer count: #{answers.count}"
|
26
|
+
answers.each do |answer|
|
27
|
+
answer.question_flow_state = "lol"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
feedbacks = Dimelo::API::Feedback.find({ :order => 'updated_at.desc' }, feedbacks_client)
|
32
|
+
puts "feedbacks count: #{feedbacks.count}"
|
33
|
+
puts "feedbacks not by anonymous and superadmin: #{feedbacks.select{|f| f.user_id.present?}.count}"
|
34
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Dimelo::CCP
|
2
|
+
module API
|
3
|
+
|
4
|
+
class BasicObject
|
5
|
+
# http://ruby-doc.org/core-1.9/classes/BasicObject.html
|
6
|
+
# http://sequel.heroku.com/2010/03/31/sequelbasicobject-and-ruby-18/
|
7
|
+
KEEP_METHODS = %w(__id__ __send__ instance_eval == equal? initialize method_missing respond_to?)
|
8
|
+
|
9
|
+
def self.remove_methods!
|
10
|
+
m = (private_instance_methods + instance_methods) - KEEP_METHODS
|
11
|
+
m.each{|m| undef_method(m)}
|
12
|
+
end
|
13
|
+
remove_methods!
|
14
|
+
end if not defined?(BasicObject)
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Dimelo::CCP
|
2
|
+
module API
|
3
|
+
class Client
|
4
|
+
|
5
|
+
attr_accessor :base_uri, :default_parameters
|
6
|
+
|
7
|
+
def initialize(base_uri, options={})
|
8
|
+
@base_uri = base_uri.is_a?(URI) ? base_uri : URI.parse(base_uri)
|
9
|
+
options = options.with_indifferent_access
|
10
|
+
@http_options = options.delete(:http_options) || {}
|
11
|
+
@default_parameters = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def check
|
15
|
+
Dimelo::CCP::API.decode_json(transport(:get, 'check'))
|
16
|
+
end
|
17
|
+
|
18
|
+
def config
|
19
|
+
Dimelo::CCP::API.decode_json(transport(:get, 'config'))
|
20
|
+
end
|
21
|
+
|
22
|
+
def transport(method, path, payload={})
|
23
|
+
response = connection.perform(method, path, default_parameters.merge(payload))
|
24
|
+
|
25
|
+
if response.success? or response.status == 422
|
26
|
+
response.body
|
27
|
+
else
|
28
|
+
raise Error.from(method, path, response.status, response.body)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def request_uri(path, params)
|
35
|
+
@base_uri.dup.tap do |uri|
|
36
|
+
uri.path = File.join(uri.path, path).chomp('/')
|
37
|
+
end.request_uri
|
38
|
+
end
|
39
|
+
|
40
|
+
def connection
|
41
|
+
@connection ||= Connection.from_uri(@base_uri, @http_options)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Dimelo::CCP
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module Openable
|
5
|
+
|
6
|
+
def open
|
7
|
+
path = "#{compute_path(attributes)}/open"
|
8
|
+
response = client.transport(:put, path)
|
9
|
+
self.attributes = Dimelo::CCP::API.decode_json(response)
|
10
|
+
errors.empty?
|
11
|
+
end
|
12
|
+
|
13
|
+
def close
|
14
|
+
path = "#{compute_path(attributes)}/close"
|
15
|
+
response = client.transport(:put, path)
|
16
|
+
self.attributes = Dimelo::CCP::API.decode_json(response)
|
17
|
+
errors.empty?
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Dimelo::CCP
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module Publishable
|
5
|
+
|
6
|
+
def publish
|
7
|
+
path = "#{compute_path(attributes)}/publish"
|
8
|
+
response = client.transport(:put, path)
|
9
|
+
self.attributes = Dimelo::CCP::API.decode_json(response)
|
10
|
+
errors.empty?
|
11
|
+
end
|
12
|
+
|
13
|
+
def unpublish
|
14
|
+
path = "#{compute_path(attributes)}/unpublish"
|
15
|
+
response = client.transport(:put, path)
|
16
|
+
self.attributes = Dimelo::CCP::API.decode_json(response)
|
17
|
+
errors.empty?
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Dimelo::CCP
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module Starrable
|
5
|
+
|
6
|
+
def star! #use method name with bang to differenciate from #star attribute
|
7
|
+
path = "#{compute_path(attributes)}/star"
|
8
|
+
response = client.transport(:put, path)
|
9
|
+
self.attributes = Dimelo::CCP::API.decode_json(response)
|
10
|
+
errors.empty?
|
11
|
+
end
|
12
|
+
|
13
|
+
def unstar!
|
14
|
+
path = "#{compute_path(attributes)}/unstar"
|
15
|
+
response = client.transport(:put, path)
|
16
|
+
self.attributes = Dimelo::CCP::API.decode_json(response)
|
17
|
+
errors.empty?
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
|
3
|
+
module Dimelo::CCP
|
4
|
+
module API
|
5
|
+
class Connection
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def from_uri(uri, options = {})
|
10
|
+
options.merge!(:use_ssl => uri.scheme == 'https')
|
11
|
+
pool[uri_key(uri)] ||= new(uri.to_s, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def uri_key(uri)
|
17
|
+
"#{uri.scheme}://#{uri.host}:#{uri.port}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def pool
|
21
|
+
@pool ||= {}
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(url, options={})
|
27
|
+
@url = url
|
28
|
+
@http_options = options
|
29
|
+
initialize_client
|
30
|
+
end
|
31
|
+
|
32
|
+
def perform(method, uri, payload={})
|
33
|
+
@client.send(method, uri, payload) do |req|
|
34
|
+
req.headers[:accept] = 'application/json'
|
35
|
+
req.headers[:user_agent] = user_agent
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def timeout
|
42
|
+
@http_options[:timeout] || 10
|
43
|
+
end
|
44
|
+
|
45
|
+
def user_agent_details
|
46
|
+
strip_non_ascii(@http_options[:user_agent] || '')
|
47
|
+
end
|
48
|
+
|
49
|
+
def strip_non_ascii(setting, replacement = '')
|
50
|
+
setting.gsub(/\P{ASCII}/, replacement)
|
51
|
+
end
|
52
|
+
|
53
|
+
def user_agent
|
54
|
+
"DimeloAPI/#{Dimelo::CCP::API::VERSION} " \
|
55
|
+
<< (user_agent_details.present? ? "(#{user_agent_details}) " : '') \
|
56
|
+
<< "Faraday/#{Faraday::VERSION} " \
|
57
|
+
<< "Ruby/#{RUBY_VERSION}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def client_options
|
61
|
+
{}.tap do |opts|
|
62
|
+
opts[:request] = request_options
|
63
|
+
opts[:ssl] = ssl_options if @http_options[:use_ssl]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def request_options
|
68
|
+
{ timeout: timeout, open_timeout: timeout }
|
69
|
+
end
|
70
|
+
|
71
|
+
def ssl_options
|
72
|
+
{ verify_mode: OpenSSL::SSL::VERIFY_NONE, verify_depth: 5 }
|
73
|
+
end
|
74
|
+
|
75
|
+
def initialize_client
|
76
|
+
@client = Faraday.new(@url, client_options) do |faraday|
|
77
|
+
faraday.request :multipart
|
78
|
+
faraday.request :url_encoded
|
79
|
+
faraday.adapter Faraday.default_adapter #adapter should be last in the list https://github.com/lostisland/faraday/issues/161
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Dimelo::CCP
|
2
|
+
module API
|
3
|
+
class Error < StandardError
|
4
|
+
attr_accessor :original_exception
|
5
|
+
|
6
|
+
# Public : Return specific Exceptions if defined
|
7
|
+
#
|
8
|
+
# Returns :
|
9
|
+
# - DefinedError descendant exception (DomainNotFoundError,...) if status & name matches declared DefinedError child
|
10
|
+
# - BaseError if status and name does not match any declared Error
|
11
|
+
# - Error if body cannot be json parsed
|
12
|
+
def self.from(method, path, http_status, body)
|
13
|
+
json = Dimelo::CCP::API.decode_json(body).symbolize_keys!
|
14
|
+
name = json.delete(:error)
|
15
|
+
status = json.delete(:status)
|
16
|
+
message = json.delete(:message)
|
17
|
+
|
18
|
+
if klass = DefinedError.descendants.find { |error| error.status == status && error.name == name}
|
19
|
+
klass.new
|
20
|
+
else
|
21
|
+
BaseError.new(name, status, message)
|
22
|
+
end
|
23
|
+
rescue ActiveSupport::JSON.parse_error
|
24
|
+
new("#{method.to_s.upcase} #{path} - #{http_status} #{body}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class BaseError < StandardError
|
29
|
+
def initialize(name, status, message='')
|
30
|
+
@name = name
|
31
|
+
@status = status
|
32
|
+
@message = message
|
33
|
+
super("error_type:#{name} - status:#{status} - body:#{message}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class DefinedError < StandardError
|
38
|
+
class_attribute :status, :name
|
39
|
+
end
|
40
|
+
|
41
|
+
class DomainNotFoundError < DefinedError
|
42
|
+
self.status = 404
|
43
|
+
self.name = 'domain_not_found'
|
44
|
+
end
|
45
|
+
|
46
|
+
class InvalidAccessTokenError < DefinedError
|
47
|
+
self.status = 403
|
48
|
+
self.name = 'invalid_access_token'
|
49
|
+
end
|
50
|
+
|
51
|
+
class InvalidUserTypeError < DefinedError #happens only on POST /users
|
52
|
+
self.status = 400
|
53
|
+
self.name = 'invalid_user_type'
|
54
|
+
end
|
55
|
+
|
56
|
+
class NotEnabledError < DefinedError
|
57
|
+
self.status = 403
|
58
|
+
self.name = 'api_not_enabled'
|
59
|
+
end
|
60
|
+
|
61
|
+
class NotFoundError < DefinedError
|
62
|
+
self.status = 404
|
63
|
+
self.name = 'not_found'
|
64
|
+
end
|
65
|
+
|
66
|
+
class SslError < DefinedError
|
67
|
+
self.status = 412
|
68
|
+
self.name = 'routing_error'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Dimelo::CCP
|
2
|
+
module API
|
3
|
+
|
4
|
+
class LazzyCollection < BasicObject
|
5
|
+
|
6
|
+
include ::Enumerable
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def new(params, &block)
|
11
|
+
if (params.has_key?(:offset) || params.has_key?('offset'))
|
12
|
+
yield params
|
13
|
+
else
|
14
|
+
instance = super(params, &block)
|
15
|
+
instance.paginator.first.is_a?(Enumerable) ? instance : instance.paginator.first
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :paginator
|
22
|
+
|
23
|
+
def initialize(params={}, &block)
|
24
|
+
@paginator = Paginator.new(params, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def [](index)
|
28
|
+
paginator.get_item_at(index)
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_a
|
32
|
+
@to_a ||= each
|
33
|
+
end
|
34
|
+
alias :to_ary :to_a
|
35
|
+
|
36
|
+
def respond_to?(method)
|
37
|
+
super || Array.public_instance_methods.include?(method.to_s)
|
38
|
+
end
|
39
|
+
|
40
|
+
def each(&block)
|
41
|
+
index = 0
|
42
|
+
while true
|
43
|
+
item = paginator.get_item_at(index)
|
44
|
+
break if item.nil?
|
45
|
+
yield item if block.present?
|
46
|
+
index += 1
|
47
|
+
end
|
48
|
+
paginator.flatten
|
49
|
+
end
|
50
|
+
|
51
|
+
def inspect
|
52
|
+
"<Dimelo::CCP::API::LazzyCollection instance>"
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
# /!\ Fetch all API, please avoid this behavior
|
58
|
+
def method_missing(name, *args, &block)
|
59
|
+
super unless respond_to?(name)
|
60
|
+
warn %{WARNING: Method '#{name}` called on LazzyCollection object from #{Kernel.caller.first}.
|
61
|
+
All API items might be fetched, so please verify that you are a consenting adult.}
|
62
|
+
self.to_a.send(name, *args, &block)
|
63
|
+
end
|
64
|
+
|
65
|
+
def warn(message)
|
66
|
+
defined?(Rails) ? Rails.logger.warn(message) : STDERR.puts(message)
|
67
|
+
end
|
68
|
+
|
69
|
+
class Paginator
|
70
|
+
|
71
|
+
DEFAULT_PAGE_SIZE = 30
|
72
|
+
|
73
|
+
attr_reader :page_cache
|
74
|
+
|
75
|
+
def initialize(params, &block)
|
76
|
+
@params = params.dup
|
77
|
+
@block = block
|
78
|
+
@page_cache = []
|
79
|
+
end
|
80
|
+
|
81
|
+
def page_size
|
82
|
+
@page_size ||= (@params[:limit] || DEFAULT_PAGE_SIZE).to_i
|
83
|
+
end
|
84
|
+
|
85
|
+
def page_offset(page_index)
|
86
|
+
page_index * page_size
|
87
|
+
end
|
88
|
+
|
89
|
+
def [](index)
|
90
|
+
page_cache[index.to_i] ||= @block.call(params_for_page(index))
|
91
|
+
end
|
92
|
+
|
93
|
+
def params_for_page(index)
|
94
|
+
@params.merge(:offset => page_offset(index.to_i), :limit => page_size)
|
95
|
+
end
|
96
|
+
|
97
|
+
def get_item_at(position)
|
98
|
+
self[position / page_size][position % page_size]
|
99
|
+
end
|
100
|
+
|
101
|
+
def flatten
|
102
|
+
page_cache.flatten(1)
|
103
|
+
end
|
104
|
+
|
105
|
+
def first
|
106
|
+
self[0]
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|