piloterr 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,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Piloterr
4
+ # Buffered HTTP response: status, lower-cased multi-value headers, content type and body bytes.
5
+ RawResponse = Struct.new(:status, :headers, :content_type, :body, keyword_init: true)
6
+
7
+ # Outcome of one Client#batch item: +value+ when +ok?+, else +error+.
8
+ BatchResult = Struct.new(:index, :value, :error, keyword_init: true) do
9
+ def ok? = error.nil?
10
+ end
11
+
12
+ # A successful JSON response; +data+ is the parsed body (string keys).
13
+ class JsonResponse
14
+ attr_reader :data, :raw_response
15
+
16
+ def initialize(data:, raw_response:)
17
+ @data = data
18
+ @raw_response = raw_response
19
+ end
20
+
21
+ def kind = :json
22
+ def json? = true
23
+ def binary? = false
24
+ end
25
+
26
+ # A successful non-JSON response (image, PDF, HTML…).
27
+ class BinaryResponse
28
+ attr_reader :bytes, :content_type, :raw_response
29
+
30
+ def initialize(bytes:, content_type:, raw_response:)
31
+ @bytes = bytes
32
+ @content_type = content_type
33
+ @raw_response = raw_response
34
+ end
35
+
36
+ def kind = :binary
37
+ def json? = false
38
+ def binary? = true
39
+
40
+ # Writes the body to +path+ and returns the number of bytes written.
41
+ def save(path)
42
+ File.binwrite(path, bytes)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "uri"
5
+
6
+ module Piloterr
7
+ # Request serialization: path filling, query pairs and JSON bodies.
8
+ module Serialization
9
+ QUERY_STRING_PARAMS = %w[fields is_async soft_fail].freeze
10
+
11
+ module_function
12
+
13
+ # Fills {name} path segments, then returns [path, query_pairs, body].
14
+ # nil values are omitted; false and 0 are sent.
15
+ def serialize(method, path_template, params)
16
+ raise SerializationError, "Request parameters must be a Hash." unless params.nil? || params.is_a?(Hash)
17
+
18
+ remaining = {}
19
+ (params || {}).each { |name, value| remaining[name.to_s] = value unless value.nil? }
20
+ path = path_template.gsub(/\{([^}]+)\}/) do
21
+ name = Regexp.last_match(1)
22
+ raise SerializationError.new("The \"#{name}\" path parameter is required.", name) unless remaining.key?(name)
23
+
24
+ URI.encode_www_form_component(wire_string(name, remaining.delete(name))).gsub("+", "%20")
25
+ end
26
+
27
+ if method == "POST"
28
+ # The API reads the global parameters from the query string, whatever the method.
29
+ globals = QUERY_STRING_PARAMS.filter_map { |name| [name, wire_string(name, remaining.delete(name))] if remaining.key?(name) }
30
+ return [path, globals, remaining]
31
+ end
32
+
33
+ pairs = []
34
+ remaining.each do |name, value|
35
+ if value.is_a?(Array)
36
+ value.each { |item| pairs << [name, wire_string(name, item)] unless item.nil? }
37
+ else
38
+ pairs << [name, wire_string(name, value)]
39
+ end
40
+ end
41
+ [path, pairs, nil]
42
+ end
43
+
44
+ def wire_string(name, value)
45
+ case value
46
+ when String then value
47
+ when true, false, Integer, Symbol then value.to_s
48
+ when Float
49
+ raise SerializationError.new("The \"#{name}\" parameter must be a finite number.", name) unless value.finite?
50
+
51
+ value == value.floor ? value.to_i.to_s : value.to_s
52
+ when Hash, Array then JSON.generate(value)
53
+ else
54
+ raise SerializationError.new("The \"#{name}\" parameter has an unsupported type (#{value.class}).", name)
55
+ end
56
+ end
57
+
58
+ def encode_query(pairs)
59
+ pairs.map { |name, value| "#{escape(name)}=#{escape(value)}" }.join("&")
60
+ end
61
+
62
+ def escape(value)
63
+ URI.encode_www_form_component(value).gsub("+", "%20")
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "uri"
5
+
6
+ module Piloterr
7
+ # Default transport on Net::HTTP. A custom transport is any object with
8
+ # +call(method:, url:, headers:, body:, timeout:)+ returning +[status, headers, body]+,
9
+ # where headers is a Hash of lower-cased names to arrays of values.
10
+ class NetHTTPTransport
11
+ def call(method:, url:, headers:, body:, timeout:)
12
+ uri = URI(url)
13
+ request = (method == "POST" ? Net::HTTP::Post : Net::HTTP::Get).new(uri)
14
+ headers.each { |name, value| request[name] = value }
15
+ request.body = body if body
16
+ http = Net::HTTP.new(uri.host, uri.port)
17
+ http.use_ssl = uri.scheme == "https"
18
+ if timeout
19
+ http.open_timeout = timeout
20
+ http.read_timeout = timeout
21
+ http.write_timeout = timeout
22
+ end
23
+ response = http.start { |connection| connection.request(request) }
24
+ [response.code.to_i, response.to_hash, response.body || ""]
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Piloterr
4
+ VERSION = "0.1.0"
5
+ end
data/lib/piloterr.rb ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "piloterr/version"
4
+ require_relative "piloterr/errors"
5
+ require_relative "piloterr/responses"
6
+ require_relative "piloterr/serialization"
7
+ require_relative "piloterr/transport"
8
+ require_relative "piloterr/endpoints"
9
+ require_relative "piloterr/operations"
10
+ require_relative "piloterr/client"
11
+
12
+ # Official Ruby SDK for the Piloterr API (https://www.piloterr.com).
13
+ module Piloterr
14
+ end