apia-client 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7855cdda856e2e80063bfb19c3c41280e3979e06de43b120e90d122768cb1144
4
+ data.tar.gz: 1ff0272215aa64ca9005f3749bcd88d681ee138ea1fb46170ed363140e8b9f27
5
+ SHA512:
6
+ metadata.gz: 60c2e8821cf3e0f96e0792d5be28b1d6feb1db85fa566094cfb587f48b16ebc0df336a72d728685c61705271ea2eb4fc9f61794b6479231f493292516067ce5f
7
+ data.tar.gz: ef94dd2485dd2a0fe28a12bd15ea915332ff544d266165677801a4ec21cda980e02f4594108680d86f62dfdffe8b97fdda4fc03b824b9f97f801efc844df9a74
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.0.0
@@ -0,0 +1,6 @@
1
+ # rubocop:disable Naming/FileName
2
+ # frozen_string_literal: true
3
+
4
+ require 'apia_client'
5
+
6
+ # rubocop:enable Naming/FileName
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia_client/request_types/delete'
4
+ require 'apia_client/request_types/get'
5
+ require 'apia_client/request_types/patch'
6
+ require 'apia_client/request_types/post'
7
+ require 'apia_client/request_types/put'
8
+
9
+ require 'apia_client/api'
10
+
11
+ module ApiaClient
12
+
13
+ class << self
14
+
15
+ def load(*args, **options)
16
+ API.load(*args, **options)
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+ require 'apia_schema_parser'
6
+ require 'apia_client/errors/request_error'
7
+ require 'apia_client/errors/connection_error'
8
+ require 'apia_client/errors/schema_not_loaded_error'
9
+ require 'apia_client/errors/timeout_error'
10
+ require 'apia_client/response'
11
+ require 'apia_client/request_proxy'
12
+
13
+ module ApiaClient
14
+ class API
15
+
16
+ attr_reader :host
17
+ attr_reader :headers
18
+ attr_reader :schema
19
+
20
+ def initialize(host, **options)
21
+ @host = host
22
+ @headers = options[:headers] || {}
23
+ @options = options
24
+ end
25
+
26
+ def ssl?
27
+ @options[:ssl].nil? || @options[:ssl] == true
28
+ end
29
+
30
+ def port
31
+ @options[:port] || (ssl? ? 443 : 80)
32
+ end
33
+
34
+ def namespace
35
+ (@options[:namespace] || '').sub(/\A\/*/, '').sub(/\/*\z/, '')
36
+ end
37
+
38
+ def schema?
39
+ !!@schema
40
+ end
41
+
42
+ def load_schema
43
+ response = request(Get.new('schema'))
44
+ @schema = ApiaSchemaParser::Schema.new(response.hash)
45
+ true
46
+ end
47
+
48
+ def request(request)
49
+ status, headers, body = make_http_request(request) do |req|
50
+ request.add_arguments_to_request(req)
51
+ end
52
+ Response.new(self, request, body, headers, status)
53
+ end
54
+
55
+ def perform(*args)
56
+ request = create_request(*args)
57
+ return if request.nil?
58
+
59
+ yield request if block_given?
60
+ request.perform
61
+ end
62
+
63
+ def create_request(method, path)
64
+ unless schema?
65
+ raise SchemaNotLoadedError, 'No schema has been loaded for this API instance'
66
+ end
67
+
68
+ route = schema.api.route_set.routes.find do |r|
69
+ r.request_method == method.to_s.upcase &&
70
+ r.path == path
71
+ end
72
+
73
+ return if route.nil?
74
+
75
+ RequestProxy.new(self, route)
76
+ end
77
+
78
+ private
79
+
80
+ def create_http(**options)
81
+ http = Net::HTTP.new(host, port)
82
+ http.use_ssl = ssl?
83
+ http.read_timeout = options[:http_read_timeout] || @options[:http_read_timeout] || 60
84
+ http.open_timeout = options[:http_open_timeout] || @options[:http_open_timeout] || 10
85
+ http
86
+ end
87
+
88
+ def make_http_request(request)
89
+ http_request = request.class.method.new("/#{namespace}/#{request.path_for_net_http}")
90
+ @headers.each do |key, value|
91
+ http_request[key] = value
92
+ end
93
+ yield http_request if block_given?
94
+ response = make_request_with_error_handling(http_request, request)
95
+ handle_response(response)
96
+ end
97
+
98
+ def make_request_with_error_handling(http_request, request)
99
+ http = create_http(
100
+ http_read_timeout: request.http_read_timeout,
101
+ http_open_timeout: request.http_open_timeout
102
+ )
103
+ http.request(http_request)
104
+ rescue Timeout::Error => e
105
+ raise ApiaClient::TimeoutError, e.message
106
+ rescue StandardError => e
107
+ raise ConnectionError, e.message
108
+ end
109
+
110
+ def handle_response(response)
111
+ if response['content-type'] =~ /application\/json/
112
+ body = JSON.parse(response.body)
113
+ else
114
+ body = response.body
115
+ end
116
+
117
+ status_code = response.code.to_i
118
+ if status_code >= 200 && status_code < 300
119
+ return [status_code, response.to_hash, body]
120
+ end
121
+
122
+ raise RequestError.new(self, status_code, body)
123
+ end
124
+
125
+ class << self
126
+
127
+ def load(*args, **options)
128
+ api = new(*args, **options)
129
+ api.load_schema
130
+ api
131
+ end
132
+
133
+ end
134
+
135
+ end
136
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiaClient
4
+ class ConnectionError < StandardError
5
+ end
6
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiaClient
4
+ class RequestError < StandardError
5
+
6
+ attr_reader :status
7
+ attr_reader :body
8
+
9
+ # rubocop:disable Lint/MissingSuper
10
+ def initialize(client, status, body)
11
+ @client = client
12
+ @status = status
13
+ @body = body
14
+
15
+ @error = @body['error']
16
+ end
17
+ # rubocop:enable Lint/MissingSuper
18
+
19
+ def to_s
20
+ string = ["[#{@status}]"]
21
+ if code && description
22
+ string << "#{code}: #{description}"
23
+ elsif code
24
+ string << code
25
+ else
26
+ string << @body
27
+ end
28
+ string.join(' ')
29
+ end
30
+
31
+ def code
32
+ return if @error.nil?
33
+
34
+ @error['code']
35
+ end
36
+
37
+ def description
38
+ return if @error.nil?
39
+
40
+ @error['description']
41
+ end
42
+
43
+ def detail
44
+ return if @error.nil?
45
+
46
+ @error['detail'] || {}
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiaClient
4
+ class SchemaNotLoadedError < StandardError
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia_client/errors/connection_error'
4
+
5
+ module ApiaClient
6
+ class TimeoutError < ConnectionError
7
+ end
8
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+
5
+ module ApiaClient
6
+ class Request
7
+
8
+ class << self
9
+
10
+ attr_accessor :method
11
+
12
+ end
13
+
14
+ attr_reader :path
15
+ attr_reader :arguments
16
+ attr_accessor :http_read_timeout
17
+ attr_accessor :http_open_timeout
18
+
19
+ def initialize(path)
20
+ @path = path
21
+ @arguments = {}
22
+ end
23
+
24
+ def path_for_net_http
25
+ path
26
+ end
27
+
28
+ def add_arguments_to_request(request)
29
+ request['Content-Type'] = 'application/json'
30
+ request.body = arguments.to_json
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiaClient
4
+ class RequestProxy
5
+
6
+ METHOD_MAP = {
7
+ get: ApiaClient::Get,
8
+ post: ApiaClient::Post,
9
+ patch: ApiaClient::Patch,
10
+ put: ApiaClient::Put,
11
+ delete: ApiaClient::Delete
12
+ }.freeze
13
+
14
+ attr_reader :route
15
+ attr_reader :request
16
+
17
+ def initialize(client, route)
18
+ @client = client
19
+ @route = route
20
+
21
+ request_class = METHOD_MAP[@route.request_method.downcase.to_sym]
22
+ @request = request_class.new(@route.path)
23
+ end
24
+
25
+ def perform
26
+ @client.request(@request)
27
+ end
28
+
29
+ def arguments
30
+ @request.arguments
31
+ end
32
+
33
+ def endpoint
34
+ @route.endpoint
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia_client/request'
4
+
5
+ module ApiaClient
6
+ class Delete < Request
7
+
8
+ self.method = Net::HTTP::Delete
9
+
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia_client/request'
4
+ require 'json'
5
+ require 'uri'
6
+
7
+ module ApiaClient
8
+ class Get < Request
9
+
10
+ self.method = Net::HTTP::Get
11
+
12
+ def path_for_net_http
13
+ querystring = URI.encode_www_form(_arguments: @arguments.to_json)
14
+ "#{path}?#{querystring}"
15
+ end
16
+
17
+ def add_arguments_to_request(request)
18
+ # Don't need to do anything for GET requests...
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia_client/request'
4
+
5
+ module ApiaClient
6
+ class Patch < Request
7
+
8
+ self.method = Net::HTTP::Patch
9
+
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia_client/request'
4
+
5
+ module ApiaClient
6
+ class Post < Request
7
+
8
+ self.method = Net::HTTP::Post
9
+
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia_client/request'
4
+
5
+ module ApiaClient
6
+ class Put < Request
7
+
8
+ self.method = Net::HTTP::Put
9
+
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiaClient
4
+ class Response
5
+
6
+ def initialize(client, request, hash, headers, status)
7
+ @client = client
8
+ @request = request
9
+ @hash = hash
10
+ @headers = headers
11
+ @status = status
12
+ end
13
+
14
+ attr_reader :request
15
+ attr_reader :hash
16
+ attr_reader :headers
17
+ attr_reader :status
18
+
19
+ end
20
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiaClient
4
+
5
+ VERSION_FILE_ROOT = File.expand_path('../../VERSION', __dir__)
6
+ if File.file?(VERSION_FILE_ROOT)
7
+ VERSION = File.read(VERSION_FILE_ROOT).strip.sub(/\Av/, '')
8
+ else
9
+ VERSION = '0.0.0.dev'
10
+ end
11
+
12
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: apia-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Adam Cooke
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-08-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: apia-schema-parser
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: A client library for talking to any Apia API.
42
+ email:
43
+ - adam@k.io
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - VERSION
49
+ - lib/apia-client.rb
50
+ - lib/apia_client.rb
51
+ - lib/apia_client/api.rb
52
+ - lib/apia_client/errors/connection_error.rb
53
+ - lib/apia_client/errors/request_error.rb
54
+ - lib/apia_client/errors/schema_not_loaded_error.rb
55
+ - lib/apia_client/errors/timeout_error.rb
56
+ - lib/apia_client/request.rb
57
+ - lib/apia_client/request_proxy.rb
58
+ - lib/apia_client/request_types/delete.rb
59
+ - lib/apia_client/request_types/get.rb
60
+ - lib/apia_client/request_types/patch.rb
61
+ - lib/apia_client/request_types/post.rb
62
+ - lib/apia_client/request_types/put.rb
63
+ - lib/apia_client/response.rb
64
+ - lib/apia_client/version.rb
65
+ homepage: https://github.com/krystal/apia-ruby-client
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '2.5'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubygems_version: 3.1.6
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: A client library for talking to any Apia API.
88
+ test_files: []