spark_api 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.
- data/History.txt +139 -0
- data/LICENSE +14 -0
- data/README.md +153 -0
- data/Rakefile +18 -0
- data/VERSION +1 -0
- data/bin/spark_api +8 -0
- data/bin/spark_api~ +8 -0
- data/lib/spark_api.rb +46 -0
- data/lib/spark_api/authentication.rb +55 -0
- data/lib/spark_api/authentication/api_auth.rb +104 -0
- data/lib/spark_api/authentication/api_auth.rb~ +104 -0
- data/lib/spark_api/authentication/base_auth.rb +47 -0
- data/lib/spark_api/authentication/base_auth.rb~ +47 -0
- data/lib/spark_api/authentication/oauth2.rb +198 -0
- data/lib/spark_api/authentication/oauth2.rb~ +199 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_base.rb +87 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_base.rb~ +87 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_code.rb +48 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_code.rb~ +49 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_password.rb +44 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_password.rb~ +45 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_refresh.rb +35 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_refresh.rb~ +36 -0
- data/lib/spark_api/authentication/oauth2_impl/middleware.rb +38 -0
- data/lib/spark_api/authentication/oauth2_impl/middleware.rb~ +39 -0
- data/lib/spark_api/authentication/oauth2_impl/password_provider.rb +24 -0
- data/lib/spark_api/authentication/oauth2_impl/password_provider.rb~ +25 -0
- data/lib/spark_api/cli.rb +158 -0
- data/lib/spark_api/cli.rb~ +158 -0
- data/lib/spark_api/cli/api_auth.rb +8 -0
- data/lib/spark_api/cli/api_auth.rb~ +8 -0
- data/lib/spark_api/cli/oauth2.rb +14 -0
- data/lib/spark_api/cli/oauth2.rb~ +14 -0
- data/lib/spark_api/cli/setup.rb +47 -0
- data/lib/spark_api/cli/setup.rb~ +47 -0
- data/lib/spark_api/client.rb +27 -0
- data/lib/spark_api/configuration.rb +54 -0
- data/lib/spark_api/configuration.rb~ +54 -0
- data/lib/spark_api/configuration/yaml.rb +101 -0
- data/lib/spark_api/configuration/yaml.rb~ +101 -0
- data/lib/spark_api/connection.rb +42 -0
- data/lib/spark_api/faraday.rb +64 -0
- data/lib/spark_api/faraday.rb~ +64 -0
- data/lib/spark_api/models.rb +33 -0
- data/lib/spark_api/models.rb~ +33 -0
- data/lib/spark_api/models/account.rb +115 -0
- data/lib/spark_api/models/account.rb~ +115 -0
- data/lib/spark_api/models/base.rb +118 -0
- data/lib/spark_api/models/base.rb~ +118 -0
- data/lib/spark_api/models/connect_prefs.rb +10 -0
- data/lib/spark_api/models/connect_prefs.rb~ +10 -0
- data/lib/spark_api/models/constraint.rb +16 -0
- data/lib/spark_api/models/constraint.rb~ +16 -0
- data/lib/spark_api/models/contact.rb +49 -0
- data/lib/spark_api/models/contact.rb~ +49 -0
- data/lib/spark_api/models/custom_fields.rb +12 -0
- data/lib/spark_api/models/custom_fields.rb~ +12 -0
- data/lib/spark_api/models/document.rb +11 -0
- data/lib/spark_api/models/document.rb~ +11 -0
- data/lib/spark_api/models/finders.rb +45 -0
- data/lib/spark_api/models/finders.rb~ +45 -0
- data/lib/spark_api/models/idx_link.rb +47 -0
- data/lib/spark_api/models/idx_link.rb~ +47 -0
- data/lib/spark_api/models/listing.rb +197 -0
- data/lib/spark_api/models/listing.rb~ +197 -0
- data/lib/spark_api/models/listing_cart.rb +72 -0
- data/lib/spark_api/models/listing_cart.rb~ +72 -0
- data/lib/spark_api/models/market_statistics.rb +33 -0
- data/lib/spark_api/models/market_statistics.rb~ +33 -0
- data/lib/spark_api/models/message.rb +21 -0
- data/lib/spark_api/models/message.rb~ +21 -0
- data/lib/spark_api/models/note.rb +41 -0
- data/lib/spark_api/models/note.rb~ +41 -0
- data/lib/spark_api/models/notification.rb +42 -0
- data/lib/spark_api/models/notification.rb~ +42 -0
- data/lib/spark_api/models/open_house.rb +24 -0
- data/lib/spark_api/models/open_house.rb~ +24 -0
- data/lib/spark_api/models/photo.rb +70 -0
- data/lib/spark_api/models/photo.rb~ +70 -0
- data/lib/spark_api/models/property_types.rb +7 -0
- data/lib/spark_api/models/property_types.rb~ +7 -0
- data/lib/spark_api/models/saved_search.rb +16 -0
- data/lib/spark_api/models/saved_search.rb~ +16 -0
- data/lib/spark_api/models/shared_listing.rb +35 -0
- data/lib/spark_api/models/shared_listing.rb~ +35 -0
- data/lib/spark_api/models/standard_fields.rb +50 -0
- data/lib/spark_api/models/standard_fields.rb~ +50 -0
- data/lib/spark_api/models/subresource.rb +19 -0
- data/lib/spark_api/models/subresource.rb~ +19 -0
- data/lib/spark_api/models/system_info.rb +14 -0
- data/lib/spark_api/models/system_info.rb~ +14 -0
- data/lib/spark_api/models/tour_of_home.rb +24 -0
- data/lib/spark_api/models/tour_of_home.rb~ +24 -0
- data/lib/spark_api/models/video.rb +16 -0
- data/lib/spark_api/models/video.rb~ +16 -0
- data/lib/spark_api/models/virtual_tour.rb +18 -0
- data/lib/spark_api/models/virtual_tour.rb~ +18 -0
- data/lib/spark_api/multi_client.rb +59 -0
- data/lib/spark_api/multi_client.rb~ +59 -0
- data/lib/spark_api/paginate.rb +109 -0
- data/lib/spark_api/paginate.rb~ +109 -0
- data/lib/spark_api/primary_array.rb +29 -0
- data/lib/spark_api/primary_array.rb~ +29 -0
- data/lib/spark_api/request.rb +96 -0
- data/lib/spark_api/request.rb~ +96 -0
- data/lib/spark_api/response.rb +70 -0
- data/lib/spark_api/response.rb~ +70 -0
- data/lib/spark_api/version.rb +4 -0
- data/lib/spark_api/version.rb~ +4 -0
- data/script/console +6 -0
- data/script/console~ +6 -0
- data/script/example.rb +27 -0
- data/script/example.rb~ +27 -0
- data/spec/fixtures/accounts/all.json +160 -0
- data/spec/fixtures/accounts/my.json +74 -0
- data/spec/fixtures/accounts/my_portal.json +20 -0
- data/spec/fixtures/accounts/my_put.json +5 -0
- data/spec/fixtures/accounts/my_save.json +5 -0
- data/spec/fixtures/accounts/office.json +142 -0
- data/spec/fixtures/accounts/password_save.json +6 -0
- data/spec/fixtures/authentication_failure.json +7 -0
- data/spec/fixtures/base.json +13 -0
- data/spec/fixtures/contacts/contacts.json +28 -0
- data/spec/fixtures/contacts/my.json +19 -0
- data/spec/fixtures/contacts/new.json +11 -0
- data/spec/fixtures/contacts/new_empty.json +8 -0
- data/spec/fixtures/contacts/new_notify.json +11 -0
- data/spec/fixtures/contacts/post.json +10 -0
- data/spec/fixtures/contacts/tags.json +11 -0
- data/spec/fixtures/count.json +10 -0
- data/spec/fixtures/empty.json +3 -0
- data/spec/fixtures/errors/expired.json +7 -0
- data/spec/fixtures/errors/failure.json +5 -0
- data/spec/fixtures/errors/failure_with_constraint.json +17 -0
- data/spec/fixtures/errors/failure_with_msg.json +7 -0
- data/spec/fixtures/generic_delete.json +1 -0
- data/spec/fixtures/generic_failure.json +5 -0
- data/spec/fixtures/listing_carts/add_listing.json +13 -0
- data/spec/fixtures/listing_carts/add_listing_post.json +5 -0
- data/spec/fixtures/listing_carts/empty.json +5 -0
- data/spec/fixtures/listing_carts/listing_cart.json +19 -0
- data/spec/fixtures/listing_carts/new.json +12 -0
- data/spec/fixtures/listing_carts/post.json +10 -0
- data/spec/fixtures/listing_carts/remove_listing.json +13 -0
- data/spec/fixtures/listings/constraints.json +18 -0
- data/spec/fixtures/listings/constraints_with_pagination.json +24 -0
- data/spec/fixtures/listings/document_index.json +19 -0
- data/spec/fixtures/listings/multiple.json +69 -0
- data/spec/fixtures/listings/no_subresources.json +38 -0
- data/spec/fixtures/listings/open_houses.json +21 -0
- data/spec/fixtures/listings/photos/index.json +469 -0
- data/spec/fixtures/listings/photos/new.json +12 -0
- data/spec/fixtures/listings/photos/post.json +20 -0
- data/spec/fixtures/listings/put.json +5 -0
- data/spec/fixtures/listings/put_expiration_date.json +5 -0
- data/spec/fixtures/listings/saved_search.json +17 -0
- data/spec/fixtures/listings/shared_listing_get.json +14 -0
- data/spec/fixtures/listings/shared_listing_new.json +9 -0
- data/spec/fixtures/listings/shared_listing_post.json +10 -0
- data/spec/fixtures/listings/tour_of_homes.json +23 -0
- data/spec/fixtures/listings/videos_index.json +18 -0
- data/spec/fixtures/listings/virtual_tours_index.json +42 -0
- data/spec/fixtures/listings/with_documents.json +52 -0
- data/spec/fixtures/listings/with_permissions.json +44 -0
- data/spec/fixtures/listings/with_photos.json +110 -0
- data/spec/fixtures/listings/with_supplement.json +39 -0
- data/spec/fixtures/listings/with_videos.json +54 -0
- data/spec/fixtures/listings/with_vtour.json +48 -0
- data/spec/fixtures/logo_fbs.png +0 -0
- data/spec/fixtures/messages/new.json +14 -0
- data/spec/fixtures/messages/new_empty.json +7 -0
- data/spec/fixtures/messages/new_with_recipients.json +15 -0
- data/spec/fixtures/messages/post.json +5 -0
- data/spec/fixtures/notes/add.json +11 -0
- data/spec/fixtures/notes/agent_shared.json +11 -0
- data/spec/fixtures/notes/agent_shared_empty.json +7 -0
- data/spec/fixtures/notes/new.json +5 -0
- data/spec/fixtures/notifications/mark_read.json +1 -0
- data/spec/fixtures/notifications/new.json +8 -0
- data/spec/fixtures/notifications/new_empty.json +7 -0
- data/spec/fixtures/notifications/notifications.json +43 -0
- data/spec/fixtures/notifications/post.json +10 -0
- data/spec/fixtures/notifications/unread.json +10 -0
- data/spec/fixtures/oauth2/access.json +3 -0
- data/spec/fixtures/oauth2/access_with_old_refresh.json +5 -0
- data/spec/fixtures/oauth2/access_with_refresh.json +5 -0
- data/spec/fixtures/oauth2/authorization_code_body.json +7 -0
- data/spec/fixtures/oauth2/error.json +3 -0
- data/spec/fixtures/oauth2/password_body.json +7 -0
- data/spec/fixtures/oauth2/refresh_body.json +7 -0
- data/spec/fixtures/oauth2_error.json +3 -0
- data/spec/fixtures/property_types/property_types.json +31 -0
- data/spec/fixtures/session.json +10 -0
- data/spec/fixtures/standardfields/city.json +1031 -0
- data/spec/fixtures/standardfields/nearby.json +53 -0
- data/spec/fixtures/standardfields/standardfields.json +188 -0
- data/spec/fixtures/standardfields/stateorprovince.json +36 -0
- data/spec/fixtures/success.json +5 -0
- data/spec/json_helper.rb +76 -0
- data/spec/mock_helper.rb +124 -0
- data/spec/oauth2_helper.rb +68 -0
- data/spec/spec_helper.rb +48 -0
- data/spec/unit/flexmls_api_spec.rb~ +23 -0
- data/spec/unit/spark_api/authentication/api_auth_spec.rb +169 -0
- data/spec/unit/spark_api/authentication/api_auth_spec.rb~ +169 -0
- data/spec/unit/spark_api/authentication/base_auth_spec.rb +10 -0
- data/spec/unit/spark_api/authentication/base_auth_spec.rb~ +10 -0
- data/spec/unit/spark_api/authentication/oauth2_impl/grant_type_base_spec.rb +10 -0
- data/spec/unit/spark_api/authentication/oauth2_impl/grant_type_base_spec.rb~ +10 -0
- data/spec/unit/spark_api/authentication/oauth2_spec.rb +205 -0
- data/spec/unit/spark_api/authentication/oauth2_spec.rb~ +205 -0
- data/spec/unit/spark_api/authentication_spec.rb +38 -0
- data/spec/unit/spark_api/authentication_spec.rb~ +38 -0
- data/spec/unit/spark_api/configuration/yaml_spec.rb +72 -0
- data/spec/unit/spark_api/configuration/yaml_spec.rb~ +72 -0
- data/spec/unit/spark_api/configuration_spec.rb +122 -0
- data/spec/unit/spark_api/configuration_spec.rb~ +122 -0
- data/spec/unit/spark_api/faraday_spec.rb +90 -0
- data/spec/unit/spark_api/faraday_spec.rb~ +90 -0
- data/spec/unit/spark_api/models/account_spec.rb +176 -0
- data/spec/unit/spark_api/models/base_spec.rb +106 -0
- data/spec/unit/spark_api/models/connect_prefs_spec.rb +9 -0
- data/spec/unit/spark_api/models/constraint_spec.rb +19 -0
- data/spec/unit/spark_api/models/contact_spec.rb +108 -0
- data/spec/unit/spark_api/models/contact_spec.rb~ +108 -0
- data/spec/unit/spark_api/models/document_spec.rb +32 -0
- data/spec/unit/spark_api/models/listing_cart_spec.rb +127 -0
- data/spec/unit/spark_api/models/listing_cart_spec.rb~ +127 -0
- data/spec/unit/spark_api/models/listing_spec.rb +320 -0
- data/spec/unit/spark_api/models/listing_spec.rb~ +320 -0
- data/spec/unit/spark_api/models/message_spec.rb +47 -0
- data/spec/unit/spark_api/models/message_spec.rb~ +47 -0
- data/spec/unit/spark_api/models/note_spec.rb +63 -0
- data/spec/unit/spark_api/models/note_spec.rb~ +63 -0
- data/spec/unit/spark_api/models/notification_spec.rb +62 -0
- data/spec/unit/spark_api/models/notification_spec.rb~ +62 -0
- data/spec/unit/spark_api/models/open_house_spec.rb +39 -0
- data/spec/unit/spark_api/models/photo_spec.rb +92 -0
- data/spec/unit/spark_api/models/property_types_spec.rb +33 -0
- data/spec/unit/spark_api/models/saved_search_spec.rb +40 -0
- data/spec/unit/spark_api/models/shared_listing_spec.rb +45 -0
- data/spec/unit/spark_api/models/shared_listing_spec.rb~ +45 -0
- data/spec/unit/spark_api/models/standard_fields_spec.rb +60 -0
- data/spec/unit/spark_api/models/system_info_spec.rb +83 -0
- data/spec/unit/spark_api/models/tour_of_home_spec.rb +44 -0
- data/spec/unit/spark_api/models/video_spec.rb +36 -0
- data/spec/unit/spark_api/models/virtual_tour_spec.rb +44 -0
- data/spec/unit/spark_api/multi_client_spec.rb +56 -0
- data/spec/unit/spark_api/multi_client_spec.rb~ +56 -0
- data/spec/unit/spark_api/paginate_spec.rb +224 -0
- data/spec/unit/spark_api/paginate_spec.rb~ +224 -0
- data/spec/unit/spark_api/primary_array_spec.rb +41 -0
- data/spec/unit/spark_api/primary_array_spec.rb~ +41 -0
- data/spec/unit/spark_api/request_spec.rb +344 -0
- data/spec/unit/spark_api/request_spec.rb~ +344 -0
- data/spec/unit/spark_api_spec.rb +23 -0
- metadata +725 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
require 'will_paginate/collection'
|
|
2
|
+
|
|
3
|
+
# =Pagination for api resource collections
|
|
4
|
+
# Will paginate adapter for the api client. Utilizes the same interface as will paginate and returns the
|
|
5
|
+
# same WillPaginate::Collection for finder results.
|
|
6
|
+
module SparkApi
|
|
7
|
+
module Paginate
|
|
8
|
+
|
|
9
|
+
DEFAULT_PAGE_SIZE = 25
|
|
10
|
+
|
|
11
|
+
# == Replacement hook for will_paginate's class method
|
|
12
|
+
# Does a best effort to mimic the will_paginate method of same name. All arguments are
|
|
13
|
+
# passed on to the finder method except the special keys for the options hash listed below.
|
|
14
|
+
#
|
|
15
|
+
# == Special parameters for paginating finders
|
|
16
|
+
# * <tt>:page</tt> -- REQUIRED, but defaults to 1 if false or nil
|
|
17
|
+
# * <tt>:per_page</tt> -- defaults to <tt>CurrentModel.per_page</tt> (which is 25 if not overridden)
|
|
18
|
+
# * <tt>:finder</tt> -- name of the finder used (default: "get"). This needs to be a class finder method on the class
|
|
19
|
+
def paginate(*args)
|
|
20
|
+
options = args.last.is_a?(::Hash) ? args.pop : {}
|
|
21
|
+
page = options.delete(:page) || 1
|
|
22
|
+
items_per_page = options.delete(:per_page) || self.per_page
|
|
23
|
+
finder = (options.delete(:finder) || 'get').to_s
|
|
24
|
+
page_options = {
|
|
25
|
+
"_pagination" => 1,
|
|
26
|
+
"_limit" => items_per_page,
|
|
27
|
+
"_page" => page
|
|
28
|
+
}
|
|
29
|
+
options.merge!(page_options)
|
|
30
|
+
args << options
|
|
31
|
+
collection = send(finder,*args)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# == Instanciate class instances from array of hash representations.
|
|
35
|
+
# Needs to be called by all finders that would like to support paging. Takes the hash result
|
|
36
|
+
# set from the request layer and instanciates instances of the class called for the finder.
|
|
37
|
+
#
|
|
38
|
+
# * result_array -- the results object returned from the api request layer. An array of hashes.
|
|
39
|
+
#
|
|
40
|
+
# :returns:
|
|
41
|
+
# An array of class instances for the Class of the calling finder
|
|
42
|
+
def collect(result_array)
|
|
43
|
+
|
|
44
|
+
# when conducting a count (pagination=count), the result_array is not an array
|
|
45
|
+
# in those cases, simply return the Fixnum
|
|
46
|
+
return result_array unless result_array.kind_of? Array
|
|
47
|
+
|
|
48
|
+
collection = result_array.collect { |item| new(item)}
|
|
49
|
+
result_array.replace(collection)
|
|
50
|
+
result_array
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Default per_page limit set on all models. Override this method in the model such ala the
|
|
54
|
+
# will_paginate gem to change
|
|
55
|
+
def per_page
|
|
56
|
+
DEFAULT_PAGE_SIZE
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# ==Paginate Api Responses
|
|
63
|
+
# Module used by the request layer to decorate the response's results array with paging support.
|
|
64
|
+
# Pagination only happens if the response includes the pagination information as specified by the
|
|
65
|
+
# API.
|
|
66
|
+
module PaginateResponse
|
|
67
|
+
attr_accessor :results
|
|
68
|
+
def method_missing(method_symbol, *arguments)
|
|
69
|
+
if results.respond_to?(method_symbol)
|
|
70
|
+
arguments.empty? ? self.results.send(method_symbol) : self.results.send(method_symbol, arguments)
|
|
71
|
+
else
|
|
72
|
+
super
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# ==Pagination Helpers
|
|
78
|
+
# Helpers to create the pagination collection
|
|
79
|
+
module PaginateHelper
|
|
80
|
+
# ==Enable pagination
|
|
81
|
+
# * results -- array of hashes representing api resources
|
|
82
|
+
# * paging_hash -- the pagination response information from the api representing paging state.
|
|
83
|
+
#
|
|
84
|
+
# :returns:
|
|
85
|
+
# The result set decorated as a WillPaginate::Collection
|
|
86
|
+
def paginate_response(results, paging_hash)
|
|
87
|
+
pager = Pagination.new(paging_hash)
|
|
88
|
+
paged_results = WillPaginate::Collection.create(pager.current_page, pager.page_size, pager.total_rows) do |p|
|
|
89
|
+
p.replace(results)
|
|
90
|
+
end
|
|
91
|
+
paged_results.extend PaginateResponse
|
|
92
|
+
paged_results.results = results
|
|
93
|
+
paged_results
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# ==Pagination
|
|
98
|
+
# Simple class representing the API's pagination response object
|
|
99
|
+
class Pagination
|
|
100
|
+
attr_accessor :total_rows, :page_size, :total_pages, :current_page
|
|
101
|
+
def initialize(hash)
|
|
102
|
+
@total_rows = hash["TotalRows"]
|
|
103
|
+
@page_size = hash["PageSize"]
|
|
104
|
+
@total_pages = hash["TotalPages"]
|
|
105
|
+
@current_page = hash["CurrentPage"]
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
require 'will_paginate/collection'
|
|
2
|
+
|
|
3
|
+
# =Pagination for api resource collections
|
|
4
|
+
# Will paginate adapter for the api client. Utilizes the same interface as will paginate and returns the
|
|
5
|
+
# same WillPaginate::Collection for finder results.
|
|
6
|
+
module FlexmlsApi
|
|
7
|
+
module Paginate
|
|
8
|
+
|
|
9
|
+
DEFAULT_PAGE_SIZE = 25
|
|
10
|
+
|
|
11
|
+
# == Replacement hook for will_paginate's class method
|
|
12
|
+
# Does a best effort to mimic the will_paginate method of same name. All arguments are
|
|
13
|
+
# passed on to the finder method except the special keys for the options hash listed below.
|
|
14
|
+
#
|
|
15
|
+
# == Special parameters for paginating finders
|
|
16
|
+
# * <tt>:page</tt> -- REQUIRED, but defaults to 1 if false or nil
|
|
17
|
+
# * <tt>:per_page</tt> -- defaults to <tt>CurrentModel.per_page</tt> (which is 25 if not overridden)
|
|
18
|
+
# * <tt>:finder</tt> -- name of the finder used (default: "get"). This needs to be a class finder method on the class
|
|
19
|
+
def paginate(*args)
|
|
20
|
+
options = args.last.is_a?(::Hash) ? args.pop : {}
|
|
21
|
+
page = options.delete(:page) || 1
|
|
22
|
+
items_per_page = options.delete(:per_page) || self.per_page
|
|
23
|
+
finder = (options.delete(:finder) || 'get').to_s
|
|
24
|
+
page_options = {
|
|
25
|
+
"_pagination" => 1,
|
|
26
|
+
"_limit" => items_per_page,
|
|
27
|
+
"_page" => page
|
|
28
|
+
}
|
|
29
|
+
options.merge!(page_options)
|
|
30
|
+
args << options
|
|
31
|
+
collection = send(finder,*args)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# == Instanciate class instances from array of hash representations.
|
|
35
|
+
# Needs to be called by all finders that would like to support paging. Takes the hash result
|
|
36
|
+
# set from the request layer and instanciates instances of the class called for the finder.
|
|
37
|
+
#
|
|
38
|
+
# * result_array -- the results object returned from the api request layer. An array of hashes.
|
|
39
|
+
#
|
|
40
|
+
# :returns:
|
|
41
|
+
# An array of class instances for the Class of the calling finder
|
|
42
|
+
def collect(result_array)
|
|
43
|
+
|
|
44
|
+
# when conducting a count (pagination=count), the result_array is not an array
|
|
45
|
+
# in those cases, simply return the Fixnum
|
|
46
|
+
return result_array unless result_array.kind_of? Array
|
|
47
|
+
|
|
48
|
+
collection = result_array.collect { |item| new(item)}
|
|
49
|
+
result_array.replace(collection)
|
|
50
|
+
result_array
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Default per_page limit set on all models. Override this method in the model such ala the
|
|
54
|
+
# will_paginate gem to change
|
|
55
|
+
def per_page
|
|
56
|
+
DEFAULT_PAGE_SIZE
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# ==Paginate Api Responses
|
|
63
|
+
# Module used by the request layer to decorate the response's results array with paging support.
|
|
64
|
+
# Pagination only happens if the response includes the pagination information as specified by the
|
|
65
|
+
# API.
|
|
66
|
+
module PaginateResponse
|
|
67
|
+
attr_accessor :results
|
|
68
|
+
def method_missing(method_symbol, *arguments)
|
|
69
|
+
if results.respond_to?(method_symbol)
|
|
70
|
+
arguments.empty? ? self.results.send(method_symbol) : self.results.send(method_symbol, arguments)
|
|
71
|
+
else
|
|
72
|
+
super
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# ==Pagination Helpers
|
|
78
|
+
# Helpers to create the pagination collection
|
|
79
|
+
module PaginateHelper
|
|
80
|
+
# ==Enable pagination
|
|
81
|
+
# * results -- array of hashes representing api resources
|
|
82
|
+
# * paging_hash -- the pagination response information from the api representing paging state.
|
|
83
|
+
#
|
|
84
|
+
# :returns:
|
|
85
|
+
# The result set decorated as a WillPaginate::Collection
|
|
86
|
+
def paginate_response(results, paging_hash)
|
|
87
|
+
pager = Pagination.new(paging_hash)
|
|
88
|
+
paged_results = WillPaginate::Collection.create(pager.current_page, pager.page_size, pager.total_rows) do |p|
|
|
89
|
+
p.replace(results)
|
|
90
|
+
end
|
|
91
|
+
paged_results.extend PaginateResponse
|
|
92
|
+
paged_results.results = results
|
|
93
|
+
paged_results
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# ==Pagination
|
|
98
|
+
# Simple class representing the API's pagination response object
|
|
99
|
+
class Pagination
|
|
100
|
+
attr_accessor :total_rows, :page_size, :total_pages, :current_page
|
|
101
|
+
def initialize(hash)
|
|
102
|
+
@total_rows = hash["TotalRows"]
|
|
103
|
+
@page_size = hash["PageSize"]
|
|
104
|
+
@total_pages = hash["TotalPages"]
|
|
105
|
+
@current_page = hash["CurrentPage"]
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module SparkApi
|
|
2
|
+
class PrimaryArray < Array
|
|
3
|
+
|
|
4
|
+
def primary
|
|
5
|
+
find_primary
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
# This is a very simplistic but reliable implementation.
|
|
11
|
+
def find_primary
|
|
12
|
+
self.each do |arg|
|
|
13
|
+
if arg.primary?
|
|
14
|
+
return arg
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
nil
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
#=== Primary: interface to implement for elements that are added to a "PrimaryArray" collection
|
|
22
|
+
module Primary
|
|
23
|
+
# Return true if the element is the primary resource in a collection.
|
|
24
|
+
# Default implementation looks for a "Primary" attribute
|
|
25
|
+
def primary?
|
|
26
|
+
@attributes.key?("Primary") && self.Primary == true
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module FlexmlsApi
|
|
2
|
+
class PrimaryArray < Array
|
|
3
|
+
|
|
4
|
+
def primary
|
|
5
|
+
find_primary
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
# This is a very simplistic but reliable implementation.
|
|
11
|
+
def find_primary
|
|
12
|
+
self.each do |arg|
|
|
13
|
+
if arg.primary?
|
|
14
|
+
return arg
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
nil
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
#=== Primary: interface to implement for elements that are added to a "PrimaryArray" collection
|
|
22
|
+
module Primary
|
|
23
|
+
# Return true if the element is the primary resource in a collection.
|
|
24
|
+
# Default implementation looks for a "Primary" attribute
|
|
25
|
+
def primary?
|
|
26
|
+
@attributes.key?("Primary") && self.Primary == true
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require 'cgi'
|
|
2
|
+
|
|
3
|
+
module SparkApi
|
|
4
|
+
# HTTP request wrapper. Performs all the api session mumbo jumbo so that the models don't have to.
|
|
5
|
+
module Request
|
|
6
|
+
# Perform an HTTP GET request
|
|
7
|
+
#
|
|
8
|
+
# * path - Path of an api resource, excluding version and endpoint (domain) information
|
|
9
|
+
# * options - Resource request options as specified being supported via and api resource
|
|
10
|
+
# :returns:
|
|
11
|
+
# Hash of the json results as documented in the api.
|
|
12
|
+
# :raises:
|
|
13
|
+
# SparkApi::ClientError or subclass if the request failed.
|
|
14
|
+
def get(path, options={})
|
|
15
|
+
request(:get, path, nil, options)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Perform an HTTP POST request
|
|
19
|
+
#
|
|
20
|
+
# * path - Path of an api resource, excluding version and endpoint (domain) information
|
|
21
|
+
# * body - Hash for post body data
|
|
22
|
+
# * options - Resource request options as specified being supported via and api resource
|
|
23
|
+
# :returns:
|
|
24
|
+
# Hash of the json results as documented in the api.
|
|
25
|
+
# :raises:
|
|
26
|
+
# SparkApi::ClientError or subclass if the request failed.
|
|
27
|
+
def post(path, body={}, options={})
|
|
28
|
+
request(:post, path, body, options)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Perform an HTTP PUT request
|
|
32
|
+
#
|
|
33
|
+
# * path - Path of an api resource, excluding version and endpoint (domain) information
|
|
34
|
+
# * body - Hash for post body data
|
|
35
|
+
# * options - Resource request options as specified being supported via and api resource
|
|
36
|
+
# :returns:
|
|
37
|
+
# Hash of the json results as documented in the api.
|
|
38
|
+
# :raises:
|
|
39
|
+
# SparkApi::ClientError or subclass if the request failed.
|
|
40
|
+
def put(path, body={}, options={})
|
|
41
|
+
request(:put, path, body, options)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Perform an HTTP DELETE request
|
|
45
|
+
#
|
|
46
|
+
# * path - Path of an api resource, excluding version and endpoint (domain) information
|
|
47
|
+
# * options - Resource request options as specified being supported via and api resource
|
|
48
|
+
# :returns:
|
|
49
|
+
# Hash of the json results as documented in the api.
|
|
50
|
+
# :raises:
|
|
51
|
+
# SparkApi::ClientError or subclass if the request failed.
|
|
52
|
+
def delete(path, options={})
|
|
53
|
+
request(:delete, path, nil, options)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
# Perform an HTTP request (no data)
|
|
59
|
+
def request(method, path, body, options)
|
|
60
|
+
unless authenticated?
|
|
61
|
+
authenticate
|
|
62
|
+
end
|
|
63
|
+
attempts = 0
|
|
64
|
+
begin
|
|
65
|
+
request_opts = {}
|
|
66
|
+
request_opts.merge!(options)
|
|
67
|
+
post_data = body.nil? ? nil : {"D" => body }.to_json
|
|
68
|
+
request_path = "/#{version}#{path}"
|
|
69
|
+
start_time = Time.now
|
|
70
|
+
SparkApi.logger.debug("#{method.to_s.upcase} Request: #{request_path}")
|
|
71
|
+
if post_data.nil?
|
|
72
|
+
response = authenticator.request(method, request_path, nil, request_opts)
|
|
73
|
+
else
|
|
74
|
+
SparkApi.logger.debug("#{method.to_s.upcase} Data: #{post_data}")
|
|
75
|
+
response = authenticator.request(method, request_path, post_data, request_opts)
|
|
76
|
+
end
|
|
77
|
+
request_time = Time.now - start_time
|
|
78
|
+
SparkApi.logger.info("[#{(request_time * 1000).to_i}ms] Api: #{method.to_s.upcase} #{request_path}")
|
|
79
|
+
rescue PermissionDenied => e
|
|
80
|
+
if(ResponseCodes::SESSION_TOKEN_EXPIRED == e.code)
|
|
81
|
+
unless (attempts +=1) > 1
|
|
82
|
+
SparkApi.logger.debug("Retrying authentication")
|
|
83
|
+
authenticate
|
|
84
|
+
retry
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
# No luck authenticating... KABOOM!
|
|
88
|
+
SparkApi.logger.error("Authentication failed or server is sending us expired tokens, nothing we can do here.")
|
|
89
|
+
raise
|
|
90
|
+
end
|
|
91
|
+
response.body
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require 'cgi'
|
|
2
|
+
|
|
3
|
+
module FlexmlsApi
|
|
4
|
+
# HTTP request wrapper. Performs all the api session mumbo jumbo so that the models don't have to.
|
|
5
|
+
module Request
|
|
6
|
+
# Perform an HTTP GET request
|
|
7
|
+
#
|
|
8
|
+
# * path - Path of an api resource, excluding version and endpoint (domain) information
|
|
9
|
+
# * options - Resource request options as specified being supported via and api resource
|
|
10
|
+
# :returns:
|
|
11
|
+
# Hash of the json results as documented in the api.
|
|
12
|
+
# :raises:
|
|
13
|
+
# FlexmlsApi::ClientError or subclass if the request failed.
|
|
14
|
+
def get(path, options={})
|
|
15
|
+
request(:get, path, nil, options)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Perform an HTTP POST request
|
|
19
|
+
#
|
|
20
|
+
# * path - Path of an api resource, excluding version and endpoint (domain) information
|
|
21
|
+
# * body - Hash for post body data
|
|
22
|
+
# * options - Resource request options as specified being supported via and api resource
|
|
23
|
+
# :returns:
|
|
24
|
+
# Hash of the json results as documented in the api.
|
|
25
|
+
# :raises:
|
|
26
|
+
# FlexmlsApi::ClientError or subclass if the request failed.
|
|
27
|
+
def post(path, body={}, options={})
|
|
28
|
+
request(:post, path, body, options)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Perform an HTTP PUT request
|
|
32
|
+
#
|
|
33
|
+
# * path - Path of an api resource, excluding version and endpoint (domain) information
|
|
34
|
+
# * body - Hash for post body data
|
|
35
|
+
# * options - Resource request options as specified being supported via and api resource
|
|
36
|
+
# :returns:
|
|
37
|
+
# Hash of the json results as documented in the api.
|
|
38
|
+
# :raises:
|
|
39
|
+
# FlexmlsApi::ClientError or subclass if the request failed.
|
|
40
|
+
def put(path, body={}, options={})
|
|
41
|
+
request(:put, path, body, options)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Perform an HTTP DELETE request
|
|
45
|
+
#
|
|
46
|
+
# * path - Path of an api resource, excluding version and endpoint (domain) information
|
|
47
|
+
# * options - Resource request options as specified being supported via and api resource
|
|
48
|
+
# :returns:
|
|
49
|
+
# Hash of the json results as documented in the api.
|
|
50
|
+
# :raises:
|
|
51
|
+
# FlexmlsApi::ClientError or subclass if the request failed.
|
|
52
|
+
def delete(path, options={})
|
|
53
|
+
request(:delete, path, nil, options)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
# Perform an HTTP request (no data)
|
|
59
|
+
def request(method, path, body, options)
|
|
60
|
+
unless authenticated?
|
|
61
|
+
authenticate
|
|
62
|
+
end
|
|
63
|
+
attempts = 0
|
|
64
|
+
begin
|
|
65
|
+
request_opts = {}
|
|
66
|
+
request_opts.merge!(options)
|
|
67
|
+
post_data = body.nil? ? nil : {"D" => body }.to_json
|
|
68
|
+
request_path = "/#{version}#{path}"
|
|
69
|
+
start_time = Time.now
|
|
70
|
+
FlexmlsApi.logger.debug("#{method.to_s.upcase} Request: #{request_path}")
|
|
71
|
+
if post_data.nil?
|
|
72
|
+
response = authenticator.request(method, request_path, nil, request_opts)
|
|
73
|
+
else
|
|
74
|
+
FlexmlsApi.logger.debug("#{method.to_s.upcase} Data: #{post_data}")
|
|
75
|
+
response = authenticator.request(method, request_path, post_data, request_opts)
|
|
76
|
+
end
|
|
77
|
+
request_time = Time.now - start_time
|
|
78
|
+
FlexmlsApi.logger.info("[#{(request_time * 1000).to_i}ms] Api: #{method.to_s.upcase} #{request_path}")
|
|
79
|
+
rescue PermissionDenied => e
|
|
80
|
+
if(ResponseCodes::SESSION_TOKEN_EXPIRED == e.code)
|
|
81
|
+
unless (attempts +=1) > 1
|
|
82
|
+
FlexmlsApi.logger.debug("Retrying authentication")
|
|
83
|
+
authenticate
|
|
84
|
+
retry
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
# No luck authenticating... KABOOM!
|
|
88
|
+
FlexmlsApi.logger.error("Authentication failed or server is sending us expired tokens, nothing we can do here.")
|
|
89
|
+
raise
|
|
90
|
+
end
|
|
91
|
+
response.body
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|