stbaldricks 12.15.0 → 15.0.0.alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +7 -38
- data/lib/stbaldricks/configuration.rb +2 -2
- data/lib/stbaldricks/default_logger.rb +2 -2
- data/lib/stbaldricks/endpoints/blog_post.rb +8 -8
- data/lib/stbaldricks/endpoints/communicate.rb +1 -1
- data/lib/stbaldricks/endpoints/config.rb +1 -1
- data/lib/stbaldricks/endpoints/deduplicator_match.rb +2 -2
- data/lib/stbaldricks/endpoints/fund.rb +1 -1
- data/lib/stbaldricks/endpoints/fundraiser.rb +1 -1
- data/lib/stbaldricks/endpoints/kid.rb +1 -1
- data/lib/stbaldricks/endpoints/lib/entity.rb +13 -11
- data/lib/stbaldricks/endpoints/newsletter_recipient.rb +11 -5
- data/lib/stbaldricks/endpoints/participant.rb +3 -3
- data/lib/stbaldricks/endpoints/photo.rb +5 -4
- data/lib/stbaldricks/endpoints/search.rb +2 -2
- data/lib/stbaldricks/endpoints/user.rb +1 -1
- data/lib/stbaldricks/entities/batch.rb +3 -5
- data/lib/stbaldricks/entities/blog_post.rb +1 -7
- data/lib/stbaldricks/entities/campaign.rb +6 -13
- data/lib/stbaldricks/entities/challenge.rb +4 -6
- data/lib/stbaldricks/entities/challenger.rb +4 -6
- data/lib/stbaldricks/entities/concerns/entity_response_concern.rb +2 -2
- data/lib/stbaldricks/entities/config.rb +3 -8
- data/lib/stbaldricks/entities/contact.rb +4 -7
- data/lib/stbaldricks/entities/contact_group.rb +2 -2
- data/lib/stbaldricks/entities/deduplicator_history.rb +1 -6
- data/lib/stbaldricks/entities/deduplicator_match.rb +4 -7
- data/lib/stbaldricks/entities/disease.rb +2 -5
- data/lib/stbaldricks/entities/document_library.rb +3 -8
- data/lib/stbaldricks/entities/document_library_category.rb +1 -4
- data/lib/stbaldricks/entities/donation/payment_type.rb +6 -5
- data/lib/stbaldricks/entities/donation.rb +13 -18
- data/lib/stbaldricks/entities/donor.rb +1 -5
- data/lib/stbaldricks/entities/event.rb +33 -44
- data/lib/stbaldricks/entities/event_application.rb +6 -10
- data/lib/stbaldricks/entities/event_donation_summary.rb +2 -5
- data/lib/stbaldricks/entities/event_participant_summary.rb +1 -3
- data/lib/stbaldricks/entities/event_supporter.rb +3 -3
- data/lib/stbaldricks/entities/fund.rb +6 -23
- data/lib/stbaldricks/entities/fundraiser.rb +16 -29
- data/lib/stbaldricks/entities/grant.rb +12 -25
- data/lib/stbaldricks/entities/institution.rb +3 -4
- data/lib/stbaldricks/entities/international_partner.rb +4 -16
- data/lib/stbaldricks/entities/kid.rb +12 -29
- data/lib/stbaldricks/entities/kid_honor.rb +1 -0
- data/lib/stbaldricks/entities/kid_institution.rb +2 -2
- data/lib/stbaldricks/entities/lib/address.rb +3 -6
- data/lib/stbaldricks/entities/lib/alternate_shipping_address.rb +1 -3
- data/lib/stbaldricks/entities/lib/base.rb +48 -40
- data/lib/stbaldricks/entities/lib/collection.rb +2 -2
- data/lib/stbaldricks/entities/lib/email_address.rb +1 -2
- data/lib/stbaldricks/entities/lib/fundraising_page.rb +1 -2
- data/lib/stbaldricks/entities/lib/location.rb +1 -0
- data/lib/stbaldricks/entities/lib/milestone.rb +1 -3
- data/lib/stbaldricks/entities/lib/name.rb +2 -2
- data/lib/stbaldricks/entities/lib/not_implemented_object.rb +1 -1
- data/lib/stbaldricks/entities/lib/payment.rb +25 -4
- data/lib/stbaldricks/entities/lib/permissions.rb +2 -4
- data/lib/stbaldricks/entities/lib/phone.rb +2 -3
- data/lib/stbaldricks/entities/lib/third_party_media.rb +1 -5
- data/lib/stbaldricks/entities/lib/top_level.rb +1 -1
- data/lib/stbaldricks/entities/matching_gift_company.rb +16 -39
- data/lib/stbaldricks/entities/memorial.rb +9 -14
- data/lib/stbaldricks/entities/message.rb +4 -15
- data/lib/stbaldricks/entities/organization.rb +11 -17
- data/lib/stbaldricks/entities/page.rb +9 -17
- data/lib/stbaldricks/entities/participant.rb +18 -28
- data/lib/stbaldricks/entities/participant_donation_summary.rb +2 -4
- data/lib/stbaldricks/entities/person.rb +24 -33
- data/lib/stbaldricks/entities/person_donation_by_year_summary.rb +1 -2
- data/lib/stbaldricks/entities/photo.rb +5 -11
- data/lib/stbaldricks/entities/recurring_gift.rb +10 -18
- data/lib/stbaldricks/entities/response.rb +1 -0
- data/lib/stbaldricks/entities/search.rb +30 -168
- data/lib/stbaldricks/entities/section.rb +1 -4
- data/lib/stbaldricks/entities/shave_schedule.rb +4 -13
- data/lib/stbaldricks/entities/summary.rb +1 -7
- data/lib/stbaldricks/entities/team.rb +12 -16
- data/lib/stbaldricks/entities/team_donation_summary.rb +2 -4
- data/lib/stbaldricks/request.rb +2 -2
- data/lib/stbaldricks/version.rb +1 -1
- metadata +85 -279
- data/lib/stbaldricks_factories.rb +0 -5
- data/spec/factories/address.rb +0 -14
- data/spec/factories/campaign/totals.rb +0 -9
- data/spec/factories/campaign.rb +0 -9
- data/spec/factories/challenge.rb +0 -19
- data/spec/factories/challenger.rb +0 -33
- data/spec/factories/collection.rb +0 -17
- data/spec/factories/contact.rb +0 -18
- data/spec/factories/contact_group.rb +0 -18
- data/spec/factories/diagnosis.rb +0 -12
- data/spec/factories/disease.rb +0 -11
- data/spec/factories/donation/participant.rb +0 -27
- data/spec/factories/donation.rb +0 -68
- data/spec/factories/donor.rb +0 -13
- data/spec/factories/email_address.rb +0 -11
- data/spec/factories/error.rb +0 -9
- data/spec/factories/event/coach_tracking/coaching_interactions.rb +0 -12
- data/spec/factories/event/coach_tracking/plaque.rb +0 -12
- data/spec/factories/event/coach_tracking/proceeds.rb +0 -12
- data/spec/factories/event/coach_tracking.rb +0 -18
- data/spec/factories/event/contacts/contact.rb +0 -17
- data/spec/factories/event/contacts/name_pieces.rb +0 -12
- data/spec/factories/event/contacts.rb +0 -16
- data/spec/factories/event/photos.rb +0 -12
- data/spec/factories/event/totals.rb +0 -15
- data/spec/factories/event/venue/location.rb +0 -18
- data/spec/factories/event/venue/social.rb +0 -16
- data/spec/factories/event/venue.rb +0 -16
- data/spec/factories/event.rb +0 -66
- data/spec/factories/event_application.rb +0 -34
- data/spec/factories/event_supporter.rb +0 -19
- data/spec/factories/fund.rb +0 -18
- data/spec/factories/fundraiser/photos.rb +0 -12
- data/spec/factories/fundraiser/policies.rb +0 -11
- data/spec/factories/fundraiser/totals.rb +0 -11
- data/spec/factories/fundraiser.rb +0 -24
- data/spec/factories/fundraising_page.rb +0 -29
- data/spec/factories/grant.rb +0 -22
- data/spec/factories/grant_institution.rb +0 -17
- data/spec/factories/institution.rb +0 -18
- data/spec/factories/kid/custom_institution.rb +0 -12
- data/spec/factories/kid.rb +0 -58
- data/spec/factories/kid_honor.rb +0 -16
- data/spec/factories/kid_institution.rb +0 -23
- data/spec/factories/lib/faker_patch.rb +0 -27
- data/spec/factories/lib/helpers.rb +0 -7
- data/spec/factories/location.rb +0 -17
- data/spec/factories/memorial/photos.rb +0 -14
- data/spec/factories/memorial/totals.rb +0 -11
- data/spec/factories/memorial/tribute.rb +0 -19
- data/spec/factories/memorial.rb +0 -22
- data/spec/factories/message.rb +0 -31
- data/spec/factories/milestone.rb +0 -15
- data/spec/factories/name_pieces.rb +0 -24
- data/spec/factories/newsletter_recipient.rb +0 -13
- data/spec/factories/organization/addresses.rb +0 -16
- data/spec/factories/organization/email_addresses.rb +0 -14
- data/spec/factories/organization/phone_numbers.rb +0 -14
- data/spec/factories/organization.rb +0 -34
- data/spec/factories/page.rb +0 -5
- data/spec/factories/participant/photos.rb +0 -14
- data/spec/factories/participant/policies.rb +0 -12
- data/spec/factories/participant/rankings/ranking.rb +0 -12
- data/spec/factories/participant/rankings.rb +0 -16
- data/spec/factories/participant/roles/role.rb +0 -154
- data/spec/factories/participant/roles.rb +0 -37
- data/spec/factories/participant/totals.rb +0 -11
- data/spec/factories/participant.rb +0 -95
- data/spec/factories/payment.rb +0 -13
- data/spec/factories/permissions.rb +0 -12
- data/spec/factories/person/addresses.rb +0 -16
- data/spec/factories/person/email_addresses.rb +0 -14
- data/spec/factories/person/phone_numbers.rb +0 -14
- data/spec/factories/person/policies.rb +0 -13
- data/spec/factories/person.rb +0 -22
- data/spec/factories/phone.rb +0 -11
- data/spec/factories/photo.rb +0 -11
- data/spec/factories/photos.rb +0 -9
- data/spec/factories/recurring_gift.rb +0 -22
- data/spec/factories/researcher.rb +0 -15
- data/spec/factories/response.rb +0 -38
- data/spec/factories/search_event.rb +0 -19
- data/spec/factories/search_fundraiser.rb +0 -15
- data/spec/factories/search_kid.rb +0 -13
- data/spec/factories/search_participant.rb +0 -15
- data/spec/factories/search_team.rb +0 -13
- data/spec/factories/shave_schedule/time_selection_permissions.rb +0 -12
- data/spec/factories/shave_schedule.rb +0 -33
- data/spec/factories/team/photos.rb +0 -12
- data/spec/factories/team/rankings/ranking.rb +0 -12
- data/spec/factories/team/rankings.rb +0 -15
- data/spec/factories/team/totals.rb +0 -14
- data/spec/factories/team.rb +0 -43
- data/spec/factories/third_party_media.rb +0 -12
- data/spec/factories/treatment_status.rb +0 -10
- data/spec/factories/user.rb +0 -14
- data/spec/factories/venue.rb +0 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1befc7db895069ebadf49a8912938f2949722509c28e3749b60c3975a5b6b492
|
|
4
|
+
data.tar.gz: 0b06917b79898a394bed7f89043cb77003dbd1066cccdc2391e4a3bc97637da7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 993ccd3b5b1151afd4d7e21b2228d6f96528b2c15b1c14315b817753f923525c6b61f4273b7292e03478b00e19659a288126c547a275694a3b5f88e946734e7e
|
|
7
|
+
data.tar.gz: d3d42b70c39049da67cd0b1eee0ffc8660f4910b6fce674511177761e67259af881024cede4faf7bb3bd25b2130bf4bb8fb243e2d3a06cc2b8a423745400382c
|
data/README.md
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
# Table of Contents
|
|
3
|
+
* [Publish Ruby Client Gem](publish-ruby-client.md)
|
|
4
|
+
* [Architecture](architecture.md)
|
|
5
|
+
* [SDK](#st-baldricks-ruby-sdk)
|
|
6
|
+
|
|
7
|
+
## St. Baldrick's Ruby SDK
|
|
2
8
|
|
|
3
9
|
### Installation
|
|
4
10
|
`gem install stbaldricks`
|
|
@@ -150,40 +156,3 @@ filter = {and: [{column: 'status_id', operator: 'equals', value: SBF::Client::Se
|
|
|
150
156
|
active_participant_within_10_mi = request_helper { SBF::Client::Search.find(model_type, nil, filter, geo_location, limit: 2) }[:results]
|
|
151
157
|
puts JSON.pretty_generate(active_participant_within_10_mi)
|
|
152
158
|
```
|
|
153
|
-
|
|
154
|
-
## Architecture
|
|
155
|
-
| Component | Location
|
|
156
|
-
|--------------------|------------------------------|
|
|
157
|
-
| Endpoints | `/lib/stbaldricks/endpoints` |
|
|
158
|
-
| Entities | `/lib/stbaldricks/entities` |
|
|
159
|
-
| Enums | `/lib/stbaldricks/enums` |
|
|
160
|
-
| Rspec Unit | `/spec/unit` |
|
|
161
|
-
| Rspec Integration | `/spec/integration` |
|
|
162
|
-
|
|
163
|
-
### Endpoints
|
|
164
|
-
* Define supported requests that can be made to the SBF API for entities in the client library.
|
|
165
|
-
* Extend `SBF::Client::EntityEndpoint` to receive common core action for the entities.
|
|
166
|
-
* If additional or non-standard functionality is required, actions can be added or overridden.
|
|
167
|
-
* Return a response object which will contain an http status code and depending on the code either error details
|
|
168
|
-
or the expected API response data for a successful request.
|
|
169
|
-
|
|
170
|
-
### Entities
|
|
171
|
-
* Object representations of the data sent to and received from the API.
|
|
172
|
-
* Top-level entities (those that can be directly requested or modified via the API) inherit from `SBF::Client::TopLevelEntity`.
|
|
173
|
-
* Supported actions on the entity are defined via `action` and `actions`.
|
|
174
|
-
* Any actions that are disallowed are blocked via `blacklist_action`.
|
|
175
|
-
* The entities also map to their corresponding endpoint for handling all related API requests.
|
|
176
|
-
* Sub-entities should still extend the `SBF::Client::BaseEntity`.
|
|
177
|
-
|
|
178
|
-
### Enums
|
|
179
|
-
* Collections of constants to assist with specifying values within a finite list.
|
|
180
|
-
|
|
181
|
-
### Rspec Tests
|
|
182
|
-
Tests are broken out into unit and integration tests. Test coverage thresholds are enforced and test additions or modifications are
|
|
183
|
-
required for nearly any client library change.
|
|
184
|
-
|
|
185
|
-
More detail on understanding and writing Rspec tests can be found in the [Rspec Guide](https://github.com/firespring/sbf/tree/master/documentation/guides/training/rspec).
|
|
186
|
-
|
|
187
|
-
#### Running Tests
|
|
188
|
-
* use `rake client:ruby:test:all` to run all tests
|
|
189
|
-
* use `TESTS={path-to-test} rake client:ruby:test:all` to run specific test(s)
|
|
@@ -16,11 +16,11 @@ module SBF
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def self.base_uri
|
|
19
|
-
@base_uri ||= ENV
|
|
19
|
+
@base_uri ||= ENV.fetch('API_ENDPOINT', DEFAULT_API_ENDPOINT)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def self.general_token
|
|
23
|
-
@general_token ||= ENV
|
|
23
|
+
@general_token ||= ENV.fetch('API_KEY', nil) || raise(SBF::Client::Configuration::Error.new('general_token', 'must be set'))
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def self.request_id
|
|
@@ -3,7 +3,7 @@ require 'logger'
|
|
|
3
3
|
module SBF
|
|
4
4
|
module Client
|
|
5
5
|
module DefaultLogger
|
|
6
|
-
def self.instance(default_stream =
|
|
6
|
+
def self.instance(default_stream = $stdout)
|
|
7
7
|
formatter = proc { |severity, datetime, _, msg|
|
|
8
8
|
# ruby logger docs suggested this method for escaping messages since messages may contain user input
|
|
9
9
|
# and are not escaped by default.
|
|
@@ -16,7 +16,7 @@ module SBF
|
|
|
16
16
|
|
|
17
17
|
logger.formatter = formatter
|
|
18
18
|
logger.level = ::Logger::DEBUG
|
|
19
|
-
logger.level = ENV
|
|
19
|
+
logger.level = ENV.fetch('SBF_CLIENT_LOG_LEVEL', ::Logger::DEBUG)
|
|
20
20
|
logger
|
|
21
21
|
end
|
|
22
22
|
end
|
|
@@ -27,17 +27,17 @@ module SBF
|
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
def find_by_term(term, order = {}, limit = 10, offset = 0, show_hidden
|
|
30
|
+
def find_by_term(term, order = {}, limit = 10, offset = 0, show_hidden: true)
|
|
31
31
|
# Build the parameter list for search; an empty find is supported
|
|
32
32
|
filter = {}
|
|
33
33
|
filter[:type] = FILTER_BY_TERM
|
|
34
34
|
filter[:term] = term unless term.empty?
|
|
35
35
|
filter[:show_hidden] = show_hidden
|
|
36
36
|
|
|
37
|
-
find(filter, order, limit, offset)
|
|
37
|
+
find(filter: filter, order: order, limit: limit, offset: offset)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
def find_by_author(author, order = {}, limit = 10, offset = 0, show_hidden
|
|
40
|
+
def find_by_author(author, order = {}, limit = 10, offset = 0, show_hidden: true)
|
|
41
41
|
raise SBF::Client::Error, 'Must provide author for the query' if author.empty?
|
|
42
42
|
|
|
43
43
|
filter = {}
|
|
@@ -45,10 +45,10 @@ module SBF
|
|
|
45
45
|
filter[:term] = author
|
|
46
46
|
filter[:show_hidden] = show_hidden
|
|
47
47
|
|
|
48
|
-
find(filter, order, limit, offset)
|
|
48
|
+
find(filter: filter, order: order, limit: limit, offset: offset)
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
def find_by_category(category, order = {}, limit = 10, offset = 0, show_hidden
|
|
51
|
+
def find_by_category(category, order = {}, limit = 10, offset = 0, show_hidden: true)
|
|
52
52
|
raise SBF::Client::Error, 'Must provide category for the query' if category.empty?
|
|
53
53
|
|
|
54
54
|
filter = {}
|
|
@@ -56,10 +56,10 @@ module SBF
|
|
|
56
56
|
filter[:term] = category
|
|
57
57
|
filter[:show_hidden] = show_hidden
|
|
58
58
|
|
|
59
|
-
find(filter, order, limit, offset)
|
|
59
|
+
find(filter: filter, order: order, limit: limit, offset: offset)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
def find_by_tag(tag, order = {}, limit = 10, offset = 0, show_hidden
|
|
62
|
+
def find_by_tag(tag, order = {}, limit = 10, offset = 0, show_hidden: true)
|
|
63
63
|
raise SBF::Client::Error, 'Must provide tag for the query' if tag.empty?
|
|
64
64
|
|
|
65
65
|
filter = {}
|
|
@@ -67,7 +67,7 @@ module SBF
|
|
|
67
67
|
filter[:term] = tag
|
|
68
68
|
filter[:show_hidden] = show_hidden
|
|
69
69
|
|
|
70
|
-
find(filter, order, limit, offset)
|
|
70
|
+
find(filter: filter, order: order, limit: limit, offset: offset)
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
end
|
|
@@ -48,7 +48,7 @@ module SBF
|
|
|
48
48
|
group_id: group_id
|
|
49
49
|
)
|
|
50
50
|
parsed_response_body = JSON.parse(response.body).symbolize!
|
|
51
|
-
parsed_response_body.map! { |c| SBF::Client::Contact.new(c) } if ok?(response)
|
|
51
|
+
parsed_response_body.map! { |c| SBF::Client::Contact.new(c, clear_changes: false) } if ok?(response)
|
|
52
52
|
|
|
53
53
|
handle_parsed_response(parsed_response_body, response)
|
|
54
54
|
end
|
|
@@ -11,7 +11,7 @@ module SBF
|
|
|
11
11
|
raise SBF::Client::StandardError, 'Unable to retrieve settings from the api' unless ok?(response)
|
|
12
12
|
|
|
13
13
|
# re-call initalize with the new api data. This should re-set all of the instance variables on the instance
|
|
14
|
-
entity.send(:initialize, parsed_response_body)
|
|
14
|
+
entity.send(:initialize, parsed_response_body, clear_changes: false)
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
end
|
|
@@ -3,7 +3,7 @@ require 'stbaldricks/endpoints/lib/entity'
|
|
|
3
3
|
module SBF
|
|
4
4
|
module Client
|
|
5
5
|
class DeduplicatorMatchEndpoint < SBF::Client::EntityEndpoint
|
|
6
|
-
def checkout(filter
|
|
6
|
+
def checkout(filter: [], order: {}, limit: 20, offset: 0, with: {})
|
|
7
7
|
filter = filter.to_json unless filter.is_a? String
|
|
8
8
|
order = order.to_json unless order.is_a? String
|
|
9
9
|
with = normalize_with(with)
|
|
@@ -15,7 +15,7 @@ module SBF
|
|
|
15
15
|
parsed_response_body = JSON.parse(response.body).symbolize!
|
|
16
16
|
|
|
17
17
|
if ok?(response)
|
|
18
|
-
parsed_response_body[:results].map! { |entity_data| target_class.new(entity_data, true) }
|
|
18
|
+
parsed_response_body[:results].map! { |entity_data| target_class.new(entity_data, clear_changes: true) }
|
|
19
19
|
SBF::Client::EntityCollection.new(parsed_response_body[:results], parsed_response_body[:total_count])
|
|
20
20
|
else
|
|
21
21
|
parsed_response_body = JSON.parse(response.body).symbolize!
|
|
@@ -11,7 +11,7 @@ module SBF
|
|
|
11
11
|
data = parsed_response_body
|
|
12
12
|
model_type = SBF::Client::ModelType.list[params[:dest_model_type]].capitalize
|
|
13
13
|
entity_class = "SBF::Client::Full#{model_type}".to_class
|
|
14
|
-
data[:results].map! { |entity| entity_class.new(entity) }
|
|
14
|
+
data[:results].map! { |entity| entity_class.new(entity, clear_changes: false) }
|
|
15
15
|
else
|
|
16
16
|
error = SBF::Client::ErrorEntity.new(parsed_response_body)
|
|
17
17
|
data = nil
|
|
@@ -7,7 +7,7 @@ module SBF
|
|
|
7
7
|
response = SBF::Client::Api::Request.post_request("#{base_uri}/move_to_different_team", id: fundraiser_id, new_team_id: new_team_id)
|
|
8
8
|
|
|
9
9
|
if ok?(response)
|
|
10
|
-
data = SBF::Client::FullFundraiser.new(JSON.parse(response.body).symbolize
|
|
10
|
+
data = SBF::Client::FullFundraiser.new(JSON.parse(response.body).symbolize!, clear_changes: false)
|
|
11
11
|
else
|
|
12
12
|
error = SBF::Client::ErrorEntity.new(JSON.parse(response.body).symbolize!)
|
|
13
13
|
end
|
|
@@ -12,7 +12,7 @@ module SBF
|
|
|
12
12
|
parsed_response_body = JSON.parse(response.body).symbolize!
|
|
13
13
|
|
|
14
14
|
if ok?(response)
|
|
15
|
-
parsed_response_body[:results].map! { |entity_data| target_class.new(entity_data) }
|
|
15
|
+
parsed_response_body[:results].map! { |entity_data| target_class.new(entity_data, clear_changes: false) }
|
|
16
16
|
SBF::Client::EntityCollection.new(parsed_response_body[:results], parsed_response_body[:total_count])
|
|
17
17
|
else
|
|
18
18
|
parsed_response_body = JSON.parse(response.body).symbolize!
|
|
@@ -10,8 +10,7 @@ require 'json'
|
|
|
10
10
|
module SBF
|
|
11
11
|
module Client
|
|
12
12
|
class EntityEndpoint
|
|
13
|
-
attr_reader :orig_target_class
|
|
14
|
-
attr_reader :base_uri
|
|
13
|
+
attr_reader :orig_target_class, :base_uri
|
|
15
14
|
|
|
16
15
|
def initialize(target_class)
|
|
17
16
|
@orig_target_class = target_class
|
|
@@ -34,7 +33,7 @@ module SBF
|
|
|
34
33
|
# @param with [Hash] The optional entity fields to include in the response
|
|
35
34
|
# @return entity [SBF::Client::BaseEntity]
|
|
36
35
|
def create(entity_or_hash, with = {})
|
|
37
|
-
entity = entity_or_hash.is_a?(Hash) ? target_class.new(entity_or_hash) : entity_or_hash
|
|
36
|
+
entity = entity_or_hash.is_a?(Hash) ? target_class.new(entity_or_hash, clear_changes: false) : entity_or_hash
|
|
38
37
|
raise SBF::Client::Error, 'Invalid Entity' unless entity.is_a?(SBF::Client::BaseEntity)
|
|
39
38
|
|
|
40
39
|
with = normalize_with(with, entity)
|
|
@@ -50,10 +49,11 @@ module SBF
|
|
|
50
49
|
# @param with [Hash] The optional entity fields to include in the response
|
|
51
50
|
# @return entity [SBF::Client::BaseEntity]
|
|
52
51
|
def update(id = nil, entity_or_hash = nil, with = {})
|
|
53
|
-
|
|
52
|
+
case entity_or_hash
|
|
53
|
+
when SBF::Client::BaseEntity
|
|
54
54
|
# If someone has passed in an entity, just convert it to a hash
|
|
55
55
|
data = entity_or_hash.dirty_data
|
|
56
|
-
|
|
56
|
+
when Hash
|
|
57
57
|
# If someone has passed in a hash, make sure all of it's values match fields in the entity class
|
|
58
58
|
data = sanitize(entity_or_hash)
|
|
59
59
|
else
|
|
@@ -89,7 +89,7 @@ module SBF
|
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
# Calls the find route for the entity.
|
|
92
|
-
def find(filter
|
|
92
|
+
def find(filter: [], order: {}, limit: 20, offset: 0, with: {})
|
|
93
93
|
filter = filter.to_json unless filter.is_a? String
|
|
94
94
|
order = order.to_json unless order.is_a? String
|
|
95
95
|
with = normalize_with(with)
|
|
@@ -98,7 +98,9 @@ module SBF
|
|
|
98
98
|
parsed_response_body = JSON.parse(response.body).symbolize!
|
|
99
99
|
|
|
100
100
|
if ok?(response)
|
|
101
|
-
parsed_response_body[:results].map!
|
|
101
|
+
parsed_response_body[:results].map! do |entity_data|
|
|
102
|
+
target_class.new(entity_data, clear_changes: true)
|
|
103
|
+
end
|
|
102
104
|
SBF::Client::EntityCollection.new(parsed_response_body[:results], parsed_response_body[:total_count])
|
|
103
105
|
else
|
|
104
106
|
parsed_response_body = JSON.parse(response.body).symbolize!
|
|
@@ -113,7 +115,7 @@ module SBF
|
|
|
113
115
|
|
|
114
116
|
# Calls the find route for the entity.
|
|
115
117
|
def find_first(filter = [], order = {}, with = {})
|
|
116
|
-
response = find(filter, order, 1,
|
|
118
|
+
response = find(filter: filter, order: order, limit: 1, with: with)
|
|
117
119
|
if response.is_a?(SBF::Client::EntityCollection)
|
|
118
120
|
response.empty? ? nil : response.first
|
|
119
121
|
else
|
|
@@ -148,7 +150,7 @@ module SBF
|
|
|
148
150
|
|
|
149
151
|
def target_class
|
|
150
152
|
class_name_pieces = @orig_target_class.to_s.split('::')
|
|
151
|
-
full_name = (class_name_pieces[0..-2] + [
|
|
153
|
+
full_name = (class_name_pieces[0..-2] + ["Full#{class_name_pieces.last}"]).join('::')
|
|
152
154
|
Object.const_defined?(full_name) ? Object.const_get(full_name) : @orig_target_class
|
|
153
155
|
end
|
|
154
156
|
private :target_class
|
|
@@ -247,7 +249,7 @@ module SBF
|
|
|
247
249
|
entity_or_hash.send(:initialize_attributes, new_data)
|
|
248
250
|
entity = entity_or_hash
|
|
249
251
|
else
|
|
250
|
-
entity = target_class.new(new_data)
|
|
252
|
+
entity = target_class.new(new_data, clear_changes: false)
|
|
251
253
|
end
|
|
252
254
|
|
|
253
255
|
# Reload entity and its sub-entities so that no fields have a 'changed' state
|
|
@@ -256,7 +258,7 @@ module SBF
|
|
|
256
258
|
entity
|
|
257
259
|
else
|
|
258
260
|
error = SBF::Client::ApiErrorEntity.new(parsed_response_body.merge(http_code: response.code))
|
|
259
|
-
entity = entity_or_hash.is_a?(SBF::Client::BaseEntity) ? entity_or_hash : target_class.new(entity_or_hash)
|
|
261
|
+
entity = entity_or_hash.is_a?(SBF::Client::BaseEntity) ? entity_or_hash : target_class.new(entity_or_hash, clear_changes: false)
|
|
260
262
|
entity.add_errors(error)
|
|
261
263
|
entity.errors_http_code = response.code
|
|
262
264
|
|
|
@@ -3,9 +3,14 @@ require 'stbaldricks/endpoints/lib/entity'
|
|
|
3
3
|
module SBF
|
|
4
4
|
module Client
|
|
5
5
|
class NewsletterRecipientEndpoint < EntityEndpoint
|
|
6
|
-
def subscribe(entity_or_email, first_name = nil, last_name = nil, welcome_email
|
|
6
|
+
def subscribe(entity_or_email, first_name = nil, last_name = nil, welcome_email: true)
|
|
7
7
|
if entity_or_email.is_a?(String) && entity_or_email && first_name && last_name
|
|
8
|
-
entity = target_class.new(
|
|
8
|
+
entity = target_class.new({
|
|
9
|
+
email_address: entity_or_email,
|
|
10
|
+
first_name: first_name,
|
|
11
|
+
last_name: last_name,
|
|
12
|
+
welcome_email: welcome_email
|
|
13
|
+
}, clear_changes: false)
|
|
9
14
|
elsif entity_or_email.is_a?(SBF::Client::BaseEntity)
|
|
10
15
|
entity = entity_or_email
|
|
11
16
|
else
|
|
@@ -21,9 +26,10 @@ module SBF
|
|
|
21
26
|
end
|
|
22
27
|
|
|
23
28
|
def unsubscribe(entity_or_email)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
case entity_or_email
|
|
30
|
+
when String
|
|
31
|
+
entity = target_class.new({email_address: entity_or_email}, clear_changes: false)
|
|
32
|
+
when SBF::Client::BaseEntity
|
|
27
33
|
entity = entity_or_email
|
|
28
34
|
else
|
|
29
35
|
raise SBF::Client::Error, 'Invalid Entity'
|
|
@@ -7,7 +7,7 @@ module SBF
|
|
|
7
7
|
response = SBF::Client::Api::Request.post_request("#{base_uri}/move_to_different_team", id: participant_id, new_team_id: new_team_id)
|
|
8
8
|
|
|
9
9
|
if ok?(response)
|
|
10
|
-
data = SBF::Client::FullParticipant.new(JSON.parse(response.body).symbolize
|
|
10
|
+
data = SBF::Client::FullParticipant.new(JSON.parse(response.body).symbolize!, clear_changes: false)
|
|
11
11
|
else
|
|
12
12
|
error = SBF::Client::ErrorEntity.new(JSON.parse(response.body).symbolize!)
|
|
13
13
|
end
|
|
@@ -26,7 +26,7 @@ module SBF
|
|
|
26
26
|
)
|
|
27
27
|
|
|
28
28
|
if ok?(response)
|
|
29
|
-
data = SBF::Client::FullParticipant.new(JSON.parse(response.body).symbolize
|
|
29
|
+
data = SBF::Client::FullParticipant.new(JSON.parse(response.body).symbolize!, clear_changes: false)
|
|
30
30
|
else
|
|
31
31
|
error = SBF::Client::ErrorEntity.new(JSON.parse(response.body).symbolize!)
|
|
32
32
|
end
|
|
@@ -38,7 +38,7 @@ module SBF
|
|
|
38
38
|
response = SBF::Client::Api::Request.post_request("#{base_uri}/move_to_different_team", id: participant_id, new_team_id: 0)
|
|
39
39
|
|
|
40
40
|
if ok?(response)
|
|
41
|
-
data = SBF::Client::FullParticipant.new(JSON.parse(response.body).symbolize
|
|
41
|
+
data = SBF::Client::FullParticipant.new(JSON.parse(response.body).symbolize!, clear_changes: false)
|
|
42
42
|
else
|
|
43
43
|
error = SBF::Client::ErrorEntity.new(JSON.parse(response.body).symbolize!)
|
|
44
44
|
end
|
|
@@ -4,7 +4,7 @@ module SBF
|
|
|
4
4
|
module Client
|
|
5
5
|
class PhotoEndpoint < SBF::Client::EntityEndpoint
|
|
6
6
|
def create(entity_or_hash, with = {})
|
|
7
|
-
entity = entity_or_hash.is_a?(Hash) ? target_class.new(entity_or_hash) : entity_or_hash
|
|
7
|
+
entity = entity_or_hash.is_a?(Hash) ? target_class.new(entity_or_hash, clear_changes: false) : entity_or_hash
|
|
8
8
|
raise SBF::Client::Error, 'Invalid Entity' unless entity.is_a?(SBF::Client::BaseEntity)
|
|
9
9
|
|
|
10
10
|
with = normalize_with(with)
|
|
@@ -20,12 +20,13 @@ module SBF
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def update(id = nil, entity_or_hash = nil, with = {})
|
|
23
|
-
|
|
23
|
+
case entity_or_hash
|
|
24
|
+
when SBF::Client::BaseEntity
|
|
24
25
|
# If someone has passed in an entity, just convert it to a hash
|
|
25
26
|
data = entity_or_hash.dirty_data
|
|
26
27
|
data[:id] = id unless id.nil?
|
|
27
28
|
data[:id] = entity_or_hash.id if id.nil? && !entity_or_hash.id.nil?
|
|
28
|
-
|
|
29
|
+
when Hash
|
|
29
30
|
# If someone has passed in a hash, make sure all of it's values match fields in the entity class
|
|
30
31
|
data = sanitize(entity_or_hash)
|
|
31
32
|
else
|
|
@@ -66,7 +67,7 @@ module SBF
|
|
|
66
67
|
data.file = file
|
|
67
68
|
data
|
|
68
69
|
else
|
|
69
|
-
photo = SBF::Client::Photo.new(data.is_a?(Hash) && data.any? ? data : {})
|
|
70
|
+
photo = SBF::Client::Photo.new((data.is_a?(Hash) && data.any?) ? data : {}, clear_changes: false)
|
|
70
71
|
photo.file = file
|
|
71
72
|
photo
|
|
72
73
|
end
|
|
@@ -18,7 +18,7 @@ module SBF
|
|
|
18
18
|
# @option [Integer] :offset The query offset.
|
|
19
19
|
# @return [Client::Api::Response] The data attribute on a successful response will be
|
|
20
20
|
# an array of search entities matching the model type(s) specified
|
|
21
|
-
def find(model_type, search_text
|
|
21
|
+
def find(model_type, search_text: nil, filters: [], geo_location: {}, options: {})
|
|
22
22
|
if [search_text, filters, geo_location].all?(&:empty?)
|
|
23
23
|
raise SBF::Client::Error, 'Must provide either a search_text, filter or geo_location for the query'
|
|
24
24
|
end
|
|
@@ -45,7 +45,7 @@ module SBF
|
|
|
45
45
|
response_data[:total_count] = parsed_response_body[:total]
|
|
46
46
|
response_data[:results] = [].tap do |arr|
|
|
47
47
|
parsed_response_body[:hits].each do |entity_data|
|
|
48
|
-
arr << target_class.const_get(entity_data[:type].capitalize).new(entity_data[:source])
|
|
48
|
+
arr << target_class.const_get(entity_data[:type].capitalize).new(entity_data[:source], clear_changes: false)
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
|
|
@@ -20,7 +20,7 @@ module SBF
|
|
|
20
20
|
|
|
21
21
|
if ok?(response)
|
|
22
22
|
SBF::Client::Configuration.user_token = parsed_response[:token]
|
|
23
|
-
data = parsed_response.merge(user: target_class.new(parsed_response[:user]))
|
|
23
|
+
data = parsed_response.merge(user: target_class.new(parsed_response[:user], clear_changes: false))
|
|
24
24
|
else
|
|
25
25
|
SBF::Client::Configuration.user_token = nil
|
|
26
26
|
error = SBF::Client::ErrorEntity.new(parsed_response)
|
|
@@ -12,17 +12,15 @@ module SBF
|
|
|
12
12
|
PROCESSED = 'processed'.freeze
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
attr_accessor :id
|
|
16
|
-
|
|
15
|
+
attr_accessor :id, :type, :amount, :filename, :created_at, :modified_at, :processed_at, :submitted_by
|
|
16
|
+
|
|
17
17
|
multitype_attr_accessor(
|
|
18
18
|
:payment_details, [
|
|
19
19
|
[->(v) { v[:type] == SBF::Client::Payment::Type::CREDIT_CARD }, 'SBF::Client::Payment::CreditCardDetails'],
|
|
20
20
|
[->(v) { v[:type] == SBF::Client::Payment::Type::NONCE }, 'SBF::Client::Payment::NonceDetails']
|
|
21
21
|
]
|
|
22
22
|
)
|
|
23
|
-
|
|
24
|
-
attr_accessor :filename
|
|
25
|
-
attr_accessor :created_at, :modified_at, :processed_at, :submitted_by
|
|
23
|
+
|
|
26
24
|
entity_collection_attr_accessor :donations, 'SBF::Client::FullDonation', 'SBF::Client::PartialDonation'
|
|
27
25
|
end
|
|
28
26
|
end
|
|
@@ -12,13 +12,7 @@ module SBF
|
|
|
12
12
|
action :find_by_category
|
|
13
13
|
action :find_by_tag
|
|
14
14
|
|
|
15
|
-
attr_reader :id
|
|
16
|
-
attr_reader :slug
|
|
17
|
-
attr_reader :url
|
|
18
|
-
attr_reader :title
|
|
19
|
-
attr_reader :content
|
|
20
|
-
attr_reader :published_at
|
|
21
|
-
attr_reader :modified_at
|
|
15
|
+
attr_reader :id, :slug, :url, :title, :content, :published_at, :modified_at
|
|
22
16
|
end
|
|
23
17
|
end
|
|
24
18
|
end
|
|
@@ -12,12 +12,8 @@ module SBF
|
|
|
12
12
|
disallow_instantiation
|
|
13
13
|
|
|
14
14
|
class Totals < SBF::Client::BaseEntity
|
|
15
|
-
attr_reader :current_year_amount_raised
|
|
16
|
-
|
|
17
|
-
attr_reader :all_years_amount_raised
|
|
18
|
-
attr_reader :participants
|
|
19
|
-
attr_reader :shavees
|
|
20
|
-
attr_reader :number_of_events
|
|
15
|
+
attr_reader :current_year_amount_raised, :previous_year_amount_raised, :all_years_amount_raised, :participants,
|
|
16
|
+
:shavees, :number_of_events
|
|
21
17
|
end
|
|
22
18
|
end
|
|
23
19
|
|
|
@@ -26,15 +22,12 @@ module SBF
|
|
|
26
22
|
end
|
|
27
23
|
|
|
28
24
|
class FullCampaign < Campaign
|
|
29
|
-
attr_accessor :id
|
|
30
|
-
attr_accessor :name
|
|
31
|
-
attr_accessor :website_url
|
|
32
|
-
attr_accessor :is_enabled
|
|
25
|
+
attr_accessor :id, :name, :website_url, :is_enabled
|
|
33
26
|
|
|
34
|
-
entity_attr_reader :totals, 'SBF::Client::Campaign::Totals', nil, true
|
|
35
|
-
entity_attr_accessor :web_page, 'SBF::Client::WebPage', nil, true
|
|
27
|
+
entity_attr_reader :totals, 'SBF::Client::Campaign::Totals', nil, optional: true
|
|
28
|
+
entity_attr_accessor :web_page, 'SBF::Client::WebPage', nil, optional: true
|
|
36
29
|
|
|
37
|
-
entity_attr_accessor :photo, 'SBF::Client::Photo', nil, true
|
|
30
|
+
entity_attr_accessor :photo, 'SBF::Client::Photo', nil, optional: true
|
|
38
31
|
end
|
|
39
32
|
end
|
|
40
33
|
end
|
|
@@ -16,13 +16,11 @@ module SBF
|
|
|
16
16
|
DECLINED = 'declined'.freeze
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
attr_accessor :id
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
entity_attr_accessor :
|
|
23
|
-
entity_attr_accessor :challengee, 'SBF::Client::Challenger', nil, true
|
|
19
|
+
attr_accessor :id, :status, :end_date, :modified_by
|
|
20
|
+
|
|
21
|
+
entity_attr_accessor :challenger, 'SBF::Client::Challenger', nil, optional: true
|
|
22
|
+
entity_attr_accessor :challengee, 'SBF::Client::Challenger', nil, optional: true
|
|
24
23
|
attr_reader :created_at, :modified_at
|
|
25
|
-
attr_accessor :modified_by
|
|
26
24
|
end
|
|
27
25
|
end
|
|
28
26
|
end
|
|
@@ -97,11 +97,9 @@ module SBF
|
|
|
97
97
|
end
|
|
98
98
|
end
|
|
99
99
|
|
|
100
|
-
attr_accessor :id
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
attr_accessor :is_accepted
|
|
104
|
-
entity_attr_reader :totals, 'SBF::Client::Challenger::Totals', nil, true
|
|
100
|
+
attr_accessor :id, :challenge_id, :type, :is_accepted
|
|
101
|
+
|
|
102
|
+
entity_attr_reader :totals, 'SBF::Client::Challenger::Totals', nil, optional: true
|
|
105
103
|
|
|
106
104
|
multitype_attr_accessor(
|
|
107
105
|
:entity,
|
|
@@ -127,7 +125,7 @@ module SBF
|
|
|
127
125
|
'SBF::Client::Challenger::PartialEvent'
|
|
128
126
|
]
|
|
129
127
|
],
|
|
130
|
-
true
|
|
128
|
+
optional: true
|
|
131
129
|
)
|
|
132
130
|
end
|
|
133
131
|
end
|
|
@@ -51,7 +51,7 @@ module EntityResponseConcern
|
|
|
51
51
|
return if errors.empty?
|
|
52
52
|
return [:base, errors[:base].first] if errors[:base].any?
|
|
53
53
|
|
|
54
|
-
errors.first
|
|
54
|
+
[errors.first.attribute, errors.first.type]
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
private def log_deprecated(method_name, method_caller = nil)
|
|
@@ -59,7 +59,7 @@ module EntityResponseConcern
|
|
|
59
59
|
caller_message = method_caller.nil? ? '' : " Called from #{method_caller.first}"
|
|
60
60
|
|
|
61
61
|
"[DEPRECATION] Use of the SBF::Client::Api::Response Interface (including the `#{method_name}` method) is deprecated"\
|
|
62
|
-
|
|
62
|
+
" for #{self.class}. Please update your code to use the #{self.class} interface accordingly. #{caller_message}"
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
end
|
|
@@ -11,14 +11,9 @@ module SBF
|
|
|
11
11
|
action :get
|
|
12
12
|
instance_action :reload
|
|
13
13
|
|
|
14
|
-
attr_reader :default_year
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
attr_reader :years_for_squire
|
|
18
|
-
attr_reader :years_for_knight
|
|
19
|
-
attr_reader :years_for_knight_commander
|
|
20
|
-
attr_reader :years_for_crusader
|
|
21
|
-
attr_reader :years_for_baron
|
|
14
|
+
attr_reader :default_year, :open_registration_years, :website_url, :years_for_squire, :years_for_knight, :years_for_knight_commander,
|
|
15
|
+
:years_for_crusader, :years_for_baron
|
|
16
|
+
|
|
22
17
|
# NOTE: If you add another attribute, be sure to mock it out in `configure :test` of app.rb
|
|
23
18
|
|
|
24
19
|
# Attempts to call the method on the cached configuration object
|
|
@@ -14,22 +14,19 @@ module SBF
|
|
|
14
14
|
WEBSITE = 'website'.freeze
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
attr_reader :id
|
|
17
|
+
attr_reader :id, :display_name, :email, :created_at, :modified_at
|
|
18
|
+
|
|
18
19
|
entity_attr_accessor :owner, 'SBF::Client::FullPerson', 'SBF::Client::PartialPerson'
|
|
19
20
|
entity_attr_accessor :person, 'SBF::Client::FullPerson', 'SBF::Client::PartialPerson'
|
|
20
21
|
attr_accessor :type
|
|
21
|
-
|
|
22
|
-
attr_reader :email
|
|
23
|
-
attr_reader :created_at, :modified_at
|
|
22
|
+
|
|
24
23
|
entity_collection_attr_reader :contact_groups, 'SBF::Client::ContactGroup'
|
|
25
24
|
end
|
|
26
25
|
|
|
27
26
|
class CustomContact < SBF::Client::Contact
|
|
28
27
|
endpoint SBF::Client::ContactEndpoint
|
|
29
28
|
|
|
30
|
-
attr_accessor :id
|
|
31
|
-
attr_accessor :display_name
|
|
32
|
-
attr_accessor :email
|
|
29
|
+
attr_accessor :id, :display_name, :email
|
|
33
30
|
end
|
|
34
31
|
end
|
|
35
32
|
end
|
|
@@ -8,8 +8,8 @@ module SBF
|
|
|
8
8
|
class ContactGroup < SBF::Client::TopLevelEntity
|
|
9
9
|
actions DEFAULT_CRUD_ACTIONS
|
|
10
10
|
|
|
11
|
-
attr_accessor :id
|
|
12
|
-
|
|
11
|
+
attr_accessor :id, :display_name
|
|
12
|
+
|
|
13
13
|
entity_attr_accessor :owner, 'SBF::Client::FullPerson'
|
|
14
14
|
multitype_collection_attr_accessor(
|
|
15
15
|
:contacts,
|
|
@@ -6,12 +6,7 @@ module SBF
|
|
|
6
6
|
action :get
|
|
7
7
|
action :find
|
|
8
8
|
|
|
9
|
-
attr_accessor :id
|
|
10
|
-
attr_accessor :from_profile_id
|
|
11
|
-
attr_accessor :to_profile_id
|
|
12
|
-
attr_accessor :checkout_id
|
|
13
|
-
attr_accessor :change_history
|
|
14
|
-
attr_accessor :date_merged
|
|
9
|
+
attr_accessor :id, :from_profile_id, :to_profile_id, :checkout_id, :change_history, :date_merged
|
|
15
10
|
end
|
|
16
11
|
end
|
|
17
12
|
end
|