kong 0.1.0
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 +7 -0
- data/.gitignore +15 -0
- data/.rubocop.yml +360 -0
- data/.travis.yml +8 -0
- data/Gemfile +8 -0
- data/LICENSE +191 -0
- data/README.md +210 -0
- data/Rakefile +5 -0
- data/kong.gemspec +24 -0
- data/lib/kong.rb +13 -0
- data/lib/kong/api.rb +14 -0
- data/lib/kong/base.rb +153 -0
- data/lib/kong/basic_auth.rb +8 -0
- data/lib/kong/belongs_to_api.rb +30 -0
- data/lib/kong/belongs_to_consumer.rb +30 -0
- data/lib/kong/client.rb +226 -0
- data/lib/kong/consumer.rb +71 -0
- data/lib/kong/error.rb +8 -0
- data/lib/kong/key_auth.rb +8 -0
- data/lib/kong/oauth2_token.rb +14 -0
- data/lib/kong/oauth_app.rb +8 -0
- data/lib/kong/plugin.rb +9 -0
- data/lib/kong/version.rb +3 -0
- data/spec/kong/api_spec.rb +27 -0
- data/spec/kong/base_spec.rb +147 -0
- data/spec/kong/basic_auth_spec.rb +26 -0
- data/spec/kong/client_spec.rb +267 -0
- data/spec/kong/consumer_spec.rb +44 -0
- data/spec/kong/key_auth_spec.rb +26 -0
- data/spec/kong/oauth2_token_spec.rb +19 -0
- data/spec/kong/oauth_app_spec.rb +19 -0
- data/spec/kong/plugin_spec.rb +26 -0
- data/spec/spec_helper.rb +19 -0
- data/tasks/rspec.rake +5 -0
- metadata +129 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
module Kong
|
2
|
+
module BelongsToConsumer
|
3
|
+
attr_accessor :consumer
|
4
|
+
|
5
|
+
# Convert API end point relative to Kong Consumer resource
|
6
|
+
def use_consumer_end_point
|
7
|
+
self.api_end_point = "/consumers/#{self.consumer_id}#{self.class::API_END_POINT}" if self.consumer_id
|
8
|
+
end
|
9
|
+
|
10
|
+
# Get Consumer resource
|
11
|
+
# @return [Kong::Consumer]
|
12
|
+
def consumer
|
13
|
+
@consumer ||= Consumer.find(self.consumer_id)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Set Consumer resource
|
17
|
+
# @param [Kong::Consumer] consumer
|
18
|
+
def consumer=(consumer)
|
19
|
+
@consumer = consumer
|
20
|
+
self.consumer_id = consumer.id
|
21
|
+
end
|
22
|
+
|
23
|
+
# Set Consumer id
|
24
|
+
# @param [String] id
|
25
|
+
def consumer_id=(id)
|
26
|
+
super(id)
|
27
|
+
use_consumer_end_point
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/kong/client.rb
ADDED
@@ -0,0 +1,226 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'json'
|
3
|
+
require 'excon'
|
4
|
+
require_relative './error'
|
5
|
+
|
6
|
+
module Kong
|
7
|
+
class Client
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
attr_accessor :default_headers
|
11
|
+
attr_reader :http_client
|
12
|
+
|
13
|
+
# Initialize api client
|
14
|
+
#
|
15
|
+
def initialize
|
16
|
+
Excon.defaults[:ssl_verify_peer] = false if ignore_ssl_errors?
|
17
|
+
@api_url = api_url
|
18
|
+
@http_client = Excon.new(@api_url, omit_default_port: true)
|
19
|
+
@default_headers = { 'Accept' => 'application/json' }
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.api_url
|
23
|
+
self.instance.api_url
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.api_url=(url)
|
27
|
+
self.instance.api_url = url
|
28
|
+
end
|
29
|
+
|
30
|
+
# Kong Admin API URL
|
31
|
+
#
|
32
|
+
# @return [String]
|
33
|
+
def api_url
|
34
|
+
@api_url ||= ENV['KONG_URI'] || 'http://localhost:8001'
|
35
|
+
end
|
36
|
+
|
37
|
+
def api_url=(url)
|
38
|
+
@api_url = url
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# Get request
|
43
|
+
#
|
44
|
+
# @param [String] path
|
45
|
+
# @param [Hash,NilClass] params
|
46
|
+
# @param [Hash] headers
|
47
|
+
# @return [Hash]
|
48
|
+
def get(path, params = nil, headers = {})
|
49
|
+
response = http_client.get(
|
50
|
+
path: path,
|
51
|
+
query: encode_params(params),
|
52
|
+
headers: request_headers(headers)
|
53
|
+
)
|
54
|
+
if response.status == 200
|
55
|
+
parse_response(response)
|
56
|
+
else
|
57
|
+
handle_error_response(response)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Post request
|
62
|
+
#
|
63
|
+
# @param [String] path
|
64
|
+
# @param [Object] obj
|
65
|
+
# @param [Hash] params
|
66
|
+
# @param [Hash] headers
|
67
|
+
# @return [Hash]
|
68
|
+
def post(path, obj, params = {}, headers = {})
|
69
|
+
request_headers = request_headers(headers)
|
70
|
+
request_options = {
|
71
|
+
path: path,
|
72
|
+
headers: request_headers,
|
73
|
+
body: encode_body(obj, request_headers['Content-Type']),
|
74
|
+
query: encode_params(params)
|
75
|
+
}
|
76
|
+
response = http_client.post(request_options)
|
77
|
+
if [200, 201].include?(response.status)
|
78
|
+
parse_response(response)
|
79
|
+
else
|
80
|
+
handle_error_response(response)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Put request
|
85
|
+
#
|
86
|
+
# @param [String] path
|
87
|
+
# @param [Object] obj
|
88
|
+
# @param [Hash] params
|
89
|
+
# @param [Hash] headers
|
90
|
+
# @return [Hash]
|
91
|
+
def patch(path, obj, params = {}, headers = {})
|
92
|
+
request_headers = request_headers(headers)
|
93
|
+
request_options = {
|
94
|
+
path: path,
|
95
|
+
headers: request_headers,
|
96
|
+
body: encode_body(obj, request_headers['Content-Type']),
|
97
|
+
query: encode_params(params)
|
98
|
+
}
|
99
|
+
|
100
|
+
response = http_client.patch(request_options)
|
101
|
+
if [200, 201].include?(response.status)
|
102
|
+
parse_response(response)
|
103
|
+
else
|
104
|
+
handle_error_response(response)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
# Put request
|
110
|
+
#
|
111
|
+
# @param [String] path
|
112
|
+
# @param [Object] obj
|
113
|
+
# @param [Hash] params
|
114
|
+
# @param [Hash] headers
|
115
|
+
# @return [Hash]
|
116
|
+
def put(path, obj, params = {}, headers = {})
|
117
|
+
request_headers = request_headers(headers)
|
118
|
+
request_options = {
|
119
|
+
path: path,
|
120
|
+
headers: request_headers,
|
121
|
+
body: encode_body(obj, request_headers['Content-Type']),
|
122
|
+
query: encode_params(params)
|
123
|
+
}
|
124
|
+
|
125
|
+
response = http_client.put(request_options)
|
126
|
+
if [200, 201].include?(response.status)
|
127
|
+
parse_response(response)
|
128
|
+
else
|
129
|
+
handle_error_response(response)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Delete request
|
134
|
+
#
|
135
|
+
# @param [String] path
|
136
|
+
# @param [Hash,String] body
|
137
|
+
# @param [Hash] params
|
138
|
+
# @param [Hash] headers
|
139
|
+
# @return [Hash]
|
140
|
+
def delete(path, body = nil, params = {}, headers = {})
|
141
|
+
request_headers = request_headers(headers)
|
142
|
+
request_options = {
|
143
|
+
path: path,
|
144
|
+
headers: request_headers,
|
145
|
+
body: encode_body(body, request_headers['Content-Type']),
|
146
|
+
query: encode_params(params)
|
147
|
+
}
|
148
|
+
response = http_client.delete(request_options)
|
149
|
+
unless response.status == 204
|
150
|
+
handle_error_response(response)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
##
|
157
|
+
# Get request headers
|
158
|
+
#
|
159
|
+
# @param [Hash] headers
|
160
|
+
# @return [Hash]
|
161
|
+
def request_headers(headers = {})
|
162
|
+
@default_headers.merge(headers)
|
163
|
+
end
|
164
|
+
|
165
|
+
##
|
166
|
+
# Encode body based on content type
|
167
|
+
#
|
168
|
+
# @param [Object] body
|
169
|
+
# @param [String] content_type
|
170
|
+
def encode_body(body, content_type)
|
171
|
+
if content_type == 'application/json'
|
172
|
+
dump_json(body)
|
173
|
+
else
|
174
|
+
body
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def encode_params(params)
|
179
|
+
return nil if params.nil?
|
180
|
+
URI.encode_www_form(params)
|
181
|
+
end
|
182
|
+
|
183
|
+
##
|
184
|
+
# Parse response
|
185
|
+
#
|
186
|
+
# @param [HTTP::Message]
|
187
|
+
# @return [Object]
|
188
|
+
def parse_response(response)
|
189
|
+
if response.headers['Content-Type'].include?('application/json')
|
190
|
+
parse_json(response.body)
|
191
|
+
else
|
192
|
+
response.body
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
##
|
197
|
+
# Parse json
|
198
|
+
#
|
199
|
+
# @param [String] json
|
200
|
+
# @return [Hash,Object,NilClass]
|
201
|
+
def parse_json(json)
|
202
|
+
JSON.parse(json) rescue nil
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
# Dump json
|
207
|
+
#
|
208
|
+
# @param [Object] obj
|
209
|
+
# @return [String]
|
210
|
+
def dump_json(obj)
|
211
|
+
JSON.dump(obj)
|
212
|
+
end
|
213
|
+
|
214
|
+
def ignore_ssl_errors?
|
215
|
+
ENV['SSL_IGNORE_ERRORS'] == 'true'
|
216
|
+
end
|
217
|
+
|
218
|
+
def handle_error_response(response)
|
219
|
+
message = response.body
|
220
|
+
if response.status == 404 && message == ''
|
221
|
+
message = 'Not found'
|
222
|
+
end
|
223
|
+
raise Error.new(response.status, message)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Kong
|
2
|
+
class Consumer
|
3
|
+
include Base
|
4
|
+
|
5
|
+
ATTRIBUTE_NAMES = %w(id custom_id username created_at).freeze
|
6
|
+
API_END_POINT = '/consumers/'.freeze
|
7
|
+
|
8
|
+
def delete
|
9
|
+
self.oauth2_tokens.each do |token|
|
10
|
+
token.delete
|
11
|
+
end
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
# List plugins
|
16
|
+
#
|
17
|
+
# @return [Array<Kong::Plugin>]
|
18
|
+
def plugins
|
19
|
+
Plugin.list({ consumer_id: self.id })
|
20
|
+
end
|
21
|
+
|
22
|
+
# List OAuth applications
|
23
|
+
#
|
24
|
+
# @return [Array<Kong::OAuthApp>]
|
25
|
+
def oauth_apps
|
26
|
+
apps = []
|
27
|
+
response = client.get("#{@api_end_point}#{self.username}/oauth2") rescue nil
|
28
|
+
if response
|
29
|
+
response['data'].each do |attributes|
|
30
|
+
apps << Kong::OAuthApp.new(attributes)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
apps
|
34
|
+
end
|
35
|
+
|
36
|
+
# List KeyAuth credentials
|
37
|
+
#
|
38
|
+
# @return [Array<Kong::KeyAuth]
|
39
|
+
def basic_auths
|
40
|
+
apps = []
|
41
|
+
response = client.get("#{@api_end_point}#{self.username}/basic-auth") rescue nil
|
42
|
+
if response
|
43
|
+
response['data'].each do |attributes|
|
44
|
+
apps << Kong::BasicAuth.new(attributes)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
apps
|
48
|
+
end
|
49
|
+
|
50
|
+
# List KeyAuth credentials
|
51
|
+
#
|
52
|
+
# @return [Array<Kong::KeyAuth]
|
53
|
+
def key_auths
|
54
|
+
apps = []
|
55
|
+
response = client.get("#{@api_end_point}#{self.username}/key-auth") rescue nil
|
56
|
+
if response
|
57
|
+
response['data'].each do |attributes|
|
58
|
+
apps << Kong::KeyAuth.new(attributes)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
apps
|
62
|
+
end
|
63
|
+
|
64
|
+
# List OAuth2Tokens
|
65
|
+
#
|
66
|
+
# @return [Array<Kong::OAuth2Token>]
|
67
|
+
def oauth2_tokens
|
68
|
+
OAuth2Token.list({ authenticated_userid: self.custom_id })
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/kong/error.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative './base'
|
2
|
+
module Kong
|
3
|
+
class OAuth2Token
|
4
|
+
include Base
|
5
|
+
ATTRIBUTE_NAMES = %w(id credential_id expires_in created_at token_type access_token refresh_token scope authenticated_userid).freeze
|
6
|
+
API_END_POINT = "/oauth2_tokens/".freeze
|
7
|
+
|
8
|
+
# Get OAuthApp resource
|
9
|
+
# @return [Kong::OAuthApp]
|
10
|
+
def oauth_app
|
11
|
+
Kong::OAuthApp.find_by_id(self.credential_id)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/kong/plugin.rb
ADDED
data/lib/kong/version.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative "../spec_helper"
|
2
|
+
|
3
|
+
describe Kong::Api do
|
4
|
+
let(:valid_attribute_names) do
|
5
|
+
%w(id name request_host request_path strip_request_path preserve_host upstream_url)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe 'ATTRIBUTE_NAMES' do
|
9
|
+
it 'contains valid names' do
|
10
|
+
expect(subject.class::ATTRIBUTE_NAMES).to eq(valid_attribute_names)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'API_END_POINT' do
|
15
|
+
it 'contains valid end point' do
|
16
|
+
expect(subject.class::API_END_POINT).to eq('/apis/')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '.plugins' do
|
21
|
+
it 'requests plugins attached to Api' do
|
22
|
+
subject.id = '12345'
|
23
|
+
expect(Kong::Plugin).to receive(:list).with({ api_id: subject.id })
|
24
|
+
subject.plugins
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require_relative "../spec_helper"
|
2
|
+
|
3
|
+
describe Kong::Base do
|
4
|
+
|
5
|
+
let(:resource_class) do
|
6
|
+
Class.new do
|
7
|
+
include Kong::Base
|
8
|
+
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:subject) do
|
13
|
+
Klass.new
|
14
|
+
end
|
15
|
+
|
16
|
+
before do
|
17
|
+
stub_const 'Klass', resource_class
|
18
|
+
stub_const 'Klass::API_END_POINT', '/resources/'
|
19
|
+
stub_const 'Klass::ATTRIBUTE_NAMES', %w(id name)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '.list' do
|
23
|
+
it 'requests GET /:resource_end_point/' do
|
24
|
+
expect(Kong::Client.instance).to receive(:get).with('/resources/', {}).and_return({ data: [] })
|
25
|
+
Klass.list
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'returns array of resource instances' do
|
29
|
+
allow(Kong::Client.instance).to receive(:get).with('/resources/', {})
|
30
|
+
.and_return({ 'data' => [{ 'id' => '12345', 'name' => 'resource' }] })
|
31
|
+
result = Klass.list
|
32
|
+
expect(result[0].is_a?(Klass)).to be_truthy
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '.all' do
|
37
|
+
it 'is alias of .list' do
|
38
|
+
expect(Klass.method(:list)).to eq(Klass.method(:all))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '.find' do
|
43
|
+
it 'creates GET /:resource_end_point/:id request' do
|
44
|
+
expect(Kong::Client.instance).to receive(:get).with('/resources/12345').and_return({ data: [] })
|
45
|
+
Klass.find('12345')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '#get' do
|
50
|
+
it 'creates GET /:resource_end_point/:id request' do
|
51
|
+
expect(Kong::Client.instance).to receive(:get).with('/resources/12345').and_return({ data: [] })
|
52
|
+
subject.get('12345')
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'returns resource instance' do
|
56
|
+
allow(Kong::Client.instance).to receive(:get).with('/resources/12345')
|
57
|
+
.and_return({ 'data' => [{ 'id' => '12345', 'name' => 'resource' }] })
|
58
|
+
result = subject.get('12345')
|
59
|
+
expect(result.is_a?(Klass)).to be_truthy
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'returns nil if resource is not found' do
|
63
|
+
allow(Kong::Client.instance).to receive(:get).with('/resources/123456')
|
64
|
+
.and_return(nil)
|
65
|
+
result = subject.get('123456')
|
66
|
+
expect(result).to be_nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#new?' do
|
71
|
+
it 'returns true if resource id is missing' do
|
72
|
+
expect(subject.new?).to be_truthy
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'returns false if resource has id' do
|
76
|
+
subject.id = '12345'
|
77
|
+
expect(subject.new?).to be_falsey
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#create' do
|
82
|
+
it 'creates POST /:resource_end_point/ request with resource attributes' do
|
83
|
+
headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
|
84
|
+
attributes = { 'name' => 'test object' }
|
85
|
+
expect(Kong::Client.instance).to receive(:post).with('/resources/', nil, attributes, headers)
|
86
|
+
.and_return(attributes)
|
87
|
+
subject.name = 'test object'
|
88
|
+
subject.create
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'returns resource instance' do
|
92
|
+
headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
|
93
|
+
attributes = { 'name' => 'test object' }
|
94
|
+
allow(Kong::Client.instance).to receive(:post).with('/resources/', nil, attributes, headers)
|
95
|
+
.and_return(attributes.merge({ 'id' => '12345' }))
|
96
|
+
subject.name = 'test object'
|
97
|
+
expect(subject.create).to eq(subject)
|
98
|
+
expect(subject.id).to eq('12345')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe '#create_or_update' do
|
103
|
+
it 'creates PUT /:resource_end_point/ request with resource attributes as json payload' do
|
104
|
+
headers = { 'Content-Type' => 'application/json' }
|
105
|
+
attributes = { 'name' => 'test object' }
|
106
|
+
expect(Kong::Client.instance).to receive(:put).with('/resources/', attributes, nil, headers)
|
107
|
+
.and_return(attributes.merge({ 'id' => '12345' }))
|
108
|
+
subject.name = 'test object'
|
109
|
+
subject.create_or_update
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'returns resource instance' do
|
113
|
+
headers = { 'Content-Type' => 'application/json' }
|
114
|
+
attributes = { 'name' => 'test object' }
|
115
|
+
expect(Kong::Client.instance).to receive(:put).with('/resources/', attributes, nil, headers)
|
116
|
+
.and_return(attributes.merge({ 'id' => '12345' }))
|
117
|
+
subject.name = 'test object'
|
118
|
+
expect(subject.create_or_update).to eq(subject)
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '#update' do
|
122
|
+
it 'creates PATCH /:resource_end_point/:resource_id request with resource attributes' do
|
123
|
+
headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
|
124
|
+
subject.id = '12345'
|
125
|
+
subject.name = 'test object'
|
126
|
+
expect(Kong::Client.instance).to receive(:patch).with('/resources/12345', nil, subject.attributes, headers)
|
127
|
+
.and_return(subject.attributes)
|
128
|
+
subject.update
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'returns resource instance' do
|
132
|
+
headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
|
133
|
+
subject.id = '12345'
|
134
|
+
subject.name = 'test object'
|
135
|
+
allow(Kong::Client.instance).to receive(:patch).with('/resources/12345', nil, subject.attributes, headers)
|
136
|
+
.and_return(subject.attributes)
|
137
|
+
expect(subject.update).to eq(subject)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'will respond to attribute' do
|
142
|
+
expect(subject.respond_to?(:name)).to be_truthy
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|