Skip to content

Commit

Permalink
Merge pull request #58 from Scalingo/refactor/requests
Browse files Browse the repository at this point in the history
  • Loading branch information
ksol authored Feb 12, 2024
2 parents cfc8ca6 + 156c309 commit 48cb2f7
Show file tree
Hide file tree
Showing 56 changed files with 540 additions and 1,276 deletions.
4 changes: 1 addition & 3 deletions lib/scalingo/api/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@ def self.register_handler!(method_name, klass)
def inspect
str = %(<#{self.class}:0x#{object_id.to_s(16)} url:"#{@url}" methods:)

methods = self.class.instance_methods - Scalingo::API::Client.instance_methods
str << methods.to_s

str << self.class.instance_methods(false).to_s
str << ">"
str
end
Expand Down
77 changes: 74 additions & 3 deletions lib/scalingo/api/endpoint.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require "addressable/template"
require "forwardable"

module Scalingo
Expand All @@ -6,19 +7,89 @@ class Endpoint
extend Forwardable
attr_reader :client

# Add a handler for a given endpoint
%i[get post put patch delete].each do |method|
# @example
# class Example < API::Endpoint
# get :all, "some-endpoint/{id}/subthings{?query*}", optional: [:query]
# post :create, "some-endpoint", root_key: :subthing
# end
define_singleton_method(method) do |name, path, **default_attrs, &default_block|
# @example
# endpoint = Example.new
# endpoint.all(id: "1", query: {page: 1})
# endpoint.create(name: "thing")
define_method(name) do |**runtime_attrs, &runtime_block|
params = {**default_attrs, **runtime_attrs}

request(method, path, **params) do |req|
default_block&.call(req, params)
runtime_block&.call(req, params)
end
end
end

# Those methods are not meant to be used outside of a class definition
private_class_method method
end

def initialize(client)
@client = client
end

def_delegator :client, :connection
def_delegator :client, :database_connection

# Perform a request to the API
def request(method, path, body: nil, root_key: nil, connected: true, basic: nil, dry_run: false, **params, &block)
# path can be an URI template
# see https://github.com/sporkmonger/addressable?tab=readme-ov-file#uri-templates
# see https://www.rfc-editor.org/rfc/rfc6570.txt
template = Addressable::Template.new(path)

# If the template has keys, we need to expand it with the params
if template.keys.present?
# We assume every variable in the template is required
expected_keys = Set.new(template.keys.map(&:to_sym))
# ... but we can opt out by specifying :optional when performing the request or in the endpoint definition
expected_keys -= params[:optional] if params[:optional].present?

# if any required key is missing, raise an error with the missing keys,
# as if it was a regular keyword argument that was not supplied
if expected_keys.present?
received_keys = Set.new(params.keys.map(&:to_sym))

unless received_keys.superset?(expected_keys)
missings = (expected_keys - received_keys).map { |item| sprintf("%p", item) }.join(" ")
raise ArgumentError, "missing keyword: #{missings}"
end
end

# Now, we can expand the template with the supplied params
actual_path = template.expand(params).to_s
else
# Otherwise, it's not a template but a string to be used as it is
actual_path = path
end

# we nest the given body under the root_key if it's present
request_body = body
request_body = {root_key => body} if request_body && root_key

# We can use the client in either connected or unconnected mode
conn = connected ? client.authenticated_connection : client.unauthenticated_connection

# We can specify basic auth credentials if needed
conn.request :authorization, :basic, basic[:user], basic[:password] if basic.present?

# Finally, perform the request
conn.public_send(method, actual_path, request_body, &block)
end

def inspect
str = %(<#{self.class}:0x#{object_id.to_s(16)} base_url:"#{@client.url}" endpoints:)

methods = self.class.instance_methods - Scalingo::API::Endpoint.instance_methods

str << methods.to_s
str << self.class.instance_methods(false).to_s
str << ">"
str
end
Expand Down
47 changes: 4 additions & 43 deletions lib/scalingo/auth/keys.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,9 @@

module Scalingo
class Auth::Keys < API::Endpoint
def all(headers = nil, &block)
data = nil

connection.get(
"keys",
data,
headers,
&block
)
end

def show(id, headers = nil, &block)
data = nil

connection.get(
"keys/#{id}",
data,
headers,
&block
)
end

def create(payload, headers = nil, &block)
data = {key: payload}

connection.post(
"keys",
data,
headers,
&block
)
end

def destroy(id, headers = nil, &block)
data = nil

connection.delete(
"keys/#{id}",
data,
headers,
&block
)
end
get :all, "keys"
get :show, "keys/{id}"
post :create, "keys", root_key: :key
delete :destroy, "keys/{id}"
end
end
47 changes: 4 additions & 43 deletions lib/scalingo/auth/scm_integrations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,9 @@

module Scalingo
class Auth::ScmIntegrations < API::Endpoint
def all(headers = nil, &block)
data = nil

connection.get(
"scm_integrations",
data,
headers,
&block
)
end

def show(id, headers = nil, &block)
data = nil

connection.get(
"scm_integrations/#{id}",
data,
headers,
&block
)
end

def create(payload, headers = nil, &block)
data = {scm_integration: payload}

connection.post(
"scm_integrations",
data,
headers,
&block
)
end

def destroy(id, headers = nil, &block)
data = nil

connection.delete(
"scm_integrations/#{id}",
data,
headers,
&block
)
end
get :all, "scm_integrations"
get :show, "scm_integrations/{id}"
post :create, "scm_integrations", root_key: :scm_integration
delete :destroy, "scm_integrations/{id}"
end
end
67 changes: 5 additions & 62 deletions lib/scalingo/auth/tokens.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,67 +2,10 @@

module Scalingo
class Auth::Tokens < API::Endpoint
def exchange(token, headers = nil, &block)
data = nil

authorization = Faraday::Utils.basic_header_from("", token)

request_headers = {
Faraday::Request::Authorization::KEY => authorization
}

request_headers.update(headers) if headers

client.unauthenticated_connection.post(
"tokens/exchange",
data,
request_headers,
&block
)
end

def all(headers = nil, &block)
data = nil

connection.get(
"tokens",
data,
headers,
&block
)
end

def create(payload, headers = nil, &block)
data = {token: payload}

connection.post(
"tokens",
data,
headers,
&block
)
end

def renew(id, headers = nil, &block)
data = nil

connection.patch(
"tokens/#{id}/renew",
data,
headers,
&block
)
end

def destroy(id, headers = nil, &block)
data = nil

connection.delete(
"tokens/#{id}",
data,
headers,
&block
)
end
post :exchange, "tokens/exchange", connected: false
get :all, "tokens"
post :create, "tokens", root_key: :token
patch :renew, "tokens/{id}/renew"
delete :destroy, "tokens/{id}"
end
end
51 changes: 4 additions & 47 deletions lib/scalingo/auth/two_factor_auth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,9 @@

module Scalingo
class Auth::TwoFactorAuth < API::Endpoint
TOTP_PROVIDER = "totp"
DEFAULT_PROVIDER = TOTP_PROVIDER
SUPPORTED_PROVIDERS = [TOTP_PROVIDER]

def status(headers = nil, &block)
data = nil

connection.get(
"client/tfa",
data,
headers,
&block
)
end

def initiate(provider = DEFAULT_PROVIDER, headers = nil, &block)
data = {tfa: {provider: provider}}

connection.post(
"client/tfa",
data,
headers,
&block
)
end

def validate(attempt, headers = nil, &block)
data = {tfa: {attempt: attempt}}

connection.post(
"client/tfa/validate",
data,
headers,
&block
)
end

def disable(headers = nil, &block)
data = nil

connection.delete(
"client/tfa",
data,
headers,
&block
)
end
get :status, "client/tfa"
post :initiate, "client/tfa", root_key: :tfa
post :validate, "client/tfa/validate", root_key: :tfa
delete :disable, "client/tfa"
end
end
35 changes: 3 additions & 32 deletions lib/scalingo/auth/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,8 @@

module Scalingo
class Auth::User < API::Endpoint
def self(headers = nil, &block)
data = nil

connection.get(
"users/self",
data,
headers,
&block
)
end

def update(payload, headers = nil, &block)
data = {user: payload}

connection.put(
"users/account",
data,
headers,
&block
)
end

def stop_free_trial(headers = nil, &block)
data = nil

connection.post(
"users/stop_free_trial",
data,
headers,
&block
)
end
get :self, "users/self"
put :update, "users/account", root_key: :user
post :stop_free_trial, "users/stop_free_trial"
end
end
Loading

0 comments on commit 48cb2f7

Please sign in to comment.