podio 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +36 -5
- data/Rakefile +3 -1
- data/lib/podio/active_podio/base.rb +311 -0
- data/lib/podio/active_podio/updatable.rb +22 -0
- data/lib/podio/client.rb +7 -4
- data/lib/podio/error.rb +1 -0
- data/lib/podio/middleware/error_response.rb +5 -1
- data/lib/podio/middleware/response_recorder.rb +1 -1
- data/lib/podio/models/app_store_share.rb +67 -0
- data/lib/podio/{areas → models}/application.rb +23 -7
- data/lib/podio/models/application_email.rb +22 -0
- data/lib/podio/models/application_field.rb +17 -0
- data/lib/podio/{areas → models}/bulletin.rb +16 -5
- data/lib/podio/models/by_line.rb +11 -0
- data/lib/podio/models/category.rb +49 -0
- data/lib/podio/models/comment.rb +63 -0
- data/lib/podio/{areas → models}/connection.rb +20 -4
- data/lib/podio/models/contact.rb +10 -0
- data/lib/podio/models/conversation.rb +72 -0
- data/lib/podio/models/conversation_message.rb +12 -0
- data/lib/podio/models/conversation_participant.rb +9 -0
- data/lib/podio/models/email_subscription_setting.rb +39 -0
- data/lib/podio/models/embed.rb +34 -0
- data/lib/podio/models/file_attachment.rb +127 -0
- data/lib/podio/models/form.rb +25 -0
- data/lib/podio/{areas → models}/hook.rb +15 -5
- data/lib/podio/{areas → models}/importer.rb +4 -6
- data/lib/podio/{areas → models}/integration.rb +40 -4
- data/lib/podio/models/item.rb +122 -0
- data/lib/podio/models/item_diff.rb +16 -0
- data/lib/podio/models/item_field.rb +19 -0
- data/lib/podio/models/item_revision.rb +18 -0
- data/lib/podio/models/news.rb +83 -0
- data/lib/podio/models/notification.rb +47 -0
- data/lib/podio/models/notification_group.rb +19 -0
- data/lib/podio/models/o_auth.rb +32 -0
- data/lib/podio/models/o_auth_client.rb +93 -0
- data/lib/podio/{areas → models}/organization.rb +39 -6
- data/lib/podio/models/organization_contact.rb +14 -0
- data/lib/podio/{areas → models}/organization_member.rb +13 -5
- data/lib/podio/models/organization_profile.rb +61 -0
- data/lib/podio/{areas/contact.rb → models/profile.rb} +23 -6
- data/lib/podio/{areas → models}/rating.rb +5 -7
- data/lib/podio/{areas → models}/search.rb +6 -7
- data/lib/podio/models/space.rb +45 -0
- data/lib/podio/models/space_contact.rb +23 -0
- data/lib/podio/models/space_invite.rb +57 -0
- data/lib/podio/models/space_member.rb +32 -0
- data/lib/podio/models/status.rb +35 -0
- data/lib/podio/{areas → models}/subscription.rb +8 -7
- data/lib/podio/{areas → models}/tag.rb +14 -7
- data/lib/podio/models/task.rb +153 -0
- data/lib/podio/models/task_label.rb +50 -0
- data/lib/podio/models/user.rb +66 -0
- data/lib/podio/models/user_status.rb +18 -0
- data/lib/podio/models/via.rb +7 -0
- data/lib/podio/{areas → models}/widget.rb +9 -5
- data/lib/podio/version.rb +1 -1
- data/lib/podio.rb +97 -39
- data/podio.gemspec +4 -3
- data/test/active_podio_test.rb +256 -0
- data/test/client_test.rb +20 -15
- data/test/fixtures/client/18a224aaf83ac57a7b8159cecdbb1263.rack +1 -0
- data/test/fixtures/client/a87c69a0624af0413a670094c6615651.rack +1 -0
- data/test/fixtures/client/ac493997db62308972c208afa76f8479.rack +1 -0
- data/test/fixtures/client/d7fbf422c77af768552423633d0389e8.rack +1 -0
- data/test/fixtures/client/e2d68afe39f5531195273ea259b63916.rack +1 -0
- data/test/fixtures/fixtures.yaml +34 -0
- data/test/models_sanity_test.rb +19 -0
- data/test/test_helper.rb +22 -28
- metadata +89 -64
- data/lib/podio/areas/app_store.rb +0 -69
- data/lib/podio/areas/comment.rb +0 -36
- data/lib/podio/areas/conversation.rb +0 -39
- data/lib/podio/areas/email.rb +0 -24
- data/lib/podio/areas/file.rb +0 -81
- data/lib/podio/areas/form.rb +0 -11
- data/lib/podio/areas/item.rb +0 -68
- data/lib/podio/areas/notification.rb +0 -39
- data/lib/podio/areas/oauth.rb +0 -107
- data/lib/podio/areas/organization_profile.rb +0 -32
- data/lib/podio/areas/space.rb +0 -77
- data/lib/podio/areas/status.rb +0 -19
- data/lib/podio/areas/task.rb +0 -108
- data/lib/podio/areas/user.rb +0 -31
- data/lib/podio/areas/user_status.rb +0 -11
data/README.md
CHANGED
@@ -48,7 +48,7 @@ If you're writing a batch job or are just playing around with the API, this is t
|
|
48
48
|
Basic Usage
|
49
49
|
-----------
|
50
50
|
|
51
|
-
After you configured the `Podio.client` singleton you can use all of the wrapper functions to do API requests. The functions are organized into
|
51
|
+
After you configured the `Podio.client` singleton you can use all of the wrapper functions to do API requests. The functions are organized into models corresponding to the official API documentation, although most API areas have multiple models associated. The method follow a common naming pattern that should be familiar to ActiveRecord users. For example:
|
52
52
|
|
53
53
|
# Getting an item
|
54
54
|
Podio::Item.find(42)
|
@@ -69,7 +69,7 @@ If there is a method missing or you want to do something special, you can use th
|
|
69
69
|
end
|
70
70
|
response.body
|
71
71
|
|
72
|
-
All the wrapped methods either return a single
|
72
|
+
All the wrapped methods either return a single model instance, an array of instances, or a simple Struct in case of pagination:
|
73
73
|
|
74
74
|
# Find all items in an app (paginated)
|
75
75
|
items = Podio::Item.find_all(app_id, :limit => 20)
|
@@ -84,10 +84,26 @@ All the wrapped methods either return a single Ruby hash, an array of Ruby hashe
|
|
84
84
|
items.total_count
|
85
85
|
|
86
86
|
|
87
|
+
Active Podio
|
88
|
+
------------
|
89
|
+
|
90
|
+
The Podio API is based on REST requests passing JSON back and forth, but we have tried to make the use of this client an experience more similar to using ActiveRecord from Rails. That means that all find methods return model instances with attributes cast to the expected type (string, integer, boolean, datetime, etc.). Also, models can be instantiated using a params hash, just like with ActiveRecord models.
|
91
|
+
|
92
|
+
While the models can be used directly from this gem, we encourage everyone using Podio in a Rails project to add models that extend the standard models:
|
93
|
+
|
94
|
+
class Item < Podio::Item # Inherits from the base model in the Podio gem
|
95
|
+
|
96
|
+
# Your custom methods, e.g.:
|
97
|
+
def application
|
98
|
+
@app_instance ||= Application.find(self.app_id)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|
87
103
|
Error Handling
|
88
104
|
--------------
|
89
105
|
|
90
|
-
All unsuccessful responses returned by the API (everything that has a 4xx or 5xx HTTP status code) will throw
|
106
|
+
All unsuccessful responses returned by the API (everything that has a 4xx or 5xx HTTP status code) will throw exceptions. All exceptions inherit from `Podio::PodioError` and have three additional properties which give you more information about the error:
|
91
107
|
|
92
108
|
begin
|
93
109
|
Podio::Space.create({:name => 'New Space', :org_id => 42})
|
@@ -100,6 +116,18 @@ All unsuccessful responses returned by the API (everything that has a 4xx or 5xx
|
|
100
116
|
puts exc.response_body['error_description']
|
101
117
|
end
|
102
118
|
|
119
|
+
On instance methods, however, exceptions are handled in a way similar to ActiveRecord. These methods returns a boolean indicating if the API request succeeded or not, and makes the code, description and parameters available when the request fails:
|
120
|
+
|
121
|
+
@space_contact = SpaceContact.new({:name => 'The Dude', :birthdate => 50.years.ago})
|
122
|
+
if @space_contact.create
|
123
|
+
# Success
|
124
|
+
else
|
125
|
+
# Error, check:
|
126
|
+
# @space_contact.error_code
|
127
|
+
# @space_contact.error_message
|
128
|
+
# @space_contact.error_parameters
|
129
|
+
end
|
130
|
+
|
103
131
|
|
104
132
|
Full Example
|
105
133
|
------------
|
@@ -114,10 +142,13 @@ Full Example
|
|
114
142
|
my_orgs = Podio::Organization.find_all
|
115
143
|
|
116
144
|
my_orgs.each do |org|
|
117
|
-
puts org
|
118
|
-
puts org
|
145
|
+
puts org.name
|
146
|
+
puts org.url
|
119
147
|
end
|
120
148
|
|
149
|
+
Note on Heroku Usage
|
150
|
+
--------------------
|
151
|
+
If you plan on using podio-rb on Heroku please note that only the 1.9.2 stack has been tested. Specifically, bamboo-mri-1.9.2 is recommended, while 1.8.7 is still stock on Heroku. Refer to their documentation for information on how to migrate your dynos
|
121
152
|
|
122
153
|
Meta
|
123
154
|
----
|
data/Rakefile
CHANGED
@@ -7,6 +7,7 @@ Bundler::GemHelper.install_tasks
|
|
7
7
|
desc 'Run tests'
|
8
8
|
Rake::TestTask.new(:test) do |t|
|
9
9
|
ENV['ENABLE_STUBS'] = 'true'
|
10
|
+
ENV['ENABLE_RECORD'] = 'false'
|
10
11
|
t.ruby_opts = ["-rubygems"] if defined? Gem
|
11
12
|
t.libs << "lib" << "test"
|
12
13
|
t.pattern = 'test/**/*_test.rb'
|
@@ -16,9 +17,10 @@ end
|
|
16
17
|
desc 'Record responses'
|
17
18
|
task :record do
|
18
19
|
ENV['ENABLE_RECORD'] = 'true'
|
20
|
+
ENV['ENABLE_STUBS'] = 'false'
|
19
21
|
|
20
22
|
Dir['test/**/*_test.rb'].each do |f|
|
21
|
-
ruby(
|
23
|
+
ruby("-Ilib:test", f)
|
22
24
|
|
23
25
|
folder_name = f.match(/test\/(.+)_test.rb/)[1]
|
24
26
|
FileUtils.mkdir_p("test/fixtures/#{folder_name}")
|
@@ -0,0 +1,311 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
|
3
|
+
# Extends the Ruby wrapper to behave a bit like ActiveRecord models
|
4
|
+
module ActivePodio
|
5
|
+
class Base
|
6
|
+
extend ActiveModel::Naming, ActiveModel::Callbacks
|
7
|
+
include ActiveModel::Conversion
|
8
|
+
|
9
|
+
class_inheritable_accessor :valid_attributes
|
10
|
+
attr_accessor :attributes, :error_code, :error_message, :error_parameters
|
11
|
+
|
12
|
+
def initialize(attributes = {})
|
13
|
+
self.valid_attributes ||= []
|
14
|
+
attributes ||= {}
|
15
|
+
self.attributes = Hash[*self.valid_attributes.collect { |n| [n.to_sym, nil] }.flatten].merge(attributes.symbolize_keys)
|
16
|
+
attributes.each do |key, value|
|
17
|
+
if self.respond_to?("#{key}=".to_sym)
|
18
|
+
self.send("#{key}=".to_sym, value)
|
19
|
+
elsif valid_attributes.include?(key.to_sym)
|
20
|
+
self.send(:[]=, key.to_sym, value)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def persisted?
|
26
|
+
! self.new_record?
|
27
|
+
end
|
28
|
+
|
29
|
+
def new_record?
|
30
|
+
! (self.respond_to?(:id) && self.id.present?)
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_param
|
34
|
+
return self.id.try(:to_s) if self.respond_to?(:id)
|
35
|
+
end
|
36
|
+
|
37
|
+
def [](attribute)
|
38
|
+
@attributes ||= {}
|
39
|
+
@attributes[attribute.to_sym]
|
40
|
+
end
|
41
|
+
|
42
|
+
def []=(attribute, value)
|
43
|
+
@attributes ||= {}
|
44
|
+
@attributes[attribute.to_sym] = value
|
45
|
+
end
|
46
|
+
|
47
|
+
def ==(other)
|
48
|
+
self.respond_to?(:id) && other.respond_to?(:id) && self.id == other.id
|
49
|
+
end
|
50
|
+
alias :eql? :==
|
51
|
+
|
52
|
+
def hash
|
53
|
+
self.id.hash if self.respond_to?(:id)
|
54
|
+
end
|
55
|
+
|
56
|
+
def as_json(options={})
|
57
|
+
self.attributes
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def klass_for_association(options)
|
63
|
+
klass_name = options[:class]
|
64
|
+
raise "Missing class name of associated model. Provide with :class => 'MyClass'." unless klass_name.present?
|
65
|
+
klass = nil
|
66
|
+
begin
|
67
|
+
klass = klass_name.constantize
|
68
|
+
rescue
|
69
|
+
klass = "Podio::#{klass_name}".constantize
|
70
|
+
end
|
71
|
+
return klass
|
72
|
+
end
|
73
|
+
|
74
|
+
class << self
|
75
|
+
|
76
|
+
public
|
77
|
+
|
78
|
+
# Defines the the supported attributes of the model
|
79
|
+
def property(name, type = :string)
|
80
|
+
self.valid_attributes ||= []
|
81
|
+
self.valid_attributes << name
|
82
|
+
|
83
|
+
case type
|
84
|
+
when :datetime
|
85
|
+
define_datetime_accessor(name)
|
86
|
+
when :date
|
87
|
+
define_date_accessor(name)
|
88
|
+
when :integer
|
89
|
+
define_integer_accessor(name)
|
90
|
+
when :boolean
|
91
|
+
define_generic_accessor(name, :setter => false)
|
92
|
+
define_boolean_accessors(name)
|
93
|
+
when :array
|
94
|
+
define_array_accessors(name)
|
95
|
+
else
|
96
|
+
define_generic_accessor(name)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Wraps a single hash provided from the API in the given model
|
101
|
+
def has_one(name, options = {})
|
102
|
+
self.send(:define_method, name) do
|
103
|
+
klass = klass_for_association(options)
|
104
|
+
instance = self.instance_variable_get("@#{name}_has_one_instance")
|
105
|
+
unless instance.present?
|
106
|
+
property = options[:property] || name.to_sym
|
107
|
+
if self[property].present?
|
108
|
+
instance = klass.new(self[property])
|
109
|
+
self.instance_variable_set("@#{name}_has_one_instance", instance)
|
110
|
+
else
|
111
|
+
instance = nil
|
112
|
+
end
|
113
|
+
end
|
114
|
+
instance
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Wraps a collection of hashes from the API to a collection of the given model
|
119
|
+
def has_many(name, options = {})
|
120
|
+
self.send(:define_method, name) do
|
121
|
+
klass = klass_for_association(options)
|
122
|
+
instances = self.instance_variable_get("@#{name}_has_many_instances")
|
123
|
+
unless instances.present?
|
124
|
+
property = options[:property] || name.to_sym
|
125
|
+
if self[property].present?
|
126
|
+
instances = self[property].map { |attributes| klass.new(attributes) }
|
127
|
+
self.instance_variable_set("@#{name}_has_many_instances", instances)
|
128
|
+
else
|
129
|
+
instances = []
|
130
|
+
end
|
131
|
+
end
|
132
|
+
instances
|
133
|
+
end
|
134
|
+
|
135
|
+
self.send(:define_method, "#{name}?") do
|
136
|
+
self.send(name).length > 0
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Returns a single instance of the model
|
141
|
+
def member(response)
|
142
|
+
new(response)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns a simple collection model instances
|
146
|
+
def list(response)
|
147
|
+
response.map! { |item| new(item) }
|
148
|
+
response
|
149
|
+
end
|
150
|
+
|
151
|
+
# Returns a struct that includes:
|
152
|
+
# * all: A collection model instances
|
153
|
+
# * count: The number of returned records
|
154
|
+
# * total_count: The total number of records matching the given conditions
|
155
|
+
def collection(response)
|
156
|
+
result = Struct.new(:all, :count, :total_count).new(response['items'], response['filtered'], response['total'])
|
157
|
+
result.all.map! { |item| new(item) }
|
158
|
+
result
|
159
|
+
end
|
160
|
+
|
161
|
+
def delegate_to_hash(hash_name, *attribute_names)
|
162
|
+
attribute_names.each do |attribute_name|
|
163
|
+
hash_index = attribute_name.to_s.gsub(/[\?!]/, '')
|
164
|
+
self.send(:define_method, attribute_name) do
|
165
|
+
self.send(hash_name)[hash_index]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Wraps the given methods in a begin/rescue block
|
171
|
+
# If no error occurs, the return value of the method, or true if nil is returned, is returned
|
172
|
+
# If a Podio::BadRequestError occurs, the method returns false and the error can be read from the error_message accessor
|
173
|
+
# If another error occurs, it is still raised
|
174
|
+
def handle_api_errors_for(*method_names)
|
175
|
+
method_names.each do |method_name|
|
176
|
+
self.send(:define_method, "#{method_name}_with_api_errors_handled") do |*args|
|
177
|
+
success, code, message, parameters, result = nil
|
178
|
+
begin
|
179
|
+
result = self.send("#{method_name}_without_api_errors_handled", *args)
|
180
|
+
success = true
|
181
|
+
rescue Podio::BadRequestError, Podio::AuthorizationError => ex
|
182
|
+
success = false
|
183
|
+
code = ex.response_body["error"]
|
184
|
+
message = ex.response_body["error_description"]
|
185
|
+
parameters = ex.response_body["error_parameters"]
|
186
|
+
end
|
187
|
+
|
188
|
+
if success
|
189
|
+
return result || true
|
190
|
+
else
|
191
|
+
@error_code = code
|
192
|
+
@error_message = message
|
193
|
+
@error_parameters = parameters
|
194
|
+
return false
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
alias_method_chain method_name, :api_errors_handled
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
private
|
203
|
+
|
204
|
+
def define_generic_accessor(name, options = {})
|
205
|
+
options.reverse_merge!(:getter => true, :setter => true)
|
206
|
+
|
207
|
+
if(options[:getter])
|
208
|
+
self.send(:define_method, name) do
|
209
|
+
self[name.to_sym]
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
if(options[:setter])
|
214
|
+
self.send(:define_method, "#{name}=") do |value|
|
215
|
+
self[name.to_sym] = value
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def define_datetime_accessor(name)
|
221
|
+
self.send(:define_method, name) do
|
222
|
+
self[name.to_sym].try(:to_datetime).try(:in_time_zone)
|
223
|
+
end
|
224
|
+
|
225
|
+
self.send(:define_method, "#{name}=") do |value|
|
226
|
+
self[name.to_sym] = if value.is_a?(DateTime)
|
227
|
+
value.try(:to_s, :db)
|
228
|
+
else
|
229
|
+
value.try(:to_s)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def define_date_accessor(name)
|
235
|
+
self.send(:define_method, name) do
|
236
|
+
self[name.to_sym].try(:to_date)
|
237
|
+
end
|
238
|
+
|
239
|
+
self.send(:define_method, "#{name}=") do |value|
|
240
|
+
self[name.to_sym] = if value.is_a?(Date)
|
241
|
+
value.try(:to_s, :db)
|
242
|
+
else
|
243
|
+
value.try(:to_s)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def define_integer_accessor(name)
|
249
|
+
self.send(:define_method, name) do
|
250
|
+
if self[name.to_sym].present?
|
251
|
+
self[name.to_sym].to_i
|
252
|
+
else
|
253
|
+
nil
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
self.send(:define_method, "#{name}=") do |value|
|
258
|
+
if value.present?
|
259
|
+
self[name.to_sym] = value.to_i
|
260
|
+
else
|
261
|
+
self[name.to_sym] = nil
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def define_boolean_accessors(name)
|
267
|
+
self.send(:define_method, "#{name}?") do
|
268
|
+
if self[name.to_sym].present?
|
269
|
+
%w{ true 1 yes }.include?(self[name.to_sym].to_s.strip.downcase)
|
270
|
+
else
|
271
|
+
nil
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
self.send(:define_method, "#{name}=") do |value|
|
276
|
+
if value == true || value == false
|
277
|
+
self[name.to_sym] = value
|
278
|
+
elsif value.present?
|
279
|
+
self[name.to_sym] = %w{ true 1 yes }.include?(value.to_s.strip.downcase)
|
280
|
+
else
|
281
|
+
self[name.to_sym] = nil
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def define_array_accessors(name)
|
287
|
+
unless name.to_s == name.to_s.pluralize
|
288
|
+
self.send(:define_method, name) do
|
289
|
+
self[name.to_sym] || []
|
290
|
+
end
|
291
|
+
|
292
|
+
self.send(:define_method, "#{name}=") do |array|
|
293
|
+
self[name.to_sym] = array.reject(&:blank?) if array.respond_to?(:reject)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
self.send(:define_method, name.to_s.pluralize) do
|
298
|
+
self[name.to_sym] || []
|
299
|
+
end
|
300
|
+
|
301
|
+
self.send(:define_method, "#{name.to_s.pluralize}=") do |array|
|
302
|
+
self[name.to_sym] = array.reject(&:blank?) if array.respond_to?(:reject)
|
303
|
+
end
|
304
|
+
|
305
|
+
self.send(:define_method, "default_#{name.to_s.singularize}") do
|
306
|
+
self[name.to_sym].try(:first).presence
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Adds functionality related to updating to an +ActivePodio+ model
|
2
|
+
module ActivePodio
|
3
|
+
module Updatable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module InstanceMethods
|
7
|
+
def update_attributes(attributes)
|
8
|
+
attributes.each do |key, value|
|
9
|
+
self.send("#{key}=".to_sym, value.presence)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def remove_nil_values(input_hash)
|
14
|
+
input_hash.inject({}) do |hash, (key, value)|
|
15
|
+
hash[key] = value if value.present?
|
16
|
+
hash
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
data/lib/podio/client.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Podio
|
2
2
|
class Client
|
3
|
-
attr_reader :api_url, :api_key, :api_secret, :connection
|
3
|
+
attr_reader :api_url, :api_key, :api_secret, :connection, :raw_connection
|
4
4
|
attr_accessor :oauth_token, :stubs, :current_http_client
|
5
5
|
|
6
6
|
def initialize(options = {})
|
@@ -95,12 +95,14 @@ module Podio
|
|
95
95
|
|
96
96
|
def setup_connections
|
97
97
|
@connection = configure_connection
|
98
|
+
@raw_connection = configure_connection(true)
|
98
99
|
@oauth_connection = configure_oauth_connection
|
99
100
|
end
|
100
101
|
|
101
|
-
def configure_connection
|
102
|
+
def configure_connection(raw=false)
|
102
103
|
Faraday::Connection.new(:url => api_url, :headers => configured_headers, :request => {:client => self}) do |builder|
|
103
|
-
builder.use Middleware::JsonRequest
|
104
|
+
builder.use Middleware::JsonRequest unless raw
|
105
|
+
builder.use Faraday::Request::Multipart if raw
|
104
106
|
builder.use Middleware::OAuth2
|
105
107
|
builder.use Middleware::Logger
|
106
108
|
|
@@ -109,7 +111,7 @@ module Podio
|
|
109
111
|
# first response middleware defined get's executed last
|
110
112
|
builder.use Middleware::DateConversion
|
111
113
|
builder.use Middleware::ErrorResponse
|
112
|
-
builder.use Middleware::JsonResponse
|
114
|
+
builder.use Middleware::JsonResponse unless raw
|
113
115
|
builder.use Middleware::ResponseRecorder if @record_mode
|
114
116
|
end
|
115
117
|
end
|
@@ -128,6 +130,7 @@ module Podio
|
|
128
130
|
|
129
131
|
def configure_oauth
|
130
132
|
@connection = configure_connection
|
133
|
+
@raw_connection = configure_connection(true)
|
131
134
|
end
|
132
135
|
end
|
133
136
|
end
|
data/lib/podio/error.rb
CHANGED
@@ -8,7 +8,11 @@ module Podio
|
|
8
8
|
when 200, 204
|
9
9
|
# pass
|
10
10
|
when 400
|
11
|
-
|
11
|
+
if env[:body]['error'] == 'invalid_grant'
|
12
|
+
raise InvalidGrantError.new(env[:body], env[:status], env[:url])
|
13
|
+
else
|
14
|
+
raise BadRequestError.new(env[:body], env[:status], env[:url])
|
15
|
+
end
|
12
16
|
when 401
|
13
17
|
if env[:body]['error_description'] =~ /expired_token/
|
14
18
|
raise TokenExpired.new(env[:body], env[:status], env[:url])
|
@@ -7,7 +7,7 @@ module Podio
|
|
7
7
|
response = "['#{Faraday::Utils.normalize_path(env[:url])}', :#{env[:method]}, #{env[:status]}, #{env[:response_headers]}, '#{env[:body]}']"
|
8
8
|
|
9
9
|
filename = Digest::MD5.hexdigest(env[:url].request_uri)
|
10
|
-
File.open("#{filename}.rack", 'w') { |f| f.write(response) }
|
10
|
+
::File.open("#{filename}.rack", 'w') { |f| f.write(response) }
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class Podio::AppStoreShare < ActivePodio::Base
|
2
|
+
property :share_id, :integer
|
3
|
+
property :type, :string
|
4
|
+
property :status, :string
|
5
|
+
property :parents, :hash
|
6
|
+
property :name, :string
|
7
|
+
property :description, :string
|
8
|
+
property :abstract, :string
|
9
|
+
property :language, :string
|
10
|
+
property :featured, :boolean
|
11
|
+
property :features, :array
|
12
|
+
property :filters, :array
|
13
|
+
property :integration, :string
|
14
|
+
property :categories, :hash
|
15
|
+
property :org, :hash
|
16
|
+
property :author, :hash
|
17
|
+
property :author_apps, :integer
|
18
|
+
property :author_packs, :integer
|
19
|
+
property :icon, :string
|
20
|
+
property :comments, :array
|
21
|
+
property :ratings, :hash
|
22
|
+
property :user_rating, :array
|
23
|
+
property :screenshots, :array
|
24
|
+
property :info, :hash
|
25
|
+
|
26
|
+
has_many :children, :class => 'AppStoreShare'
|
27
|
+
has_one :author, :class => 'ByLine'
|
28
|
+
|
29
|
+
alias_method :id, :share_id
|
30
|
+
|
31
|
+
def create
|
32
|
+
self.share_id = self.class.create(self.attributes)
|
33
|
+
end
|
34
|
+
|
35
|
+
def install(space_id, dependencies)
|
36
|
+
self.class.install(self.share_id, space_id, dependencies)
|
37
|
+
end
|
38
|
+
|
39
|
+
class << self
|
40
|
+
def create(attributes)
|
41
|
+
response = Podio.connection.post do |req|
|
42
|
+
req.url "/app_store/"
|
43
|
+
req.body = attributes
|
44
|
+
end
|
45
|
+
|
46
|
+
response.body['share_id']
|
47
|
+
end
|
48
|
+
|
49
|
+
def install(share_id, space_id, dependencies)
|
50
|
+
response = Podio.connection.post do |req|
|
51
|
+
req.url "/app_store/#{share_id}/install/v2"
|
52
|
+
req.body = {:space_id => space_id, :dependencies => dependencies}
|
53
|
+
end
|
54
|
+
|
55
|
+
response.body
|
56
|
+
end
|
57
|
+
|
58
|
+
def find(id)
|
59
|
+
member Podio.connection.get("/app_store/#{id}/v2").body
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_all_private_for_org(org_id)
|
63
|
+
list Podio.connection.get("/app_store/org/#{org_id}/").body['shares']
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
@@ -1,8 +1,25 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
class Podio::Application < ActivePodio::Base
|
2
|
+
property :app_id, :integer
|
3
|
+
property :original, :integer
|
4
|
+
property :original_revision, :integer
|
5
|
+
property :status, :string
|
6
|
+
property :icon, :string
|
7
|
+
property :space_id, :integer
|
8
|
+
property :owner_id, :integer
|
9
|
+
property :owner, :hash
|
10
|
+
property :config, :hash
|
11
|
+
property :fields, :array
|
12
|
+
property :subscribed, :boolean
|
13
|
+
property :integration, :hash
|
14
|
+
property :rights, :array
|
15
|
+
property :link, :string
|
16
|
+
|
17
|
+
has_one :integration, :class => 'Integration'
|
18
|
+
|
19
|
+
alias_method :id, :app_id
|
20
|
+
delegate_to_hash :config, :name, :item_name, :allow_edit?, :allow_attachments?, :allow_comments?, :description
|
21
|
+
|
22
|
+
class << self
|
6
23
|
def find(app_id)
|
7
24
|
member Podio.connection.get("/app/#{app_id}").body
|
8
25
|
end
|
@@ -42,7 +59,6 @@ module Podio
|
|
42
59
|
|
43
60
|
def delete(id)
|
44
61
|
Podio.connection.delete("/app/#{id}").body
|
45
|
-
end
|
46
|
-
|
62
|
+
end
|
47
63
|
end
|
48
64
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Podio::ApplicationEmail < ActivePodio::Base
|
2
|
+
include ActivePodio::Updatable
|
3
|
+
|
4
|
+
property :attachments, :boolean
|
5
|
+
property :mappings, :hash
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def get_app_configuration(app_id)
|
9
|
+
member Podio.connection.get { |req|
|
10
|
+
req.url("/email/app/#{app_id}", {})
|
11
|
+
}.body
|
12
|
+
end
|
13
|
+
|
14
|
+
def update_app_configuration(app_id, options)
|
15
|
+
Podio.connection.put { |req|
|
16
|
+
req.url "/email/app/#{app_id}"
|
17
|
+
req.body = options
|
18
|
+
}.body
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Podio::ApplicationField < ActivePodio::Base
|
2
|
+
property :field_id, :integer
|
3
|
+
property :type, :string
|
4
|
+
property :external_id, :integer
|
5
|
+
property :config, :hash
|
6
|
+
|
7
|
+
alias_method :id, :field_id
|
8
|
+
delegate_to_hash :config, :label, :description, :delta, :settings, :required?, :visible?
|
9
|
+
delegate_to_hash :settings, :allowed_values, :referenceable_types, :allowed_currencies, :valid_types
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def find(app_id, field_id)
|
13
|
+
member Podio.connection.get("/app/#{app_id}/field/#{field_id}").body
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -1,8 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
class Podio::Bulletin < ActivePodio::Base
|
2
|
+
property :bulletin_id, :integer
|
3
|
+
property :title, :string
|
4
|
+
property :summary, :string
|
5
|
+
property :text, :string
|
6
|
+
property :locale, :string
|
7
|
+
property :target_group, :string
|
8
|
+
property :created_on, :datetime
|
9
|
+
property :sent_on, :datetime
|
10
|
+
|
11
|
+
has_one :created_by, :class => 'ByLine'
|
12
|
+
|
13
|
+
alias_method :id, :bulletin_id
|
14
|
+
|
15
|
+
class << self
|
6
16
|
def create(attributes)
|
7
17
|
response = Podio.connection.post do |req|
|
8
18
|
req.url "/bulletin/"
|
@@ -44,5 +54,6 @@ module Podio
|
|
44
54
|
def send!(id)
|
45
55
|
Podio.connection.post("/bulletin/#{id}/send").body
|
46
56
|
end
|
57
|
+
|
47
58
|
end
|
48
59
|
end
|