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.
@@ -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
@@ -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.
@@ -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.
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'xod_client'
5
+ require 'irb'
6
+
7
+ IRB.start(__FILE__)
@@ -0,0 +1,12 @@
1
+ require 'active_support/all'
2
+ require 'faraday'
3
+ require 'xod_client/connection'
4
+ require 'xod_client/version'
5
+
6
+ module XodClient
7
+
8
+ def self.init(*args, **options)
9
+ Connection.new(*args, options)
10
+ end
11
+
12
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module XodClient
2
+ VERSION = '1.0.0'
3
+ 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: []