flexmls_api 0.3.2
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/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
|
+
}
|