procore 0.8.4 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -5
- data/.travis.yml +2 -0
- data/CHANGELOG.md +84 -1
- data/README.md +134 -9
- data/lib/procore.rb +1 -0
- data/lib/procore/auth/client_credentials.rb +1 -1
- data/lib/procore/auth/stores/dalli.rb +38 -0
- data/lib/procore/client.rb +1 -1
- data/lib/procore/configuration.rb +23 -0
- data/lib/procore/defaults.rb +7 -0
- data/lib/procore/requestable.rb +119 -19
- data/lib/procore/response.rb +1 -1
- data/lib/procore/version.rb +1 -1
- data/procore.gemspec +3 -0
- metadata +46 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab6a771f9ecbd227e500591c59381e08e70b1ad3cea2f82e767b4de2d88664b3
|
4
|
+
data.tar.gz: 1159fa494f5d38ce2d0a0fbcbdb465bf6203e8989a96acf3bcceab915ea9dbac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1c9f942d9c8223df00bff8c67b366adc85be3224d2a6ef41ee4e345d40cba8cc0ca71fe0361ff094be1ba358d3ad7ead40fc99d36bba8e5edac0dfc19932c57
|
7
|
+
data.tar.gz: afb9275ad4ec9b1396f3f7bed77317eb39b8e6cacf5d82dc77b38e9a934491e439795fe7df432d27fdc337800b7343327f1cb9d7b70c072ed72244bcd41d6a0a
|
data/.rubocop.yml
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-rails
|
3
|
+
- rubocop-performance
|
4
|
+
|
1
5
|
AllCops:
|
2
|
-
TargetRubyVersion: 2.
|
6
|
+
TargetRubyVersion: 2.4
|
3
7
|
Include:
|
4
8
|
- '**/Rakefile'
|
5
9
|
- '**/config.ru'
|
@@ -305,7 +309,7 @@ Style/PerlBackrefs:
|
|
305
309
|
Naming/PredicateName:
|
306
310
|
Description: 'Check the names of predicate methods.'
|
307
311
|
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark'
|
308
|
-
|
312
|
+
ForbiddenPrefixes:
|
309
313
|
- is_
|
310
314
|
Exclude:
|
311
315
|
- spec/**/*
|
@@ -437,7 +441,7 @@ Style/RedundantBegin:
|
|
437
441
|
|
438
442
|
# Layout
|
439
443
|
|
440
|
-
Layout/
|
444
|
+
Layout/ParameterAlignment:
|
441
445
|
Description: 'Here we check if the parameters on a multi-line method call or definition are aligned.'
|
442
446
|
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-double-indent'
|
443
447
|
Enabled: false
|
@@ -505,7 +509,7 @@ Lint/DeprecatedClassMethods:
|
|
505
509
|
Description: 'Check for deprecated class method calls.'
|
506
510
|
Enabled: false
|
507
511
|
|
508
|
-
Lint/
|
512
|
+
Lint/DuplicateHashKey:
|
509
513
|
Description: 'Check for duplicate keys in hash literals.'
|
510
514
|
Enabled: false
|
511
515
|
|
@@ -521,7 +525,7 @@ Lint/FormatParameterMismatch:
|
|
521
525
|
Description: 'The number of parameters to format/sprint must match the fields.'
|
522
526
|
Enabled: false
|
523
527
|
|
524
|
-
Lint/
|
528
|
+
Lint/SuppressedException:
|
525
529
|
Description: "Don't suppress exception."
|
526
530
|
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions'
|
527
531
|
Enabled: false
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,87 @@
|
|
1
|
-
##
|
1
|
+
## 1.0.0 (January 5, 2021)
|
2
|
+
|
3
|
+
* Adds support for API versioning
|
4
|
+
|
5
|
+
*Nate Baer*
|
6
|
+
|
7
|
+
### Upgrading
|
8
|
+
|
9
|
+
As of v1.0.0, this gem now defaults to making requests against Procore's new
|
10
|
+
Rest v1.0 resources, instead of the now deprecated `/vapid` namespace. Example:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
# Previously makes a request to
|
14
|
+
client.get("me")
|
15
|
+
=> app.procore.com/vapid/me
|
16
|
+
|
17
|
+
# In 1.0.0
|
18
|
+
client.get("me")
|
19
|
+
=> app.procore.com/rest/v1.0/me
|
20
|
+
```
|
21
|
+
|
22
|
+
To keep the legacy behavior, set the new `default_version` configuration option.
|
23
|
+
Note, that Rest v1.0 is a superset of the Vapid Api - there are no breaking
|
24
|
+
changes. The Vapid API will be decommissioned in December 2021.
|
25
|
+
|
26
|
+
[Read more here](https://developers.procore.com/documentation/vapid-deprecation)
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
Procore.configure do |config|
|
30
|
+
...
|
31
|
+
# Defaults to "v1.0"
|
32
|
+
config.default_version = "vapid"
|
33
|
+
...
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
All the request methods (`get`, `post`, `patch`, `put`, `delete`, `sync`) now
|
38
|
+
accept an optional version parameter to specify the version at request time.
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
client.get("me")
|
42
|
+
=> https://app.procore.com/rest/v1.0/me
|
43
|
+
|
44
|
+
client.get("me", version: "v1.1")
|
45
|
+
=> https://app.procore.com/rest/v1.1/me
|
46
|
+
|
47
|
+
client.get("me", version: "vapid")
|
48
|
+
=> https://app.procore.com/vapid/me
|
49
|
+
```
|
50
|
+
|
51
|
+
## 0.8.8 (October 17, 2019)
|
52
|
+
|
53
|
+
* Expose #sync, a method that enables calling sync-actions
|
54
|
+
|
55
|
+
*Patrick Koperwas*
|
56
|
+
|
57
|
+
* Addition of contribution guidelines to README
|
58
|
+
|
59
|
+
*Megan O'Neill*
|
60
|
+
|
61
|
+
* Fix TravisCI failures
|
62
|
+
|
63
|
+
*Patrick Koperwas*
|
64
|
+
|
65
|
+
## 0.8.7 (April 18, 2019)
|
66
|
+
|
67
|
+
* Add api_version to allow calls to procore rest endpoints
|
68
|
+
|
69
|
+
*Shane Means*
|
70
|
+
|
71
|
+
## 0.8.6 (May 10, 2018)
|
72
|
+
|
73
|
+
* Dalli Store
|
74
|
+
|
75
|
+
*Patrick Koperwas*
|
76
|
+
|
77
|
+
* Fix Requestable paths to prevent double slash in URI
|
78
|
+
|
79
|
+
*Megan O'Neill*
|
80
|
+
|
81
|
+
## 0.8.5 (May 9, 2018)
|
82
|
+
* Rescue Errno::ECONNREFUSED errors and RestClient::ServerBrokeConnection
|
83
|
+
|
84
|
+
*Casey Ochs*
|
2
85
|
|
3
86
|
## 0.8.4 (May 8, 2018)
|
4
87
|
|
data/README.md
CHANGED
@@ -4,20 +4,24 @@
|
|
4
4
|
|
5
5
|
#### Table of Contents
|
6
6
|
- [Installation](#installation)
|
7
|
+
- [1.0.0 Release](#1.0.0-release)
|
7
8
|
- [Making Requests](#making-requests)
|
8
9
|
- [Usage](#usage)
|
9
10
|
- [Error Handling](#error-handling)
|
10
11
|
- [Pagination](#pagination)
|
11
12
|
- [Navigating Through Paginated Results](#navigating-through-paginated-results)
|
12
13
|
- [Change Number of Results](#change-number-of-results)
|
14
|
+
- [Sync Actions](#sync-actions)
|
13
15
|
- [Configuration](#configuration)
|
14
16
|
- [Stores](#stores)
|
15
17
|
- [Session Store](#session-store)
|
16
18
|
- [Redis Store](#redis-store)
|
19
|
+
- [Dalli Store](#dalli-store)
|
17
20
|
- [ActiveRecord Store](#activerecord-store)
|
18
21
|
- [File Store](#file-store)
|
19
22
|
- [Memory Store](#memory-store)
|
20
23
|
- [Full Example](#full-example)
|
24
|
+
- [Contributing](#contributing)
|
21
25
|
|
22
26
|
## Installation
|
23
27
|
|
@@ -27,6 +31,11 @@ Add this line to your application's Gemfile:
|
|
27
31
|
gem "procore"
|
28
32
|
```
|
29
33
|
|
34
|
+
## 1.0.0 Release
|
35
|
+
|
36
|
+
v1.0.0 was released on January 5, 2021, and adds support the new Rest v1.0 API.
|
37
|
+
See the CHANGELOG for upgrade instructions.
|
38
|
+
|
30
39
|
## Making Requests
|
31
40
|
|
32
41
|
At the core of the gem is the `Client` class. Clients are initialized with a
|
@@ -38,19 +47,27 @@ Stores automatically manage tokens for you - refreshing, revoking and storage
|
|
38
47
|
are abstracted away to make your code as simple as possible. There are several
|
39
48
|
different [types of stores](#stores) available to you.
|
40
49
|
|
41
|
-
The Client class exposes `#get`, `#post`, `#put`, `#patch
|
42
|
-
to you.
|
50
|
+
The Client class exposes `#get`, `#post`, `#put`, `#patch`, `#sync` and
|
51
|
+
`#delete` methods to you.
|
43
52
|
|
44
53
|
```ruby
|
45
|
-
get(path, query: {})
|
46
|
-
post(path, body: {}, options: {})
|
47
|
-
put(path, body: {}, options: {})
|
48
|
-
patch(path, body: {}, options: {})
|
49
|
-
delete(path, query: {})
|
54
|
+
get(path, version: "", query: {})
|
55
|
+
post(path, version: "", body: {}, options: {})
|
56
|
+
put(path, version: "", body: {}, options: {})
|
57
|
+
patch(path, version: "", body: {}, options: {})
|
58
|
+
delete(path, version: "", query: {})
|
59
|
+
sync(path, version: "", body: {}, options: {})
|
50
60
|
```
|
51
61
|
|
52
|
-
All paths are relative
|
53
|
-
`
|
62
|
+
All paths are relative, the gem will handle expanding them. An API version may
|
63
|
+
be specified in the `version:` argument, or the default version is used. The
|
64
|
+
default version is `v1.0` unless otherwise configured.
|
65
|
+
|
66
|
+
| Example | Requested URL |
|
67
|
+
| --- | --- |
|
68
|
+
| `client.get("me")` | `https://app.procore.com/rest/v1.0/me` |
|
69
|
+
| `client.get("me", version: "v1.1")` | `https://app.procore.com/rest/v1.1/me` |
|
70
|
+
| `client.get("me", version: "vapid")` | `https://app.procore.com/vapid/me` |
|
54
71
|
|
55
72
|
Example Usage:
|
56
73
|
|
@@ -68,6 +85,19 @@ companies = client.get("companies")
|
|
68
85
|
companies.first[:name] #=> "Procore Company 1"
|
69
86
|
```
|
70
87
|
|
88
|
+
To use Procore's older API Vapid by default, the default version can be set in
|
89
|
+
either the Gem's [configuration](https://github.com/procore/ruby-sdk#configuration)
|
90
|
+
or the client's `options` hash:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
client = Procore::Client.new(
|
94
|
+
...
|
95
|
+
options {
|
96
|
+
default_version: "vapid"
|
97
|
+
}
|
98
|
+
)
|
99
|
+
```
|
100
|
+
|
71
101
|
## Usage
|
72
102
|
|
73
103
|
The first step is to place the user's token into the store. For this example,
|
@@ -258,6 +288,49 @@ puts first_page.pagination
|
|
258
288
|
Notice that because `per_page` has been set to 250, there are only two pages of
|
259
289
|
results (500 resources / 250 page size = 2 pages).
|
260
290
|
|
291
|
+
## Sync Actions
|
292
|
+
The Sync action enables batch creation or updates to resources using a single
|
293
|
+
call. When using a Sync action, the resources to be created or updated can be
|
294
|
+
specified by supplying either an `id` or an `origin_id` in the request body.
|
295
|
+
Utilizing the `origin_id` attribute for batch operations is often preferable as
|
296
|
+
it allows you to easily link to external systems by maintaining your own list of
|
297
|
+
unique resource identifiers outside of Procore.
|
298
|
+
|
299
|
+
The caller provides an array of hashes, each hash containing the attributes for
|
300
|
+
a single resource. The attribute names in each hash match those used by the
|
301
|
+
Create and Update actions for the resource. Attributes for a maximum of 1000
|
302
|
+
resources within a collection may be passed with each call. The API will always
|
303
|
+
return an HTTP status of 200.
|
304
|
+
|
305
|
+
The response body contains two attributes - `entities` and `errors`. The
|
306
|
+
attributes for each successfully created or updated resource will appear in the
|
307
|
+
entities list. The attributes for each resource will match those returned by the
|
308
|
+
Show action. For each resource which could not be created or updated, the
|
309
|
+
attributes supplied by the caller are present in the errors list, along with an
|
310
|
+
additional errors attribute which provides reasons for the failure.
|
311
|
+
|
312
|
+
[Continue reading
|
313
|
+
here.](https://developers.procore.com/documentation/using-sync-actions)
|
314
|
+
|
315
|
+
Example Usage:
|
316
|
+
|
317
|
+
```ruby
|
318
|
+
client.sync(
|
319
|
+
"projects/sync",
|
320
|
+
body: {
|
321
|
+
updates: [
|
322
|
+
{ id: 1, name: "Update 1" },
|
323
|
+
{ id: 2, name: "Update 2" },
|
324
|
+
{ id: 3, name: "Update 3" },
|
325
|
+
...
|
326
|
+
...
|
327
|
+
{ id: 5055, name: "Update 5055" },
|
328
|
+
]
|
329
|
+
},
|
330
|
+
options: { batch_size: 500, company_id: 1 },
|
331
|
+
)
|
332
|
+
```
|
333
|
+
|
261
334
|
## Configuration
|
262
335
|
|
263
336
|
The Procore Gem exposes a configuration with several options.
|
@@ -272,6 +345,15 @@ Procore.configure do |config|
|
|
272
345
|
# instead of production.
|
273
346
|
config.host = ENV.fetch("PROCORE_BASE_API_PATH", "https://app.procore.com")
|
274
347
|
|
348
|
+
# When using #sync action, sets the default batch size to use for chunking
|
349
|
+
# up a request body. Example: if the size is set to 500, and 2,000 updates
|
350
|
+
# are desired, 4 requests will be made. Note, the maximum size is 1000.
|
351
|
+
config.default_batch_size = 500
|
352
|
+
|
353
|
+
# The default API version to use if none is specified in the request.
|
354
|
+
# Should be either "v1.0" (recommended) or "vapid" (legacy).
|
355
|
+
config.default_version = "v1.0"
|
356
|
+
|
275
357
|
# Integer: Number of times to retry a failed API call. Reasons an API call
|
276
358
|
# could potentially fail:
|
277
359
|
# 1. Service is briefly down or unreachable
|
@@ -313,6 +395,14 @@ Options: `session`: Instance of a Rails session
|
|
313
395
|
|
314
396
|
For applications that want to keep access tokens in the user's session.
|
315
397
|
|
398
|
+
:warning:
|
399
|
+
We strongly discourage using the session as a token store since the rails
|
400
|
+
session is often logged by default to external apps such as bugsnag etc. Be sure
|
401
|
+
you are not logging tokens. There is also the possibility that the rails session
|
402
|
+
is using a cookie store which, depending on application settings, could be
|
403
|
+
unencrypted. Tokens should not be stored client-side if it can be avoided.
|
404
|
+
:warning:
|
405
|
+
|
316
406
|
```ruby
|
317
407
|
store = Procore::Auth::Stores::Session.new(session: session)
|
318
408
|
```
|
@@ -331,6 +421,20 @@ The key will usually be the id of the current user.
|
|
331
421
|
store = Procore::Auth::Stores::Redis.new(redis: Redis.new, key: current_user.id)
|
332
422
|
```
|
333
423
|
|
424
|
+
### Dalli Store
|
425
|
+
|
426
|
+
Options: `dalli`: Instance of Dalli
|
427
|
+
Options: `key`: Unique identifier to an access token
|
428
|
+
|
429
|
+
For applications which want to store access tokens in memcached using Dalli.
|
430
|
+
There's two required options, `dalli` which is an instance of a Dalli client,
|
431
|
+
and `key` which is a unique key which will be used to save / retrieve an access
|
432
|
+
token. The key will usually be the id of the current user.
|
433
|
+
|
434
|
+
```ruby
|
435
|
+
store = Procore::Auth::Stores::Dalli.new(dalli: Dalli.new, key: current_user.id)
|
436
|
+
```
|
437
|
+
|
334
438
|
### ActiveRecord Store
|
335
439
|
|
336
440
|
Options: `object`: Instance of an ActiveRecord model.
|
@@ -410,6 +514,27 @@ class ProjectsController
|
|
410
514
|
end
|
411
515
|
end
|
412
516
|
```
|
517
|
+
## Contributing
|
518
|
+
|
519
|
+
To contribute to the gem, please clone the repo and cut a new branch. In the PR update the changelog with a short explanation of what you've changed, and your name under the "Unreleased" section. Example changelog update:
|
520
|
+
|
521
|
+
```markdown
|
522
|
+
## Unreleased
|
523
|
+
|
524
|
+
* Short sentence of what has changed
|
525
|
+
|
526
|
+
*Your Name*
|
527
|
+
```
|
528
|
+
|
529
|
+
Please **do not** bump the gem version in your PR. This will be done in a follow up PR by the gem maintainers.
|
530
|
+
|
531
|
+
### Tests
|
532
|
+
|
533
|
+
To run the specs run the following command:
|
534
|
+
```bash
|
535
|
+
$ bundle exec rake test
|
536
|
+
```
|
537
|
+
|
413
538
|
|
414
539
|
## License
|
415
540
|
|
data/lib/procore.rb
CHANGED
@@ -7,6 +7,7 @@ require "json"
|
|
7
7
|
require "procore/auth/access_token_credentials"
|
8
8
|
require "procore/auth/client_credentials"
|
9
9
|
require "procore/auth/stores/active_record"
|
10
|
+
require "procore/auth/stores/dalli"
|
10
11
|
require "procore/auth/stores/file"
|
11
12
|
require "procore/auth/stores/memory"
|
12
13
|
require "procore/auth/stores/redis"
|
@@ -29,7 +29,7 @@ module Procore
|
|
29
29
|
raise OAuthError.new(e.description, response: e.response)
|
30
30
|
rescue Faraday::ConnectionFailed => e
|
31
31
|
raise APIConnectionError.new("Connection Error: #{e.message}")
|
32
|
-
rescue URI::BadURIError
|
32
|
+
rescue URI::BadURIError, URI::InvalidURIError
|
33
33
|
raise OAuthError.new(
|
34
34
|
"Host is not a valid URI. Check your host option to make sure it " \
|
35
35
|
"is a properly formed url",
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Procore
|
2
|
+
module Auth
|
3
|
+
module Stores
|
4
|
+
class Dalli
|
5
|
+
attr_reader :key, :dalli
|
6
|
+
def initialize(key:, dalli:)
|
7
|
+
@key = key
|
8
|
+
@dalli = dalli
|
9
|
+
end
|
10
|
+
|
11
|
+
def save(token)
|
12
|
+
dalli.set(dalli_key, token.to_json)
|
13
|
+
end
|
14
|
+
|
15
|
+
def fetch
|
16
|
+
return unless dalli.get(dalli_key)
|
17
|
+
|
18
|
+
token = JSON.parse(dalli.get(dalli_key))
|
19
|
+
Procore::Auth::Token.new(
|
20
|
+
access_token: token["access_token"],
|
21
|
+
refresh_token: token["refresh_token"],
|
22
|
+
expires_at: token["expires_at"],
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete
|
27
|
+
dalli.delete(dalli_key)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def dalli_key
|
33
|
+
"procore-dalli-#{key}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/procore/client.rb
CHANGED
@@ -32,6 +32,15 @@ module Procore
|
|
32
32
|
# @return [String]
|
33
33
|
attr_accessor :host
|
34
34
|
|
35
|
+
# @!attribute [rw] default_version
|
36
|
+
# @note defaults to Defaults::DEFAULT_VERSION
|
37
|
+
#
|
38
|
+
# The default API version to use if none is specified in the request.
|
39
|
+
# Should be either "v1.0" (recommended) or "vapid" (legacy).
|
40
|
+
#
|
41
|
+
# @return [String]
|
42
|
+
attr_accessor :default_version
|
43
|
+
|
35
44
|
# @!attribute [rw] logger
|
36
45
|
# @note defaults to nil
|
37
46
|
#
|
@@ -77,12 +86,26 @@ module Procore
|
|
77
86
|
# @return [String]
|
78
87
|
attr_accessor :user_agent
|
79
88
|
|
89
|
+
# @!attribute [rw] default_batch_size
|
90
|
+
# @note defaults to Defaults::BATCH_SIZE
|
91
|
+
#
|
92
|
+
# When using #sync action, sets the default batch size to use for chunking
|
93
|
+
# up a request body. Example, if the size is set to 500 and 2,000 updates
|
94
|
+
# are desired, 4 requests will be made.
|
95
|
+
#
|
96
|
+
# Note: The maximum size is 1,000
|
97
|
+
#
|
98
|
+
# @return [Integer]
|
99
|
+
attr_accessor :default_batch_size
|
100
|
+
|
80
101
|
def initialize
|
102
|
+
@default_batch_size = Procore::Defaults::BATCH_SIZE
|
81
103
|
@host = Procore::Defaults::API_ENDPOINT
|
82
104
|
@logger = nil
|
83
105
|
@max_retries = 1
|
84
106
|
@timeout = 1.0
|
85
107
|
@user_agent = Procore::Defaults::USER_AGENT
|
108
|
+
@default_version = Procore::Defaults::DEFAULT_VERSION
|
86
109
|
end
|
87
110
|
end
|
88
111
|
end
|
data/lib/procore/defaults.rb
CHANGED
@@ -9,10 +9,17 @@ module Procore
|
|
9
9
|
# Default User Agent header string
|
10
10
|
USER_AGENT = "Procore Ruby Gem #{Procore::VERSION}".freeze
|
11
11
|
|
12
|
+
# Default size to use for batch requests
|
13
|
+
BATCH_SIZE = 500
|
14
|
+
|
15
|
+
# Default API version to use
|
16
|
+
DEFAULT_VERSION = "v1.0"
|
17
|
+
|
12
18
|
def self.client_options
|
13
19
|
{
|
14
20
|
host: Procore.configuration.host,
|
15
21
|
user_agent: Procore.configuration.user_agent,
|
22
|
+
default_version: Procore.configuration.default_version,
|
16
23
|
}
|
17
24
|
end
|
18
25
|
end
|
data/lib/procore/requestable.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "rest-client"
|
2
|
+
require "procore/errors"
|
2
3
|
|
3
4
|
module Procore
|
4
5
|
# Module which defines HTTP verbs GET, POST, PUT, PATCH and DELETE. Is
|
@@ -10,7 +11,15 @@ module Procore
|
|
10
11
|
# @example Using #post:
|
11
12
|
# client.post("projects", name: "New Project")
|
12
13
|
module Requestable
|
14
|
+
HTTP_EXCEPTIONS = [
|
15
|
+
Errno::ECONNREFUSED,
|
16
|
+
Errno::ECONNRESET,
|
17
|
+
Procore::OAuthError,
|
18
|
+
RestClient::Exceptions::Timeout,
|
19
|
+
RestClient::ServerBrokeConnection,
|
20
|
+
].freeze
|
13
21
|
# @param path [String] URL path
|
22
|
+
# @param version [String] API version
|
14
23
|
# @param query [Hash] Query options to pass along with the request
|
15
24
|
# @option options [Hash] :company_id
|
16
25
|
#
|
@@ -18,10 +27,12 @@ module Procore
|
|
18
27
|
# client.get("my_open_items", query: { per_page: 5, filter: {} })
|
19
28
|
#
|
20
29
|
# @return [Response]
|
21
|
-
def get(path, query: {}, options: {})
|
30
|
+
def get(path, version: nil, query: {}, options: {})
|
31
|
+
full_path = full_path(path, version)
|
32
|
+
|
22
33
|
Util.log_info(
|
23
34
|
"API Request Initiated",
|
24
|
-
path:
|
35
|
+
path: full_path,
|
25
36
|
method: "GET",
|
26
37
|
query: query.to_s,
|
27
38
|
)
|
@@ -29,7 +40,7 @@ module Procore
|
|
29
40
|
with_response_handling do
|
30
41
|
RestClient::Request.execute(
|
31
42
|
method: :get,
|
32
|
-
url:
|
43
|
+
url: full_path,
|
33
44
|
headers: headers(options).merge(params: query),
|
34
45
|
timeout: Procore.configuration.timeout,
|
35
46
|
)
|
@@ -37,8 +48,9 @@ module Procore
|
|
37
48
|
end
|
38
49
|
|
39
50
|
# @param path [String] URL path
|
51
|
+
# @param version [String] API version
|
40
52
|
# @param body [Hash] Body parameters to send with the request
|
41
|
-
# @param options [Hash
|
53
|
+
# @param options [Hash] Extra request options
|
42
54
|
# @option options [String] :idempotency_token | :company_id
|
43
55
|
#
|
44
56
|
# @example Usage
|
@@ -49,10 +61,12 @@ module Procore
|
|
49
61
|
# )
|
50
62
|
#
|
51
63
|
# @return [Response]
|
52
|
-
def post(path, body: {}, options: {})
|
64
|
+
def post(path, version: nil, body: {}, options: {})
|
65
|
+
full_path = full_path(path, version)
|
66
|
+
|
53
67
|
Util.log_info(
|
54
68
|
"API Request Initiated",
|
55
|
-
path:
|
69
|
+
path: full_path,
|
56
70
|
method: "POST",
|
57
71
|
body: body.to_s,
|
58
72
|
)
|
@@ -60,7 +74,7 @@ module Procore
|
|
60
74
|
with_response_handling(request_body: body) do
|
61
75
|
RestClient::Request.execute(
|
62
76
|
method: :post,
|
63
|
-
url:
|
77
|
+
url: full_path,
|
64
78
|
payload: payload(body),
|
65
79
|
headers: headers(options),
|
66
80
|
timeout: Procore.configuration.timeout,
|
@@ -69,18 +83,21 @@ module Procore
|
|
69
83
|
end
|
70
84
|
|
71
85
|
# @param path [String] URL path
|
86
|
+
# @param version [String] API version
|
72
87
|
# @param body [Hash] Body parameters to send with the request
|
73
|
-
# @param options [Hash
|
88
|
+
# @param options [Hash] Extra request options
|
74
89
|
# @option options [String] :idempotency_token | :company_id
|
75
90
|
#
|
76
91
|
# @example Usage
|
77
92
|
# client.put("dashboards/1/users", body: [1,2,3], options: { company_id: 1 })
|
78
93
|
#
|
79
94
|
# @return [Response]
|
80
|
-
def put(path, body: {}, options: {})
|
95
|
+
def put(path, version: nil, body: {}, options: {})
|
96
|
+
full_path = full_path(path, version)
|
97
|
+
|
81
98
|
Util.log_info(
|
82
99
|
"API Request Initiated",
|
83
|
-
path:
|
100
|
+
path: full_path,
|
84
101
|
method: "PUT",
|
85
102
|
body: body.to_s,
|
86
103
|
)
|
@@ -88,7 +105,7 @@ module Procore
|
|
88
105
|
with_response_handling(request_body: body) do
|
89
106
|
RestClient::Request.execute(
|
90
107
|
method: :put,
|
91
|
-
url:
|
108
|
+
url: full_path,
|
92
109
|
payload: payload(body),
|
93
110
|
headers: headers(options),
|
94
111
|
timeout: Procore.configuration.timeout,
|
@@ -97,8 +114,9 @@ module Procore
|
|
97
114
|
end
|
98
115
|
|
99
116
|
# @param path [String] URL path
|
117
|
+
# @param version [String] API version
|
100
118
|
# @param body [Hash] Body parameters to send with the request
|
101
|
-
# @param options [Hash
|
119
|
+
# @param options [Hash] Extra request options
|
102
120
|
# @option options [String] :idempotency_token | :company_id
|
103
121
|
#
|
104
122
|
# @example Usage
|
@@ -109,10 +127,12 @@ module Procore
|
|
109
127
|
# )
|
110
128
|
#
|
111
129
|
# @return [Response]
|
112
|
-
def patch(path, body: {}, options: {})
|
130
|
+
def patch(path, version: nil, body: {}, options: {})
|
131
|
+
full_path = full_path(path, version)
|
132
|
+
|
113
133
|
Util.log_info(
|
114
134
|
"API Request Initiated",
|
115
|
-
path:
|
135
|
+
path: full_path,
|
116
136
|
method: "PATCH",
|
117
137
|
body: body.to_s,
|
118
138
|
)
|
@@ -120,7 +140,7 @@ module Procore
|
|
120
140
|
with_response_handling(request_body: body) do
|
121
141
|
RestClient::Request.execute(
|
122
142
|
method: :patch,
|
123
|
-
url:
|
143
|
+
url: full_path,
|
124
144
|
payload: payload(body),
|
125
145
|
headers: headers(options),
|
126
146
|
timeout: Procore.configuration.timeout,
|
@@ -129,6 +149,73 @@ module Procore
|
|
129
149
|
end
|
130
150
|
|
131
151
|
# @param path [String] URL path
|
152
|
+
# @param version [String] API version
|
153
|
+
# @param body [Hash] Body parameters to send with the request
|
154
|
+
# @param options [Hash] Extra request options
|
155
|
+
# @option options [String | Integer] :company_id | :batch_size
|
156
|
+
#
|
157
|
+
# @example Usage
|
158
|
+
# client.sync(
|
159
|
+
# "projects/sync",
|
160
|
+
# body: {
|
161
|
+
# updates: [
|
162
|
+
# { id: 1, name: "Update 1" },
|
163
|
+
# { id: 2, name: "Update 2" },
|
164
|
+
# { id: 3, name: "Update 3" },
|
165
|
+
# ...
|
166
|
+
# ...
|
167
|
+
# { id: 5055, name: "Update 5055" },
|
168
|
+
# ]
|
169
|
+
# },
|
170
|
+
# options: { batch_size: 500, company_id: 1 },
|
171
|
+
# )
|
172
|
+
#
|
173
|
+
# @return [Response]
|
174
|
+
def sync(path, version: nil, body: {}, options: {})
|
175
|
+
full_path = full_path(path, version)
|
176
|
+
|
177
|
+
batch_size = options[:batch_size] ||
|
178
|
+
Procore.configuration.default_batch_size
|
179
|
+
|
180
|
+
if batch_size > 1000
|
181
|
+
batch_size = 1000
|
182
|
+
end
|
183
|
+
|
184
|
+
Util.log_info(
|
185
|
+
"API Request Initiated",
|
186
|
+
path: full_path,
|
187
|
+
method: "SYNC",
|
188
|
+
batch_size: batch_size,
|
189
|
+
)
|
190
|
+
|
191
|
+
groups = body[:updates].each_slice(batch_size).to_a
|
192
|
+
|
193
|
+
responses = groups.map do |group|
|
194
|
+
batched_body = body.merge(updates: group)
|
195
|
+
with_response_handling(request_body: batched_body) do
|
196
|
+
RestClient::Request.execute(
|
197
|
+
method: :patch,
|
198
|
+
url: full_path,
|
199
|
+
payload: payload(batched_body),
|
200
|
+
headers: headers(options),
|
201
|
+
timeout: Procore.configuration.timeout,
|
202
|
+
)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
Procore::Response.new(
|
207
|
+
body: responses.reduce({}) do |combined, response|
|
208
|
+
combined.deep_merge(response.body) { |_, v1, v2| v1 + v2 }
|
209
|
+
end.to_json,
|
210
|
+
headers: responses.map(&:headers).inject({}, &:deep_merge),
|
211
|
+
code: 200,
|
212
|
+
request: responses.last&.request,
|
213
|
+
request_body: body,
|
214
|
+
)
|
215
|
+
end
|
216
|
+
|
217
|
+
# @param path [String] URL path
|
218
|
+
# @param version [String] API version
|
132
219
|
# @param query [Hash] Query options to pass along with the request
|
133
220
|
# @option options [String] :company_id
|
134
221
|
#
|
@@ -136,10 +223,12 @@ module Procore
|
|
136
223
|
# client.delete("users/1", query: {}, options: {})
|
137
224
|
#
|
138
225
|
# @return [Response]
|
139
|
-
def delete(path, query: {}, options: {})
|
226
|
+
def delete(path, version: nil, query: {}, options: {})
|
227
|
+
full_path = full_path(path, version)
|
228
|
+
|
140
229
|
Util.log_info(
|
141
230
|
"API Request Initiated",
|
142
|
-
path:
|
231
|
+
path: full_path,
|
143
232
|
method: "DELETE",
|
144
233
|
headers: headers(options),
|
145
234
|
query: query.to_s,
|
@@ -148,7 +237,7 @@ module Procore
|
|
148
237
|
with_response_handling do
|
149
238
|
RestClient::Request.execute(
|
150
239
|
method: :delete,
|
151
|
-
url:
|
240
|
+
url: full_path,
|
152
241
|
headers: headers.merge(params: query),
|
153
242
|
timeout: Procore.configuration.timeout,
|
154
243
|
)
|
@@ -163,7 +252,7 @@ module Procore
|
|
163
252
|
|
164
253
|
begin
|
165
254
|
result = yield
|
166
|
-
rescue
|
255
|
+
rescue *HTTP_EXCEPTIONS => e
|
167
256
|
if retries <= Procore.configuration.max_retries
|
168
257
|
retries += 1
|
169
258
|
sleep 1.5**retries
|
@@ -277,5 +366,16 @@ module Procore
|
|
277
366
|
def multipart?(body)
|
278
367
|
RestClient::Payload::has_file?(body)
|
279
368
|
end
|
369
|
+
|
370
|
+
def full_path(path, version)
|
371
|
+
version ||= options[:default_version]
|
372
|
+
if version == "vapid"
|
373
|
+
File.join(base_api_path, "vapid", path)
|
374
|
+
elsif /\Av\d+\.\d+\z/.match?(version)
|
375
|
+
File.join(base_api_path, "rest", version, path)
|
376
|
+
else
|
377
|
+
raise ArgumentError.new "'#{version}' is an invalid Procore API version"
|
378
|
+
end
|
379
|
+
end
|
280
380
|
end
|
281
381
|
end
|
data/lib/procore/response.rb
CHANGED
@@ -72,7 +72,7 @@ module Procore
|
|
72
72
|
|
73
73
|
def parse_pagination
|
74
74
|
headers[:link].to_s.split(", ").map(&:strip).reduce({}) do |links, link|
|
75
|
-
url, name = link.match(/vapid\/(.*?)>; rel="(\w+)"/).captures
|
75
|
+
url, name = link.match(/(?:vapid|rest\/.*?)\/(.*?)>; rel="(\w+)"/).captures
|
76
76
|
links.merge!(name.to_sym => url)
|
77
77
|
end
|
78
78
|
end
|
data/lib/procore/version.rb
CHANGED
data/procore.gemspec
CHANGED
@@ -25,12 +25,15 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.add_development_dependency "actionpack"
|
26
26
|
spec.add_development_dependency "activerecord"
|
27
27
|
spec.add_development_dependency "bundler"
|
28
|
+
spec.add_development_dependency "dalli"
|
28
29
|
spec.add_development_dependency "fakefs"
|
29
30
|
spec.add_development_dependency "minitest"
|
30
31
|
spec.add_development_dependency "pry"
|
31
32
|
spec.add_development_dependency "rake"
|
32
33
|
spec.add_development_dependency "redis"
|
33
34
|
spec.add_development_dependency "rubocop"
|
35
|
+
spec.add_development_dependency "rubocop-performance"
|
36
|
+
spec.add_development_dependency "rubocop-rails"
|
34
37
|
spec.add_development_dependency "sqlite3"
|
35
38
|
spec.add_development_dependency "webmock"
|
36
39
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: procore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Procore Engineering
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: dalli
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: fakefs
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,6 +150,34 @@ dependencies:
|
|
136
150
|
- - ">="
|
137
151
|
- !ruby/object:Gem::Version
|
138
152
|
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rubocop-performance
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: rubocop-rails
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
139
181
|
- !ruby/object:Gem::Dependency
|
140
182
|
name: sqlite3
|
141
183
|
requirement: !ruby/object:Gem::Requirement
|
@@ -227,6 +269,7 @@ files:
|
|
227
269
|
- lib/procore/auth/access_token_credentials.rb
|
228
270
|
- lib/procore/auth/client_credentials.rb
|
229
271
|
- lib/procore/auth/stores/active_record.rb
|
272
|
+
- lib/procore/auth/stores/dalli.rb
|
230
273
|
- lib/procore/auth/stores/file.rb
|
231
274
|
- lib/procore/auth/stores/memory.rb
|
232
275
|
- lib/procore/auth/stores/redis.rb
|
@@ -260,8 +303,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
260
303
|
- !ruby/object:Gem::Version
|
261
304
|
version: '0'
|
262
305
|
requirements: []
|
263
|
-
|
264
|
-
rubygems_version: 2.7.6
|
306
|
+
rubygems_version: 3.1.4
|
265
307
|
signing_key:
|
266
308
|
specification_version: 4
|
267
309
|
summary: Procore Ruby Gem
|