wcc-api 0.1.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/wcc/api.rb +7 -8
- data/lib/wcc/api/active_record_shim.rb +69 -0
- data/lib/wcc/api/base_query.rb +10 -10
- data/lib/wcc/api/controller_helpers.rb +17 -0
- data/lib/wcc/api/json.rb +2 -0
- data/lib/wcc/api/json/pagination.rb +16 -13
- data/lib/wcc/api/railtie.rb +3 -1
- data/lib/wcc/api/rest_client.rb +238 -0
- data/lib/wcc/api/rest_client/api_error.rb +26 -0
- data/lib/wcc/api/rest_client/builder.rb +82 -0
- data/lib/wcc/api/rest_client/http_adapter.rb +19 -0
- data/lib/wcc/api/rest_client/response.rb +137 -0
- data/lib/wcc/api/rest_client/typhoeus_adapter.rb +63 -0
- data/lib/wcc/api/rspec.rb +4 -2
- data/lib/wcc/api/rspec/cache_header_examples.rb +43 -0
- data/lib/wcc/api/rspec/collection_matchers.rb +4 -2
- data/lib/wcc/api/rspec/pagination_examples.rb +8 -8
- data/lib/wcc/api/version.rb +3 -1
- data/lib/wcc/api/view_helpers.rb +3 -3
- data/wcc-api.gemspec +27 -15
- metadata +177 -15
- data/.gitignore +0 -14
- data/Gemfile +0 -4
- data/Rakefile +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c0b6f43132217782441bec2a54a9d046110bf83
|
4
|
+
data.tar.gz: dba5e3f10029efa9cb252474bd7763a3764cd9e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 725780f28f63a96972d70e5c7cd4596c5e4a8bb22955123db4f8d0f30c9ed30aec5cba14a1ccc65359cb0178f1c487d2e431967861f20e87f4fa8a93b2b215e1
|
7
|
+
data.tar.gz: f46f334b4a6c7c202f22898039350a54e7ccd8de6724c9e88cfe19fe3333db6d09c0fa6a4ae80a4f473a42439e513d2327b4e40dc82f6141f9577c4a153364ef
|
data/lib/wcc/api.rb
CHANGED
@@ -1,17 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'wcc'
|
2
4
|
require 'wcc/api/version'
|
3
5
|
|
4
|
-
module WCC
|
5
|
-
|
6
|
-
PROJECT_ROOT = File.expand_path(File.join(__FILE__, '..', '..', '..'))
|
7
|
-
end
|
6
|
+
module WCC::API
|
7
|
+
PROJECT_ROOT = File.expand_path(File.join(__FILE__, '..', '..', '..'))
|
8
8
|
end
|
9
9
|
|
10
|
-
if defined?(Rails)
|
11
|
-
require 'wcc/api/railtie'
|
12
|
-
end
|
10
|
+
require 'wcc/api/railtie' if defined?(Rails)
|
13
11
|
|
14
12
|
require 'wcc/api/base_query'
|
15
13
|
require 'wcc/api/json'
|
14
|
+
require 'wcc/api/controller_helpers'
|
15
|
+
require 'wcc/api/rest_client'
|
16
16
|
require 'wcc/api/view_helpers'
|
17
|
-
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module WCC::API::ActiveRecordShim
|
2
|
+
def self.included(base)
|
3
|
+
base.public_send :include, InstanceMethods
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module InstanceMethods
|
8
|
+
def attributes
|
9
|
+
raw.keys.each_with_object({}) do |key, h|
|
10
|
+
next unless respond_to?(key)
|
11
|
+
|
12
|
+
val = public_send(key)
|
13
|
+
h[key] =
|
14
|
+
if val.is_a? Array
|
15
|
+
val.map { |v| v.respond_to?(:to_h) ? v.to_h : v }
|
16
|
+
else
|
17
|
+
val.respond_to?(:to_h) ? val.to_h : val
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def find(id)
|
25
|
+
client.public_send(endpoint).find(id)
|
26
|
+
end
|
27
|
+
|
28
|
+
def find_all(**filters)
|
29
|
+
client.public_send(endpoint).list(filters)
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_by(**filters)
|
33
|
+
raise ArgumentError, "You must provide at least one filter" if filters.empty?
|
34
|
+
|
35
|
+
find_all(filters).first
|
36
|
+
end
|
37
|
+
|
38
|
+
def model_name
|
39
|
+
name
|
40
|
+
end
|
41
|
+
|
42
|
+
def table_name
|
43
|
+
endpoint
|
44
|
+
end
|
45
|
+
|
46
|
+
def unscoped
|
47
|
+
yield
|
48
|
+
end
|
49
|
+
|
50
|
+
def find_in_batches(options, &block)
|
51
|
+
options = options ? options.dup : {}
|
52
|
+
batch_size = options.delete(:batch_size) || 1000
|
53
|
+
skip_param = [:skip, :offset]
|
54
|
+
|
55
|
+
filter = {
|
56
|
+
limit: batch_size,
|
57
|
+
offset: options.delete(:start) || 0
|
58
|
+
}
|
59
|
+
|
60
|
+
find_all(filter).each_slice(batch_size, &block)
|
61
|
+
end
|
62
|
+
|
63
|
+
def where(**conditions)
|
64
|
+
# TODO: return a Query object that implements more of the ActiveRecord query interface
|
65
|
+
# https://guides.rubyonrails.org/active_record_querying.html#conditions
|
66
|
+
find_all(conditions)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/wcc/api/base_query.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module WCC::API
|
2
4
|
class BaseQuery
|
3
|
-
attr_reader :scope, :paging
|
4
|
-
attr_accessor :limit, :offset
|
5
|
+
attr_reader :scope, :paging, :limit, :offset
|
5
6
|
attr_accessor :filter
|
6
7
|
|
7
8
|
MAX_LIMIT = 50
|
@@ -15,7 +16,7 @@ module WCC::API
|
|
15
16
|
end
|
16
17
|
|
17
18
|
def permitted_keys
|
18
|
-
%i
|
19
|
+
%i[limit offset filter]
|
19
20
|
end
|
20
21
|
|
21
22
|
def default_scope
|
@@ -27,11 +28,11 @@ module WCC::API
|
|
27
28
|
@paging = paging
|
28
29
|
set_defaults
|
29
30
|
permitted_keys.each do |key|
|
30
|
-
|
31
|
+
public_send("#{key}=", params[key]) if params.key?(key)
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
34
|
-
def call(scope=self.scope)
|
35
|
+
def call(scope = self.scope)
|
35
36
|
scope = scope.dup
|
36
37
|
scope = paged(scope)
|
37
38
|
scope = ordered(scope)
|
@@ -39,7 +40,7 @@ module WCC::API
|
|
39
40
|
scope
|
40
41
|
end
|
41
42
|
|
42
|
-
def paged(scope=self.scope)
|
43
|
+
def paged(scope = self.scope)
|
43
44
|
if paging
|
44
45
|
scope
|
45
46
|
.limit(limit)
|
@@ -49,11 +50,11 @@ module WCC::API
|
|
49
50
|
end
|
50
51
|
end
|
51
52
|
|
52
|
-
def ordered(scope=self.scope)
|
53
|
+
def ordered(scope = self.scope)
|
53
54
|
scope
|
54
55
|
end
|
55
56
|
|
56
|
-
def filtered(scope=self.scope)
|
57
|
+
def filtered(scope = self.scope)
|
57
58
|
scope
|
58
59
|
end
|
59
60
|
|
@@ -65,7 +66,7 @@ module WCC::API
|
|
65
66
|
|
66
67
|
def limit=(new_limit)
|
67
68
|
new_limit = new_limit.to_i
|
68
|
-
@limit =
|
69
|
+
@limit = new_limit > MAX_LIMIT ? MAX_LIMIT : new_limit
|
69
70
|
end
|
70
71
|
|
71
72
|
def offset=(new_offset)
|
@@ -85,4 +86,3 @@ module WCC::API
|
|
85
86
|
end
|
86
87
|
end
|
87
88
|
end
|
88
|
-
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WCC::API
|
4
|
+
module ControllerHelpers
|
5
|
+
private
|
6
|
+
|
7
|
+
def set_cache_headers(scope_or_record, options = {})
|
8
|
+
options = { public: true, must_revalidate: true }.merge!(options)
|
9
|
+
|
10
|
+
if expiry = options.delete(:expiry)
|
11
|
+
expires_in expiry, options.slice(:public, :must_revalidate)
|
12
|
+
end
|
13
|
+
|
14
|
+
fresh_when scope_or_record, options.slice(:etag, :public, :last_modified)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/wcc/api/json.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module WCC::API::JSON
|
2
4
|
class Pagination
|
3
5
|
attr_reader :query, :url_for
|
@@ -19,12 +21,12 @@ module WCC::API::JSON
|
|
19
21
|
json.sort query.sort if query.respond_to?(:sort)
|
20
22
|
json.filter query.filter
|
21
23
|
json._links do
|
22
|
-
json.self url_for.(base_url_params)
|
24
|
+
json.self url_for.call(base_url_params)
|
23
25
|
if has_previous_page?
|
24
|
-
json.previous url_for.(base_url_params.merge(offset: query.offset - query.limit))
|
26
|
+
json.previous url_for.call(base_url_params.merge(offset: query.offset - query.limit))
|
25
27
|
end
|
26
28
|
if has_next_page?
|
27
|
-
json.next url_for.(base_url_params.merge(offset: query.offset + query.limit))
|
29
|
+
json.next url_for.call(base_url_params.merge(offset: query.offset + query.limit))
|
28
30
|
end
|
29
31
|
end
|
30
32
|
end
|
@@ -41,17 +43,18 @@ module WCC::API::JSON
|
|
41
43
|
end
|
42
44
|
|
43
45
|
def base_url_params
|
44
|
-
@base_url_params ||=
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
46
|
+
@base_url_params ||=
|
47
|
+
{
|
48
|
+
filter: query.filter,
|
49
|
+
only_path: false
|
50
|
+
}.tap do |params|
|
51
|
+
if query.paging
|
52
|
+
params[:limit] = query.limit
|
53
|
+
params[:offset] = query.offset
|
54
|
+
end
|
55
|
+
params[:order_by] = query.order_by if query.respond_to?(:order_by)
|
56
|
+
params[:sort] = query.sort if query.respond_to?(:sort)
|
51
57
|
end
|
52
|
-
params[:order_by] = query.order_by if query.respond_to?(:order_by)
|
53
|
-
params[:sort] = query.sort if query.respond_to?(:sort)
|
54
|
-
end
|
55
58
|
end
|
56
59
|
end
|
57
60
|
end
|
data/lib/wcc/api/railtie.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'wcc/api'
|
2
4
|
|
3
5
|
module WCC
|
4
6
|
module API
|
5
7
|
class Railtie < Rails::Railtie
|
6
|
-
initializer 'wcc.api railtie initializer', group: :all do |
|
8
|
+
initializer 'wcc.api railtie initializer', group: :all do |_app|
|
7
9
|
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
8
10
|
inflect.acronym 'API'
|
9
11
|
end
|
@@ -0,0 +1,238 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'wcc'
|
4
|
+
require_relative 'rest_client/response'
|
5
|
+
require_relative 'rest_client/builder'
|
6
|
+
require_relative 'active_record_shim'
|
7
|
+
|
8
|
+
module WCC::API
|
9
|
+
class RestClient
|
10
|
+
attr_reader :api_url
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_reader :resources, :params
|
14
|
+
|
15
|
+
def rest_client(&block)
|
16
|
+
builder = Builder.new(self)
|
17
|
+
builder.instance_exec(&block)
|
18
|
+
builder.apply
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(api_url:, headers: nil, **options)
|
23
|
+
# normalizes a URL to have a slash on the end
|
24
|
+
@api_url = api_url.gsub(/\/+$/, '') + '/'
|
25
|
+
|
26
|
+
@adapter = RestClient.load_adapter(options[:adapter])
|
27
|
+
|
28
|
+
@options = options
|
29
|
+
@query_defaults = {}
|
30
|
+
@headers = {
|
31
|
+
'Accept' => 'application/json'
|
32
|
+
}.merge(headers || {}).freeze
|
33
|
+
@response_class = options[:response_class] || DefaultResponse
|
34
|
+
end
|
35
|
+
|
36
|
+
# performs an HTTP GET request to the specified path within the configured
|
37
|
+
# space and environment. Query parameters are merged with the defaults and
|
38
|
+
# appended to the request.
|
39
|
+
def get(path, query = {})
|
40
|
+
url = URI.join(@api_url, path)
|
41
|
+
|
42
|
+
@response_class.new(self,
|
43
|
+
{ url: url, query: query },
|
44
|
+
get_http(url, query))
|
45
|
+
end
|
46
|
+
|
47
|
+
def post(path, body = {})
|
48
|
+
url = URI.join(@api_url, path)
|
49
|
+
|
50
|
+
@response_class.new(self,
|
51
|
+
{ url: url },
|
52
|
+
post_http(url,
|
53
|
+
body.to_json,
|
54
|
+
headers: { 'Content-Type': 'application/json' }))
|
55
|
+
end
|
56
|
+
|
57
|
+
def put(path, body = {})
|
58
|
+
url = URI.join(@api_url, path)
|
59
|
+
|
60
|
+
@response_class.new(self,
|
61
|
+
{ url: url },
|
62
|
+
put_http(url,
|
63
|
+
body.to_json,
|
64
|
+
headers: { 'Content-Type': 'application/json' }))
|
65
|
+
end
|
66
|
+
|
67
|
+
def delete(path)
|
68
|
+
url = URI.join(@api_url, path)
|
69
|
+
|
70
|
+
@response_class.new(self,
|
71
|
+
{ url: url },
|
72
|
+
delete_http(url))
|
73
|
+
end
|
74
|
+
|
75
|
+
ADAPTERS = {
|
76
|
+
faraday: ['faraday', '~> 0.9'],
|
77
|
+
typhoeus: ['typhoeus', '~> 1.0']
|
78
|
+
}.freeze
|
79
|
+
|
80
|
+
def self.load_adapter(adapter)
|
81
|
+
case adapter
|
82
|
+
when nil
|
83
|
+
ADAPTERS.each do |a, spec|
|
84
|
+
begin
|
85
|
+
gem(*spec)
|
86
|
+
return load_adapter(a)
|
87
|
+
rescue Gem::LoadError
|
88
|
+
next
|
89
|
+
end
|
90
|
+
end
|
91
|
+
raise ArgumentError, 'Unable to load adapter! Please install one of '\
|
92
|
+
"#{ADAPTERS.values.map(&:join).join(',')}"
|
93
|
+
when :faraday
|
94
|
+
require 'faraday'
|
95
|
+
::Faraday.new do |faraday|
|
96
|
+
faraday.response :logger, (Rails.logger if defined?(Rails)), { headers: false, bodies: false }
|
97
|
+
faraday.adapter :net_http
|
98
|
+
end
|
99
|
+
when :typhoeus
|
100
|
+
require_relative 'rest_client/typhoeus_adapter'
|
101
|
+
TyphoeusAdapter.new
|
102
|
+
else
|
103
|
+
unless adapter.respond_to?(:get)
|
104
|
+
raise ArgumentError, "Adapter #{adapter} is not invokeable! Please "\
|
105
|
+
"pass use one of #{ADAPTERS.keys} or create a Faraday-compatible adapter"
|
106
|
+
end
|
107
|
+
adapter
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def get_http(url, query, headers = {})
|
114
|
+
headers = @headers.merge(headers || {})
|
115
|
+
|
116
|
+
q = @query_defaults.dup
|
117
|
+
q = q.merge(query) if query
|
118
|
+
|
119
|
+
resp = @adapter.get(url, q, headers)
|
120
|
+
|
121
|
+
resp = get_http(resp.headers['location'], nil, headers) if [301, 302, 307].include?(resp.status) && !@options[:no_follow_redirects]
|
122
|
+
resp
|
123
|
+
end
|
124
|
+
|
125
|
+
def post_http(url, body, headers: {})
|
126
|
+
headers = @headers.merge(headers || {})
|
127
|
+
|
128
|
+
resp = @adapter.post(url, body, headers)
|
129
|
+
|
130
|
+
resp = get_http(resp.headers['location'], nil, headers) if [301, 302, 307].include?(resp.status) && !@options[:no_follow_redirects]
|
131
|
+
resp
|
132
|
+
end
|
133
|
+
|
134
|
+
def put_http(url, body, headers: {})
|
135
|
+
headers = @headers.merge(headers || {})
|
136
|
+
|
137
|
+
resp = @adapter.put(url, body, headers)
|
138
|
+
|
139
|
+
resp = get_http(resp.headers['location'], nil, headers) if [301, 302, 307].include?(resp.status) && !@options[:no_follow_redirects]
|
140
|
+
resp
|
141
|
+
end
|
142
|
+
|
143
|
+
def delete_http(url, headers: {})
|
144
|
+
headers = @headers.merge(headers || {})
|
145
|
+
|
146
|
+
resp = @adapter.delete(url, {}, headers)
|
147
|
+
|
148
|
+
resp = get_http(resp.headers['location'], nil, headers) if [301, 302, 307].include?(resp.status) && !@options[:no_follow_redirects]
|
149
|
+
resp
|
150
|
+
end
|
151
|
+
|
152
|
+
class Resource
|
153
|
+
attr_reader :client, :endpoint, :model, :options
|
154
|
+
|
155
|
+
def initialize(client, endpoint, model, options)
|
156
|
+
@client = client
|
157
|
+
@endpoint = endpoint
|
158
|
+
@model = model
|
159
|
+
@options = options
|
160
|
+
end
|
161
|
+
|
162
|
+
def find(id, query = {})
|
163
|
+
query = (options[:query] || {}).merge(query)
|
164
|
+
resp = client.get("#{endpoint}/#{id}", query)
|
165
|
+
resp.assert_ok!
|
166
|
+
body = options[:key] ? resp.body[options[:key]] : resp.body
|
167
|
+
model.new(body, resp.headers.freeze)
|
168
|
+
end
|
169
|
+
|
170
|
+
def list(**filters)
|
171
|
+
query = extract_params(filters)
|
172
|
+
query = (options[:query] || {}).merge(query)
|
173
|
+
query = query.merge!(apply_filters(filters, options[:filters]))
|
174
|
+
resp = client.get(endpoint, query)
|
175
|
+
resp.assert_ok!
|
176
|
+
resp.items.map { |s| model.new(s) }
|
177
|
+
end
|
178
|
+
|
179
|
+
def create(body)
|
180
|
+
resp = client.post(endpoint, body)
|
181
|
+
resp.assert_ok!
|
182
|
+
maybe_model_from_response(resp)
|
183
|
+
end
|
184
|
+
|
185
|
+
def update(id, body)
|
186
|
+
resp = client.put("#{endpoint}/#{id}", body)
|
187
|
+
resp.assert_ok!
|
188
|
+
maybe_model_from_response(resp)
|
189
|
+
end
|
190
|
+
|
191
|
+
def destroy(id)
|
192
|
+
resp = client.delete("#{endpoint}/#{id}")
|
193
|
+
resp.assert_ok!
|
194
|
+
maybe_model_from_response(resp)
|
195
|
+
end
|
196
|
+
|
197
|
+
protected
|
198
|
+
|
199
|
+
def maybe_model_from_response(resp)
|
200
|
+
return true if resp.status == 204
|
201
|
+
return true unless resp.raw_body.present?
|
202
|
+
|
203
|
+
body = options[:key] ? resp.body[options[:key]] : resp.body
|
204
|
+
return true unless body.present?
|
205
|
+
|
206
|
+
model.new(body, resp.headers.freeze)
|
207
|
+
end
|
208
|
+
|
209
|
+
def extract_params(filters)
|
210
|
+
filters.each_with_object({}) do |(k, _v), h|
|
211
|
+
k_s = k.to_s
|
212
|
+
h[k_s] = filters.delete(k) if client.class.params.include?(k_s)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def filter_key(filter_name)
|
217
|
+
filter_name
|
218
|
+
end
|
219
|
+
|
220
|
+
def apply_filters(filters, expected_filters)
|
221
|
+
defaults = default_filters(expected_filters) || {}
|
222
|
+
filters.each_with_object(defaults) do |(k, v), h|
|
223
|
+
k = k.to_s
|
224
|
+
raise ArgumentError, "Unknown filter '#{k}'" unless expected_filters.include?(k)
|
225
|
+
|
226
|
+
h[filter_key(k)] = v
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def default_filters(expected_filters)
|
231
|
+
options[:default_filters]&.each_with_object({}) do |(k, v), h|
|
232
|
+
k = k.to_s
|
233
|
+
h[filter_key(k)] = v if expected_filters.include?(k)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WCC::API
|
4
|
+
class RestClient
|
5
|
+
class ApiError < StandardError
|
6
|
+
attr_reader :response
|
7
|
+
|
8
|
+
def self.[](code)
|
9
|
+
case code
|
10
|
+
when 404
|
11
|
+
NotFoundError
|
12
|
+
else
|
13
|
+
ApiError
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(response)
|
18
|
+
@response = response
|
19
|
+
super(response.error_message)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class NotFoundError < ApiError
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module WCC::API
|
2
|
+
class RestClient
|
3
|
+
class Builder
|
4
|
+
def initialize(klass)
|
5
|
+
@klass = klass
|
6
|
+
end
|
7
|
+
|
8
|
+
def params(*params)
|
9
|
+
@params = params.map(&:to_s)
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_writer :resource_class
|
13
|
+
def resource_class
|
14
|
+
@resource_class ||=
|
15
|
+
@klass.const_get("Resource") || WCC::API::RestClient::Resource
|
16
|
+
end
|
17
|
+
|
18
|
+
def resource(endpoint, model:, **options, &block)
|
19
|
+
@resources ||= {}
|
20
|
+
|
21
|
+
resource_class = options[:resource_class] || self.resource_class
|
22
|
+
if block_given?
|
23
|
+
resource_class = Class.new(resource_class, &block)
|
24
|
+
end
|
25
|
+
@resources[endpoint] = options.merge({
|
26
|
+
resource_class: resource_class,
|
27
|
+
model: model,
|
28
|
+
})
|
29
|
+
end
|
30
|
+
|
31
|
+
def apply
|
32
|
+
closed_params = (@params || []).freeze
|
33
|
+
resources = @resources
|
34
|
+
klass = @klass
|
35
|
+
|
36
|
+
klass.class_exec do
|
37
|
+
define_singleton_method :params do
|
38
|
+
closed_params
|
39
|
+
end
|
40
|
+
|
41
|
+
define_singleton_method :default do
|
42
|
+
@default ||= new
|
43
|
+
end
|
44
|
+
|
45
|
+
define_singleton_method :default= do |client|
|
46
|
+
@default = client
|
47
|
+
end
|
48
|
+
|
49
|
+
resources.each do |(endpoint, options)|
|
50
|
+
attr_name = options[:attribute] || endpoint.downcase
|
51
|
+
resource_class = options[:resource_class]
|
52
|
+
|
53
|
+
define_method attr_name do
|
54
|
+
instance_variable_get("@#{attr_name}") ||
|
55
|
+
instance_variable_set("@#{attr_name}",
|
56
|
+
resource_class.new(self, endpoint, options[:model], @options.merge(options))
|
57
|
+
)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
resources.each do |(endpoint, options)|
|
63
|
+
options[:model].class_exec do
|
64
|
+
define_singleton_method :client do
|
65
|
+
klass.default
|
66
|
+
end
|
67
|
+
|
68
|
+
define_singleton_method :endpoint do
|
69
|
+
endpoint
|
70
|
+
end
|
71
|
+
|
72
|
+
define_singleton_method :key do
|
73
|
+
options[:key]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
options[:model].send(:include, WCC::API::ActiveRecordShim)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
gem 'http'
|
4
|
+
require 'http'
|
5
|
+
|
6
|
+
module WCC::API
|
7
|
+
class RestClient
|
8
|
+
class HttpAdapter
|
9
|
+
def call(url, query, headers = {}, proxy = {})
|
10
|
+
if proxy[:host]
|
11
|
+
HTTP[headers].via(proxy[:host], proxy[:port], proxy[:username], proxy[:password])
|
12
|
+
.get(url, params: query)
|
13
|
+
else
|
14
|
+
HTTP[headers].get(url, params: query)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require_relative 'api_error'
|
5
|
+
|
6
|
+
module WCC::API
|
7
|
+
class RestClient
|
8
|
+
class AbstractResponse
|
9
|
+
extend ::Forwardable
|
10
|
+
|
11
|
+
attr_reader :raw_response
|
12
|
+
attr_reader :raw_body
|
13
|
+
attr_reader :client
|
14
|
+
attr_reader :request
|
15
|
+
|
16
|
+
def_delegators :raw_response, :status, :headers
|
17
|
+
alias_method :code, :status
|
18
|
+
|
19
|
+
def body
|
20
|
+
@body ||= ::JSON.parse(raw_body)
|
21
|
+
end
|
22
|
+
alias_method :to_json, :body
|
23
|
+
|
24
|
+
def initialize(client, request, raw_response)
|
25
|
+
@client = client
|
26
|
+
@request = request
|
27
|
+
@raw_response = raw_response
|
28
|
+
@raw_body = raw_response.body.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
def skip
|
32
|
+
throw new NotImplementedError, 'Please implement "skip" parsing in response class'
|
33
|
+
end
|
34
|
+
|
35
|
+
def count
|
36
|
+
throw new NotImplementedError, 'Please implement "count" parsing in response class'
|
37
|
+
end
|
38
|
+
|
39
|
+
def collection_response?
|
40
|
+
page_items.nil? ? false : true
|
41
|
+
end
|
42
|
+
|
43
|
+
def page_items
|
44
|
+
throw new NotImplementedError, 'Please implement "page_items" parsing in response class'
|
45
|
+
end
|
46
|
+
|
47
|
+
def error_message
|
48
|
+
parsed_message =
|
49
|
+
begin
|
50
|
+
body.dig('error', 'message') || body.dig('message')
|
51
|
+
rescue ::JSON::ParserError
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
parsed_message || "#{code}: #{raw_response.body}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def next_page?
|
58
|
+
return false unless collection_response?
|
59
|
+
return false if count.nil?
|
60
|
+
|
61
|
+
page_items.length + skip < count
|
62
|
+
end
|
63
|
+
|
64
|
+
def next_page
|
65
|
+
return unless next_page?
|
66
|
+
|
67
|
+
@next_page ||= @client.get(
|
68
|
+
@request[:url],
|
69
|
+
(@request[:query] || {}).merge(next_page_query)
|
70
|
+
)
|
71
|
+
@next_page.assert_ok!
|
72
|
+
end
|
73
|
+
|
74
|
+
def assert_ok!
|
75
|
+
return self if code >= 200 && code < 300
|
76
|
+
|
77
|
+
raise ApiError[code], self
|
78
|
+
end
|
79
|
+
|
80
|
+
# This method has a bit of complexity that is better kept in one location
|
81
|
+
def each_page(&block)
|
82
|
+
raise ArgumentError, 'Not a collection response' unless collection_response?
|
83
|
+
|
84
|
+
ret =
|
85
|
+
Enumerator.new do |y|
|
86
|
+
y << self
|
87
|
+
|
88
|
+
if next_page?
|
89
|
+
next_page.each_page.each do |page|
|
90
|
+
y << page
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
if block_given?
|
96
|
+
ret.map(&block)
|
97
|
+
else
|
98
|
+
ret.lazy
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def items
|
103
|
+
return unless collection_response?
|
104
|
+
|
105
|
+
each_page.flat_map(&:page_items)
|
106
|
+
end
|
107
|
+
|
108
|
+
def first
|
109
|
+
raise ArgumentError, 'Not a collection response' unless collection_response?
|
110
|
+
|
111
|
+
page_items.first
|
112
|
+
end
|
113
|
+
|
114
|
+
def next_page_query
|
115
|
+
return unless collection_response?
|
116
|
+
|
117
|
+
{
|
118
|
+
skip: page_items.length + skip
|
119
|
+
}
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class DefaultResponse < AbstractResponse
|
124
|
+
def skip
|
125
|
+
body['skip']
|
126
|
+
end
|
127
|
+
|
128
|
+
def count
|
129
|
+
body['total']
|
130
|
+
end
|
131
|
+
|
132
|
+
def page_items
|
133
|
+
body['items']
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
gem 'typhoeus'
|
5
|
+
require 'typhoeus'
|
6
|
+
|
7
|
+
class WCC::API::RestClient::TyphoeusAdapter
|
8
|
+
def get(url, params = {}, headers = {})
|
9
|
+
req = OpenStruct.new(params: params, headers: headers)
|
10
|
+
yield req if block_given?
|
11
|
+
Response.new(
|
12
|
+
Typhoeus.get(
|
13
|
+
url,
|
14
|
+
params: req.params,
|
15
|
+
headers: req.headers
|
16
|
+
)
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def post(url, body, headers = {})
|
21
|
+
Response.new(
|
22
|
+
Typhoeus.post(
|
23
|
+
url,
|
24
|
+
body: body.is_a?(String) ? body : body.to_json,
|
25
|
+
headers: headers
|
26
|
+
)
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def put(url, body, headers = {})
|
31
|
+
Response.new(
|
32
|
+
Typhoeus.put(
|
33
|
+
url,
|
34
|
+
body: body.is_a?(String) ? body : body.to_json,
|
35
|
+
headers: headers
|
36
|
+
)
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def delete(url, query = {}, headers = {})
|
41
|
+
Response.new(
|
42
|
+
Typhoeus.delete(
|
43
|
+
url,
|
44
|
+
headers: headers
|
45
|
+
)
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
class Response < SimpleDelegator
|
50
|
+
def raw
|
51
|
+
__getobj__
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s
|
55
|
+
body&.to_s
|
56
|
+
end
|
57
|
+
|
58
|
+
def status
|
59
|
+
code
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
data/lib/wcc/api/rspec.rb
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.shared_examples_for 'cached resource defaults' do
|
4
|
+
it 'sets etag' do
|
5
|
+
expect(response.etag).to_not be_nil
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'sets public: true' do
|
9
|
+
expect(response.cache_control[:public]).to be_truthy
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
RSpec.shared_examples_for 'cached member resource' do
|
14
|
+
include_examples 'cached resource defaults'
|
15
|
+
|
16
|
+
it 'sets last modified' do
|
17
|
+
expect(response.last_modified).to_not be_nil
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'does not set max age' do
|
21
|
+
expect(response.cache_control[:max_age]).to be_nil
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'does not set must_revalidate' do
|
25
|
+
expect(response.cache_control[:must_revalidate]).to be_nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
RSpec.shared_examples_for 'cached collection resource' do |max_age|
|
30
|
+
include_examples 'cached resource defaults'
|
31
|
+
|
32
|
+
it 'sets max age' do
|
33
|
+
expect(response.cache_control[:max_age]).to eq(max_age.to_s)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'sets must_revalidate: true' do
|
37
|
+
expect(response.cache_control[:must_revalidate]).to be_truthy
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'does not set last modified' do
|
41
|
+
expect(response.last_modified).to be_nil
|
42
|
+
end
|
43
|
+
end
|
@@ -1,7 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module WCC::API::RSpec
|
2
4
|
module CollectionMatchers
|
3
5
|
def collection_match(coll1, coll2, matcher = :eq)
|
4
|
-
coll1
|
6
|
+
coll1 = coll1.to_a
|
7
|
+
coll2 = coll2.to_a
|
5
8
|
expect(coll1.size).to eq(coll2.size)
|
6
9
|
|
7
10
|
coll1.zip(coll2).each do |actual, expected|
|
@@ -10,4 +13,3 @@ module WCC::API::RSpec
|
|
10
13
|
end
|
11
14
|
end
|
12
15
|
end
|
13
|
-
|
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
RSpec.shared_examples_for :linked_pagination_object do |url_method, base_options, total|
|
2
|
-
it
|
4
|
+
it 'includes link to current page' do
|
3
5
|
url = public_send(
|
4
6
|
url_method,
|
5
7
|
base_options.merge(limit: 2, offset: 2)
|
@@ -9,20 +11,19 @@ RSpec.shared_examples_for :linked_pagination_object do |url_method, base_options
|
|
9
11
|
expect(subject['_links']['self']).to eq(url)
|
10
12
|
end
|
11
13
|
|
12
|
-
it
|
14
|
+
it 'includes link to next page when there is a next page' do
|
13
15
|
get public_send(url_method, limit: 2)
|
14
16
|
url = public_send(url_method,
|
15
|
-
base_options.merge(limit: 2, offset: 2)
|
16
|
-
)
|
17
|
+
base_options.merge(limit: 2, offset: 2))
|
17
18
|
expect(subject['_links']['next']).to eq(url)
|
18
19
|
end
|
19
20
|
|
20
|
-
it
|
21
|
+
it 'does not include link to next page when this is the last page' do
|
21
22
|
get public_send(url_method, limit: 2, offset: total - 1)
|
22
23
|
expect(subject['_links']['next']).to be_nil
|
23
24
|
end
|
24
25
|
|
25
|
-
it
|
26
|
+
it 'includes link to previous page when there is a previous page' do
|
26
27
|
get public_send(url_method, limit: 2, offset: 2)
|
27
28
|
url = public_send(
|
28
29
|
url_method,
|
@@ -31,9 +32,8 @@ RSpec.shared_examples_for :linked_pagination_object do |url_method, base_options
|
|
31
32
|
expect(subject['_links']['previous']).to eq(url)
|
32
33
|
end
|
33
34
|
|
34
|
-
it
|
35
|
+
it 'does not include link to next page when this is the last page' do
|
35
36
|
get public_send(url_method, limit: 2)
|
36
37
|
expect(subject['_links']['previous']).to be_nil
|
37
38
|
end
|
38
39
|
end
|
39
|
-
|
data/lib/wcc/api/version.rb
CHANGED
data/lib/wcc/api/view_helpers.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module WCC::API
|
2
4
|
module ViewHelpers
|
3
|
-
|
4
5
|
def api_pagination_for(query:)
|
5
6
|
WCC::API::JSON::Pagination.new(
|
6
7
|
query,
|
7
|
-
url_for: ->
|
8
|
+
url_for: ->(params) { url_for(params) }
|
8
9
|
).to_builder
|
9
10
|
end
|
10
|
-
|
11
11
|
end
|
12
12
|
end
|
data/wcc-api.gemspec
CHANGED
@@ -1,25 +1,37 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
require 'wcc/api/version'
|
5
6
|
|
6
7
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
8
|
+
spec.name = 'wcc-api'
|
8
9
|
spec.version = WCC::API::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.summary
|
12
|
-
|
13
|
-
|
14
|
-
spec.
|
10
|
+
spec.authors = ['Watermark Dev']
|
11
|
+
spec.email = ['dev@watermark.org']
|
12
|
+
spec.summary =
|
13
|
+
spec.description = 'Holds common code used in our applications that host ' \
|
14
|
+
'APIs and those that consume them.'
|
15
|
+
spec.homepage = 'https://github.com/watermarkchurch/wcc-api'
|
16
|
+
spec.license = 'MIT'
|
15
17
|
|
16
|
-
spec.files =
|
17
|
-
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.files = Dir['lib/**/*'] + %w[LICENSE.txt README.md wcc-api.gemspec]
|
18
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = [
|
20
|
+
spec.require_paths = ['lib']
|
20
21
|
|
21
|
-
spec.add_dependency
|
22
|
+
spec.add_dependency 'wcc-base'
|
22
23
|
|
23
|
-
spec.add_development_dependency
|
24
|
-
spec.add_development_dependency
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
25
|
+
spec.add_development_dependency 'dotenv', '~> 0.10.0'
|
26
|
+
spec.add_development_dependency 'faraday', '~> 0.9'
|
27
|
+
spec.add_development_dependency 'guard', '~> 2.15'
|
28
|
+
spec.add_development_dependency 'guard-rspec', '~> 4.7'
|
29
|
+
spec.add_development_dependency 'guard-rubocop', '~> 1.3'
|
30
|
+
spec.add_development_dependency 'httplog', '~> 1.0'
|
31
|
+
spec.add_development_dependency 'rake', '~> 12.3'
|
32
|
+
spec.add_development_dependency 'rspec', '~> 3.3'
|
33
|
+
spec.add_development_dependency 'rspec_junit_formatter', '~> 0.3.0'
|
34
|
+
spec.add_development_dependency 'rubocop', '0.69.0'
|
35
|
+
spec.add_development_dependency 'typhoeus', '~> 1.3'
|
36
|
+
spec.add_development_dependency 'webmock', '~> 3.0'
|
25
37
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wcc-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Watermark Dev
|
8
|
-
autorequire:
|
7
|
+
- Watermark Dev
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: wcc-base
|
@@ -38,38 +38,199 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: dotenv
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.10.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.10.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: faraday
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.9'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.9'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: guard
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.15'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.15'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: guard-rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '4.7'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '4.7'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: guard-rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.3'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.3'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: httplog
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.0'
|
41
125
|
- !ruby/object:Gem::Dependency
|
42
126
|
name: rake
|
43
127
|
requirement: !ruby/object:Gem::Requirement
|
44
128
|
requirements:
|
45
129
|
- - "~>"
|
46
130
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
131
|
+
version: '12.3'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '12.3'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rspec
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '3.3'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '3.3'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rspec_junit_formatter
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: 0.3.0
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: 0.3.0
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: rubocop
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - '='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: 0.69.0
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - '='
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: 0.69.0
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: typhoeus
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - "~>"
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '1.3'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - "~>"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '1.3'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: webmock
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - "~>"
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '3.0'
|
48
202
|
type: :development
|
49
203
|
prerelease: false
|
50
204
|
version_requirements: !ruby/object:Gem::Requirement
|
51
205
|
requirements:
|
52
206
|
- - "~>"
|
53
207
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
55
|
-
description:
|
208
|
+
version: '3.0'
|
209
|
+
description: Holds common code used in our applications that host APIs and those that
|
210
|
+
consume them.
|
56
211
|
email:
|
57
212
|
- dev@watermark.org
|
58
213
|
executables: []
|
59
214
|
extensions: []
|
60
215
|
extra_rdoc_files: []
|
61
216
|
files:
|
62
|
-
- ".gitignore"
|
63
|
-
- Gemfile
|
64
217
|
- LICENSE.txt
|
65
218
|
- README.md
|
66
|
-
- Rakefile
|
67
219
|
- lib/wcc/api.rb
|
220
|
+
- lib/wcc/api/active_record_shim.rb
|
68
221
|
- lib/wcc/api/base_query.rb
|
222
|
+
- lib/wcc/api/controller_helpers.rb
|
69
223
|
- lib/wcc/api/json.rb
|
70
224
|
- lib/wcc/api/json/pagination.rb
|
71
225
|
- lib/wcc/api/railtie.rb
|
226
|
+
- lib/wcc/api/rest_client.rb
|
227
|
+
- lib/wcc/api/rest_client/api_error.rb
|
228
|
+
- lib/wcc/api/rest_client/builder.rb
|
229
|
+
- lib/wcc/api/rest_client/http_adapter.rb
|
230
|
+
- lib/wcc/api/rest_client/response.rb
|
231
|
+
- lib/wcc/api/rest_client/typhoeus_adapter.rb
|
72
232
|
- lib/wcc/api/rspec.rb
|
233
|
+
- lib/wcc/api/rspec/cache_header_examples.rb
|
73
234
|
- lib/wcc/api/rspec/collection_matchers.rb
|
74
235
|
- lib/wcc/api/rspec/pagination_examples.rb
|
75
236
|
- lib/wcc/api/version.rb
|
@@ -79,7 +240,7 @@ homepage: https://github.com/watermarkchurch/wcc-api
|
|
79
240
|
licenses:
|
80
241
|
- MIT
|
81
242
|
metadata: {}
|
82
|
-
post_install_message:
|
243
|
+
post_install_message:
|
83
244
|
rdoc_options: []
|
84
245
|
require_paths:
|
85
246
|
- lib
|
@@ -94,9 +255,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
255
|
- !ruby/object:Gem::Version
|
95
256
|
version: '0'
|
96
257
|
requirements: []
|
97
|
-
rubyforge_project:
|
98
|
-
rubygems_version: 2.
|
99
|
-
signing_key:
|
258
|
+
rubyforge_project:
|
259
|
+
rubygems_version: 2.6.11
|
260
|
+
signing_key:
|
100
261
|
specification_version: 4
|
101
|
-
summary: Holds common code used in our applications that host APIs
|
262
|
+
summary: Holds common code used in our applications that host APIs and those that
|
263
|
+
consume them.
|
102
264
|
test_files: []
|
data/.gitignore
DELETED
data/Gemfile
DELETED
data/Rakefile
DELETED