reso_transport 1.5.5 → 1.5.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.gitpod.yml +2 -0
- data/.rubocop.yml +33 -0
- data/README.md +27 -9
- data/bin/console +10 -10
- data/bin/rake +29 -0
- data/lib/reso_transport.rb +22 -34
- data/lib/reso_transport/authentication/fetch_token_auth.rb +30 -11
- data/lib/reso_transport/base_metadata.rb +61 -0
- data/lib/reso_transport/client.rb +33 -17
- data/lib/reso_transport/datasystem.rb +22 -0
- data/lib/reso_transport/datasystem_parser.rb +26 -0
- data/lib/reso_transport/entity_set.rb +2 -4
- data/lib/reso_transport/entity_type.rb +11 -13
- data/lib/reso_transport/metadata.rb +12 -30
- data/lib/reso_transport/metadata_cache.rb +4 -5
- data/lib/reso_transport/metadata_parser.rb +31 -22
- data/lib/reso_transport/query.rb +44 -42
- data/lib/reso_transport/resource.rb +22 -9
- data/lib/reso_transport/version.rb +1 -1
- data/reso_transport.gemspec +20 -20
- metadata +20 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49293478c0827ccb4bdca1bb6c9974900f8c0c16853b247487120a0ed3bb79df
|
4
|
+
data.tar.gz: c9d8c93979377ed18d22a179bae44175961b44e8641eb091930f99542d1e610f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4464fe0a9aa0006f25c5eab29dab8422401ac2ebf435b712e65d5727e43606bb0df4fdf895e5a1d6a1e3e44d4937d19c42389577d28568351832ccae64d55ebb
|
7
|
+
data.tar.gz: 729d643455ddd8e7a827ddc6aea2a8512f93bc65a3e469993da5d2cd5085ecdc5eed4befee4f376cd9986f51a81d363fdeb042a1f8f51e2e51d7230f265c2d7a
|
data/.gitignore
CHANGED
data/.gitpod.yml
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.6
|
3
|
+
|
4
|
+
Layout/FirstHashElementIndentation:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
Layout/MultilineMethodCallIndentation:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Metrics/AbcSize:
|
11
|
+
Max: 20
|
12
|
+
|
13
|
+
Metrics/BlockLength:
|
14
|
+
Exclude:
|
15
|
+
- test/**/*
|
16
|
+
|
17
|
+
Metrics/ClassLength:
|
18
|
+
Max: 150
|
19
|
+
|
20
|
+
Metrics/MethodLength:
|
21
|
+
Max: 15
|
22
|
+
|
23
|
+
Metrics/ModuleLength:
|
24
|
+
Max: 150
|
25
|
+
|
26
|
+
Style/ColonMethodCall:
|
27
|
+
Enabled: false
|
28
|
+
|
29
|
+
Style/Documentation:
|
30
|
+
Enabled: false
|
31
|
+
|
32
|
+
Style/FrozenStringLiteralComment:
|
33
|
+
Enabled: false
|
data/README.md
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/wrstudios/reso_transport)
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/reso_transport.svg)](https://badge.fury.io/rb/reso_transport)
|
3
|
+
|
1
4
|
# ResoTransport
|
2
5
|
|
3
6
|
A Ruby gem for connecting to and interacting with RESO WebAPI services. Learn more about what that is by checking out the [RESO WebAPI](https://www.reso.org/reso-web-api/) Documentation.
|
@@ -51,7 +54,7 @@ Or you can set a logger for each specific instance of a client which can be usef
|
|
51
54
|
|
52
55
|
### Getting Connected
|
53
56
|
|
54
|
-
There are 2 strategies for authentication.
|
57
|
+
There are 2 strategies for authentication.
|
55
58
|
|
56
59
|
**Bearer Token**
|
57
60
|
|
@@ -60,7 +63,8 @@ It's simple to use a static access token if your token never expires:
|
|
60
63
|
```ruby
|
61
64
|
@client = ResoTransport::Client.new({
|
62
65
|
md_file: METADATA_CACHE,
|
63
|
-
endpoint: ENDPOINT_URL
|
66
|
+
endpoint: ENDPOINT_URL,
|
67
|
+
use_replication_endpoint: false # this is the default and can be ommitted
|
64
68
|
authentication: {
|
65
69
|
access_token: TOKEN,
|
66
70
|
token_type: "Bearer" # this is the default and can be ommitted
|
@@ -89,7 +93,8 @@ If the connection requires requesting a new token periodically, it's easy to pro
|
|
89
93
|
|
90
94
|
This will pre-fetch a token from the provided endpoint when the current token is either non-existent or has expired.
|
91
95
|
|
92
|
-
|
96
|
+
The `use_replication_endpoint` flag will append `/replication` to all resource queries if set to `true`. This is required
|
97
|
+
by some data sources to query resources beyond 10,000 records.
|
93
98
|
|
94
99
|
### Caching Metadata
|
95
100
|
|
@@ -108,9 +113,9 @@ If you don't have access to the file system, like on Heroku, or you just don't w
|
|
108
113
|
|
109
114
|
```ruby
|
110
115
|
class MyCacheStore < ResoTransport::MetadataCache
|
111
|
-
|
116
|
+
|
112
117
|
def read
|
113
|
-
# read `name` from somewhere
|
118
|
+
# read `name` from somewhere
|
114
119
|
end
|
115
120
|
|
116
121
|
def write(data)
|
@@ -158,12 +163,25 @@ Once you have a successful connection you can explore what resources are availab
|
|
158
163
|
#=> {"Property"=>#<ResoTransport::Resource entity_set="Property", schema="ODataService">, "Office"=>#<ResoTransport::Resource entity_set="Office", schema="ODataService">, "Member"=>#<ResoTransport::Resource entity_set="Member", schema="ODataService">}
|
159
164
|
|
160
165
|
@client.resources["Property"]
|
161
|
-
#=> #<ResoTransport::Resource entity_set="Property", schema="ODataService">
|
166
|
+
#=> #<ResoTransport::Resource entity_set="Property", schema="ODataService">
|
162
167
|
|
163
168
|
@client.resources["Property"].query.limit(1).results
|
164
169
|
#=> Results Array
|
165
170
|
```
|
166
171
|
|
172
|
+
If the resource contains localizations you can access those as well.
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
@client.resources["Property"].localizations
|
176
|
+
#=> {"CommercialSale"=>{"Name"=>"CommercialSale", "ResourcePath"=>"/Property?Class=CommercialSale", "Description"=>"Contains data for Commercial searches.", "DateTimeStamp"=>"2021-05-03T18:13:20.643-07:00"}, "Residential"=>{"Name"=>"Residential", "ResourcePath"=>"/Property?Class=Residential", "Description"=>"Contains data for Residential searches.", "DateTimeStamp"=>"2021-05-03T18:13:20.643-07:00"}}
|
177
|
+
```
|
178
|
+
|
179
|
+
If a resource contains localizations you must select one by name, before querying, like so:
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
@client.resources["Property"].localization('Residential').query.limit(1).results
|
183
|
+
```
|
184
|
+
|
167
185
|
#### Querying
|
168
186
|
|
169
187
|
ResoTransport provides powerful querying capabilities:
|
@@ -196,7 +214,7 @@ To see what child records can be expanded look at `expandable`:
|
|
196
214
|
|
197
215
|
```ruby
|
198
216
|
@resource.expandable
|
199
|
-
#=> [#<struct ResoTransport::Property name="Media", data_type="Collection(RESO.Media)", attrs={"Name"=>"Media", "Type"=>"Collection(RESO.Media)"}, multi=true, enum=nil, complex_type=nil, entity_type=#<struct ResoTransport::EntityType name="Media", base_type=nil, primary_key="MediaKey", schema="CoreLogic.DataStandard.RESO.DD">> ...]
|
217
|
+
#=> [#<struct ResoTransport::Property name="Media", data_type="Collection(RESO.Media)", attrs={"Name"=>"Media", "Type"=>"Collection(RESO.Media)"}, multi=true, enum=nil, complex_type=nil, entity_type=#<struct ResoTransport::EntityType name="Media", base_type=nil, primary_key="MediaKey", schema="CoreLogic.DataStandard.RESO.DD">> ...]
|
200
218
|
```
|
201
219
|
|
202
220
|
Use `expand` to expand child records with the top level results.
|
@@ -210,7 +228,7 @@ You have several options to expand multiple child record sets. Each of these wil
|
|
210
228
|
|
211
229
|
```ruby
|
212
230
|
@resource.query.expand("Media", "Office").limit(10).results
|
213
|
-
|
231
|
+
|
214
232
|
@resource.query.expand(["Media", "Office"]).limit(10).results
|
215
233
|
|
216
234
|
@resource.query.expand("Media").expand("Office").limit(10).results
|
@@ -257,7 +275,7 @@ When querying for an enumeration value, you can provide either the system name,
|
|
257
275
|
|
258
276
|
```ruby
|
259
277
|
@resource.query.eq(StandardStatus: "Active Under Contract").limit(1).compile_params
|
260
|
-
#=> {"$top"=>1, "$filter"=>"StandardStatus eq 'ActiveUnderContract'"}
|
278
|
+
#=> {"$top"=>1, "$filter"=>"StandardStatus eq 'ActiveUnderContract'"}
|
261
279
|
```
|
262
280
|
|
263
281
|
## Development
|
data/bin/console
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'reso_transport'
|
5
5
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -12,19 +12,19 @@ require "reso_transport"
|
|
12
12
|
#
|
13
13
|
|
14
14
|
ResoTransport.configure do |c|
|
15
|
-
c.logger = Logger.new(
|
15
|
+
c.logger = Logger.new('log/console.log')
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
require "irb"
|
18
|
+
require 'irb'
|
20
19
|
require 'yaml'
|
21
20
|
require 'byebug'
|
22
21
|
|
23
|
-
SECRETS = YAML.load_file(
|
22
|
+
SECRETS = YAML.load_file('secrets.yml')
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
24
|
+
SECRETS.each do |name, data|
|
25
|
+
data[:logger] = Logger.new($stdout)
|
26
|
+
client = ResoTransport::Client.new(data)
|
27
|
+
instance_variable_set("@#{name}", client)
|
28
|
+
end
|
29
29
|
|
30
30
|
IRB.start(__FILE__)
|
data/bin/rake
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rake", "rake")
|
data/lib/reso_transport.rb
CHANGED
@@ -5,41 +5,30 @@ require 'faraday'
|
|
5
5
|
require 'json'
|
6
6
|
require 'time'
|
7
7
|
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
# module Utils
|
27
|
-
|
28
|
-
# def escape(str)
|
29
|
-
# str.to_s.gsub(ESCAPE_RE) do |match|
|
30
|
-
# '%' + match.unpack('H2' * match.bytesize).join('%').upcase
|
31
|
-
# end.gsub(" ","%20")
|
32
|
-
|
33
|
-
# end
|
34
|
-
# end
|
35
|
-
# end
|
36
|
-
|
37
|
-
Faraday::Utils.default_space_encoding = "%20"
|
8
|
+
require 'reso_transport/version'
|
9
|
+
require 'reso_transport/configuration'
|
10
|
+
require 'reso_transport/authentication'
|
11
|
+
require 'reso_transport/client'
|
12
|
+
require 'reso_transport/resource'
|
13
|
+
require 'reso_transport/metadata'
|
14
|
+
require 'reso_transport/metadata_cache'
|
15
|
+
require 'reso_transport/metadata_parser'
|
16
|
+
require 'reso_transport/datasystem'
|
17
|
+
require 'reso_transport/datasystem_parser'
|
18
|
+
require 'reso_transport/schema'
|
19
|
+
require 'reso_transport/entity_set'
|
20
|
+
require 'reso_transport/entity_type'
|
21
|
+
require 'reso_transport/enum'
|
22
|
+
require 'reso_transport/property'
|
23
|
+
require 'reso_transport/query'
|
24
|
+
|
25
|
+
Faraday::Utils.default_space_encoding = '%20'
|
38
26
|
|
39
27
|
module ResoTransport
|
40
28
|
class Error < StandardError; end
|
29
|
+
|
41
30
|
class AccessDenied < StandardError; end
|
42
|
-
ODATA_TIME_FORMAT =
|
31
|
+
ODATA_TIME_FORMAT = '%Y-%m-%dT%H:%M:%SZ'.freeze
|
43
32
|
|
44
33
|
class << self
|
45
34
|
attr_writer :configuration
|
@@ -53,8 +42,7 @@ module ResoTransport
|
|
53
42
|
yield(configuration)
|
54
43
|
end
|
55
44
|
|
56
|
-
def self.split_schema_and_class_name(
|
57
|
-
|
45
|
+
def self.split_schema_and_class_name(text)
|
46
|
+
text.to_s.partition(/(\w+)$/).first(2).map { |s| s.sub(/\.$/, '') }
|
58
47
|
end
|
59
|
-
|
60
48
|
end
|
@@ -1,20 +1,32 @@
|
|
1
1
|
module ResoTransport
|
2
2
|
module Authentication
|
3
3
|
class FetchTokenAuth < AuthStrategy
|
4
|
-
attr_reader :
|
5
|
-
|
4
|
+
attr_reader :endpoint,
|
5
|
+
:client_id,
|
6
|
+
:client_secret,
|
7
|
+
:grant_type,
|
8
|
+
:scope,
|
9
|
+
:username,
|
10
|
+
:password
|
11
|
+
|
6
12
|
def initialize(options)
|
7
|
-
|
8
|
-
|
13
|
+
super()
|
14
|
+
|
15
|
+
@grant_type = options.fetch(:grant_type, 'client_credentials')
|
16
|
+
@scope = options.fetch(:scope, 'api')
|
9
17
|
@client_id = options.fetch(:client_id)
|
10
18
|
@client_secret = options.fetch(:client_secret)
|
11
19
|
@endpoint = options.fetch(:endpoint)
|
20
|
+
@username = options.fetch(:username, nil)
|
21
|
+
@password = options.fetch(:password, nil)
|
22
|
+
end
|
12
23
|
|
13
|
-
|
24
|
+
def connection
|
25
|
+
@connection ||= Faraday.new(@endpoint) do |faraday|
|
14
26
|
faraday.request :url_encoded
|
15
27
|
faraday.response :logger, ResoTransport.configuration.logger if ResoTransport.configuration.logger
|
16
28
|
faraday.adapter Faraday.default_adapter
|
17
|
-
faraday.basic_auth
|
29
|
+
faraday.basic_auth client_id, client_secret
|
18
30
|
end
|
19
31
|
end
|
20
32
|
|
@@ -30,19 +42,26 @@ module ResoTransport
|
|
30
42
|
Access.new({
|
31
43
|
access_token: json.fetch('access_token'),
|
32
44
|
expires_in: json.fetch('expires_in', 1 << (1.size * 8 - 2) - 1),
|
33
|
-
token_type: json.fetch('token_type',
|
45
|
+
token_type: json.fetch('token_type', 'Bearer')
|
34
46
|
})
|
35
47
|
end
|
36
48
|
|
37
49
|
private
|
38
50
|
|
39
51
|
def auth_params
|
40
|
-
{
|
41
|
-
client_id:
|
52
|
+
params = {
|
53
|
+
client_id: client_id,
|
42
54
|
client_secret: client_secret,
|
43
|
-
grant_type:
|
44
|
-
scope:
|
55
|
+
grant_type: grant_type,
|
56
|
+
scope: scope
|
45
57
|
}
|
58
|
+
|
59
|
+
if grant_type == 'password'
|
60
|
+
params[:username] = username
|
61
|
+
params[:password] = password
|
62
|
+
end
|
63
|
+
|
64
|
+
params
|
46
65
|
end
|
47
66
|
end
|
48
67
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module ResoTransport
|
2
|
+
class BaseMetadata
|
3
|
+
MIME_TYPES = {
|
4
|
+
xml: 'application/xml',
|
5
|
+
json: 'application/json'
|
6
|
+
}.freeze
|
7
|
+
|
8
|
+
attr_reader :client
|
9
|
+
|
10
|
+
def initialize(client)
|
11
|
+
@client = client
|
12
|
+
@prefix = nil
|
13
|
+
@classname = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def prefix
|
17
|
+
raise 'prefix not set' unless @prefix
|
18
|
+
|
19
|
+
@prefix
|
20
|
+
end
|
21
|
+
|
22
|
+
def classname
|
23
|
+
raise 'classname not set' unless @classname
|
24
|
+
|
25
|
+
@classname
|
26
|
+
end
|
27
|
+
|
28
|
+
def parser
|
29
|
+
@parser ||= Object::const_get("#{classname}Parser").new.parse(data)
|
30
|
+
end
|
31
|
+
|
32
|
+
def data
|
33
|
+
if cache_file
|
34
|
+
cache.read || cache.write(raw)
|
35
|
+
else
|
36
|
+
raw
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def cache
|
41
|
+
@cache ||= client.send("#{prefix}_cache").new(cache_file)
|
42
|
+
end
|
43
|
+
|
44
|
+
def cache_file
|
45
|
+
@cache_file ||= client.send "#{prefix}_file"
|
46
|
+
end
|
47
|
+
|
48
|
+
def raw
|
49
|
+
if response.success?
|
50
|
+
response.body
|
51
|
+
else
|
52
|
+
puts response.body
|
53
|
+
raise "Error getting #{classname}!"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def response
|
58
|
+
raise 'Must implement response method'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -1,35 +1,52 @@
|
|
1
1
|
module ResoTransport
|
2
2
|
class Client
|
3
|
-
attr_reader :connection, :uid, :vendor, :endpoint, :
|
4
|
-
|
3
|
+
attr_reader :connection, :uid, :vendor, :endpoint, :authentication, :md_file, :md_cache, :ds_file, :ds_cache,
|
4
|
+
:use_replication_endpoint
|
5
|
+
|
5
6
|
def initialize(options)
|
6
|
-
@
|
7
|
-
@
|
8
|
-
@
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@
|
12
|
-
@
|
13
|
-
|
14
|
-
@
|
7
|
+
@use_replication_endpoint = options.fetch(:use_replication_endpoint, false)
|
8
|
+
@endpoint = options.fetch(:endpoint)
|
9
|
+
@md_file = options.fetch(:md_file, nil)
|
10
|
+
@ds_file = options.fetch(:ds_file, nil)
|
11
|
+
@authentication = ensure_valid_auth_strategy(options.fetch(:authentication))
|
12
|
+
@vendor = options.fetch(:vendor, {})
|
13
|
+
@faraday_options = options.fetch(:faraday_options, {})
|
14
|
+
@logger = options.fetch(:logger, nil)
|
15
|
+
@md_cache = options.fetch(:md_cache, ResoTransport::MetadataCache)
|
16
|
+
@ds_cache = options.fetch(:ds_cache, ResoTransport::MetadataCache)
|
17
|
+
@connection = establish_connection
|
18
|
+
end
|
19
|
+
|
20
|
+
def establish_connection
|
21
|
+
Faraday.new(@endpoint, @faraday_options) do |faraday|
|
15
22
|
faraday.request :url_encoded
|
16
23
|
faraday.response :logger, @logger || ResoTransport.configuration.logger
|
17
|
-
#yield faraday if block_given?
|
18
24
|
faraday.use Authentication::Middleware, @authentication
|
19
|
-
faraday.adapter Faraday.default_adapter #unless faraday.builder.send(:adapter_set?)
|
25
|
+
faraday.adapter Faraday.default_adapter # unless faraday.builder.send(:adapter_set?)
|
20
26
|
end
|
21
27
|
end
|
22
28
|
|
23
29
|
def resources
|
24
|
-
@resources ||= metadata.entity_sets.map {|es| {es.name =>
|
30
|
+
@resources ||= metadata.entity_sets.map { |es| { es.name => resource_for(es) } }.reduce(:merge!)
|
31
|
+
end
|
32
|
+
|
33
|
+
def resource_for(entity_set)
|
34
|
+
localizations = {}
|
35
|
+
localizations = datasystem.localizations_for(entity_set.entity_type) if metadata.datasystem?
|
36
|
+
|
37
|
+
Resource.new(self, entity_set, localizations)
|
25
38
|
end
|
26
39
|
|
27
40
|
def metadata
|
28
41
|
@metadata ||= Metadata.new(self)
|
29
42
|
end
|
30
43
|
|
44
|
+
def datasystem
|
45
|
+
@datasystem ||= Datasystem.new(self)
|
46
|
+
end
|
47
|
+
|
31
48
|
def to_s
|
32
|
-
%(#<ResoTransport::Client endpoint="#{endpoint}", md_file="#{md_file}">)
|
49
|
+
%(#<ResoTransport::Client endpoint="#{endpoint}", md_file="#{md_file}", ds_file="#{ds_file}">)
|
33
50
|
end
|
34
51
|
|
35
52
|
def inspect
|
@@ -41,7 +58,7 @@ module ResoTransport
|
|
41
58
|
def ensure_valid_auth_strategy(options)
|
42
59
|
case options
|
43
60
|
when Hash
|
44
|
-
if options.
|
61
|
+
if options.key?(:endpoint)
|
45
62
|
Authentication::FetchTokenAuth.new(options)
|
46
63
|
else
|
47
64
|
Authentication::StaticTokenAuth.new(options)
|
@@ -50,6 +67,5 @@ module ResoTransport
|
|
50
67
|
raise ArgumentError, "#{options.inspect} invalid: cannot determine strategy"
|
51
68
|
end
|
52
69
|
end
|
53
|
-
|
54
70
|
end
|
55
71
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative 'base_metadata'
|
2
|
+
|
3
|
+
module ResoTransport
|
4
|
+
class Datasystem < BaseMetadata
|
5
|
+
def initialize(client)
|
6
|
+
super client
|
7
|
+
@prefix = 'ds'
|
8
|
+
@classname = self.class.name
|
9
|
+
end
|
10
|
+
|
11
|
+
def localizations_for(resource_name)
|
12
|
+
localizations = parser.resources.dig(resource_name, 'Localizations') || []
|
13
|
+
localizations.map { |l| [l['Name'], l] }.to_h
|
14
|
+
end
|
15
|
+
|
16
|
+
def response
|
17
|
+
@response ||= client.connection.get('DataSystem') do |req|
|
18
|
+
req.headers['Accept'] = 'application/json'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ResoTransport
|
2
|
+
class DatasystemParser
|
3
|
+
def parse(doc)
|
4
|
+
begin
|
5
|
+
data = doc.is_a?(File) ? doc.read : doc
|
6
|
+
@json = JSON.parse data
|
7
|
+
rescue JSON::ParserError => e
|
8
|
+
@json = {}
|
9
|
+
puts e.message
|
10
|
+
end
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
# value ->
|
15
|
+
# Resources ->
|
16
|
+
# Name ->
|
17
|
+
# ResourcePath ->
|
18
|
+
# Localizations ->
|
19
|
+
# Name ->
|
20
|
+
# ResourcePath ->
|
21
|
+
|
22
|
+
def resources
|
23
|
+
@resources ||= @json['value'].map { |v| v['Resources'] }.flatten.compact.map { |r| [r['Name'], r] }.to_h
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,11 +1,9 @@
|
|
1
1
|
module ResoTransport
|
2
2
|
EntitySet = Struct.new(:name, :schema, :entity_type) do
|
3
|
-
|
4
3
|
def self.from_stream(args)
|
5
|
-
schema, entity_type = ResoTransport.split_schema_and_class_name(args[
|
4
|
+
schema, entity_type = ResoTransport.split_schema_and_class_name(args['EntityType'])
|
6
5
|
|
7
|
-
new(args[
|
6
|
+
new(args['Name'], schema, entity_type)
|
8
7
|
end
|
9
|
-
|
10
8
|
end
|
11
9
|
end
|
@@ -1,30 +1,29 @@
|
|
1
1
|
module ResoTransport
|
2
2
|
EntityType = Struct.new(:name, :base_type, :primary_key, :schema) do
|
3
|
-
|
4
3
|
def self.from_stream(args)
|
5
|
-
new(args[
|
4
|
+
new(args['Name'], args['BaseType'])
|
6
5
|
end
|
7
6
|
|
8
7
|
def parse(record)
|
9
|
-
record.each_pair do |k,v|
|
8
|
+
record.each_pair do |k, v|
|
10
9
|
next if v.nil?
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
|
11
|
+
property = property_map[k] || navigation_property_map[k]
|
12
|
+
record[k] = property.parse(v) if property
|
14
13
|
end
|
15
14
|
end
|
16
15
|
|
17
16
|
def parse_value(record)
|
18
|
-
record.each_pair do |k,v|
|
17
|
+
record.each_pair do |k, v|
|
19
18
|
next if v.nil?
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
|
20
|
+
property = property_map[k] || navigation_property_map[k]
|
21
|
+
record[k] = property.parse(v) if property
|
23
22
|
end
|
24
23
|
end
|
25
24
|
|
26
25
|
def property_map
|
27
|
-
@property_map ||= properties.
|
26
|
+
@property_map ||= properties.each_with_object({}) { |p, hsh| hsh[p.name] = p; }
|
28
27
|
end
|
29
28
|
|
30
29
|
def properties
|
@@ -32,7 +31,7 @@ module ResoTransport
|
|
32
31
|
end
|
33
32
|
|
34
33
|
def navigation_property_map
|
35
|
-
@navigation_property_map ||= navigation_properties.
|
34
|
+
@navigation_property_map ||= navigation_properties.each_with_object({}) { |p, hsh| hsh[p.name] = p; }
|
36
35
|
end
|
37
36
|
|
38
37
|
def navigation_properties
|
@@ -42,6 +41,5 @@ module ResoTransport
|
|
42
41
|
def enumerations
|
43
42
|
@enumerations ||= []
|
44
43
|
end
|
45
|
-
|
46
44
|
end
|
47
45
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
-
|
2
|
-
Metadata = Struct.new(:client) do
|
1
|
+
require_relative 'base_metadata'
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
module ResoTransport
|
4
|
+
class Metadata < BaseMetadata
|
5
|
+
def initialize(client)
|
6
|
+
super client
|
7
|
+
@prefix = 'md'
|
8
|
+
@classname = self.class.name
|
9
|
+
end
|
8
10
|
|
9
11
|
def entity_sets
|
10
12
|
parser.entity_sets
|
@@ -14,34 +16,14 @@ module ResoTransport
|
|
14
16
|
parser.schemas
|
15
17
|
end
|
16
18
|
|
17
|
-
def
|
18
|
-
|
19
|
+
def datasystem?
|
20
|
+
parser.datasystem?
|
19
21
|
end
|
20
22
|
|
21
|
-
def
|
22
|
-
@
|
23
|
-
end
|
24
|
-
|
25
|
-
def get_data
|
26
|
-
if client.md_file
|
27
|
-
md_cache.read || md_cache.write(raw)
|
28
|
-
else
|
29
|
-
raw
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def raw
|
34
|
-
resp = client.connection.get("$metadata") do |req|
|
23
|
+
def response
|
24
|
+
@response ||= client.connection.get('$metadata') do |req|
|
35
25
|
req.headers['Accept'] = MIME_TYPES[client.vendor.fetch(:metadata_format, :xml).to_sym]
|
36
26
|
end
|
37
|
-
|
38
|
-
if resp.success?
|
39
|
-
resp.body
|
40
|
-
else
|
41
|
-
puts resp.body
|
42
|
-
raise "Error getting metadata!"
|
43
|
-
end
|
44
27
|
end
|
45
|
-
|
46
28
|
end
|
47
29
|
end
|
@@ -7,15 +7,14 @@ module ResoTransport
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def read
|
10
|
-
if File.exist?(name)
|
11
|
-
|
12
|
-
|
10
|
+
return nil if !File.exist?(name) || File.size(name).zero?
|
11
|
+
|
12
|
+
File.new(name)
|
13
13
|
end
|
14
14
|
|
15
15
|
def write(raw)
|
16
|
-
File.open(name,
|
16
|
+
File.open(name, 'w') { |f| f.write(raw.force_encoding('UTF-8')) } if raw.length.positive?
|
17
17
|
File.new(name)
|
18
18
|
end
|
19
|
-
|
20
19
|
end
|
21
20
|
end
|
@@ -14,12 +14,14 @@ module ResoTransport
|
|
14
14
|
@current_complex_type = nil
|
15
15
|
@current_enum_type = nil
|
16
16
|
@current_member = nil
|
17
|
+
|
18
|
+
@datasystem = nil
|
17
19
|
end
|
18
20
|
|
19
21
|
def parse(doc)
|
20
22
|
REXML::Document.parse_stream(doc, self)
|
21
23
|
finalize
|
22
|
-
|
24
|
+
self
|
23
25
|
end
|
24
26
|
|
25
27
|
def finalize
|
@@ -54,55 +56,62 @@ module ResoTransport
|
|
54
56
|
|
55
57
|
def tag_start(name, args)
|
56
58
|
case name
|
57
|
-
when
|
59
|
+
when 'Schema'
|
58
60
|
@schemas << ResoTransport::Schema.from_stream(args)
|
59
|
-
when
|
61
|
+
when 'EntitySet'
|
60
62
|
@entity_sets << ResoTransport::EntitySet.from_stream(args)
|
61
|
-
when
|
63
|
+
when 'EntityType'
|
62
64
|
@current_entity_type = ResoTransport::EntityType.from_stream(args)
|
63
|
-
when
|
65
|
+
when 'ComplexType'
|
64
66
|
@current_complex_type = ResoTransport::EntityType.from_stream(args)
|
65
|
-
when
|
67
|
+
when 'PropertyRef'
|
66
68
|
@current_entity_type.primary_key = args['Name']
|
67
|
-
when
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
when 'Property'
|
70
|
+
if @current_entity_type
|
71
|
+
@current_entity_type.properties << ResoTransport::Property.from_stream(args.merge(schema: @schemas.last))
|
72
|
+
end
|
73
|
+
if @current_complex_type
|
74
|
+
@current_complex_type.properties << ResoTransport::Property.from_stream(args.merge(schema: @schemas.last))
|
75
|
+
end
|
76
|
+
when 'NavigationProperty'
|
71
77
|
@current_entity_type.navigation_properties << ResoTransport::Property.from_stream(args)
|
72
|
-
when
|
78
|
+
when 'EnumType'
|
73
79
|
@current_enum_type = ResoTransport::Enum.from_stream(args.merge(schema: @schemas.last))
|
74
|
-
when
|
80
|
+
when 'Member'
|
75
81
|
@current_member = ResoTransport::Member.from_stream(args)
|
76
|
-
when
|
82
|
+
when 'Annotation'
|
77
83
|
if @current_enum_type && @current_member
|
78
84
|
@current_member.annotation = args['String']
|
79
|
-
|
80
|
-
|
81
|
-
#raise args.inspect
|
82
|
-
end
|
85
|
+
elsif @current_entity_type || @current_complex_type
|
86
|
+
# raise args.inspect
|
83
87
|
end
|
84
88
|
end
|
85
|
-
rescue => e
|
89
|
+
rescue StandardError => e
|
86
90
|
puts e.inspect
|
87
91
|
puts "Error processing Tag: #{[name, args].inspect}"
|
88
92
|
end
|
89
93
|
|
90
94
|
def tag_end(name)
|
91
95
|
case name
|
92
|
-
when
|
96
|
+
when 'EntityType'
|
93
97
|
@current_entity_type.schema = @schemas.last.namespace
|
94
98
|
@schemas.last.entity_types << @current_entity_type
|
95
|
-
when
|
99
|
+
when 'ComplexType'
|
96
100
|
@current_complex_type.schema = @schemas.last.namespace
|
97
101
|
@schemas.last.complex_types << @current_complex_type
|
98
|
-
when
|
102
|
+
when 'EnumType'
|
99
103
|
@enumerations << @current_enum_type
|
100
104
|
@current_enum_type = nil
|
101
|
-
when
|
105
|
+
when 'Member'
|
102
106
|
@current_enum_type.members << @current_member
|
103
107
|
@current_member = nil
|
104
108
|
end
|
105
109
|
end
|
106
110
|
|
111
|
+
def datasystem?
|
112
|
+
return @datasystem unless @datasystem.nil?
|
113
|
+
|
114
|
+
@datasystem = @schemas.any? { |s| s.entity_types.any? { |t| t.name == 'DataSystem' } }
|
115
|
+
end
|
107
116
|
end
|
108
117
|
end
|
data/lib/reso_transport/query.rb
CHANGED
@@ -1,23 +1,22 @@
|
|
1
1
|
module ResoTransport
|
2
2
|
Query = Struct.new(:resource) do
|
3
|
-
|
4
|
-
def all(*contexts, &block)
|
3
|
+
def all(*_contexts, &block)
|
5
4
|
new_query_context('and')
|
6
5
|
instance_eval(&block)
|
7
6
|
clear_query_context
|
8
|
-
|
7
|
+
self
|
9
8
|
end
|
10
9
|
|
11
10
|
def any(&block)
|
12
11
|
new_query_context('or')
|
13
12
|
instance_eval(&block)
|
14
13
|
clear_query_context
|
15
|
-
|
14
|
+
self
|
16
15
|
end
|
17
16
|
|
18
|
-
[
|
17
|
+
%i[eq ne gt ge lt le].each do |op|
|
19
18
|
define_method(op) do |conditions|
|
20
|
-
conditions.each_pair do |k,v|
|
19
|
+
conditions.each_pair do |k, v|
|
21
20
|
current_query_context << "#{k} #{op} #{encode_value(k, v)}"
|
22
21
|
end
|
23
22
|
return self
|
@@ -26,36 +25,36 @@ module ResoTransport
|
|
26
25
|
|
27
26
|
def limit(size)
|
28
27
|
options[:top] = size
|
29
|
-
|
28
|
+
self
|
30
29
|
end
|
31
30
|
|
32
31
|
def offset(size)
|
33
32
|
options[:skip] = size
|
34
|
-
|
33
|
+
self
|
35
34
|
end
|
36
35
|
|
37
|
-
def order(field, dir=nil)
|
38
|
-
options[:orderby] = [field, dir].join(
|
39
|
-
|
36
|
+
def order(field, dir = nil)
|
37
|
+
options[:orderby] = [field, dir].join(' ').strip
|
38
|
+
self
|
40
39
|
end
|
41
40
|
|
42
41
|
def include_count
|
43
42
|
options[:count] = true
|
44
|
-
|
43
|
+
self
|
45
44
|
end
|
46
45
|
|
47
46
|
def select(*fields)
|
48
|
-
os = options.fetch(:select,
|
49
|
-
options[:select] = (os + Array(fields)).uniq.join(
|
47
|
+
os = options.fetch(:select, '').split(',')
|
48
|
+
options[:select] = (os + Array(fields)).uniq.join(',')
|
50
49
|
|
51
|
-
|
50
|
+
self
|
52
51
|
end
|
53
52
|
|
54
53
|
def expand(*names)
|
55
|
-
ex = options.fetch(:expand,
|
56
|
-
options[:expand] = (ex + Array(names)).uniq.join(
|
54
|
+
ex = options.fetch(:expand, '').split(',')
|
55
|
+
options[:expand] = (ex + Array(names)).uniq.join(',')
|
57
56
|
|
58
|
-
|
57
|
+
self
|
59
58
|
end
|
60
59
|
|
61
60
|
def count
|
@@ -63,7 +62,7 @@ module ResoTransport
|
|
63
62
|
limit(1).include_count
|
64
63
|
resp = resource.get(compile_params)
|
65
64
|
parsed_body = JSON.parse(resp.body)
|
66
|
-
parsed_body.fetch(
|
65
|
+
parsed_body.fetch('@odata.count', 0)
|
67
66
|
end
|
68
67
|
|
69
68
|
def results
|
@@ -72,21 +71,28 @@ module ResoTransport
|
|
72
71
|
if resp[:success]
|
73
72
|
resp[:results]
|
74
73
|
else
|
75
|
-
puts resp
|
76
|
-
raise
|
74
|
+
puts resp[:meta]
|
75
|
+
raise 'Request Failed'
|
77
76
|
end
|
78
77
|
end
|
79
78
|
|
80
79
|
def execute
|
81
80
|
resp = resource.get(compile_params)
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
81
|
+
if resp.success?
|
82
|
+
parsed_body = JSON.parse(resp.body)
|
83
|
+
results = Array(parsed_body.delete('value'))
|
84
|
+
|
85
|
+
{
|
86
|
+
success: resp.success? && !parsed_body.key?('error'),
|
87
|
+
meta: parsed_body,
|
88
|
+
results: resource.parse(results)
|
89
|
+
}
|
90
|
+
else
|
91
|
+
{
|
92
|
+
success: false,
|
93
|
+
meta: resp.body
|
94
|
+
}
|
95
|
+
end
|
90
96
|
end
|
91
97
|
|
92
98
|
def new_query_context(context)
|
@@ -110,7 +116,7 @@ module ResoTransport
|
|
110
116
|
end
|
111
117
|
|
112
118
|
def sub_queries
|
113
|
-
@sub_queries ||= Hash.new {|h,k| h[k] = { context: 'and', criteria: [] } }
|
119
|
+
@sub_queries ||= Hash.new { |h, k| h[k] = { context: 'and', criteria: [] } }
|
114
120
|
end
|
115
121
|
|
116
122
|
def compile_filters
|
@@ -120,36 +126,32 @@ module ResoTransport
|
|
120
126
|
|
121
127
|
filter_chunks = []
|
122
128
|
|
123
|
-
if global && global[:criteria]&.any?
|
124
|
-
filter_chunks << global[:criteria].join(" #{global[:context]} ")
|
125
|
-
end
|
129
|
+
filter_chunks << global[:criteria].join(" #{global[:context]} ") if global && global[:criteria]&.any?
|
126
130
|
|
127
131
|
filter_chunks << filter_groups.map do |g|
|
128
132
|
"(#{g[:criteria].join(" #{g[:context]} ")})"
|
129
|
-
end.join(
|
133
|
+
end.join(' and ')
|
130
134
|
|
131
|
-
filter_chunks.reject {|c| c ==
|
135
|
+
filter_chunks.reject { |c| c == '' }.join(' and ')
|
132
136
|
end
|
133
137
|
|
134
138
|
def compile_params
|
135
139
|
params = {}
|
136
140
|
|
137
|
-
options.each_pair do |k,v|
|
141
|
+
options.each_pair do |k, v|
|
138
142
|
params["$#{k}"] = v
|
139
143
|
end
|
140
144
|
|
141
|
-
|
142
|
-
params["$filter"] = compile_filters
|
143
|
-
end
|
145
|
+
params['$filter'] = compile_filters unless sub_queries.empty?
|
144
146
|
|
145
147
|
params
|
146
148
|
end
|
147
149
|
|
148
|
-
def encode_value(key,
|
150
|
+
def encode_value(key, val)
|
149
151
|
field = resource.property(key.to_s)
|
150
152
|
raise "Couldn't find property #{key} for #{resource.name}" if field.nil?
|
151
|
-
field.encode(v)
|
152
|
-
end
|
153
153
|
|
154
|
+
field.encode(val)
|
155
|
+
end
|
154
156
|
end
|
155
157
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module ResoTransport
|
2
|
-
Resource = Struct.new(:client, :entity_set) do
|
3
|
-
|
2
|
+
Resource = Struct.new(:client, :entity_set, :localizations, :local) do
|
4
3
|
def query
|
5
4
|
Query.new(self)
|
6
5
|
end
|
@@ -10,7 +9,7 @@ module ResoTransport
|
|
10
9
|
end
|
11
10
|
|
12
11
|
def property(name)
|
13
|
-
properties.detect {|p| p.name == name }
|
12
|
+
properties.detect { |p| p.name == name }
|
14
13
|
end
|
15
14
|
|
16
15
|
def properties
|
@@ -20,13 +19,13 @@ module ResoTransport
|
|
20
19
|
def expandable
|
21
20
|
entity_type.navigation_properties
|
22
21
|
end
|
23
|
-
|
22
|
+
|
24
23
|
def entity_type
|
25
|
-
@entity_type ||= schema.entity_types.detect {|et| et.name == entity_set.entity_type }
|
24
|
+
@entity_type ||= schema.entity_types.detect { |et| et.name == entity_set.entity_type }
|
26
25
|
end
|
27
26
|
|
28
27
|
def schema
|
29
|
-
@schema ||= md.schemas.detect {|s| s.namespace == entity_set.schema }
|
28
|
+
@schema ||= md.schemas.detect { |s| s.namespace == entity_set.schema }
|
30
29
|
end
|
31
30
|
|
32
31
|
def md
|
@@ -34,15 +33,30 @@ module ResoTransport
|
|
34
33
|
end
|
35
34
|
|
36
35
|
def parse(results)
|
37
|
-
results.map {|r| entity_type.parse(r) }
|
36
|
+
results.map { |r| entity_type.parse(r) }
|
38
37
|
end
|
39
38
|
|
40
39
|
def get(params)
|
41
|
-
client.connection.get(
|
40
|
+
client.connection.get(url, params) do |req|
|
42
41
|
req.headers['Accept'] = 'application/json'
|
43
42
|
end
|
44
43
|
end
|
45
44
|
|
45
|
+
def url
|
46
|
+
return local['ResourcePath'].gsub(%r{^/}, '') if local
|
47
|
+
|
48
|
+
raise 'Localization required' if localizations.any? && local.nil?
|
49
|
+
|
50
|
+
return "#{name}/replication" if client.use_replication_endpoint
|
51
|
+
|
52
|
+
name
|
53
|
+
end
|
54
|
+
|
55
|
+
def localization(name)
|
56
|
+
self.local = localizations[name] if localizations.key?(name)
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
46
60
|
def to_s
|
47
61
|
%(#<ResoTransport::Resource entity_set="#{name}", schema="#{schema&.namespace}">)
|
48
62
|
end
|
@@ -50,6 +64,5 @@ module ResoTransport
|
|
50
64
|
def inspect
|
51
65
|
to_s
|
52
66
|
end
|
53
|
-
|
54
67
|
end
|
55
68
|
end
|
data/reso_transport.gemspec
CHANGED
@@ -1,35 +1,35 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path("../lib", __FILE__)
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
3
|
+
require 'reso_transport/version'
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
6
|
+
spec.name = 'reso_transport'
|
8
7
|
spec.version = ResoTransport::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
8
|
+
spec.authors = ['Jon Druse']
|
9
|
+
spec.email = ['jon@wrstudios.com']
|
11
10
|
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
14
|
-
spec.homepage =
|
15
|
-
spec.license =
|
11
|
+
spec.summary = 'A utility for consuming RESO Web API connections'
|
12
|
+
spec.description = 'Supports Trestle, Spark, Bridge Interactive, MLS Grid'
|
13
|
+
spec.homepage = 'http://github.com/wrstudios/reso_transport'
|
14
|
+
spec.license = 'MIT'
|
16
15
|
|
16
|
+
spec.required_ruby_version = '>= 2.6'
|
17
17
|
|
18
18
|
# Specify which files should be added to the gem when it is released.
|
19
19
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
20
|
-
spec.files
|
20
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
21
21
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
22
22
|
end
|
23
|
-
spec.bindir =
|
23
|
+
spec.bindir = 'exe'
|
24
24
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
|
-
spec.require_paths = [
|
25
|
+
spec.require_paths = ['lib']
|
26
26
|
|
27
|
-
spec.add_dependency
|
27
|
+
spec.add_dependency 'faraday', '~> 1.0.1'
|
28
28
|
|
29
|
-
spec.add_development_dependency
|
30
|
-
spec.add_development_dependency
|
31
|
-
spec.add_development_dependency
|
32
|
-
spec.add_development_dependency
|
33
|
-
spec.add_development_dependency
|
34
|
-
spec.add_development_dependency
|
29
|
+
spec.add_development_dependency 'bundler', '~> 2'
|
30
|
+
spec.add_development_dependency 'byebug', '~> 11'
|
31
|
+
spec.add_development_dependency 'minitest', '~> 5.0'
|
32
|
+
spec.add_development_dependency 'minitest-rg', '~> 5.0'
|
33
|
+
spec.add_development_dependency 'rake', '~> 13'
|
34
|
+
spec.add_development_dependency 'vcr', '~> 6.0'
|
35
35
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reso_transport
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.
|
4
|
+
version: 1.5.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Druse
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-05-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -39,19 +39,19 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '2'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: byebug
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '11'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '11'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: minitest
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,33 +81,33 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '5.0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: rake
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
89
|
+
version: '13'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
96
|
+
version: '13'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: vcr
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
103
|
+
version: '6.0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
110
|
+
version: '6.0'
|
111
111
|
description: Supports Trestle, Spark, Bridge Interactive, MLS Grid
|
112
112
|
email:
|
113
113
|
- jon@wrstudios.com
|
@@ -116,6 +116,8 @@ extensions: []
|
|
116
116
|
extra_rdoc_files: []
|
117
117
|
files:
|
118
118
|
- ".gitignore"
|
119
|
+
- ".gitpod.yml"
|
120
|
+
- ".rubocop.yml"
|
119
121
|
- ".ruby-version"
|
120
122
|
- ".travis.yml"
|
121
123
|
- CODE_OF_CONDUCT.md
|
@@ -125,6 +127,7 @@ files:
|
|
125
127
|
- README.md
|
126
128
|
- Rakefile
|
127
129
|
- bin/console
|
130
|
+
- bin/rake
|
128
131
|
- bin/setup
|
129
132
|
- lib/reso_transport.rb
|
130
133
|
- lib/reso_transport/authentication.rb
|
@@ -133,8 +136,11 @@ files:
|
|
133
136
|
- lib/reso_transport/authentication/fetch_token_auth.rb
|
134
137
|
- lib/reso_transport/authentication/middleware.rb
|
135
138
|
- lib/reso_transport/authentication/static_token_auth.rb
|
139
|
+
- lib/reso_transport/base_metadata.rb
|
136
140
|
- lib/reso_transport/client.rb
|
137
141
|
- lib/reso_transport/configuration.rb
|
142
|
+
- lib/reso_transport/datasystem.rb
|
143
|
+
- lib/reso_transport/datasystem_parser.rb
|
138
144
|
- lib/reso_transport/entity_set.rb
|
139
145
|
- lib/reso_transport/entity_type.rb
|
140
146
|
- lib/reso_transport/enum.rb
|
@@ -160,7 +166,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
160
166
|
requirements:
|
161
167
|
- - ">="
|
162
168
|
- !ruby/object:Gem::Version
|
163
|
-
version: '
|
169
|
+
version: '2.6'
|
164
170
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
171
|
requirements:
|
166
172
|
- - ">="
|