printreleaf 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'irb'
4
+ require 'irb/completion'
5
+
6
+ require "bundler/setup"
7
+ require "printreleaf"
8
+
9
+ IRB.conf[:PROMPT_MODE] = :SIMPLE
10
+ IRB.conf[:AUTO_INDENT] = true
11
+ IRB.conf[:SAVE_HISTORY] = 100
12
+
13
+ IRB.start
14
+
@@ -0,0 +1,37 @@
1
+ # stdlib
2
+ require "date"
3
+ require "forwardable"
4
+ require "json"
5
+
6
+ # dependencies
7
+ require "hashie"
8
+ require "restclient"
9
+
10
+ # libs
11
+ require "printreleaf/version"
12
+ require "printreleaf/error"
13
+ require "printreleaf/util"
14
+ require "printreleaf/transforms"
15
+ require "printreleaf/api"
16
+ require "printreleaf/resource"
17
+ require "printreleaf/relation"
18
+ require "printreleaf/actions"
19
+
20
+ require "printreleaf/forestry/project"
21
+ require "printreleaf/paper/type"
22
+
23
+ require "printreleaf/account"
24
+ require "printreleaf/certificate"
25
+ require "printreleaf/deposit"
26
+ require "printreleaf/invitation"
27
+ require "printreleaf/server"
28
+ require "printreleaf/source"
29
+ require "printreleaf/transaction_item"
30
+ require "printreleaf/transaction"
31
+ require "printreleaf/user"
32
+ require "printreleaf/volume_period"
33
+
34
+ module PrintReleaf
35
+ extend API
36
+ end
37
+
@@ -0,0 +1,100 @@
1
+ module PrintReleaf
2
+ class Account < Resource
3
+ path "/accounts"
4
+
5
+ action :find
6
+ action :list
7
+ action :create
8
+ action :update
9
+ action :activate
10
+ action :deactivate
11
+ action :delete
12
+
13
+ property :id
14
+ property :name
15
+ property :role
16
+ property :parent_id
17
+ property :status
18
+ property :created_at, transform_with: Transforms::Date
19
+ property :activated_at, transform_with: Transforms::Date
20
+ property :deactivated_at, transform_with: Transforms::Date
21
+ property :accounts_count, transform_with: Transforms::Integer
22
+ property :users_count, transform_with: Transforms::Integer
23
+ property :mtd_pages, transform_with: Transforms::Integer
24
+ property :qtd_pages, transform_with: Transforms::Integer
25
+ property :ytd_pages, transform_with: Transforms::Integer
26
+ property :lifetime_pages, transform_with: Transforms::Integer
27
+ property :mtd_trees, transform_with: Transforms::Float
28
+ property :qtd_trees, transform_with: Transforms::Float
29
+ property :ytd_trees, transform_with: Transforms::Float
30
+ property :lifetime_trees, transform_with: Transforms::Float
31
+
32
+ def self.mine
33
+ response = PrintReleaf.get("/account")
34
+ self.new(response)
35
+ end
36
+
37
+ # Account URI is always root, even when it has an owner.
38
+ # /accounts/456
39
+ # Instead of:
40
+ # /accounts/123/accounts/456
41
+ def uri
42
+ Util.join_uri(self.class.uri, self.id)
43
+ end
44
+
45
+ def active?
46
+ status == "active"
47
+ end
48
+
49
+ def inactive?
50
+ status == "inactive"
51
+ end
52
+
53
+ def parent
54
+ return nil if parent_id.nil?
55
+ @parent ||= Account.find(parent_id)
56
+ end
57
+
58
+ # Alias
59
+ def children
60
+ accounts
61
+ end
62
+
63
+ def accounts
64
+ @accounts ||= Relation.new(self, Account)
65
+ end
66
+
67
+ def certificates
68
+ @certificates ||= Relation.new(self, Certificate)
69
+ end
70
+
71
+ def deposits
72
+ @deposits ||= Relation.new(self, Deposit)
73
+ end
74
+
75
+ def invitations
76
+ @invitations ||= Relation.new(self, Invitation)
77
+ end
78
+
79
+ def servers
80
+ @servers ||= Relation.new(self, Server)
81
+ end
82
+
83
+ def sources
84
+ @sources ||= Relation.new(self, Source)
85
+ end
86
+
87
+ def transactions
88
+ @transactions ||= Relation.new(self, Transaction)
89
+ end
90
+
91
+ def users
92
+ @users ||= Relation.new(self, User)
93
+ end
94
+
95
+ def volume
96
+ @volume ||= Relation.new(self, VolumePeriod)
97
+ end
98
+ end
99
+ end
100
+
@@ -0,0 +1,123 @@
1
+ module PrintReleaf
2
+ module Actions
3
+ module Find
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ base.include(InstanceMethods)
7
+ end
8
+
9
+ def self.extended(base)
10
+ base.extend(ClassMethods)
11
+ base.extend(InstanceMethods)
12
+ end
13
+
14
+ module ClassMethods
15
+ def find(id)
16
+ uri = Util.join_uri(self.uri, id)
17
+ response = PrintReleaf.get(uri)
18
+ self.new(response)
19
+ end
20
+ end
21
+
22
+ module InstanceMethods
23
+ def reload
24
+ response = PrintReleaf.get(self.uri)
25
+ self.reset(response)
26
+ end
27
+ end
28
+ end
29
+
30
+ module List
31
+ def self.included(base)
32
+ base.extend(ClassMethods)
33
+ base.include(InstanceMethods)
34
+ end
35
+
36
+ def self.extended(base)
37
+ base.extend(InstanceMethods)
38
+ end
39
+
40
+ module ClassMethods
41
+ def list(params={})
42
+ PrintReleaf.get(self.uri, params).map do |response|
43
+ self.new(response)
44
+ end
45
+ end
46
+
47
+ def first
48
+ list.first
49
+ end
50
+
51
+ def last
52
+ list.last
53
+ end
54
+
55
+ def count
56
+ list.count
57
+ end
58
+
59
+ def length
60
+ list.length
61
+ end
62
+ end
63
+
64
+ module InstanceMethods
65
+ def list(params={})
66
+ PrintReleaf.get(self.uri, params).map do |response|
67
+ self.new(response)
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ module Create
74
+ def self.included(base)
75
+ base.extend(self)
76
+ end
77
+
78
+ def create(params)
79
+ response = PrintReleaf.post(self.uri, params)
80
+ self.new(response)
81
+ end
82
+ end
83
+
84
+ module Update
85
+ def save
86
+ if self.id.nil?
87
+ response = PrintReleaf.post(self.uri, self.to_hash)
88
+ else
89
+ response = PrintReleaf.patch(self.uri, changes)
90
+ end
91
+ reset(response)
92
+ return true
93
+ end
94
+ end
95
+
96
+ module Delete
97
+ def delete
98
+ response = PrintReleaf.delete(self.uri)
99
+ reset(response)
100
+ return true
101
+ end
102
+ end
103
+
104
+ module Activate
105
+ def activate
106
+ uri = Util.join_uri(self.uri, "activate")
107
+ response = PrintReleaf.post(uri)
108
+ reset(response)
109
+ return true
110
+ end
111
+ end
112
+
113
+ module Deactivate
114
+ def deactivate
115
+ uri = Util.join_uri(self.uri, "deactivate")
116
+ response = PrintReleaf.post(uri)
117
+ reset(response)
118
+ return true
119
+ end
120
+ end
121
+ end
122
+ end
123
+
@@ -0,0 +1,162 @@
1
+ module PrintReleaf
2
+ module API
3
+ extend self
4
+
5
+ ENDPOINT = "api.printreleaf.com/v1/"
6
+ PROTOCOL = "https"
7
+ MAX_RETRY_COUNT = 2
8
+ RETRY_DELAY_BASE = 1.5 # Base for exponential delay
9
+
10
+ NETWORK_EXCEPTIONS = [
11
+ SocketError,
12
+ Errno::ECONNREFUSED,
13
+ Errno::ECONNRESET,
14
+ Errno::ETIMEDOUT,
15
+ RestClient::RequestTimeout
16
+ ]
17
+
18
+ API_EXCEPTIONS = {
19
+ 400 => BadRequest,
20
+ 401 => Unauthorized,
21
+ 403 => Forbidden,
22
+ 404 => NotFound,
23
+ 429 => RateLimitExceeded,
24
+ 500 => ServerError
25
+ }
26
+
27
+ attr_writer :api_key
28
+ attr_writer :endpoint
29
+ attr_writer :protocol
30
+ attr_accessor :logger
31
+
32
+ def api_key
33
+ if @api_key.nil? or @api_key.strip.to_s.empty?
34
+ raise Error, "Missing API Key."
35
+ else
36
+ return @api_key
37
+ end
38
+ end
39
+
40
+ def endpoint
41
+ @endpoint || ENDPOINT
42
+ end
43
+
44
+ def protocol
45
+ @protocol || PROTOCOL
46
+ end
47
+
48
+ def get(uri="/", params={})
49
+ request :get, uri, params
50
+ end
51
+
52
+ def post(uri, data={})
53
+ request :post, uri, data
54
+ end
55
+
56
+ def patch(uri, data={})
57
+ request :patch, uri, data
58
+ end
59
+
60
+ def delete(uri)
61
+ request :delete, uri
62
+ end
63
+
64
+ def request(verb, uri, params={})
65
+ perform_request do
66
+ uri = Util.join_uri(endpoint, uri)
67
+ url = "#{protocol}://#{api_key}:@#{uri}"
68
+
69
+ unless logger.nil?
70
+ logger.info "[PrintReleaf] #{verb.upcase} #{uri}"
71
+ end
72
+
73
+ response = case verb
74
+ when :get; RestClient.get url, params: params, accept: :json
75
+ when :post; RestClient.post url, params.to_json, accept: :json, content_type: :json
76
+ when :patch; RestClient.patch url, params.to_json, accept: :json, content_type: :json
77
+ when :delete; RestClient.delete url
78
+ end
79
+
80
+ JSON.parse(response.body)
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def perform_request
87
+ retry_count = 0
88
+ begin
89
+ yield
90
+ rescue => e
91
+ if should_retry?(e, retry_count)
92
+ retry_count += 1
93
+ sleep retry_delay(retry_count)
94
+ retry
95
+ else
96
+ handle_error(e, retry_count)
97
+ end
98
+ end
99
+ end
100
+
101
+ def handle_error(e, retry_count)
102
+ case e
103
+ when RestClient::ExceptionWithResponse
104
+ if e.response
105
+ handle_api_error(e, retry_count)
106
+ else
107
+ handle_restclient_error(e, retry_count)
108
+ end
109
+ when JSON::ParserError
110
+ handle_json_error(e, retry_count)
111
+ when *NETWORK_EXCEPTIONS
112
+ handle_network_error(e, retry_count)
113
+ else
114
+ raise
115
+ end
116
+ end
117
+
118
+ def handle_api_error(e, retry_count=0)
119
+ # We likely got an http status code outside the 200-399 range.
120
+ # If this is a GET or DELETE request, it is likely the resource is not owned by the client.
121
+ # If this is a POST, PUT, or PATCH, the data might be invalid.
122
+ code = e.response.code
123
+ message = e.response ? JSON.parse(e.response.body)["error"] : "Something went wrong. Please try again."
124
+ message += " (code=#{code})"
125
+ message += " Request was retried #{retry_count} times." if retry_count > 0
126
+ exception = API_EXCEPTIONS[e.response.code] || Error
127
+ raise exception, message
128
+ end
129
+
130
+ def handle_json_error(e, retry_count=0)
131
+ # We received the data fine, but we're unable to parse it.
132
+ # Re-raise a generic error.
133
+ message = "Unable to parse response. Please try again."
134
+ message += " Request was retried #{retry_count} times." if retry_count > 0
135
+ message += " (#{e.class.name})"
136
+ raise ResponseError, message
137
+ end
138
+
139
+ def handle_network_error(e, retry_count=0)
140
+ message = "Unexpected error communicating when trying to connect to PrintReleaf."
141
+ message += " Request was retried #{retry_count} times." if retry_count > 0
142
+ message += " (#{e.class.name})"
143
+ raise NetworkError, message
144
+ end
145
+
146
+ def handle_restclient_error(e, retry_count=0)
147
+ message = "Something went wrong with the request. Please try again."
148
+ message += " Request was retried #{retry_count} times." if retry_count > 0
149
+ message += " (#{e.class.name})"
150
+ raise RequestError, message
151
+ end
152
+
153
+ def should_retry?(e, retry_count=0)
154
+ NETWORK_EXCEPTIONS.include?(e.class) && retry_count < MAX_RETRY_COUNT
155
+ end
156
+
157
+ def retry_delay(retry_count=0)
158
+ RETRY_DELAY_BASE ** retry_count
159
+ end
160
+ end
161
+ end
162
+