skull_island 0.1.0 → 0.1.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/.gitignore +1 -0
- data/.rubocop.yml +39 -0
- data/.travis.yml +9 -2
- data/Gemfile +3 -1
- data/Gemfile.lock +127 -0
- data/README.md +348 -2
- data/Rakefile +13 -3
- data/bin/console +4 -3
- data/lib/core_extensions/string/transformations.rb +30 -0
- data/lib/skull_island/api_client.rb +36 -0
- data/lib/skull_island/api_client_base.rb +86 -0
- data/lib/skull_island/api_exception.rb +7 -0
- data/lib/skull_island/exceptions/api_client_not_configured.rb +9 -0
- data/lib/skull_island/exceptions/immutable_modification.rb +9 -0
- data/lib/skull_island/exceptions/invalid_arguments.rb +9 -0
- data/lib/skull_island/exceptions/invalid_cache_size.rb +9 -0
- data/lib/skull_island/exceptions/invalid_options.rb +9 -0
- data/lib/skull_island/exceptions/invalid_property.rb +9 -0
- data/lib/skull_island/exceptions/invalid_where_query.rb +9 -0
- data/lib/skull_island/exceptions/new_instance_with_id.rb +9 -0
- data/lib/skull_island/helpers/api_client.rb +64 -0
- data/lib/skull_island/helpers/resource.rb +178 -0
- data/lib/skull_island/helpers/resource_class.rb +74 -0
- data/lib/skull_island/lru_cache.rb +175 -0
- data/lib/skull_island/resource.rb +198 -0
- data/lib/skull_island/resource_collection.rb +193 -0
- data/lib/skull_island/resources/certificate.rb +36 -0
- data/lib/skull_island/resources/consumer.rb +20 -0
- data/lib/skull_island/resources/plugin.rb +144 -0
- data/lib/skull_island/resources/route.rb +83 -0
- data/lib/skull_island/resources/service.rb +94 -0
- data/lib/skull_island/resources/upstream.rb +129 -0
- data/lib/skull_island/resources/upstream_target.rb +86 -0
- data/lib/skull_island/rspec/fake_api_client.rb +63 -0
- data/lib/skull_island/rspec.rb +3 -0
- data/lib/skull_island/simple_api_client.rb +18 -0
- data/lib/skull_island/validations/api_client.rb +45 -0
- data/lib/skull_island/validations/resource.rb +24 -0
- data/lib/skull_island/version.rb +3 -1
- data/lib/skull_island.rb +47 -1
- data/skull_island.gemspec +16 -13
- metadata +66 -7
@@ -0,0 +1,198 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SkullIsland
|
4
|
+
# A generic API resource
|
5
|
+
# TODO: Thread safety
|
6
|
+
class Resource
|
7
|
+
attr_accessor :api_client
|
8
|
+
attr_reader :errors
|
9
|
+
|
10
|
+
include Comparable
|
11
|
+
include Validations::Resource
|
12
|
+
include Helpers::Resource
|
13
|
+
extend Helpers::ResourceClass
|
14
|
+
|
15
|
+
# Can this type of resource be changed client-side?
|
16
|
+
def self.immutable(status)
|
17
|
+
raise Exceptions::InvalidArguments unless status.is_a?(TrueClass) || status.is_a?(FalseClass)
|
18
|
+
|
19
|
+
@immutable = status
|
20
|
+
end
|
21
|
+
|
22
|
+
# Define a property for a model
|
23
|
+
# @!macro [attach] property
|
24
|
+
# The $1 property
|
25
|
+
# @todo add more validations on options and names
|
26
|
+
def self.property(name, options = {})
|
27
|
+
@properties ||= {}
|
28
|
+
|
29
|
+
invalid_prop_names = %i[
|
30
|
+
> < = class def
|
31
|
+
% ! / . ? * {}
|
32
|
+
\[\]
|
33
|
+
]
|
34
|
+
|
35
|
+
raise(Exceptions::InvalidProperty) if invalid_prop_names.include?(name.to_sym)
|
36
|
+
|
37
|
+
@properties[name.to_sym] = options
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.gen_getter_method(name, opts)
|
41
|
+
determine_getter_names(name, opts).each do |method_name|
|
42
|
+
define_method(method_name) do
|
43
|
+
name_as_string = name.to_s
|
44
|
+
reload if @lazy && !@entity.key?(name_as_string)
|
45
|
+
|
46
|
+
if opts[:postprocess]
|
47
|
+
send("postprocess_#{name}".to_sym, @entity[name_as_string])
|
48
|
+
else
|
49
|
+
@entity[name_as_string]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.gen_setter_method(name, opts)
|
56
|
+
determine_setter_names(name, opts).each do |method_name|
|
57
|
+
define_method(method_name) do |value|
|
58
|
+
raise Exceptions::ImmutableModification if immutable?
|
59
|
+
|
60
|
+
if opts[:validate]
|
61
|
+
raise Exceptions::InvalidArguments unless send("validate_#{name}".to_sym, value)
|
62
|
+
end
|
63
|
+
@entity[name.to_s] = if opts[:preprocess]
|
64
|
+
send("preprocess_#{name}".to_sym, value)
|
65
|
+
else
|
66
|
+
value
|
67
|
+
end
|
68
|
+
@tainted = true
|
69
|
+
@modified_properties << name.to_sym
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.gen_property_methods
|
75
|
+
properties.each do |prop, opts|
|
76
|
+
# Getter methods
|
77
|
+
next if opts[:id_property]
|
78
|
+
|
79
|
+
gen_getter_method(prop, opts) unless opts[:write_only]
|
80
|
+
|
81
|
+
# Setter methods (don't make one for obviously read-only properties)
|
82
|
+
gen_setter_method(prop, opts) unless opts[:read_only]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# The URI (relative to the API base) for this object (or its index/list)
|
87
|
+
def self.relative_uri
|
88
|
+
route_key
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.all(options = {})
|
92
|
+
# TODO: Add validations for options
|
93
|
+
# TODO: add validation checks for the required pieces
|
94
|
+
|
95
|
+
api_client = options[:api_client] || APIClient.instance
|
96
|
+
|
97
|
+
root = 'data' # root for API JSON response data
|
98
|
+
# TODO: do something with lazy requests...
|
99
|
+
|
100
|
+
ResourceCollection.new(
|
101
|
+
api_client.get(relative_uri)[root].collect do |record|
|
102
|
+
unless options[:lazy]
|
103
|
+
api_client.invalidate_cache_for "#{relative_uri}/#{record['id']}"
|
104
|
+
api_client.cache("#{relative_uri}/#{record['id']}") do
|
105
|
+
record
|
106
|
+
end
|
107
|
+
end
|
108
|
+
new(
|
109
|
+
entity: record,
|
110
|
+
lazy: (options[:lazy] ? true : false),
|
111
|
+
tainted: false,
|
112
|
+
api_client: api_client
|
113
|
+
)
|
114
|
+
end,
|
115
|
+
type: self,
|
116
|
+
api_client: api_client
|
117
|
+
)
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.from_hash(hash)
|
121
|
+
# TODO: better options validations
|
122
|
+
raise Exceptions::InvalidOptions unless options.is_a?(Hash)
|
123
|
+
|
124
|
+
api_client = options[:api_client] || APIClient.instance
|
125
|
+
|
126
|
+
new(
|
127
|
+
entity: hash,
|
128
|
+
lazy: true,
|
129
|
+
tainted: true,
|
130
|
+
api_client: api_client
|
131
|
+
)
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.get(id, options = {})
|
135
|
+
# TODO: Add validations for options
|
136
|
+
|
137
|
+
api_client = options[:api_client] || APIClient.instance
|
138
|
+
|
139
|
+
if options[:lazy]
|
140
|
+
new(
|
141
|
+
entity: { 'id' => id },
|
142
|
+
lazy: true,
|
143
|
+
tainted: false,
|
144
|
+
api_client: api_client
|
145
|
+
)
|
146
|
+
else
|
147
|
+
entity_data = api_client.cache("#{relative_uri}/#{id}") do |client|
|
148
|
+
client.get("#{relative_uri}/#{id}")
|
149
|
+
end
|
150
|
+
|
151
|
+
new(
|
152
|
+
entity: entity_data,
|
153
|
+
lazy: false,
|
154
|
+
tainted: false,
|
155
|
+
api_client: api_client
|
156
|
+
)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.where(attribute, value, options = {})
|
161
|
+
# TODO: validate incoming options
|
162
|
+
options[:comparison] ||= value.is_a?(Regexp) ? :match : '=='
|
163
|
+
api_client = options[:api_client] || APIClient.instance
|
164
|
+
all(lazy: (options[:lazy] ? true : false), api_client: api_client).where(
|
165
|
+
attribute, value, comparison: options[:comparison]
|
166
|
+
)
|
167
|
+
end
|
168
|
+
|
169
|
+
def initialize(options = {})
|
170
|
+
# TODO: better options validations
|
171
|
+
raise Exceptions::InvalidOptions unless options.is_a?(Hash)
|
172
|
+
|
173
|
+
@entity = options[:entity] || {}
|
174
|
+
|
175
|
+
# Allows lazy-loading if we're told this is a lazy instance
|
176
|
+
# This means only the minimal attributes were fetched.
|
177
|
+
# This shouldn't be set by end-users.
|
178
|
+
@lazy = options.key?(:lazy) ? options[:lazy] : false
|
179
|
+
# This allows local, user-created instances to be differentiated from fetched
|
180
|
+
# instances from the backend API. This shouldn't be set by end-users.
|
181
|
+
@tainted = options.key?(:tainted) ? options[:tainted] : true
|
182
|
+
# This is the API Client used to get data for this resource
|
183
|
+
@api_client = options[:api_client] || APIClient.instance
|
184
|
+
@errors = {}
|
185
|
+
# A place to store which properties have been modified
|
186
|
+
@modified_properties = []
|
187
|
+
|
188
|
+
validate_mutability
|
189
|
+
validate_id
|
190
|
+
|
191
|
+
self.class.class_eval { gen_property_methods }
|
192
|
+
end
|
193
|
+
|
194
|
+
def relative_uri
|
195
|
+
"#{self.class.relative_uri}/#{id}"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SkullIsland
|
4
|
+
# The ResourceCollection class
|
5
|
+
# Should not allow or use mixed types
|
6
|
+
class ResourceCollection
|
7
|
+
include Enumerable
|
8
|
+
include Comparable
|
9
|
+
|
10
|
+
# @return [Class] a collection of this {Resource} subclass
|
11
|
+
attr_reader :type
|
12
|
+
|
13
|
+
def initialize(list, options = {})
|
14
|
+
# TODO: better options validations
|
15
|
+
raise Exceptions::InvalidOptions unless options.is_a?(Hash)
|
16
|
+
raise Exceptions::InvalidArguments if list.empty? && options[:type].nil?
|
17
|
+
|
18
|
+
@api_client = options[:api_client] || APIClient.instance
|
19
|
+
@list = list
|
20
|
+
@type = options[:type] || list.first.class
|
21
|
+
end
|
22
|
+
|
23
|
+
def each(&block)
|
24
|
+
@list.each(&block)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Does the collection contain anything?
|
28
|
+
# @return [Boolean]
|
29
|
+
def empty?
|
30
|
+
@list.empty?
|
31
|
+
end
|
32
|
+
|
33
|
+
# Provide the first (or first `number`) entries
|
34
|
+
# @param number [Fixnum] How many to provide
|
35
|
+
# @return [ResourceCollection,Resource]
|
36
|
+
def first(number = nil)
|
37
|
+
if number
|
38
|
+
self.class.new(@list.first(number), type: @type, api_client: @api_client)
|
39
|
+
else
|
40
|
+
@list.first
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Provide the last (or last `number`) entries
|
45
|
+
# @param number [Fixnum] How many to provide
|
46
|
+
# @return [ResourceCollection,Resource]
|
47
|
+
def last(number = nil)
|
48
|
+
if number
|
49
|
+
self.class.new(@list.last(number), type: @type, api_client: @api_client)
|
50
|
+
else
|
51
|
+
@list.last
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Merge two collections
|
56
|
+
# @param other [ResourceCollection]
|
57
|
+
# @return [ResourceCollection]
|
58
|
+
def merge(other)
|
59
|
+
raise Exceptions::InvalidArguments unless other.is_a?(self.class)
|
60
|
+
|
61
|
+
self + (other - self)
|
62
|
+
end
|
63
|
+
|
64
|
+
# An alias for {#type}
|
65
|
+
def model
|
66
|
+
type
|
67
|
+
end
|
68
|
+
|
69
|
+
# Hacked together #or() method in the same spirit as #where().
|
70
|
+
# This method can be chained for multiple / more specific queries.
|
71
|
+
#
|
72
|
+
# @param attribute [Symbol] the attribute to query
|
73
|
+
# @param value [Object] the value to compare against
|
74
|
+
# - allowed options are "'==', '!=', '>', '>=', '<', '<=', and 'match'"
|
75
|
+
# @raise [Exceptions::InvalidWhereQuery] if not the right kind of comparison
|
76
|
+
# @return [ResourceCollection]
|
77
|
+
def or(attribute, value, options = {})
|
78
|
+
options[:comparison] ||= value.is_a?(Regexp) ? :match : '=='
|
79
|
+
if empty?
|
80
|
+
@type.where(attribute, value, comparison: options[:comparison], api_client: @api_client)
|
81
|
+
else
|
82
|
+
merge first.class.where(
|
83
|
+
attribute, value,
|
84
|
+
comparison: options[:comparison],
|
85
|
+
api_client: @api_client
|
86
|
+
)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Pass pagination through to the Array (which passes to will_paginate)
|
91
|
+
def paginate(*args)
|
92
|
+
@list.paginate(*args)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns the number of Resource instances in the collection
|
96
|
+
# @return [Fixnum]
|
97
|
+
def size
|
98
|
+
@list.size
|
99
|
+
end
|
100
|
+
|
101
|
+
# Allow complex sorting like an Array
|
102
|
+
# @return [ResourceCollection] sorted collection
|
103
|
+
def sort(&block)
|
104
|
+
self.class.new(super(&block), type: @type, api_client: @api_client)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Horribly inefficient way to allow querying Resources by their attributes.
|
108
|
+
# This method can be chained for multiple / more specific queries.
|
109
|
+
#
|
110
|
+
# @param attribute [Symbol] the attribute to query
|
111
|
+
# @param value [Object] the value to compare against
|
112
|
+
# - allowed options are "'==', '!=', '>', '>=', '<', '<=', and 'match'"
|
113
|
+
# @raise [Exceptions::InvalidWhereQuery] if not the right kind of comparison
|
114
|
+
# @return [ResourceCollection]
|
115
|
+
def where(attribute, value, options = {})
|
116
|
+
valid_comparisons = %i[== != > >= < <= match]
|
117
|
+
options[:comparison] ||= value.is_a?(Regexp) ? :match : '=='
|
118
|
+
unless valid_comparisons.include?(options[:comparison].to_sym)
|
119
|
+
raise Exceptions::InvalidWhereQuery
|
120
|
+
end
|
121
|
+
|
122
|
+
self.class.new(
|
123
|
+
@list.collect do |item|
|
124
|
+
if item.send(attribute).nil?
|
125
|
+
nil
|
126
|
+
elsif item.send(attribute).send(options[:comparison].to_sym, value)
|
127
|
+
item
|
128
|
+
end
|
129
|
+
end.compact,
|
130
|
+
type: @type,
|
131
|
+
api_client: @api_client
|
132
|
+
)
|
133
|
+
end
|
134
|
+
|
135
|
+
alias and where
|
136
|
+
|
137
|
+
# Return the collection item at the specified index
|
138
|
+
# @return [Resource,ResourceCollection] the item at the requested index
|
139
|
+
def [](index)
|
140
|
+
if index.is_a?(Range)
|
141
|
+
self.class.new(@list[index], type: @type, api_client: @api_client)
|
142
|
+
else
|
143
|
+
@list[index]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Return a collection after subtracting from the original
|
148
|
+
# @return [ResourceCollection]
|
149
|
+
def -(other)
|
150
|
+
if other.respond_to?(:to_a)
|
151
|
+
self.class.new(@list - other.to_a, type: @type, api_client: @api_client)
|
152
|
+
elsif other.is_a?(Resource)
|
153
|
+
self.class.new(@list - Array(other), type: @type, api_client: @api_client)
|
154
|
+
else
|
155
|
+
raise Exceptions::InvalidArguments
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Return a collection after adding to the original
|
160
|
+
# Warning: this may cause duplicates or mixed type joins! For safety,
|
161
|
+
# use #merge
|
162
|
+
# @return [ResourceCollection]
|
163
|
+
def +(other)
|
164
|
+
if other.is_a?(self.class)
|
165
|
+
self.class.new(@list + other.to_a, type: @type, api_client: @api_client)
|
166
|
+
elsif other.is_a?(@type)
|
167
|
+
self.class.new(@list + [other], type: @type, api_client: @api_client)
|
168
|
+
else
|
169
|
+
raise Exceptions::InvalidArguments
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def <<(other)
|
174
|
+
raise Exceptions::InvalidArguments, 'Resource Type Mismatch' unless other.class == @type
|
175
|
+
|
176
|
+
@list << other
|
177
|
+
end
|
178
|
+
|
179
|
+
def <=>(other)
|
180
|
+
collect(&:id).sort <=> other.collect(&:id).sort
|
181
|
+
end
|
182
|
+
|
183
|
+
# Allow comparison of collection
|
184
|
+
# @return [Boolean] do the collections contain the same resource ids?
|
185
|
+
def ==(other)
|
186
|
+
if other.is_a? self.class
|
187
|
+
collect(&:id).sort == other.collect(&:id).sort
|
188
|
+
else
|
189
|
+
false
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SkullIsland
|
4
|
+
# Resource classes go here...
|
5
|
+
module Resources
|
6
|
+
# The Certificate resource class
|
7
|
+
#
|
8
|
+
# @see https://docs.konghq.com/0.14.x/admin-api/#certificate-object Certificate API definition
|
9
|
+
class Certificate < Resource
|
10
|
+
property :cert, required: true, validate: true
|
11
|
+
property :key, required: true, validate: true
|
12
|
+
property :snis, validate: true
|
13
|
+
property :created_at, read_only: true, postprocess: true
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# Used to validate {#cert} on set
|
18
|
+
def validate_cert(value)
|
19
|
+
# only String is allowed
|
20
|
+
value.is_a?(String)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Used to validate {#key} on set
|
24
|
+
def validate_key(value)
|
25
|
+
# only String is allowed
|
26
|
+
value.is_a?(String)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Used to validate {#snis} on set
|
30
|
+
def validate_snis(value)
|
31
|
+
# only Array is allowed
|
32
|
+
value.is_a?(Array)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SkullIsland
|
4
|
+
# Resource classes go here...
|
5
|
+
module Resources
|
6
|
+
# The Consumer resource class
|
7
|
+
#
|
8
|
+
# @see https://docs.konghq.com/0.14.x/admin-api/#consumer-object Consumer API definition
|
9
|
+
class Consumer < Resource
|
10
|
+
property :username
|
11
|
+
property :custom_id
|
12
|
+
property :created_at, read_only: true, postprocess: true
|
13
|
+
|
14
|
+
# Provides a collection of related {Plugin} instances
|
15
|
+
def plugins
|
16
|
+
Plugin.where(:consumer, self, api_client: api_client)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SkullIsland
|
4
|
+
# Resource classes go here...
|
5
|
+
module Resources
|
6
|
+
# The Plugin resource class
|
7
|
+
#
|
8
|
+
# @see https://docs.konghq.com/0.14.x/admin-api/#plugin-object Plugin API definition
|
9
|
+
class Plugin < Resource
|
10
|
+
property :name
|
11
|
+
property :enabled, type: :boolean
|
12
|
+
# property :run_on # 1.0.x only
|
13
|
+
property :config, validate: true
|
14
|
+
property :consumer_id, validate: true, preprocess: true, postprocess: true, as: :consumer
|
15
|
+
property :route_id, validate: true, preprocess: true, postprocess: true, as: :route
|
16
|
+
property :service_id, validate: true, preprocess: true, postprocess: true, as: :service
|
17
|
+
property :created_at, read_only: true, postprocess: true
|
18
|
+
|
19
|
+
def self.enabled_names(api_client: APIClient.instance)
|
20
|
+
api_client.get("#{relative_uri}/enabled")['enabled_plugins']
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.schema(name, api_client: APIClient.instance)
|
24
|
+
api_client.get("#{relative_uri}/schema/#{name}")
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# TODO: 1.0.x requires refactoring as `consumer_id` becomes `consumer`
|
30
|
+
def postprocess_consumer_id(value)
|
31
|
+
if value.is_a?(Hash)
|
32
|
+
Consumer.new(
|
33
|
+
entity: value,
|
34
|
+
lazy: true,
|
35
|
+
tainted: false
|
36
|
+
)
|
37
|
+
elsif value.is_a?(String)
|
38
|
+
Consumer.new(
|
39
|
+
entity: { 'id' => value },
|
40
|
+
lazy: true,
|
41
|
+
tainted: false
|
42
|
+
)
|
43
|
+
else
|
44
|
+
value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# TODO: 1.0.x requires refactoring as `consumer_id` becomes `consumer`
|
49
|
+
def preprocess_consumer_id(input)
|
50
|
+
if input.is_a?(Hash)
|
51
|
+
input['id']
|
52
|
+
elsif input.is_a?(Consumer)
|
53
|
+
input.id
|
54
|
+
else
|
55
|
+
input
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# TODO: 1.0.x requires refactoring as `route_id` becomes `route`
|
60
|
+
def postprocess_route_id(value)
|
61
|
+
if value.is_a?(Hash)
|
62
|
+
Route.new(
|
63
|
+
entity: value,
|
64
|
+
lazy: true,
|
65
|
+
tainted: false
|
66
|
+
)
|
67
|
+
elsif value.is_a?(String)
|
68
|
+
Route.new(
|
69
|
+
entity: { 'id' => value },
|
70
|
+
lazy: true,
|
71
|
+
tainted: false
|
72
|
+
)
|
73
|
+
else
|
74
|
+
value
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# TODO: 1.0.x requires refactoring as `route_id` becomes `route`
|
79
|
+
def preprocess_route_id(input)
|
80
|
+
if input.is_a?(Hash)
|
81
|
+
input['id']
|
82
|
+
elsif input.is_a?(Route)
|
83
|
+
input.id
|
84
|
+
else
|
85
|
+
input
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# TODO: 1.0.x requires refactoring as `service_id` becomes `service`
|
90
|
+
def postprocess_service_id(value)
|
91
|
+
if value.is_a?(Hash)
|
92
|
+
Service.new(
|
93
|
+
entity: value,
|
94
|
+
lazy: true,
|
95
|
+
tainted: false
|
96
|
+
)
|
97
|
+
elsif value.is_a?(String)
|
98
|
+
Service.new(
|
99
|
+
entity: { 'id' => value },
|
100
|
+
lazy: true,
|
101
|
+
tainted: false
|
102
|
+
)
|
103
|
+
else
|
104
|
+
value
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# TODO: 1.0.x requires refactoring as `service_id` becomes `service`
|
109
|
+
def preprocess_service_id(input)
|
110
|
+
if input.is_a?(Hash)
|
111
|
+
input['id']
|
112
|
+
elsif input.is_a?(Service)
|
113
|
+
input.id
|
114
|
+
else
|
115
|
+
input
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Used to validate {#config} on set
|
120
|
+
def validate_config(value)
|
121
|
+
# only Hashes are allowed
|
122
|
+
value.is_a?(Hash)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Used to validate {#consumer} on set
|
126
|
+
def validate_consumer_id(value)
|
127
|
+
# allow either a Consumer object or a Hash of a specific structure
|
128
|
+
value.is_a?(Consumer) || (value.is_a?(Hash) && value['id'].is_a?(String))
|
129
|
+
end
|
130
|
+
|
131
|
+
# Used to validate {#route} on set
|
132
|
+
def validate_route_id(value)
|
133
|
+
# allow either a Route object or a Hash of a specific structure
|
134
|
+
value.is_a?(Route) || (value.is_a?(Hash) && value['id'].is_a?(String))
|
135
|
+
end
|
136
|
+
|
137
|
+
# Used to validate {#service} on set
|
138
|
+
def validate_service_id(value)
|
139
|
+
# allow either a Service object or a Hash of a specific structure
|
140
|
+
value.is_a?(Service) || (value.is_a?(Hash) && value['id'].is_a?(String))
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SkullIsland
|
4
|
+
# Resource classes go here...
|
5
|
+
module Resources
|
6
|
+
# The Route resource class
|
7
|
+
#
|
8
|
+
# @see https://docs.konghq.com/0.14.x/admin-api/#route-object Route API definition
|
9
|
+
class Route < Resource
|
10
|
+
property :name
|
11
|
+
property :methods
|
12
|
+
property :paths
|
13
|
+
property :protocols, validate: true
|
14
|
+
property :hosts, validate: true
|
15
|
+
property :regex_priority, validate: true
|
16
|
+
property :strip_path, type: :boolean
|
17
|
+
property :preserve_host, type: :boolean
|
18
|
+
# The following are 1.0.x only
|
19
|
+
# property :snis
|
20
|
+
# property :sources
|
21
|
+
# property :destinations
|
22
|
+
property :service, validate: true, preprocess: true, postprocess: true
|
23
|
+
property :created_at, read_only: true, postprocess: true
|
24
|
+
property :updated_at, read_only: true, postprocess: true
|
25
|
+
|
26
|
+
# Provides a collection of related {Plugin} instances
|
27
|
+
def plugins
|
28
|
+
Plugin.where(:route, self, api_client: api_client)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def postprocess_service(value)
|
34
|
+
if value.is_a?(Hash)
|
35
|
+
Service.new(
|
36
|
+
entity: value,
|
37
|
+
lazy: true,
|
38
|
+
tainted: false
|
39
|
+
)
|
40
|
+
else
|
41
|
+
value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def preprocess_service(input)
|
46
|
+
if input.is_a?(Hash)
|
47
|
+
input
|
48
|
+
else
|
49
|
+
{ 'id' => input.id }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Used to validate {#protocols} on set
|
54
|
+
def validate_protocols(value)
|
55
|
+
value.is_a?(Array) && # Must be an array
|
56
|
+
(1..4).cover?(value.size) && # Must be exactly 1..4 in size
|
57
|
+
value.uniq == value && # Must not have duplicate values
|
58
|
+
(value - %w[http https tls tcp]).empty? # Must only contain appropriate protocols
|
59
|
+
end
|
60
|
+
|
61
|
+
# Used to validate {#hosts} on set
|
62
|
+
def validate_hosts(value)
|
63
|
+
# allow only valid hostnames
|
64
|
+
value.each do |host|
|
65
|
+
return false unless host.match?(host_regex) && !host.match?(/_/)
|
66
|
+
end
|
67
|
+
true
|
68
|
+
end
|
69
|
+
|
70
|
+
# Used to validate {#regex_priority} on set
|
71
|
+
def validate_regex_priority(value)
|
72
|
+
# only positive Integers are allowed
|
73
|
+
value.is_a?(Integer) && (value.positive? || value.zero?)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Used to validate {#service} on set
|
77
|
+
def validate_service(value)
|
78
|
+
# allow either a Service object or a Hash of a specific structure
|
79
|
+
value.is_a?(Service) || (value.is_a?(Hash) && value['id'].is_a?(String))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|