procore 0.8.6 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.rubocop.yml +9 -5
- data/.travis.yml +11 -14
- data/CHANGELOG.md +86 -2
- data/README.md +132 -10
- data/lib/procore/auth/access_token_credentials.rb +11 -0
- data/lib/procore/auth/client_credentials.rb +1 -1
- data/lib/procore/client.rb +49 -25
- data/lib/procore/configuration.rb +23 -0
- data/lib/procore/defaults.rb +8 -1
- data/lib/procore/requestable.rb +93 -15
- data/lib/procore/response.rb +1 -1
- data/lib/procore/version.rb +1 -1
- data/procore.gemspec +2 -0
- metadata +31 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d521ee6322fa923e85f77d6c836d7e5f9cb208df
|
4
|
+
data.tar.gz: 48eee44692be1ce01bd486ec57dc05699b1c9198
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a94c2bd6877729616f381027130ee9216093a9fc5ffb9bc2af473dffa45ac1050d39a280fd92ad6e6978ff6b8d2551afe464002743a46d0b6b34be9a5161a86d
|
7
|
+
data.tar.gz: aaa762a18d06ea9e432111fadd89ae6eb389ff645ef2f9b488278f1710c3d368595054565e5d7b97ece4f9e73f6c6c3ff80d00c628658613c47f712b82261d42
|
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.5
|
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
@@ -1,28 +1,25 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
- 2.3
|
4
|
+
- 2.4
|
5
|
+
- 2.5
|
6
|
+
- 2.6
|
7
7
|
services:
|
8
|
-
|
9
|
-
|
8
|
+
- redis-server
|
9
|
+
- memcached
|
10
10
|
before_install:
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
- gem update --system
|
12
|
+
- gem install bundler
|
14
13
|
cache: bundler
|
15
|
-
|
16
14
|
script: bundle exec rake
|
17
|
-
|
18
15
|
deploy:
|
19
16
|
provider: rubygems
|
20
17
|
api_key:
|
21
|
-
secure:
|
22
|
-
gem:
|
18
|
+
secure: LC+5qDeaqBtA8IIDt0yHNYsTA/KEs7IK/hESF2V2f4bhmMapifEVSjikoZVE1lTe0R2+CyO+5mWHloFeTLayTchpZhW3yfduei3l2tktZOH9UCsXIQZKveSZlCMQ+XOn65Gstbd/5krh9+axKZswgM5YgBhjXNRgjL+yOGh2kXN0exk5nBvIBFxSCQdwqGiBDcrtgJ4eWrlPgofXszMFb3ogNQcIuUcKmAOC0j/fNag4qQV0jnp3zHDuOjrsARPJn4w6RMqSLpFVUYpJw3tE94GDW5kILHr7taTPj12PeYNbYjW7kRSgMYbQlIWUKLeyMp+vw3kNqd3Ysat+M+Mrf3z2KRKR5w62IJnCq758uerCdUo5v/vsWHYFcJvZWLrvsPSk+xQWysAqRdkun7ZBCK/FLR75S6Vkm0DBnbqd/yYp6MP9Vbi77gIZpWMXANfdk1aWGVlPNpskYeWyqPjwZX1nYybJQIIpZfX21F2WPrNA9r/cj1KYjb8vKopMEdn9GP9xMeUXXu85K+ZVPqSD/AblrLHyD2x7cES/i4+XcQSaKUOSLxTUqemAiDRBahDCehPOOeLHnu/rUzgQr1gzQL0YcLNgHovmIdstljBys62JC4gy376cNawiSW4ZPMHWg6PewqQ6Ozg4Lkytx06bBwOos55dFgI4k0T4H0/6QiE=
|
19
|
+
gem: ruby-sdk
|
23
20
|
on:
|
24
21
|
tags: true
|
25
22
|
repo: procore/ruby-sdk
|
26
|
-
|
23
|
+
skip_cleanup: 'true'
|
27
24
|
notifications:
|
28
25
|
email: false
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,88 @@
|
|
1
|
-
##
|
1
|
+
## 1.1.1 (May 5, 2021)
|
2
|
+
|
3
|
+
* Change default host to 'api.procore.com'.
|
4
|
+
|
5
|
+
PR #42 - https://github.com/procore/ruby-sdk/pull/42
|
6
|
+
|
7
|
+
*Nate Baer*
|
8
|
+
|
9
|
+
## 1.1.0 (March 11, 2021)
|
10
|
+
|
11
|
+
* Allow tokens to be revoked and manually refreshed.
|
12
|
+
|
13
|
+
PR #39 - https://github.com/procore/ruby-sdk/pull/39
|
14
|
+
|
15
|
+
*Nate Baer*
|
16
|
+
|
17
|
+
## 1.0.0 (January 5, 2021)
|
18
|
+
|
19
|
+
* Adds support for API versioning
|
20
|
+
|
21
|
+
*Nate Baer*
|
22
|
+
|
23
|
+
### Upgrading
|
24
|
+
|
25
|
+
As of v1.0.0, this gem now defaults to making requests against Procore's new
|
26
|
+
Rest v1.0 resources, instead of the now deprecated `/vapid` namespace. Example:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
# Previously makes a request to
|
30
|
+
client.get("me")
|
31
|
+
=> app.procore.com/vapid/me
|
32
|
+
|
33
|
+
# In 1.0.0
|
34
|
+
client.get("me")
|
35
|
+
=> app.procore.com/rest/v1.0/me
|
36
|
+
```
|
37
|
+
|
38
|
+
To keep the legacy behavior, set the new `default_version` configuration option.
|
39
|
+
Note, that Rest v1.0 is a superset of the Vapid Api - there are no breaking
|
40
|
+
changes. The Vapid API will be decommissioned in December 2021.
|
41
|
+
|
42
|
+
[Read more here](https://developers.procore.com/documentation/vapid-deprecation)
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
Procore.configure do |config|
|
46
|
+
...
|
47
|
+
# Defaults to "v1.0"
|
48
|
+
config.default_version = "vapid"
|
49
|
+
...
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
All the request methods (`get`, `post`, `patch`, `put`, `delete`, `sync`) now
|
54
|
+
accept an optional version parameter to specify the version at request time.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
client.get("me")
|
58
|
+
=> https://app.procore.com/rest/v1.0/me
|
59
|
+
|
60
|
+
client.get("me", version: "v1.1")
|
61
|
+
=> https://app.procore.com/rest/v1.1/me
|
62
|
+
|
63
|
+
client.get("me", version: "vapid")
|
64
|
+
=> https://app.procore.com/vapid/me
|
65
|
+
```
|
66
|
+
|
67
|
+
## 0.8.8 (October 17, 2019)
|
68
|
+
|
69
|
+
* Expose #sync, a method that enables calling sync-actions
|
70
|
+
|
71
|
+
*Patrick Koperwas*
|
72
|
+
|
73
|
+
* Addition of contribution guidelines to README
|
74
|
+
|
75
|
+
*Megan O'Neill*
|
76
|
+
|
77
|
+
* Fix TravisCI failures
|
78
|
+
|
79
|
+
*Patrick Koperwas*
|
80
|
+
|
81
|
+
## 0.8.7 (April 18, 2019)
|
82
|
+
|
83
|
+
* Add api_version to allow calls to procore rest endpoints
|
84
|
+
|
85
|
+
*Shane Means*
|
2
86
|
|
3
87
|
## 0.8.6 (May 10, 2018)
|
4
88
|
|
@@ -7,7 +91,7 @@
|
|
7
91
|
*Patrick Koperwas*
|
8
92
|
|
9
93
|
* Fix Requestable paths to prevent double slash in URI
|
10
|
-
|
94
|
+
|
11
95
|
*Megan O'Neill*
|
12
96
|
|
13
97
|
## 0.8.5 (May 9, 2018)
|
data/README.md
CHANGED
@@ -4,12 +4,14 @@
|
|
4
4
|
|
5
5
|
#### Table of Contents
|
6
6
|
- [Installation](#installation)
|
7
|
+
- [1.0.0 Release](#100-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)
|
@@ -19,6 +21,7 @@
|
|
19
21
|
- [File Store](#file-store)
|
20
22
|
- [Memory Store](#memory-store)
|
21
23
|
- [Full Example](#full-example)
|
24
|
+
- [Contributing](#contributing)
|
22
25
|
|
23
26
|
## Installation
|
24
27
|
|
@@ -28,6 +31,11 @@ Add this line to your application's Gemfile:
|
|
28
31
|
gem "procore"
|
29
32
|
```
|
30
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
|
+
|
31
39
|
## Making Requests
|
32
40
|
|
33
41
|
At the core of the gem is the `Client` class. Clients are initialized with a
|
@@ -39,19 +47,27 @@ Stores automatically manage tokens for you - refreshing, revoking and storage
|
|
39
47
|
are abstracted away to make your code as simple as possible. There are several
|
40
48
|
different [types of stores](#stores) available to you.
|
41
49
|
|
42
|
-
The Client class exposes `#get`, `#post`, `#put`, `#patch
|
43
|
-
to you.
|
50
|
+
The Client class exposes `#get`, `#post`, `#put`, `#patch`, `#sync` and
|
51
|
+
`#delete` methods to you.
|
44
52
|
|
45
53
|
```ruby
|
46
|
-
get(path, query: {})
|
47
|
-
post(path, body: {}, options: {})
|
48
|
-
put(path, body: {}, options: {})
|
49
|
-
patch(path, body: {}, options: {})
|
50
|
-
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: {})
|
51
60
|
```
|
52
61
|
|
53
|
-
All paths are relative
|
54
|
-
`
|
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://api.procore.com/rest/v1.0/me` |
|
69
|
+
| `client.get("me", version: "v1.1")` | `https://api.procore.com/rest/v1.1/me` |
|
70
|
+
| `client.get("me", version: "vapid")` | `https://api.procore.com/vapid/me` |
|
55
71
|
|
56
72
|
Example Usage:
|
57
73
|
|
@@ -69,6 +85,19 @@ companies = client.get("companies")
|
|
69
85
|
companies.first[:name] #=> "Procore Company 1"
|
70
86
|
```
|
71
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
|
+
|
72
101
|
## Usage
|
73
102
|
|
74
103
|
The first step is to place the user's token into the store. For this example,
|
@@ -112,6 +141,18 @@ client = Procore::Client.new(
|
|
112
141
|
client.get("me")
|
113
142
|
```
|
114
143
|
|
144
|
+
Expired tokens will automatically be refreshed, but can also be refreshed manually:
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
client.refresh
|
148
|
+
```
|
149
|
+
|
150
|
+
Tokens may also be manually revoked, forcing the client to refresh its token on the next request:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
client.revoke
|
154
|
+
```
|
155
|
+
|
115
156
|
## Error Handling
|
116
157
|
|
117
158
|
The Procore Gem raises errors whenever a request returns a non `2xx` response.
|
@@ -259,6 +300,49 @@ puts first_page.pagination
|
|
259
300
|
Notice that because `per_page` has been set to 250, there are only two pages of
|
260
301
|
results (500 resources / 250 page size = 2 pages).
|
261
302
|
|
303
|
+
## Sync Actions
|
304
|
+
The Sync action enables batch creation or updates to resources using a single
|
305
|
+
call. When using a Sync action, the resources to be created or updated can be
|
306
|
+
specified by supplying either an `id` or an `origin_id` in the request body.
|
307
|
+
Utilizing the `origin_id` attribute for batch operations is often preferable as
|
308
|
+
it allows you to easily link to external systems by maintaining your own list of
|
309
|
+
unique resource identifiers outside of Procore.
|
310
|
+
|
311
|
+
The caller provides an array of hashes, each hash containing the attributes for
|
312
|
+
a single resource. The attribute names in each hash match those used by the
|
313
|
+
Create and Update actions for the resource. Attributes for a maximum of 1000
|
314
|
+
resources within a collection may be passed with each call. The API will always
|
315
|
+
return an HTTP status of 200.
|
316
|
+
|
317
|
+
The response body contains two attributes - `entities` and `errors`. The
|
318
|
+
attributes for each successfully created or updated resource will appear in the
|
319
|
+
entities list. The attributes for each resource will match those returned by the
|
320
|
+
Show action. For each resource which could not be created or updated, the
|
321
|
+
attributes supplied by the caller are present in the errors list, along with an
|
322
|
+
additional errors attribute which provides reasons for the failure.
|
323
|
+
|
324
|
+
[Continue reading
|
325
|
+
here.](https://developers.procore.com/documentation/using-sync-actions)
|
326
|
+
|
327
|
+
Example Usage:
|
328
|
+
|
329
|
+
```ruby
|
330
|
+
client.sync(
|
331
|
+
"projects/sync",
|
332
|
+
body: {
|
333
|
+
updates: [
|
334
|
+
{ id: 1, name: "Update 1" },
|
335
|
+
{ id: 2, name: "Update 2" },
|
336
|
+
{ id: 3, name: "Update 3" },
|
337
|
+
...
|
338
|
+
...
|
339
|
+
{ id: 5055, name: "Update 5055" },
|
340
|
+
]
|
341
|
+
},
|
342
|
+
options: { batch_size: 500, company_id: 1 },
|
343
|
+
)
|
344
|
+
```
|
345
|
+
|
262
346
|
## Configuration
|
263
347
|
|
264
348
|
The Procore Gem exposes a configuration with several options.
|
@@ -271,7 +355,16 @@ Procore.configure do |config|
|
|
271
355
|
# Base API host name. Alter this depending on your environment - in a
|
272
356
|
# staging or test environment you may want to point this at a sandbox
|
273
357
|
# instead of production.
|
274
|
-
config.host = ENV.fetch("PROCORE_BASE_API_PATH", "https://
|
358
|
+
config.host = ENV.fetch("PROCORE_BASE_API_PATH", "https://api.procore.com")
|
359
|
+
|
360
|
+
# When using #sync action, sets the default batch size to use for chunking
|
361
|
+
# up a request body. Example: if the size is set to 500, and 2,000 updates
|
362
|
+
# are desired, 4 requests will be made. Note, the maximum size is 1000.
|
363
|
+
config.default_batch_size = 500
|
364
|
+
|
365
|
+
# The default API version to use if none is specified in the request.
|
366
|
+
# Should be either "v1.0" (recommended) or "vapid" (legacy).
|
367
|
+
config.default_version = "v1.0"
|
275
368
|
|
276
369
|
# Integer: Number of times to retry a failed API call. Reasons an API call
|
277
370
|
# could potentially fail:
|
@@ -314,6 +407,14 @@ Options: `session`: Instance of a Rails session
|
|
314
407
|
|
315
408
|
For applications that want to keep access tokens in the user's session.
|
316
409
|
|
410
|
+
:warning:
|
411
|
+
We strongly discourage using the session as a token store since the rails
|
412
|
+
session is often logged by default to external apps such as bugsnag etc. Be sure
|
413
|
+
you are not logging tokens. There is also the possibility that the rails session
|
414
|
+
is using a cookie store which, depending on application settings, could be
|
415
|
+
unencrypted. Tokens should not be stored client-side if it can be avoided.
|
416
|
+
:warning:
|
417
|
+
|
317
418
|
```ruby
|
318
419
|
store = Procore::Auth::Stores::Session.new(session: session)
|
319
420
|
```
|
@@ -425,6 +526,27 @@ class ProjectsController
|
|
425
526
|
end
|
426
527
|
end
|
427
528
|
```
|
529
|
+
## Contributing
|
530
|
+
|
531
|
+
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:
|
532
|
+
|
533
|
+
```markdown
|
534
|
+
## Unreleased
|
535
|
+
|
536
|
+
* Short sentence of what has changed
|
537
|
+
|
538
|
+
*Your Name*
|
539
|
+
```
|
540
|
+
|
541
|
+
Please **do not** bump the gem version in your PR. This will be done in a follow up PR by the gem maintainers.
|
542
|
+
|
543
|
+
### Tests
|
544
|
+
|
545
|
+
To run the specs run the following command:
|
546
|
+
```bash
|
547
|
+
$ bundle exec rake test
|
548
|
+
```
|
549
|
+
|
428
550
|
|
429
551
|
## License
|
430
552
|
|
@@ -32,6 +32,17 @@ module Procore
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
def revoke(token:)
|
36
|
+
request = {
|
37
|
+
client_id: @client_id,
|
38
|
+
client_secret: @client_secret,
|
39
|
+
token: token.access_token,
|
40
|
+
}
|
41
|
+
client.request(:post, "/oauth/revoke", body: request)
|
42
|
+
rescue RestClient::ExceptionWithResponse
|
43
|
+
raise OAuthError.new(e.description, response: e.response)
|
44
|
+
end
|
45
|
+
|
35
46
|
private
|
36
47
|
|
37
48
|
def client
|
@@ -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",
|
data/lib/procore/client.rb
CHANGED
@@ -38,19 +38,56 @@ module Procore
|
|
38
38
|
@store = store
|
39
39
|
end
|
40
40
|
|
41
|
+
# @raise [OAuthError] if a token cannot be refreshed.
|
42
|
+
def refresh
|
43
|
+
token = fetch_token
|
44
|
+
|
45
|
+
begin
|
46
|
+
new_token = @credentials.refresh(
|
47
|
+
token: token.access_token,
|
48
|
+
refresh: token.refresh_token,
|
49
|
+
)
|
50
|
+
|
51
|
+
Util.log_info("Token Refresh Successful", store: store)
|
52
|
+
store.save(new_token)
|
53
|
+
rescue RuntimeError
|
54
|
+
Util.log_error("Token Refresh Failed", store: store)
|
55
|
+
raise Procore::OAuthError.new(
|
56
|
+
"Unable to refresh the access token. Perhaps the Procore API is " \
|
57
|
+
"down or your access token store is misconfigured. Either " \
|
58
|
+
"way, you should clear the store and prompt the user to sign in " \
|
59
|
+
"again.",
|
60
|
+
)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# @raise [OAuthError] if a token cannot be revoked.
|
65
|
+
def revoke
|
66
|
+
token = fetch_token
|
67
|
+
|
68
|
+
begin
|
69
|
+
@credentials.revoke(token: token)
|
70
|
+
Util.log_info("Token Revocation Successful", store: store)
|
71
|
+
rescue RuntimeError
|
72
|
+
Util.log_error("Token Revocation Failed", store: store)
|
73
|
+
raise Procore::OAuthError.new(
|
74
|
+
"Unable to revoke the access token. Perhaps the Procore API is " \
|
75
|
+
"down or your access token store is misconfigured. Either " \
|
76
|
+
"way, you should clear the store and prompt the user to sign in " \
|
77
|
+
"again.",
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
41
82
|
private
|
42
83
|
|
43
84
|
def base_api_path
|
44
|
-
"#{options[:host]}
|
85
|
+
"#{options[:host]}"
|
45
86
|
end
|
46
87
|
|
47
|
-
# @raise [OAuthError] if the store does not have a token stored
|
48
|
-
|
49
|
-
# @raise [OAuthError] if a token cannot be refreshed.
|
50
|
-
# @raise [OAuthError] if incorrect credentials have been supplied.
|
51
|
-
def access_token
|
88
|
+
# @raise [OAuthError] if the store does not have a token stored.
|
89
|
+
def fetch_token
|
52
90
|
token = store.fetch
|
53
|
-
|
54
91
|
if token.nil? || token.invalid?
|
55
92
|
raise Procore::MissingTokenError.new(
|
56
93
|
"Unable to retreive an access token from the store. Double check " \
|
@@ -58,28 +95,15 @@ module Procore
|
|
58
95
|
"before attempting to make API requests",
|
59
96
|
)
|
60
97
|
end
|
98
|
+
token
|
99
|
+
end
|
61
100
|
|
101
|
+
def access_token
|
102
|
+
token = fetch_token
|
62
103
|
if token.expired?
|
63
104
|
Util.log_info("Token Expired", store: store)
|
64
|
-
|
65
|
-
token = @credentials.refresh(
|
66
|
-
token: token.access_token,
|
67
|
-
refresh: token.refresh_token,
|
68
|
-
)
|
69
|
-
|
70
|
-
Util.log_info("Token Refresh Successful", store: store)
|
71
|
-
store.save(token)
|
72
|
-
rescue RuntimeError
|
73
|
-
Util.log_error("Token Refresh Failed", store: store)
|
74
|
-
raise Procore::OAuthError.new(
|
75
|
-
"Unable to refresh the access token. Perhaps the Procore API is " \
|
76
|
-
"down or the your access token store is misconfigured. Either " \
|
77
|
-
"way, you should clear the store and prompt the user to sign in " \
|
78
|
-
"again.",
|
79
|
-
)
|
80
|
-
end
|
105
|
+
refresh
|
81
106
|
end
|
82
|
-
|
83
107
|
token.access_token
|
84
108
|
end
|
85
109
|
end
|
@@ -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
@@ -4,15 +4,22 @@ module Procore
|
|
4
4
|
# Specifies some sensible defaults for certain configurations + clients
|
5
5
|
class Defaults
|
6
6
|
# Default API endpoint
|
7
|
-
API_ENDPOINT = "https://
|
7
|
+
API_ENDPOINT = "https://api.procore.com".freeze
|
8
8
|
|
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
@@ -19,6 +19,7 @@ module Procore
|
|
19
19
|
RestClient::ServerBrokeConnection,
|
20
20
|
].freeze
|
21
21
|
# @param path [String] URL path
|
22
|
+
# @param version [String] API version
|
22
23
|
# @param query [Hash] Query options to pass along with the request
|
23
24
|
# @option options [Hash] :company_id
|
24
25
|
#
|
@@ -26,8 +27,8 @@ module Procore
|
|
26
27
|
# client.get("my_open_items", query: { per_page: 5, filter: {} })
|
27
28
|
#
|
28
29
|
# @return [Response]
|
29
|
-
def get(path, query: {}, options: {})
|
30
|
-
full_path = full_path(path)
|
30
|
+
def get(path, version: nil, query: {}, options: {})
|
31
|
+
full_path = full_path(path, version)
|
31
32
|
|
32
33
|
Util.log_info(
|
33
34
|
"API Request Initiated",
|
@@ -47,8 +48,9 @@ module Procore
|
|
47
48
|
end
|
48
49
|
|
49
50
|
# @param path [String] URL path
|
51
|
+
# @param version [String] API version
|
50
52
|
# @param body [Hash] Body parameters to send with the request
|
51
|
-
# @param options [Hash
|
53
|
+
# @param options [Hash] Extra request options
|
52
54
|
# @option options [String] :idempotency_token | :company_id
|
53
55
|
#
|
54
56
|
# @example Usage
|
@@ -59,8 +61,8 @@ module Procore
|
|
59
61
|
# )
|
60
62
|
#
|
61
63
|
# @return [Response]
|
62
|
-
def post(path, body: {}, options: {})
|
63
|
-
full_path = full_path(path)
|
64
|
+
def post(path, version: nil, body: {}, options: {})
|
65
|
+
full_path = full_path(path, version)
|
64
66
|
|
65
67
|
Util.log_info(
|
66
68
|
"API Request Initiated",
|
@@ -81,16 +83,17 @@ module Procore
|
|
81
83
|
end
|
82
84
|
|
83
85
|
# @param path [String] URL path
|
86
|
+
# @param version [String] API version
|
84
87
|
# @param body [Hash] Body parameters to send with the request
|
85
|
-
# @param options [Hash
|
88
|
+
# @param options [Hash] Extra request options
|
86
89
|
# @option options [String] :idempotency_token | :company_id
|
87
90
|
#
|
88
91
|
# @example Usage
|
89
92
|
# client.put("dashboards/1/users", body: [1,2,3], options: { company_id: 1 })
|
90
93
|
#
|
91
94
|
# @return [Response]
|
92
|
-
def put(path, body: {}, options: {})
|
93
|
-
full_path = full_path(path)
|
95
|
+
def put(path, version: nil, body: {}, options: {})
|
96
|
+
full_path = full_path(path, version)
|
94
97
|
|
95
98
|
Util.log_info(
|
96
99
|
"API Request Initiated",
|
@@ -111,8 +114,9 @@ module Procore
|
|
111
114
|
end
|
112
115
|
|
113
116
|
# @param path [String] URL path
|
117
|
+
# @param version [String] API version
|
114
118
|
# @param body [Hash] Body parameters to send with the request
|
115
|
-
# @param options [Hash
|
119
|
+
# @param options [Hash] Extra request options
|
116
120
|
# @option options [String] :idempotency_token | :company_id
|
117
121
|
#
|
118
122
|
# @example Usage
|
@@ -123,8 +127,8 @@ module Procore
|
|
123
127
|
# )
|
124
128
|
#
|
125
129
|
# @return [Response]
|
126
|
-
def patch(path, body: {}, options: {})
|
127
|
-
full_path = full_path(path)
|
130
|
+
def patch(path, version: nil, body: {}, options: {})
|
131
|
+
full_path = full_path(path, version)
|
128
132
|
|
129
133
|
Util.log_info(
|
130
134
|
"API Request Initiated",
|
@@ -145,6 +149,73 @@ module Procore
|
|
145
149
|
end
|
146
150
|
|
147
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
|
148
219
|
# @param query [Hash] Query options to pass along with the request
|
149
220
|
# @option options [String] :company_id
|
150
221
|
#
|
@@ -152,8 +223,8 @@ module Procore
|
|
152
223
|
# client.delete("users/1", query: {}, options: {})
|
153
224
|
#
|
154
225
|
# @return [Response]
|
155
|
-
def delete(path, query: {}, options: {})
|
156
|
-
full_path = full_path(path)
|
226
|
+
def delete(path, version: nil, query: {}, options: {})
|
227
|
+
full_path = full_path(path, version)
|
157
228
|
|
158
229
|
Util.log_info(
|
159
230
|
"API Request Initiated",
|
@@ -296,8 +367,15 @@ module Procore
|
|
296
367
|
RestClient::Payload::has_file?(body)
|
297
368
|
end
|
298
369
|
|
299
|
-
def full_path(path)
|
300
|
-
|
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 Procore::InvalidRequestError.new "#{version} is an invalid Procore API version"
|
378
|
+
end
|
301
379
|
end
|
302
380
|
end
|
303
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
@@ -32,6 +32,8 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.add_development_dependency "rake"
|
33
33
|
spec.add_development_dependency "redis"
|
34
34
|
spec.add_development_dependency "rubocop"
|
35
|
+
spec.add_development_dependency "rubocop-performance"
|
36
|
+
spec.add_development_dependency "rubocop-rails"
|
35
37
|
spec.add_development_dependency "sqlite3"
|
36
38
|
spec.add_development_dependency "webmock"
|
37
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:
|
4
|
+
version: 1.1.1
|
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-05-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -150,6 +150,34 @@ dependencies:
|
|
150
150
|
- - ">="
|
151
151
|
- !ruby/object:Gem::Version
|
152
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'
|
153
181
|
- !ruby/object:Gem::Dependency
|
154
182
|
name: sqlite3
|
155
183
|
requirement: !ruby/object:Gem::Requirement
|
@@ -276,7 +304,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
276
304
|
version: '0'
|
277
305
|
requirements: []
|
278
306
|
rubyforge_project:
|
279
|
-
rubygems_version: 2.
|
307
|
+
rubygems_version: 2.6.14
|
280
308
|
signing_key:
|
281
309
|
specification_version: 4
|
282
310
|
summary: Procore Ruby Gem
|