podio 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +1 -4
- data/README.md +82 -67
- data/lib/podio.rb +15 -2
- data/lib/podio/client.rb +11 -7
- data/lib/podio/middleware/error_response.rb +3 -1
- data/lib/podio/middleware/json_response.rb +6 -1
- data/lib/podio/middleware/logger.rb +4 -3
- data/lib/podio/middleware/oauth2.rb +5 -5
- data/lib/podio/models/answer.rb +8 -0
- data/lib/podio/models/app_store_category.rb +2 -2
- data/lib/podio/models/app_store_share.rb +1 -0
- data/lib/podio/models/application.rb +12 -4
- data/lib/podio/models/application_field.rb +10 -0
- data/lib/podio/models/batch.rb +1 -0
- data/lib/podio/models/calendar_event.rb +6 -5
- data/lib/podio/models/campaign.rb +88 -0
- data/lib/podio/models/category.rb +1 -1
- data/lib/podio/models/contract.rb +18 -20
- data/lib/podio/models/contract_attribution.rb +24 -0
- data/lib/podio/models/contract_period.rb +22 -0
- data/lib/podio/models/contract_price.rb +37 -26
- data/lib/podio/models/device.rb +22 -0
- data/lib/podio/models/email_subscription_setting.rb +8 -0
- data/lib/podio/models/embed.rb +5 -2
- data/lib/podio/models/experiment.rb +26 -3
- data/lib/podio/models/extension.rb +90 -0
- data/lib/podio/models/external_file.rb +3 -3
- data/lib/podio/models/file_attachment.rb +25 -2
- data/lib/podio/models/form.rb +2 -1
- data/lib/podio/models/grant.rb +1 -0
- data/lib/podio/models/invoice.rb +13 -0
- data/lib/podio/models/item.rb +24 -11
- data/lib/podio/models/item_field.rb +6 -0
- data/lib/podio/models/item_transaction.rb +34 -0
- data/lib/podio/models/live.rb +1 -17
- data/lib/podio/models/notification.rb +5 -0
- data/lib/podio/models/o_auth.rb +9 -2
- data/lib/podio/models/o_auth_client.rb +5 -3
- data/lib/podio/models/o_auth_scope.rb +11 -0
- data/lib/podio/models/organization.rb +26 -8
- data/lib/podio/models/organization_member.rb +6 -0
- data/lib/podio/models/organization_membership.rb +15 -0
- data/lib/podio/models/profile.rb +2 -0
- data/lib/podio/models/promotion.rb +6 -8
- data/lib/podio/models/promotion_group.rb +51 -0
- data/lib/podio/models/promotion_group_member.rb +32 -0
- data/lib/podio/models/rating.rb +4 -2
- data/lib/podio/models/reference.rb +2 -1
- data/lib/podio/models/space.rb +11 -5
- data/lib/podio/models/tag.rb +36 -0
- data/lib/podio/models/view.rb +5 -0
- data/lib/podio/models/vote.rb +8 -0
- data/lib/podio/models/voting.rb +9 -0
- data/lib/podio/models/voucher.rb +68 -0
- data/lib/podio/version.rb +1 -1
- data/podio.gemspec +10 -3
- data/test/client_test.rb +14 -0
- metadata +31 -13
- data/lib/podio/models/contract_price_v2.rb +0 -31
- data/lib/podio/models/date_election.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8be7a5e8b46cf3874900c1bfb9452ec3319890da
|
4
|
+
data.tar.gz: 1ed2281e8019c04a4e44e58afc75e323f183c9d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 330066725f8cd6d4cbafdcc5160b715618ade623cd57ad0aca8b656d8675a79bbf975fa6dba5078d12be2ff50e4756d6f4a7fc9aa7ed4e72f85cc69e95b2d2c7
|
7
|
+
data.tar.gz: e3620e6009381f05a79c0524d48c3ef0bc227887f758eb5014bb2820b077601b92d1c121f875b78eb4c6b2eb6b41ea0e0ba0481a3e748a98011ced416ae11b33
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -17,7 +17,9 @@ Configuration
|
|
17
17
|
|
18
18
|
The main way of using the Podio library is via a singleton client, which you set up like this:
|
19
19
|
|
20
|
-
|
20
|
+
```ruby
|
21
|
+
Podio.setup(:api_key => 'YOUR_API_KEY', :api_secret => 'YOUR_API_SECRET')
|
22
|
+
```
|
21
23
|
|
22
24
|
This initializes a `Podio::Client` object and assigns it to a thread-local, which is used by all methods in this library.
|
23
25
|
|
@@ -31,58 +33,66 @@ After the configuration you need to authenticate against the API. The client sup
|
|
31
33
|
|
32
34
|
The default OAuth flow to be used when you authenticate Podio users from your web application. See the `sinatra.rb` in the examples folder.
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
+
```ruby
|
37
|
+
# Redirect the user to the authorize url
|
38
|
+
Podio.client.authorize_url(:redirect_uri => redirect_uri)
|
36
39
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
+
# In the callback you get the authorization_code
|
41
|
+
# wich you use to get the access token
|
42
|
+
Podio.client.authenticate_with_auth_code(params[:code], redirect_uri)
|
43
|
+
```
|
40
44
|
|
41
45
|
### Username and Password Flow
|
42
46
|
|
43
47
|
If you're writing a batch job or are just playing around with the API, this is the easiest to get started. Do not use this for authenticating users other than yourself, the web server flow is meant for that.
|
44
48
|
|
45
|
-
|
46
|
-
|
49
|
+
```ruby
|
50
|
+
Podio.client.authenticate_with_credentials('USERNAME', 'PASSWORD')
|
51
|
+
```
|
47
52
|
|
48
53
|
Basic Usage
|
49
54
|
-----------
|
50
55
|
|
51
56
|
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
57
|
|
53
|
-
|
54
|
-
|
58
|
+
```ruby
|
59
|
+
# Getting an item
|
60
|
+
Podio::Item.find(42)
|
55
61
|
|
56
|
-
|
57
|
-
|
62
|
+
# Posting a status message on space with id 23
|
63
|
+
Podio::Status.create(23, {:value => 'This is the text of the status message'})
|
64
|
+
```
|
58
65
|
|
59
66
|
If there is a method missing or you want to do something special, you can use the Faraday connection directly. This allows you to do arbitrary HTTP requests to the Podio API with authentication, JSON parsing and error handling already taken care of. The same examples would look like this:
|
60
67
|
|
61
|
-
|
62
|
-
|
63
|
-
|
68
|
+
```ruby
|
69
|
+
# Getting an item
|
70
|
+
response = Podio.connection.get('/item/42')
|
71
|
+
response.body
|
64
72
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
73
|
+
# Posting a status message on space with id 23
|
74
|
+
response = Podio.connection.post do |req|
|
75
|
+
req.url '/status/space/23/'
|
76
|
+
req.body = {:value => 'This is the text of the status message'}
|
77
|
+
end
|
78
|
+
response.body
|
79
|
+
```
|
71
80
|
|
72
81
|
All the wrapped methods either return a single model instance, an array of instances, or a simple Struct in case of pagination:
|
73
82
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
# get count of returned items in this call
|
78
|
-
items.count
|
83
|
+
```ruby
|
84
|
+
# Find all items in an app (paginated)
|
85
|
+
items = Podio::Item.find_all(app_id, :limit => 20)
|
79
86
|
|
80
|
-
|
81
|
-
|
87
|
+
# get count of returned items in this call
|
88
|
+
items.count
|
82
89
|
|
83
|
-
|
84
|
-
|
90
|
+
# get the returned items in an array
|
91
|
+
items.all
|
85
92
|
|
93
|
+
# get count of all items in this app
|
94
|
+
items.total_count
|
95
|
+
```
|
86
96
|
|
87
97
|
Active Podio
|
88
98
|
------------
|
@@ -91,61 +101,66 @@ The Podio API is based on REST requests passing JSON back and forth, but we have
|
|
91
101
|
|
92
102
|
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
103
|
|
94
|
-
|
95
|
-
|
96
|
-
# Your custom methods, e.g.:
|
97
|
-
def application
|
98
|
-
@app_instance ||= Application.find(self.app_id)
|
99
|
-
end
|
100
|
-
end
|
104
|
+
```ruby
|
105
|
+
class Item < Podio::Item # Inherits from the base model in the Podio gem
|
101
106
|
|
107
|
+
# Your custom methods, e.g.:
|
108
|
+
def application
|
109
|
+
@app_instance ||= Application.find(self.app_id)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
```
|
102
113
|
|
103
114
|
Error Handling
|
104
115
|
--------------
|
105
116
|
|
106
117
|
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:
|
107
118
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
119
|
+
```ruby
|
120
|
+
begin
|
121
|
+
Podio::Space.create({:name => 'New Space', :org_id => 42})
|
122
|
+
rescue Podio::BadRequestError => exc
|
123
|
+
puts exc.response_body # parsed JSON response from the API
|
124
|
+
puts exc.response_status # status code of the response
|
125
|
+
puts exc.url # uri of the API request
|
114
126
|
|
115
|
-
|
116
|
-
|
117
|
-
|
127
|
+
# you normally want this one, a human readable error description
|
128
|
+
puts exc.message
|
129
|
+
end
|
130
|
+
```
|
118
131
|
|
119
132
|
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
133
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
134
|
+
```ruby
|
135
|
+
@space_contact = SpaceContact.new({:name => 'The Dude', :birthdate => 50.years.ago})
|
136
|
+
if @space_contact.create
|
137
|
+
# Success
|
138
|
+
else
|
139
|
+
# Error, check:
|
140
|
+
# @space_contact.error_code
|
141
|
+
# @space_contact.error_message
|
142
|
+
# @space_contact.error_parameters
|
143
|
+
end
|
144
|
+
```
|
131
145
|
|
132
146
|
Full Example
|
133
147
|
------------
|
134
148
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
Podio.setup(:api_key => 'YOUR_API_KEY', :api_secret => 'YOUR_API_SECRET')
|
139
|
-
Podio.client.authenticate_with_credentials('YOUR_PODIO_ACCOUNT', 'YOUR_PODIO_PASSWORD')
|
149
|
+
```ruby
|
150
|
+
require 'rubygems'
|
151
|
+
require 'podio'
|
140
152
|
|
141
|
-
|
142
|
-
|
153
|
+
Podio.setup(:api_key => 'YOUR_API_KEY', :api_secret => 'YOUR_API_SECRET')
|
154
|
+
Podio.client.authenticate_with_credentials('YOUR_PODIO_ACCOUNT', 'YOUR_PODIO_PASSWORD')
|
143
155
|
|
144
|
-
|
145
|
-
|
146
|
-
puts org.url
|
147
|
-
end
|
156
|
+
# Print a list of organizations I'm a member of
|
157
|
+
my_orgs = Podio::Organization.find_all
|
148
158
|
|
159
|
+
my_orgs.each do |org|
|
160
|
+
puts org.name
|
161
|
+
puts org.url
|
162
|
+
end
|
163
|
+
```
|
149
164
|
|
150
165
|
Meta
|
151
166
|
----
|
data/lib/podio.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'faraday'
|
2
|
+
require 'active_support'
|
2
3
|
require 'active_support/core_ext'
|
3
4
|
|
4
5
|
require 'podio/error'
|
@@ -69,6 +70,7 @@ module Podio
|
|
69
70
|
autoload :Action, 'podio/models/action'
|
70
71
|
autoload :ActivationStatus, 'podio/models/activation_status'
|
71
72
|
autoload :Activity, 'podio/models/activity'
|
73
|
+
autoload :Answer, 'podio/models/answer'
|
72
74
|
autoload :AppStoreShare, 'podio/models/app_store_share'
|
73
75
|
autoload :AppStoreCategory, 'podio/models/app_store_category'
|
74
76
|
autoload :Application, 'podio/models/application'
|
@@ -79,6 +81,7 @@ module Podio
|
|
79
81
|
autoload :ByLine, 'podio/models/by_line'
|
80
82
|
autoload :CalendarEvent, 'podio/models/calendar_event'
|
81
83
|
autoload :CalendarMute, 'podio/models/calendar_mute'
|
84
|
+
autoload :Campaign, 'podio/models/campaign'
|
82
85
|
autoload :Category, 'podio/models/category'
|
83
86
|
autoload :Comment, 'podio/models/comment'
|
84
87
|
autoload :Condition, 'podio/models/condition'
|
@@ -87,19 +90,21 @@ module Podio
|
|
87
90
|
autoload :Contact, 'podio/models/contact'
|
88
91
|
autoload :Contract, 'podio/models/contract'
|
89
92
|
autoload :ContractAccounting, 'podio/models/contract_accounting'
|
93
|
+
autoload :ContractAttribution, 'podio/models/contract_attribution'
|
90
94
|
autoload :ContractEvent, 'podio/models/contract_event'
|
95
|
+
autoload :ContractPeriod, 'podio/models/contract_period'
|
91
96
|
autoload :ContractPrice, 'podio/models/contract_price'
|
92
|
-
autoload :ContractPriceV2, 'podio/models/contract_price_v2'
|
93
97
|
autoload :ContractUser, 'podio/models/contract_user'
|
94
98
|
autoload :Conversation, 'podio/models/conversation'
|
95
99
|
autoload :ConversationEvent, 'podio/models/conversation_event'
|
96
100
|
autoload :ConversationMessage, 'podio/models/conversation_message'
|
97
101
|
autoload :ConversationParticipant, 'podio/models/conversation_participant'
|
98
|
-
autoload :
|
102
|
+
autoload :Device, 'podio/models/device'
|
99
103
|
autoload :EmailSubscriptionSetting, 'podio/models/email_subscription_setting'
|
100
104
|
autoload :EmailContact, 'podio/models/email_contact'
|
101
105
|
autoload :Embed, 'podio/models/embed'
|
102
106
|
autoload :Experiment, 'podio/models/experiment'
|
107
|
+
autoload :Extension, 'podio/models/extension'
|
103
108
|
autoload :ExternalFile, 'podio/models/external_file'
|
104
109
|
autoload :FileAttachment, 'podio/models/file_attachment'
|
105
110
|
autoload :Filter, 'podio/models/filter'
|
@@ -114,6 +119,7 @@ module Podio
|
|
114
119
|
autoload :ItemDiff, 'podio/models/item_diff'
|
115
120
|
autoload :ItemField, 'podio/models/item_field'
|
116
121
|
autoload :ItemRevision, 'podio/models/item_revision'
|
122
|
+
autoload :ItemTransaction, 'podio/models/item_transaction'
|
117
123
|
autoload :LinkedAccount, 'podio/models/linked_account'
|
118
124
|
autoload :LinkedAccountData, 'podio/models/linked_account_data'
|
119
125
|
autoload :Live, 'podio/models/live'
|
@@ -122,13 +128,17 @@ module Podio
|
|
122
128
|
autoload :NotificationGroup, 'podio/models/notification_group'
|
123
129
|
autoload :OAuth, 'podio/models/o_auth'
|
124
130
|
autoload :OAuthClient, 'podio/models/o_auth_client'
|
131
|
+
autoload :OAuthScope, 'podio/models/o_auth_scope'
|
125
132
|
autoload :Organization, 'podio/models/organization'
|
126
133
|
autoload :OrganizationContact, 'podio/models/organization_contact'
|
127
134
|
autoload :OrganizationMember, 'podio/models/organization_member'
|
135
|
+
autoload :OrganizationMembership, 'podio/models/organization_membership'
|
128
136
|
autoload :OrganizationProfile, 'podio/models/organization_profile'
|
129
137
|
autoload :Pin, 'podio/models/pin'
|
130
138
|
autoload :Profile, 'podio/models/profile'
|
131
139
|
autoload :Promotion, 'podio/models/promotion'
|
140
|
+
autoload :PromotionGroup, 'podio/models/promotion_group'
|
141
|
+
autoload :PromotionGroupMember, 'podio/models/promotion_group_member'
|
132
142
|
autoload :Question, 'podio/models/question'
|
133
143
|
autoload :QuestionAnswer, 'podio/models/question_answer'
|
134
144
|
autoload :QuestionOption, 'podio/models/question_option'
|
@@ -156,6 +166,9 @@ module Podio
|
|
156
166
|
autoload :UserStatus, 'podio/models/user_status'
|
157
167
|
autoload :Via, 'podio/models/via'
|
158
168
|
autoload :View, 'podio/models/view'
|
169
|
+
autoload :Vote, 'podio/models/vote'
|
170
|
+
autoload :Voting, 'podio/models/voting'
|
171
|
+
autoload :Voucher, 'podio/models/voucher'
|
159
172
|
autoload :Widget, 'podio/models/widget'
|
160
173
|
|
161
174
|
end
|
data/lib/podio/client.rb
CHANGED
@@ -11,6 +11,7 @@ module Podio
|
|
11
11
|
@oauth_token = options[:oauth_token]
|
12
12
|
@headers = options[:custom_headers] || {}
|
13
13
|
@adapter = options[:adapter] || Faraday.default_adapter
|
14
|
+
@request_options = options[:request_options] || {}
|
14
15
|
|
15
16
|
if options[:enable_stubs]
|
16
17
|
@enable_stubs = true
|
@@ -52,11 +53,14 @@ module Podio
|
|
52
53
|
end
|
53
54
|
|
54
55
|
# Sign in as a user using credentials
|
55
|
-
def authenticate_with_credentials(username, password)
|
56
|
+
def authenticate_with_credentials(username, password, offering_id=nil)
|
57
|
+
body = {:grant_type => 'password', :client_id => api_key, :client_secret => api_secret, :username => username, :password => password}
|
58
|
+
body[:offering_id] = offering_id if offering_id.present?
|
59
|
+
|
56
60
|
response = @oauth_connection.post do |req|
|
57
61
|
req.url '/oauth/token'
|
58
62
|
req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
59
|
-
req.body =
|
63
|
+
req.body = body
|
60
64
|
end
|
61
65
|
|
62
66
|
@oauth_token = OAuthToken.new(response.body)
|
@@ -177,12 +181,12 @@ module Podio
|
|
177
181
|
end
|
178
182
|
|
179
183
|
def configure_connection
|
180
|
-
Faraday::Connection.new(:url => api_url, :headers => configured_headers, :request =>
|
184
|
+
Faraday::Connection.new(:url => api_url, :headers => configured_headers, :request => @request_options) do |builder|
|
181
185
|
builder.use Middleware::JsonRequest
|
182
186
|
builder.use Faraday::Request::Multipart
|
183
187
|
builder.use Faraday::Request::UrlEncoded
|
184
|
-
builder.use Middleware::OAuth2
|
185
|
-
builder.use Middleware::Logger
|
188
|
+
builder.use Middleware::OAuth2, :podio_client => self
|
189
|
+
builder.use Middleware::Logger, :podio_client => self
|
186
190
|
|
187
191
|
builder.adapter(*default_adapter)
|
188
192
|
|
@@ -198,7 +202,7 @@ module Podio
|
|
198
202
|
|
199
203
|
def configure_oauth_connection
|
200
204
|
conn = @connection.dup
|
201
|
-
conn.options
|
205
|
+
conn.options.update(@request_options)
|
202
206
|
conn.headers.delete('authorization')
|
203
207
|
conn.headers.delete('X-Podio-Dry-Run') if @test_mode # oauth requests don't really work well in test mode
|
204
208
|
conn
|
@@ -206,7 +210,7 @@ module Podio
|
|
206
210
|
|
207
211
|
def configure_trusted_connection
|
208
212
|
conn = @connection.dup
|
209
|
-
conn.options
|
213
|
+
conn.options.update(@request_options)
|
210
214
|
conn.headers.delete('authorization')
|
211
215
|
conn.basic_auth(api_key, api_secret)
|
212
216
|
conn
|
@@ -7,11 +7,16 @@ module Podio
|
|
7
7
|
require 'multi_json'
|
8
8
|
|
9
9
|
def on_complete(env)
|
10
|
-
if env[:body].is_a?(String) && is_json?(env) && env[:status] != 500
|
10
|
+
if env[:body].is_a?(String) && is_json?(env) && should_unmarshal?(env) && env[:status] != 500
|
11
11
|
env[:body] = parse(env[:body])
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
def should_unmarshal?(env)
|
16
|
+
# don't unmarshal data from the API with a content-disposition header.
|
17
|
+
not env[:response_headers]['content-disposition'] =~ /filename=/
|
18
|
+
end
|
19
|
+
|
15
20
|
def is_json?(env)
|
16
21
|
env[:response_headers]['content-type'] =~ /application\/json/
|
17
22
|
end
|
@@ -7,13 +7,14 @@ module Podio
|
|
7
7
|
# Preserve request body
|
8
8
|
env[:request_body] = env[:body] if env[:body]
|
9
9
|
|
10
|
-
|
10
|
+
@podio_client.log(env) do
|
11
11
|
@app.call(env)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
def initialize(app)
|
16
|
-
super
|
15
|
+
def initialize(app, options={})
|
16
|
+
super(app)
|
17
|
+
@podio_client = options[:podio_client]
|
17
18
|
end
|
18
19
|
end
|
19
20
|
end
|
@@ -4,16 +4,15 @@ module Podio
|
|
4
4
|
module Middleware
|
5
5
|
class OAuth2 < Faraday::Middleware
|
6
6
|
def call(env)
|
7
|
-
podio_client = env[:request][:client]
|
8
7
|
orig_env = env.dup
|
9
8
|
|
10
9
|
begin
|
11
10
|
@app.call(env)
|
12
11
|
rescue TokenExpired
|
13
|
-
podio_client.refresh_access_token
|
12
|
+
@podio_client.refresh_access_token
|
14
13
|
|
15
14
|
# new access token needs to be put into the header
|
16
|
-
orig_env[:request_headers].merge!(podio_client.configured_headers)
|
15
|
+
orig_env[:request_headers].merge!(@podio_client.configured_headers)
|
17
16
|
|
18
17
|
# rewind the body if this was a file upload
|
19
18
|
orig_env[:body].rewind if orig_env[:body].respond_to?(:rewind)
|
@@ -23,8 +22,9 @@ module Podio
|
|
23
22
|
end
|
24
23
|
end
|
25
24
|
|
26
|
-
def initialize(app)
|
27
|
-
super
|
25
|
+
def initialize(app, options={})
|
26
|
+
super(app)
|
27
|
+
@podio_client = options[:podio_client]
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|