flexmls_api 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +20 -0
- data/Gemfile.lock +60 -0
- data/LICENSE +14 -0
- data/README.md +128 -0
- data/Rakefile +78 -0
- data/VERSION +1 -0
- data/lib/flexmls_api/authentication.rb +104 -0
- data/lib/flexmls_api/client.rb +20 -0
- data/lib/flexmls_api/configuration.rb +40 -0
- data/lib/flexmls_api/faraday.rb +52 -0
- data/lib/flexmls_api/models/base.rb +76 -0
- data/lib/flexmls_api/models/connect_prefs.rb +10 -0
- data/lib/flexmls_api/models/contact.rb +25 -0
- data/lib/flexmls_api/models/custom_fields.rb +12 -0
- data/lib/flexmls_api/models/document.rb +11 -0
- data/lib/flexmls_api/models/idx_link.rb +45 -0
- data/lib/flexmls_api/models/listing.rb +110 -0
- data/lib/flexmls_api/models/market_statistics.rb +33 -0
- data/lib/flexmls_api/models/photo.rb +15 -0
- data/lib/flexmls_api/models/property_types.rb +7 -0
- data/lib/flexmls_api/models/standard_fields.rb +7 -0
- data/lib/flexmls_api/models/subresource.rb +13 -0
- data/lib/flexmls_api/models/system_info.rb +7 -0
- data/lib/flexmls_api/models/video.rb +16 -0
- data/lib/flexmls_api/models/virtual_tour.rb +18 -0
- data/lib/flexmls_api/models.rb +21 -0
- data/lib/flexmls_api/paginate.rb +87 -0
- data/lib/flexmls_api/request.rb +172 -0
- data/lib/flexmls_api/version.rb +4 -0
- data/lib/flexmls_api.rb +41 -0
- data/spec/fixtures/contacts.json +25 -0
- data/spec/fixtures/listing_document_index.json +19 -0
- data/spec/fixtures/listing_no_subresources.json +38 -0
- data/spec/fixtures/listing_photos_index.json +469 -0
- data/spec/fixtures/listing_videos_index.json +18 -0
- data/spec/fixtures/listing_virtual_tours_index.json +42 -0
- data/spec/fixtures/listing_with_documents.json +52 -0
- data/spec/fixtures/listing_with_photos.json +110 -0
- data/spec/fixtures/listing_with_supplement.json +39 -0
- data/spec/fixtures/listing_with_videos.json +54 -0
- data/spec/fixtures/listing_with_vtour.json +48 -0
- data/spec/fixtures/session.json +10 -0
- data/spec/json_helper.rb +77 -0
- data/spec/spec_helper.rb +78 -0
- data/spec/unit/flexmls_api/configuration_spec.rb +97 -0
- data/spec/unit/flexmls_api/faraday_spec.rb +94 -0
- data/spec/unit/flexmls_api/models/base_spec.rb +62 -0
- data/spec/unit/flexmls_api/models/connect_prefs_spec.rb +9 -0
- data/spec/unit/flexmls_api/models/contact_spec.rb +70 -0
- data/spec/unit/flexmls_api/models/document_spec.rb +39 -0
- data/spec/unit/flexmls_api/models/listing_spec.rb +174 -0
- data/spec/unit/flexmls_api/models/photo_spec.rb +59 -0
- data/spec/unit/flexmls_api/models/property_types_spec.rb +20 -0
- data/spec/unit/flexmls_api/models/standard_fields_spec.rb +42 -0
- data/spec/unit/flexmls_api/models/system_info_spec.rb +37 -0
- data/spec/unit/flexmls_api/models/video_spec.rb +43 -0
- data/spec/unit/flexmls_api/models/virtual_tour_spec.rb +46 -0
- data/spec/unit/flexmls_api/paginate_spec.rb +221 -0
- data/spec/unit/flexmls_api/request_spec.rb +288 -0
- data/spec/unit/flexmls_api_spec.rb +44 -0
- metadata +315 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
module FlexmlsApi
|
2
|
+
module Models
|
3
|
+
class Video < Base
|
4
|
+
extend Subresource
|
5
|
+
self.element_name = 'videos'
|
6
|
+
|
7
|
+
def branded?
|
8
|
+
attributes['Type'] == 'branded'
|
9
|
+
end
|
10
|
+
|
11
|
+
def unbranded?
|
12
|
+
attributes['Type'] == 'unbranded'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module FlexmlsApi
|
2
|
+
module Models
|
3
|
+
class VirtualTour < Base
|
4
|
+
extend Subresource
|
5
|
+
self.element_name="virtualtours"
|
6
|
+
|
7
|
+
|
8
|
+
def branded?
|
9
|
+
attributes["Type"] == "branded"
|
10
|
+
end
|
11
|
+
|
12
|
+
def unbranded?
|
13
|
+
attributes["Type"] == "unbranded"
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.expand_path('../models/base', __FILE__)
|
2
|
+
require File.expand_path('../models/listing', __FILE__)
|
3
|
+
require File.expand_path('../models/subresource', __FILE__)
|
4
|
+
require File.expand_path('../models/photo', __FILE__)
|
5
|
+
require File.expand_path('../models/system_info', __FILE__)
|
6
|
+
require File.expand_path('../models/standard_fields', __FILE__)
|
7
|
+
require File.expand_path('../models/custom_fields', __FILE__)
|
8
|
+
require File.expand_path('../models/property_types', __FILE__)
|
9
|
+
require File.expand_path('../models/connect_prefs', __FILE__)
|
10
|
+
require File.expand_path('../models/contact', __FILE__)
|
11
|
+
require File.expand_path('../models/idx_link', __FILE__)
|
12
|
+
require File.expand_path('../models/market_statistics', __FILE__)
|
13
|
+
require File.expand_path('../models/video', __FILE__)
|
14
|
+
require File.expand_path('../models/virtual_tour', __FILE__)
|
15
|
+
require File.expand_path('../models/document', __FILE__)
|
16
|
+
|
17
|
+
module FlexmlsApi
|
18
|
+
module Models
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,87 @@
|
|
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
|
+
collection = result_array.collect { |item| new(item)}
|
44
|
+
result_array.replace(collection)
|
45
|
+
result_array
|
46
|
+
end
|
47
|
+
|
48
|
+
# Default per_page limit set on all models. Override this method in the model such ala the
|
49
|
+
# will_paginate gem to change
|
50
|
+
def per_page
|
51
|
+
DEFAULT_PAGE_SIZE
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# ==Paginate Api Responses
|
56
|
+
# Module use by the request layer to decorate the response's results array with paging support.
|
57
|
+
# Pagination only happens if the response includes the pagination information as specified by the
|
58
|
+
# API.
|
59
|
+
module PaginateResponse
|
60
|
+
# ==Enable pagination
|
61
|
+
# * results -- array of hashes representing api resources
|
62
|
+
# * paging_hash -- the pagination response information from the api representing paging state.
|
63
|
+
#
|
64
|
+
# :returns:
|
65
|
+
# The result set decorated as a WillPaginate::Collection
|
66
|
+
def paginate_response(results, paging_hash)
|
67
|
+
pager = Pagination.new(paging_hash)
|
68
|
+
paged_results = WillPaginate::Collection.create(pager.current_page, pager.page_size, pager.total_rows) do |p|
|
69
|
+
p.replace(results)
|
70
|
+
end
|
71
|
+
paged_results
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# ==Pagination
|
76
|
+
# Simple class representing the API's pagination response object
|
77
|
+
class Pagination
|
78
|
+
attr_accessor :total_rows, :page_size, :total_pages, :current_page
|
79
|
+
def initialize(hash)
|
80
|
+
@total_rows = hash["TotalRows"]
|
81
|
+
@page_size = hash["PageSize"]
|
82
|
+
@total_pages = hash["TotalPages"]
|
83
|
+
@current_page = hash["CurrentPage"]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
|
2
|
+
module FlexmlsApi
|
3
|
+
# HTTP request wrapper. Performs all the api session mumbo jumbo so that the models don't have to.
|
4
|
+
module Request
|
5
|
+
include PaginateResponse
|
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
|
+
if @session.nil? || @session.expired?
|
61
|
+
authenticate
|
62
|
+
end
|
63
|
+
attempts = 0
|
64
|
+
begin
|
65
|
+
request_opts = {
|
66
|
+
"AuthToken" => @session.auth_token
|
67
|
+
}
|
68
|
+
request_opts.merge!(options)
|
69
|
+
post_data = body.nil? ? nil : {"D" => body }.to_json
|
70
|
+
sig = sign_token(path, request_opts, post_data)
|
71
|
+
request_path = "/#{version}#{path}?ApiSig=#{sig}#{build_url_parameters(request_opts)}"
|
72
|
+
FlexmlsApi.logger.debug("Request: #{request_path}")
|
73
|
+
start_time = Time.now
|
74
|
+
if post_data.nil?
|
75
|
+
response = connection.send(method, request_path)
|
76
|
+
else
|
77
|
+
FlexmlsApi.logger.debug("Data: #{post_data}")
|
78
|
+
response = connection.send(method, request_path, post_data)
|
79
|
+
end
|
80
|
+
request_time = Time.now - start_time
|
81
|
+
FlexmlsApi.logger.info("[#{request_time}s] Api: #{method.to_s.upcase} #{request_path}")
|
82
|
+
rescue PermissionDenied => e
|
83
|
+
if(ResponseCodes::SESSION_TOKEN_EXPIRED == e.code)
|
84
|
+
unless (attempts +=1) > 1
|
85
|
+
FlexmlsApi.logger.debug("Retrying authentication")
|
86
|
+
authenticate
|
87
|
+
retry
|
88
|
+
end
|
89
|
+
end
|
90
|
+
# No luck authenticating... KABOOM!
|
91
|
+
FlexmlsApi.logger.error("Authentication failed or server is sending us expired tokens, nothing we can do here.")
|
92
|
+
raise
|
93
|
+
end
|
94
|
+
results = response.body.results
|
95
|
+
paging = response.body.pagination
|
96
|
+
unless paging.nil?
|
97
|
+
results = paginate_response(results, paging)
|
98
|
+
end
|
99
|
+
results
|
100
|
+
end
|
101
|
+
|
102
|
+
# Format a hash as request parameters
|
103
|
+
#
|
104
|
+
# :returns:
|
105
|
+
# Stringized form of the parameters as needed for the http request
|
106
|
+
def build_url_parameters(parameters={})
|
107
|
+
str = ""
|
108
|
+
parameters.map do |key,value|
|
109
|
+
str << "&#{key}=#{value}"
|
110
|
+
end
|
111
|
+
str
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# All known response codes listed in the API
|
116
|
+
module ResponseCodes
|
117
|
+
NOT_FOUND = 404
|
118
|
+
METHOD_NOT_ALLOWED = 405
|
119
|
+
INVALID_KEY = 1000
|
120
|
+
DISABLED_KEY = 1010
|
121
|
+
API_USER_REQUIRED = 1015
|
122
|
+
SESSION_TOKEN_EXPIRED = 1020
|
123
|
+
SSL_REQUIRED = 1030
|
124
|
+
INVALID_JSON = 1035
|
125
|
+
INVALID_FIELD = 1040
|
126
|
+
MISSING_PARAMETER = 1050
|
127
|
+
INVALID_PARAMETER = 1053
|
128
|
+
CONFLICTING_DATA = 1055
|
129
|
+
NOT_AVAILABLE= 1500
|
130
|
+
RATE_LIMIT_EXCEEDED = 1550
|
131
|
+
end
|
132
|
+
|
133
|
+
# Errors built from API responses
|
134
|
+
class InvalidResponse < StandardError; end
|
135
|
+
class ClientError < StandardError
|
136
|
+
attr_reader :code, :status
|
137
|
+
def initialize (code, status)
|
138
|
+
@code = code
|
139
|
+
@status = status
|
140
|
+
end
|
141
|
+
end
|
142
|
+
class NotFound < ClientError; end
|
143
|
+
class PermissionDenied < ClientError; end
|
144
|
+
class NotAllowed < ClientError; end
|
145
|
+
class BadResourceRequest < ClientError; end
|
146
|
+
|
147
|
+
|
148
|
+
# Nice and handy class wrapper for the api response hash
|
149
|
+
class ApiResponse
|
150
|
+
attr_accessor :code, :message, :results, :success, :pagination
|
151
|
+
def initialize(d)
|
152
|
+
begin
|
153
|
+
hash = d["D"]
|
154
|
+
if hash.nil? || hash.empty?
|
155
|
+
raise InvalidResponse, "The server response could not be understood"
|
156
|
+
end
|
157
|
+
self.message = hash["Message"]
|
158
|
+
self.code = hash["Code"]
|
159
|
+
self.results = hash["Results"]
|
160
|
+
self.success = hash["Success"]
|
161
|
+
self.pagination = hash["Pagination"]
|
162
|
+
rescue Exception => e
|
163
|
+
FlexmlsApi.logger.error "Unable to understand the response! #{d}"
|
164
|
+
raise
|
165
|
+
end
|
166
|
+
end
|
167
|
+
def success?
|
168
|
+
@success
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
data/lib/flexmls_api.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# Flexmlsapi
|
2
|
+
require 'rubygems'
|
3
|
+
require 'curb'
|
4
|
+
require 'json'
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
require File.expand_path('../flexmls_api/version', __FILE__)
|
8
|
+
require File.expand_path('../flexmls_api/configuration', __FILE__)
|
9
|
+
require File.expand_path('../flexmls_api/authentication', __FILE__)
|
10
|
+
require File.expand_path('../flexmls_api/paginate', __FILE__)
|
11
|
+
require File.expand_path('../flexmls_api/request', __FILE__)
|
12
|
+
require File.expand_path('../flexmls_api/client', __FILE__)
|
13
|
+
require File.expand_path('../flexmls_api/faraday', __FILE__)
|
14
|
+
require File.expand_path('../flexmls_api/models', __FILE__)
|
15
|
+
|
16
|
+
module FlexmlsApi
|
17
|
+
extend Configuration
|
18
|
+
|
19
|
+
def self.logger
|
20
|
+
if @logger.nil?
|
21
|
+
@logger = Logger.new(STDOUT)
|
22
|
+
@logger.level = Logger::INFO
|
23
|
+
end
|
24
|
+
@logger
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.client(opts={})
|
28
|
+
Thread.current[:flexmls_api_client] ||= FlexmlsApi::Client.new(opts)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.method_missing(method, *args, &block)
|
32
|
+
return super unless (client.respond_to?(method))
|
33
|
+
client.send(method, *args, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.reset
|
37
|
+
reset_configuration
|
38
|
+
Thread.current[:flexmls_api_client] = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
{
|
2
|
+
"D": {
|
3
|
+
"Success": true,
|
4
|
+
"Results": [
|
5
|
+
{
|
6
|
+
"ResourceUri":"/v1/contacts/20101230223226074201000000",
|
7
|
+
"DisplayName":"Contact One",
|
8
|
+
"Id":"20101230223226074201000000",
|
9
|
+
"PrimaryEmail":"contact1@fbsdata.com"
|
10
|
+
},
|
11
|
+
{
|
12
|
+
"ResourceUri":"/v1/contacts/20101230223226074202000000",
|
13
|
+
"DisplayName":"Contact Two",
|
14
|
+
"Id":"20101230223226074202000000",
|
15
|
+
"PrimaryEmail":"contact2fbsdata.com"
|
16
|
+
},
|
17
|
+
{
|
18
|
+
"ResourceUri":"/v1/contacts/20101230223226074203000000",
|
19
|
+
"DisplayName":"Contact Three",
|
20
|
+
"Id":"20101230223226074203000000",
|
21
|
+
"PrimaryEmail":"contact3@fbsdata.com"
|
22
|
+
}
|
23
|
+
]
|
24
|
+
}
|
25
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
{
|
2
|
+
"D": {
|
3
|
+
"Results": [
|
4
|
+
{
|
5
|
+
"Uri": "http://images.dev.fbsdata.com/documents/cda/20060725224801143085000000.pdf",
|
6
|
+
"ResourceUri": "/v1/listings/20060725224713296297000000/documents/20060725224801143085000000",
|
7
|
+
"Name": "Disclosure",
|
8
|
+
"Id": "20060725224801143085000000"
|
9
|
+
},
|
10
|
+
{
|
11
|
+
"Uri": "http://images.dev.fbsdata.com/documents/cda/20060725224818080340000000.pdf",
|
12
|
+
"ResourceUri": "/v1/listings/20060725224713296297000000/documents/20060725224818080340000000",
|
13
|
+
"Name": "Plat Map",
|
14
|
+
"Id": "20060725224818080340000000"
|
15
|
+
}
|
16
|
+
],
|
17
|
+
"Success": true
|
18
|
+
}
|
19
|
+
}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
{
|
2
|
+
"D": {
|
3
|
+
"Results": [
|
4
|
+
{
|
5
|
+
"ResourceUri": "/v1/listings/20060725224713296297000000",
|
6
|
+
"StandardFields": {
|
7
|
+
"StreetNumber": "7298",
|
8
|
+
"Longitude": "-116.3237",
|
9
|
+
"City": "Bonners Ferry",
|
10
|
+
"ListingId": "06-9395",
|
11
|
+
"PublicRemarks": "Afforadable home in town close to hospital,yet quiet country like setting. Good views. Must see",
|
12
|
+
"BuildingAreaTotal": "924.0",
|
13
|
+
"YearBuilt": 1977,
|
14
|
+
"StreetName": "BIRCH",
|
15
|
+
"ListPrice": "50000.0",
|
16
|
+
"PostalCode": "83805",
|
17
|
+
"Latitude": "48.7001",
|
18
|
+
"BathsThreeQuarter": null,
|
19
|
+
"BathsFull": null,
|
20
|
+
"BathsTotal": "1.0",
|
21
|
+
"StateOrProvince": "ID",
|
22
|
+
"PropertyType": "A",
|
23
|
+
"StreetAdditionalInfo": null,
|
24
|
+
"StreetDirPrefix": null,
|
25
|
+
"BedsTotal": 3,
|
26
|
+
"StreetDirSuffix": null,
|
27
|
+
"ListingKey": "20060725224713296297000000",
|
28
|
+
"ListOfficeName": "Century 21 On The Lake",
|
29
|
+
"BathsHalf": null,
|
30
|
+
"ModificationTimestamp": "2010-11-22T20:47:21Z",
|
31
|
+
"CountyOrParish": "Boundary"
|
32
|
+
},
|
33
|
+
"Id": "20060725224713296297000000"
|
34
|
+
}
|
35
|
+
],
|
36
|
+
"Success": true
|
37
|
+
}
|
38
|
+
}
|