xod_client 1.0.0
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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +95 -0
- data/bin/console +7 -0
- data/lib/xod_client.rb +12 -0
- data/lib/xod_client/concerns/data_endpoints.rb +78 -0
- data/lib/xod_client/concerns/info_endpoints.rb +30 -0
- data/lib/xod_client/concerns/parse_util.rb +11 -0
- data/lib/xod_client/config.rb +88 -0
- data/lib/xod_client/connection.rb +62 -0
- data/lib/xod_client/endpoint_call.rb +122 -0
- data/lib/xod_client/token_refresher.rb +30 -0
- data/lib/xod_client/version.rb +3 -0
- metadata +181 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 29f4e13cd84127865af96ae8aab6865d3e155aa5
|
4
|
+
data.tar.gz: a4b1cb2bfb56a4ac365413192ad5f2330b41ef59
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 56fd725db012c67c8432a192a6b19b354999105b7429b3fb47562f9b5d2d77fbb37f94de88f2ee0b38b040958f4168a72eea7daa722f181731cac8a0289d72bf
|
7
|
+
data.tar.gz: 855b575302f0d3e5d824d46185caa97188b9ef602759b05d5c6e0f14a57c8710a33c59305dc28983213906c07377ea29f2b18acfb98588276689e4c4ec77af74
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 TeamSatchel
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# Xod Client
|
2
|
+
|
3
|
+
Fetch Groupcall's [Xporter on Demand](https://xporter.groupcall.com) data with ease.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add gem to Gemfile as usual
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'xod_client'
|
11
|
+
```
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
First initialize XoD client with _relying party_, _estab_ and _secret password_ components:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
client = XodClient.init('relying-party.com', '3281101', 'secret')
|
19
|
+
```
|
20
|
+
|
21
|
+
Optionally you can pass previously remembered token options, to avoid refresh token request:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
token_options = { token: 'abcd', token_expires_at: Time.iso8601('2018-06-24T18:55:26.1852229Z') }
|
25
|
+
client = XodClient.init('relying-party.com', '3281101', 'secret', token_options)
|
26
|
+
```
|
27
|
+
|
28
|
+
You can request a new token in the following way:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
client.refresh_token
|
32
|
+
client.token #=> 'new-token'
|
33
|
+
client.token_expires_at #=> new-expiry-time
|
34
|
+
```
|
35
|
+
|
36
|
+
By calling any endpoint current token is checked and if it's stale a new one is requested automatically.
|
37
|
+
|
38
|
+
## Calling XoD endpoints
|
39
|
+
|
40
|
+
You can call any endpoint described in [XoD documentation](https://xporter.groupcall.com/) by its name, eg.:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
client.endpoint('school.schoolinfo').fetch
|
44
|
+
#-OR-
|
45
|
+
client.endpoint(endpoint_name: 'school.schoolinfo').fetch
|
46
|
+
```
|
47
|
+
|
48
|
+
For many endpoints there are handy shortcuts, eg.:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
# info endpoints:
|
52
|
+
client.token_details
|
53
|
+
client.scopes
|
54
|
+
client.queries
|
55
|
+
client.logs
|
56
|
+
client.usage
|
57
|
+
client.gdpr_ids
|
58
|
+
|
59
|
+
# data endpoints:
|
60
|
+
client.groups
|
61
|
+
client.school_info
|
62
|
+
client.staff
|
63
|
+
client.students
|
64
|
+
client.timetable
|
65
|
+
client.timetable_model
|
66
|
+
```
|
67
|
+
|
68
|
+
You can pass arguments to endpoints, eg.:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
client.groups(type: 'RegGrp', options: %w(includeStaffMembers includeStudentMembers), page_size: 10)
|
72
|
+
```
|
73
|
+
|
74
|
+
Note: `options` and `select` arguments can be passed as an array or string of values concatenated by `,`
|
75
|
+
|
76
|
+
## Fetcher methods
|
77
|
+
|
78
|
+
To fetch results from endpoint you can use these methods:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
client.groups.fetch #=> returns json response
|
82
|
+
client.groups(page_size: 100).fetch { |endpoint| } #=> fetches page by page and passes endpoint object to a block
|
83
|
+
client.groups.each { |group| } #=> returns each group json to a block
|
84
|
+
client.groups.first #=> returns first group json
|
85
|
+
client.groups[:__pagination__] #=> returns arbitrary data from json by key
|
86
|
+
```
|
87
|
+
|
88
|
+
## Exceptions handling
|
89
|
+
|
90
|
+
In case of connection failures the code tries to call endpoint up to 3 times. Then it throws default Net HTTP errors.
|
91
|
+
|
92
|
+
If XoD returns a JSON error response then `XodClient::ResponseError` exception is raised.
|
93
|
+
|
94
|
+
## License
|
95
|
+
`xod_client` is MIT licensed. See the [accompanying file](LICENSE.txt) for the full text.
|
data/bin/console
ADDED
data/lib/xod_client.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
module XodClient
|
2
|
+
module DataEndpoints
|
3
|
+
# COMMON_PARAMS: estab, updated_since, page, page_size, changed_rows, select, callback_url
|
4
|
+
|
5
|
+
# params: options, id, type, code, COMMON_PARAMS
|
6
|
+
# options: includeStaffMembers
|
7
|
+
# includeStudentMembers
|
8
|
+
# includeMembersIds
|
9
|
+
# includeMembersIdaasIds
|
10
|
+
# includeMembersXIDs
|
11
|
+
# includeSubjects
|
12
|
+
# includeGroupTypes
|
13
|
+
# type: YearGrp, HouseGrp, RegGrp, TeachingGrp
|
14
|
+
def groups(**params)
|
15
|
+
endpoint('school.groups', params)
|
16
|
+
end
|
17
|
+
|
18
|
+
def health(**params)
|
19
|
+
endpoint('school.health', params)
|
20
|
+
end
|
21
|
+
|
22
|
+
# params: COMMON_PARAMS
|
23
|
+
def school_info(**params)
|
24
|
+
endpoint('school.schoolinfo', params)
|
25
|
+
end
|
26
|
+
|
27
|
+
# params: id, staff_status, COMMON_PARAMS
|
28
|
+
# options: includePhotos
|
29
|
+
# includeGroupIds
|
30
|
+
# includeGroupIdaasIds
|
31
|
+
# includeGroupXIDs
|
32
|
+
# teachersOnly
|
33
|
+
# includeFuture
|
34
|
+
# includePrevious
|
35
|
+
# staff_status: Current, Future, Previous
|
36
|
+
def staff(**params)
|
37
|
+
endpoint('school.staff', params)
|
38
|
+
end
|
39
|
+
|
40
|
+
# params: options, id, year_group_id, student_status, onlygetDBStatus, COMMON_PARAMS
|
41
|
+
# options: includePhotos
|
42
|
+
# includeGroupIds
|
43
|
+
# includeGroupIdaasIds
|
44
|
+
# includeGroupXIDs
|
45
|
+
# includeParentIdaasIds
|
46
|
+
# includeParentXIDs
|
47
|
+
# includeAttStats
|
48
|
+
# includeAttMarkString
|
49
|
+
# includeSiblingsList
|
50
|
+
# includeSiblingsResultset
|
51
|
+
# includeLeavers
|
52
|
+
# includePreAdmissions
|
53
|
+
# includeGuests
|
54
|
+
# includeSubsidiary
|
55
|
+
def students(**params)
|
56
|
+
endpoint('school.students', params)
|
57
|
+
end
|
58
|
+
|
59
|
+
# params: options, date_from, date_to, COMMON_PARAMS
|
60
|
+
# options: includeLessons
|
61
|
+
# includeLessonStaff
|
62
|
+
# includeLessonRooms
|
63
|
+
def timetable(**params)
|
64
|
+
endpoint('school.timetable', params)
|
65
|
+
end
|
66
|
+
|
67
|
+
# params: COMMON_PARAMS
|
68
|
+
def timetable_model(**params)
|
69
|
+
endpoint('school.timetablemodel', params)
|
70
|
+
end
|
71
|
+
|
72
|
+
# params: date_from, date_to, COMMON_PARAMS
|
73
|
+
def timetable_structure(**params)
|
74
|
+
endpoint('school.timetablestructure', params)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module XodClient
|
2
|
+
module InfoEndpoints
|
3
|
+
|
4
|
+
def token_details
|
5
|
+
endpoint(:token_details)
|
6
|
+
end
|
7
|
+
|
8
|
+
def scopes
|
9
|
+
endpoint(:scopes)
|
10
|
+
end
|
11
|
+
|
12
|
+
def queries
|
13
|
+
endpoint(:queries)
|
14
|
+
end
|
15
|
+
|
16
|
+
# params: tracking_id, starting_at, take
|
17
|
+
def logs(**params)
|
18
|
+
endpoint(:logs, params)
|
19
|
+
end
|
20
|
+
|
21
|
+
def usage
|
22
|
+
endpoint(:usage)
|
23
|
+
end
|
24
|
+
|
25
|
+
def gdpr_ids
|
26
|
+
endpoint(:gdpr_ids)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module XodClient
|
2
|
+
module ParseUtil
|
3
|
+
|
4
|
+
def parse_xod_response(response)
|
5
|
+
json = JSON.parse(response.body).deep_transform_keys! { |key| key.underscore.to_sym }
|
6
|
+
raise ResponseError, json[:exception_message] if json[:exception_message]
|
7
|
+
json
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module XodClient
|
2
|
+
class Config
|
3
|
+
|
4
|
+
def login_url
|
5
|
+
'https://login.groupcall.com/idaas/sts/STS/GetToken'
|
6
|
+
end
|
7
|
+
|
8
|
+
def root_url
|
9
|
+
'https://xporter.groupcall.com'
|
10
|
+
end
|
11
|
+
|
12
|
+
def retry_options
|
13
|
+
{ max: 2, interval: 0.25.seconds, backoff_factor: 2, retry_statuses: [500, 502, 503] }
|
14
|
+
end
|
15
|
+
|
16
|
+
def user_agent
|
17
|
+
'TeamSatchel XoD Client'
|
18
|
+
end
|
19
|
+
|
20
|
+
# rubocop:disable MethodLength
|
21
|
+
def endpoints
|
22
|
+
@endpoints ||= {
|
23
|
+
token_details: '/api/v1/TokenDetails',
|
24
|
+
queries: '/api/v1/Queries',
|
25
|
+
scopes: '/api/v1/Scopes',
|
26
|
+
logs: '/api/v1/TokenDetails',
|
27
|
+
usage: '/api/v1/Usage',
|
28
|
+
gdpr_ids: '/api/v1/GDPRIDs',
|
29
|
+
'edubase.search' => '/api/v1/RunQuery/?id=edubase.search',
|
30
|
+
'school.achievementfordaterange' => '/api/v1/School/{estab}/AchievementForDateRange/{id}',
|
31
|
+
'school.achievementfortoday' => '/api/v1/School/{estab}/AchievementForToday/{id}',
|
32
|
+
'school.agencyagent' => '/api/v1/School/{estab}/AgencyAgent/',
|
33
|
+
'school.assessmentresults' => '/api/v1/School/{estab}/assessmentresults/',
|
34
|
+
'school.assessmentresultsbyaspect' => '/api/v1/School/{estab}/assessmentresults/',
|
35
|
+
'school.assessmentresultsbymarksheet' => '/api/v1/School/{estab}/assessmentresults/',
|
36
|
+
'school.assessmentresultsbystudent' => '/api/v1/School/{estab}/assessmentresults/',
|
37
|
+
'school.assessmentstructure' => '/api/v1/School/{estab}/assessmentstructure/',
|
38
|
+
'school.attendancecodes' => '/api/v1/School/{estab}/AttendanceCodes/{id}',
|
39
|
+
'school.attendancefordate' => '/api/v1/School/{estab}/AttendanceForDate/{id}',
|
40
|
+
'school.attendancefordaterange' => '/api/v1/School/{estab}/AttendanceForDateRange/{id}',
|
41
|
+
'school.behaviourfordaterange' => '/api/v1/School/{estab}/BehaviourForDateRange/{id}',
|
42
|
+
'school.behaviourfortoday' => '/api/v1/School/{estab}/BehaviourForToday/{id}',
|
43
|
+
'school.calendar' => '/api/v1/School/{estab}/Calendar/{id}',
|
44
|
+
'school.conductfordaterange' => '/api/v1/School/{estab}/ConductForDateRange/{id}',
|
45
|
+
'school.conductfortoday' => '/api/v1/School/{estab}/ConductForToday/{id}',
|
46
|
+
'school.conductlookups' => '/api/v1/School/{estab}/conductlookups/',
|
47
|
+
'school.conductpoints' => '/api/v1/School/{estab}/ConductPoints/{id}',
|
48
|
+
'school.contacts' => '/api/v1/School/{estab}/Contacts/{id}',
|
49
|
+
'school.counts' => '/api/v1/School/{estab}/Counts',
|
50
|
+
'school.entitlementhistory' => '/api/v1/School/{estab}/EntitlementHistory/{id}',
|
51
|
+
'school.groups' => '/api/v1/School/{estab}/Groups/{id}',
|
52
|
+
'school.health' => '/api/v1/School/{estab}/Health',
|
53
|
+
'school.linkeddocuments' => '/api/v1/School/{estab}/LinkedDocuments',
|
54
|
+
'school.linkeddocumenttypes' => '/api/v1/School/{estab}/LinkedDocumentTypes',
|
55
|
+
'school.personcomms' => '/api/v1/School/{estab}/PersonComms/{id}',
|
56
|
+
'school.photos' => '/api/v1/School/{estab}/Photos/{id}',
|
57
|
+
'school.schoolinfo' => '/api/v1/School/{estab}/SchoolInfo',
|
58
|
+
'school.seatingplans' => '/api/v1/School/{estab}/SeatingPlans',
|
59
|
+
'school.sen' => '/api/v1/School/{estab}/SEN/{id}',
|
60
|
+
'school.staff' => '/api/v1/School/{estab}/Staff/{id}',
|
61
|
+
'school.staffabsence' => '/api/v1/School/{estab}/StaffAbsence/{id}',
|
62
|
+
'school.staffcontracts' => '/api/v1/School/{estab}/StaffContracts',
|
63
|
+
'school.studentcontacts' => '/api/v1/School/{estab}/StudentContacts/{id}',
|
64
|
+
'school.studentexclusions' => '/api/v1/School/{estab}/StudentExclusions/{id}',
|
65
|
+
'school.studentmeals' => '/api/v1/School/{estab}/StudentMeals/{id}',
|
66
|
+
'school.studentmedical' => '/api/v1/School/{estab}/StudentMedical/{id}',
|
67
|
+
'school.students' => '/api/v1/School/{estab}/Students/{id}',
|
68
|
+
'school.studentschoolhistory' => '/api/v1/School/{estab}/StudentSchoolHistory/{id}',
|
69
|
+
'school.studentuserdefinedfields' => '/api/v1/School/{estab}/studentuserdefinedfields/{id}',
|
70
|
+
'school.timetable' => '/api/v1/School/{estab}/Timetable/',
|
71
|
+
'school.timetableforstaff' => '/api/v1/School/{estab}/TimetableForStaff/{id}',
|
72
|
+
'school.timetableforstudent' => '/api/v1/School/{estab}/TimetableForStudent/{id}',
|
73
|
+
'school.timetablemodel' => '/api/v1/School/{estab}/TimetableModel',
|
74
|
+
'school.timetablestructure' => '/api/v1/School/{estab}/TimetableStructure',
|
75
|
+
'school.userdefinedfields' => '/api/v1/School/{estab}/UserDefinedFields/{id}',
|
76
|
+
'POST.Attendance' => '/api/v1/Attendance/{estab}/',
|
77
|
+
'POST.AttendanceAsync' => '/api/v1/AttendanceAsync/{estab}/',
|
78
|
+
'POST.ConductAch' => '/api/v1/Writeback/Conduct',
|
79
|
+
'POST.ConductAsync' => '/api/v1/Writeback/ConductAsync',
|
80
|
+
'POST.ConductBeh' => '/api/v1/Writeback/Conduct',
|
81
|
+
'POST.LessonAttendance' => '/api/v1/Attendance/{estab}/',
|
82
|
+
'POST.SeatingPlan' => '/api/v1/Writeback/SeatingPlan',
|
83
|
+
'POST.efw.Detention' => '/api/v1/Writeback/?id=efw.Detention',
|
84
|
+
}.with_indifferent_access
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'xod_client/config'
|
2
|
+
require 'xod_client/concerns/data_endpoints'
|
3
|
+
require 'xod_client/concerns/info_endpoints'
|
4
|
+
require 'xod_client/concerns/parse_util'
|
5
|
+
require 'xod_client/token_refresher'
|
6
|
+
require 'xod_client/endpoint_call'
|
7
|
+
|
8
|
+
module XodClient
|
9
|
+
ResponseError = Class.new(StandardError)
|
10
|
+
|
11
|
+
class Connection
|
12
|
+
include InfoEndpoints
|
13
|
+
include DataEndpoints
|
14
|
+
|
15
|
+
attr_reader :relying_party, :estab, :secret, :token, :token_expires_at,
|
16
|
+
:response_logger, :token_refreshed_proc, :config
|
17
|
+
|
18
|
+
# rubocop:disable Metrics/ParameterLists
|
19
|
+
def initialize(relying_party, estab, secret,
|
20
|
+
token: nil, token_expires_at: nil, response_logger: nil, token_refreshed_proc: nil)
|
21
|
+
@relying_party = relying_party
|
22
|
+
@estab = estab
|
23
|
+
@secret = secret
|
24
|
+
@token = token
|
25
|
+
@token_expires_at = token_expires_at
|
26
|
+
@response_logger = response_logger
|
27
|
+
@token_refreshed_proc = token_refreshed_proc
|
28
|
+
@config = Config.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def endpoint(endpoint_name = nil, **params)
|
32
|
+
endpoint_name ||= params.delete(:endpoint_name) || raise(ArgumentError, 'Endpoint name should be provided')
|
33
|
+
ensure_token
|
34
|
+
|
35
|
+
EndpointCall.new(self, endpoint_name, params)
|
36
|
+
end
|
37
|
+
|
38
|
+
def ensure_token
|
39
|
+
refresh_token unless valid_token?
|
40
|
+
end
|
41
|
+
|
42
|
+
def refresh_token
|
43
|
+
@token, @token_expires_at = TokenRefresher.new(self).perform
|
44
|
+
token_refreshed_proc&.call(token: @token, token_expires_at: @token_expires_at)
|
45
|
+
end
|
46
|
+
|
47
|
+
def valid_token?
|
48
|
+
token && token_expires_at&.future?
|
49
|
+
end
|
50
|
+
|
51
|
+
def faraday(**options)
|
52
|
+
Faraday.new(options) do |conn|
|
53
|
+
conn.request :retry, config.retry_options
|
54
|
+
conn.response :logger, response_logger, bodies: true if response_logger
|
55
|
+
conn.adapter :net_http
|
56
|
+
conn.headers['Content-Type'] = 'application/json'
|
57
|
+
conn.headers['User-Agent'] = config.user_agent
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module XodClient
|
2
|
+
class EndpointCall
|
3
|
+
include ParseUtil
|
4
|
+
|
5
|
+
attr_reader :xod_conn, :endpoint_name, :params, :json
|
6
|
+
|
7
|
+
def initialize(xod_conn, endpoint_name, data_transformer: nil, **params)
|
8
|
+
@xod_conn = xod_conn
|
9
|
+
@endpoint_name = endpoint_name
|
10
|
+
@data_transformer = data_transformer
|
11
|
+
@params = params
|
12
|
+
end
|
13
|
+
|
14
|
+
def fetch(force: false, **custom_params, &block)
|
15
|
+
return @json if @json && !force
|
16
|
+
|
17
|
+
params = @params.merge(custom_params)
|
18
|
+
conn = xod_conn.faraday(url: xod_conn.config.root_url, headers: { 'Authorization': "Idaas #{xod_conn.token}" })
|
19
|
+
method = endpoint_name.to_s.start_with?('POST') ? :post : :get
|
20
|
+
path = build_endpoint_path(params)
|
21
|
+
|
22
|
+
res = conn.send(method, path)
|
23
|
+
@json = process_xod_response(res)
|
24
|
+
|
25
|
+
if block
|
26
|
+
block.call(self)
|
27
|
+
|
28
|
+
if (pagination = @json.dig(:__pagination__, 0))
|
29
|
+
((pagination[:page_number] + 1)..pagination[:page_count]).each do |page|
|
30
|
+
fetch!(page: page)
|
31
|
+
block.call(self)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
@json
|
37
|
+
end
|
38
|
+
|
39
|
+
def fetch!(**custom_params)
|
40
|
+
fetch(custom_params.merge(force: true))
|
41
|
+
end
|
42
|
+
|
43
|
+
def each(&block)
|
44
|
+
fetch do |endpoint|
|
45
|
+
endpoint.first_array.each { |json| block.call(json) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def first_array
|
50
|
+
fetch[first_array_key]
|
51
|
+
end
|
52
|
+
|
53
|
+
def first_array_of_deleted
|
54
|
+
fetch[:"#{first_array_key}_deleted"] || []
|
55
|
+
end
|
56
|
+
|
57
|
+
def first
|
58
|
+
first_array.first
|
59
|
+
end
|
60
|
+
|
61
|
+
def first_array_key
|
62
|
+
@json.keys.find { |k| @json[k].is_a?(Array) }
|
63
|
+
end
|
64
|
+
|
65
|
+
def next_changed_rows
|
66
|
+
@json.dig(:__changed_rows__, 0, :next_changed_rows)
|
67
|
+
end
|
68
|
+
|
69
|
+
def [](key)
|
70
|
+
fetch[key]
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def process_xod_response(res)
|
76
|
+
json = parse_xod_response(res)
|
77
|
+
@data_transformer&.perform(json) || json
|
78
|
+
end
|
79
|
+
|
80
|
+
def call_block_for_each(what, &block)
|
81
|
+
array_key = first_array_key
|
82
|
+
array = @json[array_key]
|
83
|
+
case what
|
84
|
+
when :item then array.each { |item| block.call(item) }
|
85
|
+
when :batch then block.call(array)
|
86
|
+
when :batch_with_deleted then block.call(array, json[:"#{array_key}_deleted"] || [])
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def build_endpoint_path(params)
|
91
|
+
prefix = build_endpoint_prefix(params)
|
92
|
+
params_string = build_endpoint_params(params)
|
93
|
+
|
94
|
+
"#{prefix}#{params_string}"
|
95
|
+
end
|
96
|
+
|
97
|
+
def build_endpoint_prefix(params)
|
98
|
+
prefix = xod_conn.config.endpoints[endpoint_name] || raise(ArgumentError, "Invalid endpoint #{endpoint_name}")
|
99
|
+
prefix.gsub(/{(\w+)}/) do
|
100
|
+
params.delete($1.to_sym) ||
|
101
|
+
(case $1 when 'estab' then xod_conn.estab when 'id' then '' end) ||
|
102
|
+
raise(ArgumentError, "Param #{$1} is required")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def build_endpoint_params(params)
|
107
|
+
try_inline_array_param(params, :options)
|
108
|
+
try_inline_array_param(params, :select)
|
109
|
+
params.delete_if { |_k, v| v.blank? }
|
110
|
+
params.transform_keys! { |key| key.to_s.camelize(:lower) }
|
111
|
+
|
112
|
+
"?#{params.to_query}" if params.any?
|
113
|
+
end
|
114
|
+
|
115
|
+
def try_inline_array_param(params, param_name)
|
116
|
+
if (value = params[param_name]) && value.is_a?(Array)
|
117
|
+
params[param_name] = value.join(',')
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module XodClient
|
2
|
+
class TokenRefresher
|
3
|
+
include ParseUtil
|
4
|
+
|
5
|
+
attr_reader :xod_conn
|
6
|
+
|
7
|
+
def initialize(xod_conn)
|
8
|
+
@xod_conn = xod_conn
|
9
|
+
end
|
10
|
+
|
11
|
+
def perform
|
12
|
+
res = xod_conn.faraday.post do |req|
|
13
|
+
req.url xod_conn.config.login_url
|
14
|
+
req.headers['Content-Type'] = 'application/json'
|
15
|
+
req.body = {
|
16
|
+
relyingParty: xod_conn.relying_party,
|
17
|
+
thirdPartyId: 'XporterOnDemand',
|
18
|
+
estab: xod_conn.estab,
|
19
|
+
password: xod_conn.secret
|
20
|
+
}.to_json
|
21
|
+
end
|
22
|
+
token_json = parse_xod_response(res)
|
23
|
+
token = token_json[:token]
|
24
|
+
token_expires_at = Time.iso8601(token_json[:expires])
|
25
|
+
|
26
|
+
[token, token_expires_at]
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xod_client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Lev Lukomskyi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-12-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10'
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '13'
|
37
|
+
type: :development
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '10'
|
44
|
+
- - "<"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '13'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '3'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '3'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: vcr
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '4'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '4'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: webmock
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '3'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '3'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: rubocop-rspec
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '1'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '1'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: activesupport
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '4'
|
110
|
+
- - "<"
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '6'
|
113
|
+
type: :runtime
|
114
|
+
prerelease: false
|
115
|
+
version_requirements: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '4'
|
120
|
+
- - "<"
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '6'
|
123
|
+
- !ruby/object:Gem::Dependency
|
124
|
+
name: faraday
|
125
|
+
requirement: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - "~>"
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
type: :runtime
|
131
|
+
prerelease: false
|
132
|
+
version_requirements: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - "~>"
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
description: Call info/data/writeback XoD endpoints with this library
|
138
|
+
email:
|
139
|
+
- help@teamsatchel.com
|
140
|
+
executables:
|
141
|
+
- console
|
142
|
+
extensions: []
|
143
|
+
extra_rdoc_files: []
|
144
|
+
files:
|
145
|
+
- LICENSE.txt
|
146
|
+
- README.md
|
147
|
+
- bin/console
|
148
|
+
- lib/xod_client.rb
|
149
|
+
- lib/xod_client/concerns/data_endpoints.rb
|
150
|
+
- lib/xod_client/concerns/info_endpoints.rb
|
151
|
+
- lib/xod_client/concerns/parse_util.rb
|
152
|
+
- lib/xod_client/config.rb
|
153
|
+
- lib/xod_client/connection.rb
|
154
|
+
- lib/xod_client/endpoint_call.rb
|
155
|
+
- lib/xod_client/token_refresher.rb
|
156
|
+
- lib/xod_client/version.rb
|
157
|
+
homepage: https://www.teamsatchel.com
|
158
|
+
licenses:
|
159
|
+
- MIT
|
160
|
+
metadata: {}
|
161
|
+
post_install_message:
|
162
|
+
rdoc_options: []
|
163
|
+
require_paths:
|
164
|
+
- lib
|
165
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
166
|
+
requirements:
|
167
|
+
- - ">="
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: '0'
|
170
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - ">="
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0'
|
175
|
+
requirements: []
|
176
|
+
rubyforge_project:
|
177
|
+
rubygems_version: 2.6.14
|
178
|
+
signing_key:
|
179
|
+
specification_version: 4
|
180
|
+
summary: Fetch Groupcall's Xporter on Demand data with ease
|
181
|
+
test_files: []
|