ecoportal-api 0.8.5 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +20 -20
- data/.rspec +3 -3
- data/.rubocop.yml +55 -55
- data/.travis.yml +5 -5
- data/.yardopts +10 -10
- data/CHANGELOG.md +257 -236
- data/Gemfile +6 -6
- data/LICENSE +21 -21
- data/README.md +34 -34
- data/Rakefile +27 -27
- data/bin/console +14 -14
- data/bin/setup +8 -8
- data/ecoportal-api.gemspec +36 -36
- data/lib/ecoportal/api/common/base_class.rb +33 -29
- data/lib/ecoportal/api/common/base_model.rb +195 -177
- data/lib/ecoportal/api/common/batch_operation.rb +119 -119
- data/lib/ecoportal/api/common/batch_response.rb +34 -34
- data/lib/ecoportal/api/common/client.rb +198 -196
- data/lib/ecoportal/api/common/doc_helpers.rb +29 -29
- data/lib/ecoportal/api/common/elastic_apm_integration.rb +112 -112
- data/lib/ecoportal/api/common/hash_diff.rb +41 -41
- data/lib/ecoportal/api/common/logging.rb +12 -12
- data/lib/ecoportal/api/common/response.rb +31 -31
- data/lib/ecoportal/api/common/wrapped_response.rb +54 -54
- data/lib/ecoportal/api/common.rb +18 -18
- data/lib/ecoportal/api/errors/base.rb +8 -8
- data/lib/ecoportal/api/errors/time_out.rb +8 -8
- data/lib/ecoportal/api/errors.rb +9 -9
- data/lib/ecoportal/api/internal/account.rb +99 -100
- data/lib/ecoportal/api/internal/login_provider.rb +9 -9
- data/lib/ecoportal/api/internal/login_providers.rb +33 -33
- data/lib/ecoportal/api/internal/people.rb +14 -14
- data/lib/ecoportal/api/internal/permissions.rb +14 -13
- data/lib/ecoportal/api/internal/person.rb +101 -53
- data/lib/ecoportal/api/internal/person_details.rb +9 -9
- data/lib/ecoportal/api/internal/person_schema.rb +10 -10
- data/lib/ecoportal/api/internal/person_schemas.rb +11 -11
- data/lib/ecoportal/api/internal/policy_group.rb +9 -9
- data/lib/ecoportal/api/internal/policy_groups.rb +32 -32
- data/lib/ecoportal/api/internal/preferences.rb +31 -31
- data/lib/ecoportal/api/internal/schema_field.rb +8 -8
- data/lib/ecoportal/api/internal/schema_field_value.rb +8 -8
- data/lib/ecoportal/api/internal.rb +31 -31
- data/lib/ecoportal/api/logger.rb +62 -62
- data/lib/ecoportal/api/v1/people.rb +218 -218
- data/lib/ecoportal/api/v1/person.rb +138 -135
- data/lib/ecoportal/api/v1/person_details.rb +94 -82
- data/lib/ecoportal/api/v1/person_schema.rb +53 -53
- data/lib/ecoportal/api/v1/person_schemas.rb +48 -48
- data/lib/ecoportal/api/v1/schema_field.rb +34 -34
- data/lib/ecoportal/api/v1/schema_field_value.rb +65 -65
- data/lib/ecoportal/api/v1.rb +49 -49
- data/lib/ecoportal/api/version.rb +5 -5
- data/lib/ecoportal/api.rb +16 -16
- metadata +3 -3
@@ -1,218 +1,218 @@
|
|
1
|
-
module Ecoportal
|
2
|
-
module API
|
3
|
-
class V1
|
4
|
-
# @attr_reader client [Common::Client] a `Common::Client` object that holds the configuration of the api connection.
|
5
|
-
class People
|
6
|
-
extend Common::BaseClass
|
7
|
-
include Enumerable
|
8
|
-
include Common::DocHelpers
|
9
|
-
|
10
|
-
JOB_TIMEOUT = 240
|
11
|
-
DELAY_STATUS_CHECK = 5
|
12
|
-
|
13
|
-
class_resolver :person_class, "Ecoportal::API::V1::Person"
|
14
|
-
|
15
|
-
attr_reader :client
|
16
|
-
|
17
|
-
# @param client [Common::Client] a `Common::Client` object that holds the configuration of the api connection.
|
18
|
-
# @return [People] an instance object ready to make people api requests.
|
19
|
-
def initialize(client)
|
20
|
-
@client = client
|
21
|
-
end
|
22
|
-
|
23
|
-
# Iterates all the people of the organization.
|
24
|
-
# @note
|
25
|
-
# - it ignores the key `cursor_id:` of `params:`.
|
26
|
-
# - `each` is called by `to_a`
|
27
|
-
# @param params [Hash]
|
28
|
-
# @option params [String] :per_page the number of people you get per request.
|
29
|
-
# @option params [String] :q some text to search. Omit this parameter to target all the people.
|
30
|
-
# @param silent [Boolean] `false` to show percentage of progress.
|
31
|
-
# @yield [person] does some stuff with the person.
|
32
|
-
# @yieldparam person [Person]
|
33
|
-
def each(params: {}, silent: false, &block)
|
34
|
-
return to_enum(:each, params: params, silent: silent) unless block
|
35
|
-
cursor_id = nil; results = 0
|
36
|
-
puts "\n" unless silent
|
37
|
-
loop do
|
38
|
-
params.update(cursor_id: cursor_id) if cursor_id
|
39
|
-
body = nil; response = nil; count = 5
|
40
|
-
loop do
|
41
|
-
response = client.get("/people", params: params)
|
42
|
-
body = response && body_data(response.body)
|
43
|
-
break if response.success? || count <= 0
|
44
|
-
puts "Request failed - Status #{response.status}: #{body}"
|
45
|
-
count -= 1
|
46
|
-
sleep(0.5)
|
47
|
-
end
|
48
|
-
raise "Request failed - Status #{response.status}: #{body}" unless response.success?
|
49
|
-
|
50
|
-
unless silent || (total = body["total_results"]) == 0
|
51
|
-
results += body["results"].length
|
52
|
-
percent = results * 100 / total
|
53
|
-
msg = "People GET"
|
54
|
-
msg += " (search=#{params[:q]})" if params.key?(:q)
|
55
|
-
print "#{msg}: #{percent.round}% (of #{total})\r"
|
56
|
-
$stdout.flush
|
57
|
-
end
|
58
|
-
|
59
|
-
body["results"].each do |person|
|
60
|
-
yield person_class.new(person)
|
61
|
-
end
|
62
|
-
break unless (cursor_id = body["cursor_id"])
|
63
|
-
end
|
64
|
-
self
|
65
|
-
end
|
66
|
-
|
67
|
-
# Gets all the people via api requests.
|
68
|
-
# @note it ignores the key `:cursor_id` in `params:`.
|
69
|
-
# @param params [Hash]
|
70
|
-
# @option params [Integer] :per_page the number of people you get per request.
|
71
|
-
# @option params [String] :q some text to search.
|
72
|
-
# @param silent [Boolean] `false` to show percentage of progress.
|
73
|
-
# @return [Array<Person>] the array of people got via api.
|
74
|
-
def get_all(params: {}, silent: false)
|
75
|
-
each(params: params, silent: silent).to_a
|
76
|
-
end
|
77
|
-
|
78
|
-
# Gets a person via api.
|
79
|
-
# @note if the request has `success?` the returned `object.result` gives an object with that `Person`.
|
80
|
-
# @param doc [String, Hash, Person] data containing an `id` (internal or external) of the target person.
|
81
|
-
# @return [Person] the person with `id` (internal or external) contained in `doc`.
|
82
|
-
def get(doc)
|
83
|
-
id = get_id(doc)
|
84
|
-
response = client.get("/people/"+CGI.escape(id))
|
85
|
-
body = body_data(response.body)
|
86
|
-
return person_class.new(body) if response.success?
|
87
|
-
raise "Could not get person #{id} - Error #{response.status}: #{body}"
|
88
|
-
end
|
89
|
-
|
90
|
-
# Requests an update of a person via api.
|
91
|
-
# @param doc [Person, Hash] data that at least contains an `id` (internal or external) of the target person.
|
92
|
-
# @return [Response] an object with the api response.
|
93
|
-
def update(doc)
|
94
|
-
body = get_body(doc)
|
95
|
-
id = get_id(doc)
|
96
|
-
client.patch("/people/"+CGI.escape(id), data: body)
|
97
|
-
end
|
98
|
-
|
99
|
-
# Requests to create a person via api.
|
100
|
-
# @param doc [Person, Hash] data that at least contains an `id` (internal or external) of the target person.
|
101
|
-
# @return [Response] an object with the api response.
|
102
|
-
def create(doc)
|
103
|
-
body = get_body(doc)
|
104
|
-
client.post("/people", data: body)
|
105
|
-
end
|
106
|
-
|
107
|
-
# Requests to update an existing person or if it does not exist, to create it, via api.
|
108
|
-
# @param doc [Person, Hash] data that at least contains an `id` (internal or external) of the target person.
|
109
|
-
# @return [Response] an object with the api response.
|
110
|
-
def upsert(doc)
|
111
|
-
body = get_body(doc)
|
112
|
-
id = get_id(doc)
|
113
|
-
client.post("/people/"+CGI.escape(id), data: body)
|
114
|
-
end
|
115
|
-
|
116
|
-
# Requests to completelly remove from an organization an existing person via api.
|
117
|
-
# @param doc [Person, Hash] data that at least contains an `id` (internal or external) of the target person.
|
118
|
-
# @return [Response] an object with the api response.
|
119
|
-
def delete(doc)
|
120
|
-
id = get_id(doc)
|
121
|
-
client.delete("/people/"+CGI.escape(id))
|
122
|
-
end
|
123
|
-
|
124
|
-
# Creates a `BatchOperation` and yields it to the given bock.
|
125
|
-
# @yield [batch_op] adds multiple api requests for the current batch.
|
126
|
-
# @yieldparam batch_op [BatchOperation]
|
127
|
-
# @param job_mode [Boolean] whether or not it should use batch jobs
|
128
|
-
# @return [Ecoportal::API::Common::Response] the results of the batch
|
129
|
-
def batch(job_mode: true, &block)
|
130
|
-
return job(&block) if job_mode
|
131
|
-
operation = Common::BatchOperation.new("/people", person_class, logger: client.logger)
|
132
|
-
yield operation
|
133
|
-
# The batch operation is responsible for logging the output
|
134
|
-
client.post("/people/batch", data: operation.as_json).tap do |response|
|
135
|
-
operation.process_response(response)
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
# @return [Ecoportal::API::Common::Response] the results of the batch job
|
140
|
-
def job
|
141
|
-
operation = Common::BatchOperation.new("/people", person_class, logger: client.logger)
|
142
|
-
yield operation
|
143
|
-
job_id = create_job(operation)
|
144
|
-
status = wait_for_job_completion(job_id)
|
145
|
-
|
146
|
-
if status&.complete?
|
147
|
-
job_result(job_id, operation)
|
148
|
-
else
|
149
|
-
msg = "Job `#{job_id}` not complete. Probably timeout after #{JOB_TIMEOUT} seconds. Current status: #{status}"
|
150
|
-
raise API::Errors::TimeOut.new msg
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
# Creates a new `Person` object.
|
155
|
-
# @return [Person] new empty person object of the current version.
|
156
|
-
def new
|
157
|
-
person_class.new
|
158
|
-
end
|
159
|
-
|
160
|
-
private
|
161
|
-
|
162
|
-
JobStatus = Struct.new(:id, :complete?, :errored?, :progress)
|
163
|
-
def job_status(job_id)
|
164
|
-
response = client.get("/people/job/#{CGI.escape(job_id)}/status")
|
165
|
-
body = response && body_data(response.body)
|
166
|
-
raise "Status error (#{response.status}) - Errors: #{body}" unless response.success?
|
167
|
-
JobStatus.new(
|
168
|
-
body["id"],
|
169
|
-
body["complete"],
|
170
|
-
body["errored"],
|
171
|
-
body["progress"]
|
172
|
-
)
|
173
|
-
end
|
174
|
-
|
175
|
-
# @return [Ecoportal::API::Common::Response] the results of the batch job
|
176
|
-
def job_result(job_id, operation)
|
177
|
-
client.get("/people/job/#{CGI.escape(job_id)}").tap do |response|
|
178
|
-
operation.process_response(response)
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
def wait_for_job_completion(job_id)
|
183
|
-
# timeout library is evil. So we make poor-man timeout.
|
184
|
-
# https://jvns.ca/blog/2015/11/27/why-rubys-timeout-is-dangerous-and-thread-dot-raise-is-terrifying/
|
185
|
-
before = Time.now
|
186
|
-
while true
|
187
|
-
status = job_status(job_id)
|
188
|
-
break status if status.complete?
|
189
|
-
break status if Time.now >= before + JOB_TIMEOUT
|
190
|
-
sleep(DELAY_STATUS_CHECK)
|
191
|
-
status
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
# @return [String] the `id` of the created batch job
|
196
|
-
def create_job(operation)
|
197
|
-
job_id = nil
|
198
|
-
client.post("/people/job", data: operation.as_json).tap do |response|
|
199
|
-
job_id = body_data(response.body)["id"] if response.success?
|
200
|
-
raise "Could not create job - Error (#{response.status}): #{body_data(response.body)}" unless job_id
|
201
|
-
end
|
202
|
-
job_id
|
203
|
-
end
|
204
|
-
|
205
|
-
# Hook for other api versions to obtain the raw data of a response
|
206
|
-
# @note this was introduced to allow `v2` to reuse this class
|
207
|
-
def body_data(body)
|
208
|
-
body
|
209
|
-
end
|
210
|
-
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
require 'ecoportal/api/v1/schema_field_value'
|
217
|
-
require 'ecoportal/api/v1/person_details'
|
218
|
-
require 'ecoportal/api/v1/person'
|
1
|
+
module Ecoportal
|
2
|
+
module API
|
3
|
+
class V1
|
4
|
+
# @attr_reader client [Common::Client] a `Common::Client` object that holds the configuration of the api connection.
|
5
|
+
class People
|
6
|
+
extend Common::BaseClass
|
7
|
+
include Enumerable
|
8
|
+
include Common::DocHelpers
|
9
|
+
|
10
|
+
JOB_TIMEOUT = 240
|
11
|
+
DELAY_STATUS_CHECK = 5
|
12
|
+
|
13
|
+
class_resolver :person_class, "Ecoportal::API::V1::Person"
|
14
|
+
|
15
|
+
attr_reader :client
|
16
|
+
|
17
|
+
# @param client [Common::Client] a `Common::Client` object that holds the configuration of the api connection.
|
18
|
+
# @return [People] an instance object ready to make people api requests.
|
19
|
+
def initialize(client)
|
20
|
+
@client = client
|
21
|
+
end
|
22
|
+
|
23
|
+
# Iterates all the people of the organization.
|
24
|
+
# @note
|
25
|
+
# - it ignores the key `cursor_id:` of `params:`.
|
26
|
+
# - `each` is called by `to_a`
|
27
|
+
# @param params [Hash]
|
28
|
+
# @option params [String] :per_page the number of people you get per request.
|
29
|
+
# @option params [String] :q some text to search. Omit this parameter to target all the people.
|
30
|
+
# @param silent [Boolean] `false` to show percentage of progress.
|
31
|
+
# @yield [person] does some stuff with the person.
|
32
|
+
# @yieldparam person [Person]
|
33
|
+
def each(params: {}, silent: false, &block)
|
34
|
+
return to_enum(:each, params: params, silent: silent) unless block
|
35
|
+
cursor_id = nil; results = 0
|
36
|
+
puts "\n" unless silent
|
37
|
+
loop do
|
38
|
+
params.update(cursor_id: cursor_id) if cursor_id
|
39
|
+
body = nil; response = nil; count = 5
|
40
|
+
loop do
|
41
|
+
response = client.get("/people", params: params)
|
42
|
+
body = response && body_data(response.body)
|
43
|
+
break if response.success? || count <= 0
|
44
|
+
puts "Request failed - Status #{response.status}: #{body}"
|
45
|
+
count -= 1
|
46
|
+
sleep(0.5)
|
47
|
+
end
|
48
|
+
raise "Request failed - Status #{response.status}: #{body}" unless response.success?
|
49
|
+
|
50
|
+
unless silent || (total = body["total_results"]) == 0
|
51
|
+
results += body["results"].length
|
52
|
+
percent = results * 100 / total
|
53
|
+
msg = "People GET"
|
54
|
+
msg += " (search=#{params[:q]})" if params.key?(:q)
|
55
|
+
print "#{msg}: #{percent.round}% (of #{total})\r"
|
56
|
+
$stdout.flush
|
57
|
+
end
|
58
|
+
|
59
|
+
body["results"].each do |person|
|
60
|
+
yield person_class.new(person)
|
61
|
+
end
|
62
|
+
break unless (cursor_id = body["cursor_id"])
|
63
|
+
end
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
# Gets all the people via api requests.
|
68
|
+
# @note it ignores the key `:cursor_id` in `params:`.
|
69
|
+
# @param params [Hash]
|
70
|
+
# @option params [Integer] :per_page the number of people you get per request.
|
71
|
+
# @option params [String] :q some text to search.
|
72
|
+
# @param silent [Boolean] `false` to show percentage of progress.
|
73
|
+
# @return [Array<Person>] the array of people got via api.
|
74
|
+
def get_all(params: {}, silent: false)
|
75
|
+
each(params: params, silent: silent).to_a
|
76
|
+
end
|
77
|
+
|
78
|
+
# Gets a person via api.
|
79
|
+
# @note if the request has `success?` the returned `object.result` gives an object with that `Person`.
|
80
|
+
# @param doc [String, Hash, Person] data containing an `id` (internal or external) of the target person.
|
81
|
+
# @return [Person] the person with `id` (internal or external) contained in `doc`.
|
82
|
+
def get(doc)
|
83
|
+
id = get_id(doc)
|
84
|
+
response = client.get("/people/"+CGI.escape(id))
|
85
|
+
body = body_data(response.body)
|
86
|
+
return person_class.new(body) if response.success?
|
87
|
+
raise "Could not get person #{id} - Error #{response.status}: #{body}"
|
88
|
+
end
|
89
|
+
|
90
|
+
# Requests an update of a person via api.
|
91
|
+
# @param doc [Person, Hash] data that at least contains an `id` (internal or external) of the target person.
|
92
|
+
# @return [Response] an object with the api response.
|
93
|
+
def update(doc)
|
94
|
+
body = get_body(doc)
|
95
|
+
id = get_id(doc)
|
96
|
+
client.patch("/people/"+CGI.escape(id), data: body)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Requests to create a person via api.
|
100
|
+
# @param doc [Person, Hash] data that at least contains an `id` (internal or external) of the target person.
|
101
|
+
# @return [Response] an object with the api response.
|
102
|
+
def create(doc)
|
103
|
+
body = get_body(doc)
|
104
|
+
client.post("/people", data: body)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Requests to update an existing person or if it does not exist, to create it, via api.
|
108
|
+
# @param doc [Person, Hash] data that at least contains an `id` (internal or external) of the target person.
|
109
|
+
# @return [Response] an object with the api response.
|
110
|
+
def upsert(doc)
|
111
|
+
body = get_body(doc)
|
112
|
+
id = get_id(doc)
|
113
|
+
client.post("/people/"+CGI.escape(id), data: body)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Requests to completelly remove from an organization an existing person via api.
|
117
|
+
# @param doc [Person, Hash] data that at least contains an `id` (internal or external) of the target person.
|
118
|
+
# @return [Response] an object with the api response.
|
119
|
+
def delete(doc)
|
120
|
+
id = get_id(doc)
|
121
|
+
client.delete("/people/"+CGI.escape(id))
|
122
|
+
end
|
123
|
+
|
124
|
+
# Creates a `BatchOperation` and yields it to the given bock.
|
125
|
+
# @yield [batch_op] adds multiple api requests for the current batch.
|
126
|
+
# @yieldparam batch_op [BatchOperation]
|
127
|
+
# @param job_mode [Boolean] whether or not it should use batch jobs
|
128
|
+
# @return [Ecoportal::API::Common::Response] the results of the batch
|
129
|
+
def batch(job_mode: true, &block)
|
130
|
+
return job(&block) if job_mode
|
131
|
+
operation = Common::BatchOperation.new("/people", person_class, logger: client.logger)
|
132
|
+
yield operation
|
133
|
+
# The batch operation is responsible for logging the output
|
134
|
+
client.post("/people/batch", data: operation.as_json).tap do |response|
|
135
|
+
operation.process_response(response)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# @return [Ecoportal::API::Common::Response] the results of the batch job
|
140
|
+
def job
|
141
|
+
operation = Common::BatchOperation.new("/people", person_class, logger: client.logger)
|
142
|
+
yield operation
|
143
|
+
job_id = create_job(operation)
|
144
|
+
status = wait_for_job_completion(job_id)
|
145
|
+
|
146
|
+
if status&.complete?
|
147
|
+
job_result(job_id, operation)
|
148
|
+
else
|
149
|
+
msg = "Job `#{job_id}` not complete. Probably timeout after #{JOB_TIMEOUT} seconds. Current status: #{status}"
|
150
|
+
raise API::Errors::TimeOut.new msg
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Creates a new `Person` object.
|
155
|
+
# @return [Person] new empty person object of the current version.
|
156
|
+
def new
|
157
|
+
person_class.new
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
JobStatus = Struct.new(:id, :complete?, :errored?, :progress)
|
163
|
+
def job_status(job_id)
|
164
|
+
response = client.get("/people/job/#{CGI.escape(job_id)}/status")
|
165
|
+
body = response && body_data(response.body)
|
166
|
+
raise "Status error (#{response.status}) - Errors: #{body}" unless response.success?
|
167
|
+
JobStatus.new(
|
168
|
+
body["id"],
|
169
|
+
body["complete"],
|
170
|
+
body["errored"],
|
171
|
+
body["progress"]
|
172
|
+
)
|
173
|
+
end
|
174
|
+
|
175
|
+
# @return [Ecoportal::API::Common::Response] the results of the batch job
|
176
|
+
def job_result(job_id, operation)
|
177
|
+
client.get("/people/job/#{CGI.escape(job_id)}").tap do |response|
|
178
|
+
operation.process_response(response)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def wait_for_job_completion(job_id)
|
183
|
+
# timeout library is evil. So we make poor-man timeout.
|
184
|
+
# https://jvns.ca/blog/2015/11/27/why-rubys-timeout-is-dangerous-and-thread-dot-raise-is-terrifying/
|
185
|
+
before = Time.now
|
186
|
+
while true
|
187
|
+
status = job_status(job_id)
|
188
|
+
break status if status.complete?
|
189
|
+
break status if Time.now >= before + JOB_TIMEOUT
|
190
|
+
sleep(DELAY_STATUS_CHECK)
|
191
|
+
status
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# @return [String] the `id` of the created batch job
|
196
|
+
def create_job(operation)
|
197
|
+
job_id = nil
|
198
|
+
client.post("/people/job", data: operation.as_json).tap do |response|
|
199
|
+
job_id = body_data(response.body)["id"] if response.success?
|
200
|
+
raise "Could not create job - Error (#{response.status}): #{body_data(response.body)}" unless job_id
|
201
|
+
end
|
202
|
+
job_id
|
203
|
+
end
|
204
|
+
|
205
|
+
# Hook for other api versions to obtain the raw data of a response
|
206
|
+
# @note this was introduced to allow `v2` to reuse this class
|
207
|
+
def body_data(body)
|
208
|
+
body
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
require 'ecoportal/api/v1/schema_field_value'
|
217
|
+
require 'ecoportal/api/v1/person_details'
|
218
|
+
require 'ecoportal/api/v1/person'
|