kong 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
@@ -0,0 +1,8 @@
1
+ module Kong
2
+ class Error < StandardError
3
+ def initialize(status, message)
4
+ @status = status
5
+ super(message)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Kong
2
+ class KeyAuth
3
+ include Base
4
+ include BelongsToConsumer
5
+ ATTRIBUTE_NAMES = %w(id key consumer_id).freeze
6
+ API_END_POINT = "/key-auth/".freeze
7
+ end
8
+ end
@@ -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
@@ -0,0 +1,8 @@
1
+ module Kong
2
+ class OAuthApp
3
+ include Base
4
+ include BelongsToConsumer
5
+ ATTRIBUTE_NAMES = %w(id name client_id client_secret redirect_uri consumer_id).freeze
6
+ API_END_POINT = "/oauth2/".freeze
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ module Kong
2
+ class Plugin
3
+ include Base
4
+ include BelongsToApi
5
+
6
+ ATTRIBUTE_NAMES = %w(id api_id name config enabled consumer_id).freeze
7
+ API_END_POINT = '/plugins/'.freeze
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Kong
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -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