procore 0.8.7 → 1.1.2
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 -15
- data/CHANGELOG.md +87 -1
- data/README.md +132 -10
- data/lib/procore/auth/access_token_credentials.rb +11 -0
- data/lib/procore/client.rb +49 -29
- data/lib/procore/configuration.rb +23 -0
- data/lib/procore/defaults.rb +8 -1
- data/lib/procore/requestable.rb +94 -16
- data/lib/procore/response.rb +3 -4
- data/lib/procore/version.rb +1 -1
- data/procore-1.1.1.gem +0 -0
- data/procore.gemspec +3 -1
- metadata +37 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 49f3e1daa62e1998646029758f63b21f973a9851
|
|
4
|
+
data.tar.gz: a2613a7ea2ddd4aa4ce6cbb7531a267af94dc195
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 92474ea3b4e4ac1c4658ed192e929e9ac6b203dead4b1b5e0e424fc545d723197a606e906e224f7809f2914ed7dca83243d38bb9c6ee783d1422c7c36f0ecb90
|
|
7
|
+
data.tar.gz: 707e8d18312f9c029fe86849eecc84dc50f4fd52191c4938c858a7ad0d420aac6798673575fb413a8c37b90def7f5c4bfdc5826a20f23525a2a253f52d586558
|
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,29 +1,25 @@
|
|
|
1
1
|
language: ruby
|
|
2
2
|
rvm:
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
- 2.3
|
|
4
|
+
- 2.4
|
|
5
|
+
- 2.5
|
|
6
|
+
- 2.6
|
|
8
7
|
services:
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
- redis-server
|
|
9
|
+
- memcached
|
|
11
10
|
before_install:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
- gem update --system
|
|
12
|
+
- gem install bundler
|
|
15
13
|
cache: bundler
|
|
16
|
-
|
|
17
14
|
script: bundle exec rake
|
|
18
|
-
|
|
19
15
|
deploy:
|
|
20
16
|
provider: rubygems
|
|
21
17
|
api_key:
|
|
22
|
-
secure:
|
|
23
|
-
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
|
|
24
20
|
on:
|
|
25
21
|
tags: true
|
|
26
22
|
repo: procore/ruby-sdk
|
|
27
|
-
|
|
23
|
+
skip_cleanup: 'true'
|
|
28
24
|
notifications:
|
|
29
25
|
email: false
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,90 @@
|
|
|
1
|
-
##
|
|
1
|
+
## 1.1.2 (Jun 4, 2021)
|
|
2
|
+
|
|
3
|
+
* Add Procore-Sdk-Version header to all requests
|
|
4
|
+
|
|
5
|
+
PR #44 - https://github.com/procore/ruby-sdk/pull/44
|
|
6
|
+
|
|
7
|
+
*Benjamin Ross*
|
|
8
|
+
|
|
9
|
+
## 1.1.1 (May 5, 2021)
|
|
10
|
+
|
|
11
|
+
* Change default host to 'api.procore.com'.
|
|
12
|
+
|
|
13
|
+
PR #42 - https://github.com/procore/ruby-sdk/pull/42
|
|
14
|
+
|
|
15
|
+
*Nate Baer*
|
|
16
|
+
|
|
17
|
+
## 1.1.0 (March 11, 2021)
|
|
18
|
+
|
|
19
|
+
* Allow tokens to be revoked and manually refreshed.
|
|
20
|
+
|
|
21
|
+
PR #39 - https://github.com/procore/ruby-sdk/pull/39
|
|
22
|
+
|
|
23
|
+
*Nate Baer*
|
|
24
|
+
|
|
25
|
+
## 1.0.0 (January 5, 2021)
|
|
26
|
+
|
|
27
|
+
* Adds support for API versioning
|
|
28
|
+
|
|
29
|
+
*Nate Baer*
|
|
30
|
+
|
|
31
|
+
### Upgrading
|
|
32
|
+
|
|
33
|
+
As of v1.0.0, this gem now defaults to making requests against Procore's new
|
|
34
|
+
Rest v1.0 resources, instead of the now deprecated `/vapid` namespace. Example:
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
# Previously makes a request to
|
|
38
|
+
client.get("me")
|
|
39
|
+
=> app.procore.com/vapid/me
|
|
40
|
+
|
|
41
|
+
# In 1.0.0
|
|
42
|
+
client.get("me")
|
|
43
|
+
=> app.procore.com/rest/v1.0/me
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
To keep the legacy behavior, set the new `default_version` configuration option.
|
|
47
|
+
Note, that Rest v1.0 is a superset of the Vapid Api - there are no breaking
|
|
48
|
+
changes. The Vapid API will be decommissioned in December 2021.
|
|
49
|
+
|
|
50
|
+
[Read more here](https://developers.procore.com/documentation/vapid-deprecation)
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
Procore.configure do |config|
|
|
54
|
+
...
|
|
55
|
+
# Defaults to "v1.0"
|
|
56
|
+
config.default_version = "vapid"
|
|
57
|
+
...
|
|
58
|
+
end
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
All the request methods (`get`, `post`, `patch`, `put`, `delete`, `sync`) now
|
|
62
|
+
accept an optional version parameter to specify the version at request time.
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
client.get("me")
|
|
66
|
+
=> https://app.procore.com/rest/v1.0/me
|
|
67
|
+
|
|
68
|
+
client.get("me", version: "v1.1")
|
|
69
|
+
=> https://app.procore.com/rest/v1.1/me
|
|
70
|
+
|
|
71
|
+
client.get("me", version: "vapid")
|
|
72
|
+
=> https://app.procore.com/vapid/me
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 0.8.8 (October 17, 2019)
|
|
76
|
+
|
|
77
|
+
* Expose #sync, a method that enables calling sync-actions
|
|
78
|
+
|
|
79
|
+
*Patrick Koperwas*
|
|
80
|
+
|
|
81
|
+
* Addition of contribution guidelines to README
|
|
82
|
+
|
|
83
|
+
*Megan O'Neill*
|
|
84
|
+
|
|
85
|
+
* Fix TravisCI failures
|
|
86
|
+
|
|
87
|
+
*Patrick Koperwas*
|
|
2
88
|
|
|
3
89
|
## 0.8.7 (April 18, 2019)
|
|
4
90
|
|
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,30 @@ 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` |
|
|
71
|
+
|
|
72
|
+
In addition to the settings above, you will need to set `company_id` in the request
|
|
73
|
+
options to work with [Multiple Procore Zones (MPZ)](https://developers.procore.com/documentation/tutorial-mpz).
|
|
55
74
|
|
|
56
75
|
Example Usage:
|
|
57
76
|
|
|
@@ -67,6 +86,24 @@ client = Procore::Client.new(
|
|
|
67
86
|
companies = client.get("companies")
|
|
68
87
|
|
|
69
88
|
companies.first[:name] #=> "Procore Company 1"
|
|
89
|
+
|
|
90
|
+
# Get a company's projects (note the company_id value in options)
|
|
91
|
+
projects = client.get("projects", query: {company_id: <company_id>}, options: {company_id: <company_id>})
|
|
92
|
+
|
|
93
|
+
projects.first[:name] #=> "Project 1"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
To use Procore's older API Vapid by default, the default version can be set in
|
|
97
|
+
either the Gem's [configuration](https://github.com/procore/ruby-sdk#configuration)
|
|
98
|
+
or the client's `options` hash:
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
client = Procore::Client.new(
|
|
102
|
+
...
|
|
103
|
+
options {
|
|
104
|
+
default_version: "vapid"
|
|
105
|
+
}
|
|
106
|
+
)
|
|
70
107
|
```
|
|
71
108
|
|
|
72
109
|
## Usage
|
|
@@ -112,6 +149,18 @@ client = Procore::Client.new(
|
|
|
112
149
|
client.get("me")
|
|
113
150
|
```
|
|
114
151
|
|
|
152
|
+
Expired tokens will automatically be refreshed, but can also be refreshed manually:
|
|
153
|
+
|
|
154
|
+
```ruby
|
|
155
|
+
client.refresh
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Tokens may also be manually revoked, forcing the client to refresh its token on the next request:
|
|
159
|
+
|
|
160
|
+
```ruby
|
|
161
|
+
client.revoke
|
|
162
|
+
```
|
|
163
|
+
|
|
115
164
|
## Error Handling
|
|
116
165
|
|
|
117
166
|
The Procore Gem raises errors whenever a request returns a non `2xx` response.
|
|
@@ -259,6 +308,49 @@ puts first_page.pagination
|
|
|
259
308
|
Notice that because `per_page` has been set to 250, there are only two pages of
|
|
260
309
|
results (500 resources / 250 page size = 2 pages).
|
|
261
310
|
|
|
311
|
+
## Sync Actions
|
|
312
|
+
The Sync action enables batch creation or updates to resources using a single
|
|
313
|
+
call. When using a Sync action, the resources to be created or updated can be
|
|
314
|
+
specified by supplying either an `id` or an `origin_id` in the request body.
|
|
315
|
+
Utilizing the `origin_id` attribute for batch operations is often preferable as
|
|
316
|
+
it allows you to easily link to external systems by maintaining your own list of
|
|
317
|
+
unique resource identifiers outside of Procore.
|
|
318
|
+
|
|
319
|
+
The caller provides an array of hashes, each hash containing the attributes for
|
|
320
|
+
a single resource. The attribute names in each hash match those used by the
|
|
321
|
+
Create and Update actions for the resource. Attributes for a maximum of 1000
|
|
322
|
+
resources within a collection may be passed with each call. The API will always
|
|
323
|
+
return an HTTP status of 200.
|
|
324
|
+
|
|
325
|
+
The response body contains two attributes - `entities` and `errors`. The
|
|
326
|
+
attributes for each successfully created or updated resource will appear in the
|
|
327
|
+
entities list. The attributes for each resource will match those returned by the
|
|
328
|
+
Show action. For each resource which could not be created or updated, the
|
|
329
|
+
attributes supplied by the caller are present in the errors list, along with an
|
|
330
|
+
additional errors attribute which provides reasons for the failure.
|
|
331
|
+
|
|
332
|
+
[Continue reading
|
|
333
|
+
here.](https://developers.procore.com/documentation/using-sync-actions)
|
|
334
|
+
|
|
335
|
+
Example Usage:
|
|
336
|
+
|
|
337
|
+
```ruby
|
|
338
|
+
client.sync(
|
|
339
|
+
"projects/sync",
|
|
340
|
+
body: {
|
|
341
|
+
updates: [
|
|
342
|
+
{ id: 1, name: "Update 1" },
|
|
343
|
+
{ id: 2, name: "Update 2" },
|
|
344
|
+
{ id: 3, name: "Update 3" },
|
|
345
|
+
...
|
|
346
|
+
...
|
|
347
|
+
{ id: 5055, name: "Update 5055" },
|
|
348
|
+
]
|
|
349
|
+
},
|
|
350
|
+
options: { batch_size: 500, company_id: 1 },
|
|
351
|
+
)
|
|
352
|
+
```
|
|
353
|
+
|
|
262
354
|
## Configuration
|
|
263
355
|
|
|
264
356
|
The Procore Gem exposes a configuration with several options.
|
|
@@ -271,7 +363,16 @@ Procore.configure do |config|
|
|
|
271
363
|
# Base API host name. Alter this depending on your environment - in a
|
|
272
364
|
# staging or test environment you may want to point this at a sandbox
|
|
273
365
|
# instead of production.
|
|
274
|
-
config.host = ENV.fetch("PROCORE_BASE_API_PATH", "https://
|
|
366
|
+
config.host = ENV.fetch("PROCORE_BASE_API_PATH", "https://api.procore.com")
|
|
367
|
+
|
|
368
|
+
# When using #sync action, sets the default batch size to use for chunking
|
|
369
|
+
# up a request body. Example: if the size is set to 500, and 2,000 updates
|
|
370
|
+
# are desired, 4 requests will be made. Note, the maximum size is 1000.
|
|
371
|
+
config.default_batch_size = 500
|
|
372
|
+
|
|
373
|
+
# The default API version to use if none is specified in the request.
|
|
374
|
+
# Should be either "v1.0" (recommended) or "vapid" (legacy).
|
|
375
|
+
config.default_version = "v1.0"
|
|
275
376
|
|
|
276
377
|
# Integer: Number of times to retry a failed API call. Reasons an API call
|
|
277
378
|
# could potentially fail:
|
|
@@ -433,6 +534,27 @@ class ProjectsController
|
|
|
433
534
|
end
|
|
434
535
|
end
|
|
435
536
|
```
|
|
537
|
+
## Contributing
|
|
538
|
+
|
|
539
|
+
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:
|
|
540
|
+
|
|
541
|
+
```markdown
|
|
542
|
+
## Unreleased
|
|
543
|
+
|
|
544
|
+
* Short sentence of what has changed
|
|
545
|
+
|
|
546
|
+
*Your Name*
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
Please **do not** bump the gem version in your PR. This will be done in a follow up PR by the gem maintainers.
|
|
550
|
+
|
|
551
|
+
### Tests
|
|
552
|
+
|
|
553
|
+
To run the specs run the following command:
|
|
554
|
+
```bash
|
|
555
|
+
$ bundle exec rake test
|
|
556
|
+
```
|
|
557
|
+
|
|
436
558
|
|
|
437
559
|
## License
|
|
438
560
|
|
|
@@ -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
|
data/lib/procore/client.rb
CHANGED
|
@@ -38,23 +38,56 @@ module Procore
|
|
|
38
38
|
@store = store
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
# @raise [OAuthError] if a token cannot be refreshed.
|
|
42
|
+
def refresh
|
|
43
|
+
token = fetch_token
|
|
42
44
|
|
|
43
|
-
|
|
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
|
|
45
62
|
end
|
|
46
63
|
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
49
80
|
end
|
|
50
81
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
token = store.fetch
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
def base_api_path
|
|
85
|
+
"#{options[:host]}"
|
|
86
|
+
end
|
|
57
87
|
|
|
88
|
+
# @raise [OAuthError] if the store does not have a token stored.
|
|
89
|
+
def fetch_token
|
|
90
|
+
token = store.fetch
|
|
58
91
|
if token.nil? || token.invalid?
|
|
59
92
|
raise Procore::MissingTokenError.new(
|
|
60
93
|
"Unable to retreive an access token from the store. Double check " \
|
|
@@ -62,28 +95,15 @@ module Procore
|
|
|
62
95
|
"before attempting to make API requests",
|
|
63
96
|
)
|
|
64
97
|
end
|
|
98
|
+
token
|
|
99
|
+
end
|
|
65
100
|
|
|
101
|
+
def access_token
|
|
102
|
+
token = fetch_token
|
|
66
103
|
if token.expired?
|
|
67
104
|
Util.log_info("Token Expired", store: store)
|
|
68
|
-
|
|
69
|
-
token = @credentials.refresh(
|
|
70
|
-
token: token.access_token,
|
|
71
|
-
refresh: token.refresh_token,
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
Util.log_info("Token Refresh Successful", store: store)
|
|
75
|
-
store.save(token)
|
|
76
|
-
rescue RuntimeError
|
|
77
|
-
Util.log_error("Token Refresh Failed", store: store)
|
|
78
|
-
raise Procore::OAuthError.new(
|
|
79
|
-
"Unable to refresh the access token. Perhaps the Procore API is " \
|
|
80
|
-
"down or the your access token store is misconfigured. Either " \
|
|
81
|
-
"way, you should clear the store and prompt the user to sign in " \
|
|
82
|
-
"again.",
|
|
83
|
-
)
|
|
84
|
-
end
|
|
105
|
+
refresh
|
|
85
106
|
end
|
|
86
|
-
|
|
87
107
|
token.access_token
|
|
88
108
|
end
|
|
89
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",
|
|
@@ -204,7 +275,6 @@ module Procore
|
|
|
204
275
|
code: result.code,
|
|
205
276
|
request: result.request,
|
|
206
277
|
request_body: request_body,
|
|
207
|
-
api_version: api_version
|
|
208
278
|
)
|
|
209
279
|
|
|
210
280
|
case result.code
|
|
@@ -273,6 +343,7 @@ module Procore
|
|
|
273
343
|
"Accepts" => "application/json",
|
|
274
344
|
"Authorization" => "Bearer #{access_token}",
|
|
275
345
|
"Content-Type" => "application/json",
|
|
346
|
+
"Procore-Sdk-Version" => "ruby-#{Procore::VERSION}",
|
|
276
347
|
"User-Agent" => Procore.configuration.user_agent,
|
|
277
348
|
}.tap do |headers|
|
|
278
349
|
if options[:idempotency_token]
|
|
@@ -297,8 +368,15 @@ module Procore
|
|
|
297
368
|
RestClient::Payload::has_file?(body)
|
|
298
369
|
end
|
|
299
370
|
|
|
300
|
-
def full_path(path)
|
|
301
|
-
|
|
371
|
+
def full_path(path, version)
|
|
372
|
+
version ||= options[:default_version]
|
|
373
|
+
if version == "vapid"
|
|
374
|
+
File.join(base_api_path, "vapid", path)
|
|
375
|
+
elsif /\Av\d+\.\d+\z/.match?(version)
|
|
376
|
+
File.join(base_api_path, "rest", version, path)
|
|
377
|
+
else
|
|
378
|
+
raise Procore::InvalidRequestError.new "#{version} is an invalid Procore API version"
|
|
379
|
+
end
|
|
302
380
|
end
|
|
303
381
|
end
|
|
304
382
|
end
|
data/lib/procore/response.rb
CHANGED
|
@@ -45,12 +45,11 @@ module Procore
|
|
|
45
45
|
# @return [Integer] Status Code returned from Procore API.
|
|
46
46
|
# @!attribute [r] pagination
|
|
47
47
|
# @return [Hash<Symbol, String>] Pagination URLs
|
|
48
|
-
attr_reader :headers, :code, :pagination, :request, :request_body
|
|
48
|
+
attr_reader :headers, :code, :pagination, :request, :request_body
|
|
49
49
|
|
|
50
|
-
def initialize(body:, headers:, code:, request:, request_body
|
|
50
|
+
def initialize(body:, headers:, code:, request:, request_body:)
|
|
51
51
|
@code = code
|
|
52
52
|
@headers = headers
|
|
53
|
-
@api_version = api_version
|
|
54
53
|
@pagination = parse_pagination
|
|
55
54
|
@request = request
|
|
56
55
|
@request_body = request_body
|
|
@@ -73,7 +72,7 @@ module Procore
|
|
|
73
72
|
|
|
74
73
|
def parse_pagination
|
|
75
74
|
headers[:link].to_s.split(", ").map(&:strip).reduce({}) do |links, link|
|
|
76
|
-
url, name = link.match(
|
|
75
|
+
url, name = link.match(/(?:vapid|rest\/.*?)\/(.*?)>; rel="(\w+)"/).captures
|
|
77
76
|
links.merge!(name.to_sym => url)
|
|
78
77
|
end
|
|
79
78
|
end
|
data/lib/procore/version.rb
CHANGED
data/procore-1.1.1.gem
ADDED
|
Binary file
|
data/procore.gemspec
CHANGED
|
@@ -32,7 +32,9 @@ 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 "
|
|
35
|
+
spec.add_development_dependency "rubocop-performance"
|
|
36
|
+
spec.add_development_dependency "rubocop-rails"
|
|
37
|
+
spec.add_development_dependency "sqlite3"
|
|
36
38
|
spec.add_development_dependency "webmock"
|
|
37
39
|
|
|
38
40
|
spec.add_dependency "activesupport", "> 2.4"
|
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.2
|
|
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-06-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: actionpack
|
|
@@ -150,20 +150,48 @@ 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
|
|
156
184
|
requirements:
|
|
157
|
-
- - "
|
|
185
|
+
- - ">="
|
|
158
186
|
- !ruby/object:Gem::Version
|
|
159
|
-
version:
|
|
187
|
+
version: '0'
|
|
160
188
|
type: :development
|
|
161
189
|
prerelease: false
|
|
162
190
|
version_requirements: !ruby/object:Gem::Requirement
|
|
163
191
|
requirements:
|
|
164
|
-
- - "
|
|
192
|
+
- - ">="
|
|
165
193
|
- !ruby/object:Gem::Version
|
|
166
|
-
version:
|
|
194
|
+
version: '0'
|
|
167
195
|
- !ruby/object:Gem::Dependency
|
|
168
196
|
name: webmock
|
|
169
197
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -255,6 +283,7 @@ files:
|
|
|
255
283
|
- lib/procore/response.rb
|
|
256
284
|
- lib/procore/util.rb
|
|
257
285
|
- lib/procore/version.rb
|
|
286
|
+
- procore-1.1.1.gem
|
|
258
287
|
- procore.gemspec
|
|
259
288
|
homepage: https://github.com/procore/ruby-sdk
|
|
260
289
|
licenses:
|
|
@@ -275,7 +304,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
275
304
|
- !ruby/object:Gem::Version
|
|
276
305
|
version: '0'
|
|
277
306
|
requirements: []
|
|
278
|
-
|
|
307
|
+
rubyforge_project:
|
|
308
|
+
rubygems_version: 2.6.14
|
|
279
309
|
signing_key:
|
|
280
310
|
specification_version: 4
|
|
281
311
|
summary: Procore Ruby Gem
|