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.
@@ -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