scalingo-ruby-api 1.1.1 → 2.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b675815e2e7ddd2cec118d6a5fcd090feec9a4ffeb396e681aa4e40c7eb01da
4
- data.tar.gz: f7d3d4a8a902e1cec4f94d83b68a023f36bc5d2138950e16a6215f85bf28d498
3
+ metadata.gz: 9cac65a8cb02d5c36c8a21f1079f5da30a22499183f4df5f5c218c0f31df8362
4
+ data.tar.gz: f5d702234ea4b5c2f13ac430f945dcc1e66986258a56526a8ae0167a3fa9e55b
5
5
  SHA512:
6
- metadata.gz: 4fa502142904cc808b139bf7d2d49cdb84216aede098f9f76f1f92ff39dc8a1c69f3d60c9ba6d550c18b61fcd715e8226be30127f099c338a35422d8604ecfce
7
- data.tar.gz: 74c516c26bd5a82a251e2dc733b0d21d3868d19f0c1d1632e327fad63291662ed392a81636df4150254ffd8af409392aaf64c43189f7a5f4ff7113f8b3e07389
6
+ metadata.gz: d47763e0fb6b43781cdeaccda286a666efdc5240671889b2a1a7c152c2a32b5512685eacf945b2e0f3787ec64dd814b785c16d42c49ebc546d830b8dda69e26a
7
+ data.tar.gz: 56a4f6d9996a71d2d1b2ca43fd1bd9948d0e90559ec9415d2e0d4b426f86087926fd3f9bda21c90fef55be014ce31ff9555504512779ed9cec918d37c10bd7d6
@@ -0,0 +1,21 @@
1
+ ## 2.0.0 - 2019/12/05
2
+
3
+ * Compatibility with new API tokens
4
+ * Compatibility with multi-region infrastructure
5
+ * Add missing resources: `regions`, `autoscalers`, `notifiers`, `alerts`
6
+
7
+ ## 1.1.1 - 2019/01/08
8
+
9
+ * Update metadata on rubygems
10
+
11
+ ## 1.1.0 - 2019/01/08
12
+
13
+ * Add /apps/:id/containers and /apps/:id/stats endpoints
14
+
15
+ ## 1.0.0 - 2017/24/01
16
+
17
+ * First tag 1.0.0, API won't be broken
18
+
19
+ ## 1.0.0.alpha1
20
+
21
+ * Initial alpha release
@@ -0,0 +1,90 @@
1
+ # Scalingo-ruby-api
2
+
3
+ A ruby wrapper for the Scalingo API
4
+
5
+ ## Installation
6
+
7
+ ```ruby
8
+ gem 'scalingo-ruby-api', '~> 2'
9
+ ```
10
+
11
+ And then execute:
12
+
13
+ ```
14
+ bundle
15
+ ```
16
+
17
+ Or install it yourself as:
18
+
19
+ ```
20
+ gem install scalingo-ruby-api
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ### Global configuration
26
+
27
+ ```ruby
28
+ require 'scalingo'
29
+
30
+ Scalingo.token = 'tk-your-token'
31
+ Scalingo.region = 'osc-fr1'
32
+
33
+ # Or
34
+
35
+ Scalingo.configure do |config|
36
+ config.token = 'tk-your-token'
37
+ config.region = 'osc-fr1'
38
+ end
39
+
40
+ client = Scalingo::Client.new
41
+ ```
42
+
43
+ ### Client configuration
44
+
45
+ ```ruby
46
+ client = Scalingo::Client.new(region: 'osc-fr1', token: 'tk-your-token')
47
+
48
+ # Or both can be combined
49
+
50
+ Scalingo.token = 'tk-your-token'
51
+
52
+ client_region1 = Scalingo::Client.new(region: 'osc-fr1')
53
+ client_region2 = Scalingo::Client.new(region: 'agora-fr1')
54
+ ```
55
+
56
+ ### Examples
57
+
58
+ ```ruby
59
+ # Get all apps
60
+ apps = client.apps.all
61
+
62
+ # Get logs of one app
63
+ app = client.apps.find('my-app')
64
+ puts app.logs.dump
65
+ ```
66
+
67
+ ### Notes
68
+
69
+ Client contains a cache with the regions endpoints and the current valid bearer
70
+ token used for authentication.
71
+
72
+ If a lot of calls are done and to avoid making useless requests and slowing
73
+ down your queries, it is encouraged to re-use your client.
74
+
75
+ ## Hacking
76
+
77
+ Using another authentication endpoint for hacking is possible through
78
+
79
+ ```
80
+ client = Scalingo::Client.new(auth_endpoint: 'http://172.17.0.1:1234/v1', region: 'local')
81
+ apps = client.apps.all
82
+ ```
83
+
84
+ ## Special Thanks
85
+
86
+ To aki017 who made [slack-ruby-gem](http://github.com/aki017/slack-ruby-gem).
87
+
88
+ It was used as an inspirational source for this project.
89
+
90
+ Thanks [Aethelflaed](https://github.com/Aethelflaed) for the original implementation of this gem
@@ -14,11 +14,6 @@ module Scalingo
14
14
  Scalingo::Client.new(options)
15
15
  end
16
16
 
17
- def self.method_missing(method, *args, &block)
18
- return super unless client.respond_to?(method)
19
- client.send(method, *args, &block)
20
- end
21
-
22
17
  def self.respond_to?(method)
23
18
  return client.respond_to?(method) || super
24
19
  end
@@ -1,7 +1,9 @@
1
1
  require_relative 'connection'
2
+ require_relative 'jwt'
2
3
  require_relative 'request'
3
4
  require_relative 'configuration'
4
5
  require_relative 'endpoint'
6
+ require_relative 'regions_cache'
5
7
 
6
8
  module Scalingo
7
9
  class Api
@@ -15,8 +17,10 @@ module Scalingo
15
17
  end
16
18
 
17
19
  include Connection
20
+ include JWT
18
21
  include Request
19
22
  include Endpoint
23
+ include RegionsCache
20
24
  end
21
25
  end
22
26
 
@@ -9,9 +9,10 @@ module Scalingo
9
9
  :adapter,
10
10
  :token,
11
11
  :endpoint,
12
+ :auth_endpoint,
13
+ :region,
12
14
  :user_agent,
13
15
  :proxy,
14
- :parse_json,
15
16
  ].freeze
16
17
 
17
18
  # The adapter that will be used to connect if none is set
@@ -22,10 +23,8 @@ module Scalingo
22
23
  # By default, don't set an token
23
24
  DEFAULT_TOKEN = nil
24
25
 
25
- # The endpoint that will be used to connect if none is set
26
- #
27
- # @note There is no reason to use any other endpoint at this time
28
- DEFAULT_ENDPOINT = 'https://api.scalingo.com/v1/'.freeze
26
+ # The endpoint to exchange the token with a JWT
27
+ DEFAULT_AUTH_ENDPOINT = 'https://auth.scalingo.com/v1'.freeze
29
28
 
30
29
  # By default, don't use a proxy server
31
30
  DEFAULT_PROXY = nil
@@ -33,9 +32,6 @@ module Scalingo
33
32
  # The user agent that will be sent to the Api endpoint if none is set
34
33
  DEFAULT_USER_AGENT = "Scalingo Ruby Gem #{Scalingo::VERSION}".freeze
35
34
 
36
- # Parse json by default, only changed when getting text result (e.g. Logs)
37
- DEFAULT_PARSE_JSON = true
38
-
39
35
  # @private
40
36
  attr_accessor *VALID_OPTIONS_KEYS
41
37
 
@@ -58,12 +54,13 @@ module Scalingo
58
54
 
59
55
  # Reset all configuration options to defaults
60
56
  def reset
61
- self.adapter = DEFAULT_ADAPTER
62
- self.token = DEFAULT_TOKEN
63
- self.endpoint = DEFAULT_ENDPOINT
64
- self.user_agent = DEFAULT_USER_AGENT
65
- self.proxy = DEFAULT_PROXY
66
- self.parse_json = DEFAULT_PARSE_JSON
57
+ self.adapter = DEFAULT_ADAPTER
58
+ self.token = DEFAULT_TOKEN
59
+ self.endpoint = nil
60
+ self.auth_endpoint = DEFAULT_AUTH_ENDPOINT
61
+ self.region = nil
62
+ self.user_agent = DEFAULT_USER_AGENT
63
+ self.proxy = DEFAULT_PROXY
67
64
  end
68
65
  end
69
66
  end
@@ -1,31 +1,32 @@
1
1
  require 'faraday_middleware'
2
- Dir[File.expand_path('../../faraday/*.rb', __FILE__)].each{|f| require f}
2
+ Dir[File.expand_path('../faraday/*.rb', __dir__)].each { |f| require f }
3
3
 
4
4
  module Scalingo
5
5
  module Connection
6
6
  private
7
- def connection
8
- raise MissingToken.new if !token
9
7
 
10
- options = {
11
- :headers => {
12
- 'Accept' => 'application/json; charset=utf-8',
13
- 'Content-Type' => 'application/json',
14
- 'User-Agent' => user_agent,
15
- },
16
- :proxy => proxy,
17
- :url => endpoint,
18
- }
8
+ def build_connection(opts = {})
9
+ raise MissingToken if !token
19
10
 
20
- Faraday::Connection.new(options) do |connection|
11
+ Faraday::Connection.new(connection_options) do |connection|
21
12
  connection.use Faraday::Request::Multipart
22
13
  connection.use Faraday::Request::UrlEncoded
23
- connection.use Faraday::Response::ParseJson if parse_json
24
14
  connection.use FaradayMiddleware::RaiseHttpException
15
+ connection.response :json, :content_type => /\bjson$/
25
16
  connection.adapter(adapter)
26
- connection.basic_auth('', token)
27
17
  end
28
18
  end
19
+
20
+ def connection_options
21
+ return {
22
+ headers: {
23
+ 'Accept' => 'application/json; charset=utf-8',
24
+ 'Content-Type' => 'application/json',
25
+ 'User-Agent' => user_agent,
26
+ },
27
+ proxy: proxy,
28
+ }
29
+ end
29
30
  end
30
31
  end
31
32
 
@@ -9,8 +9,13 @@ module Scalingo
9
9
  module ClassMethods
10
10
  def resources(name, opts = {})
11
11
  name = name.to_s
12
+
13
+ endpoint_opts = { auth_api: opts[:auth_api] }
14
+
12
15
  define_method(name.pluralize.underscore) do
13
- Scalingo::Endpoint.const_get(name.pluralize.camelize).new(self)
16
+ Scalingo::Endpoint.const_get(
17
+ name.pluralize.camelize,
18
+ ).new(self, nil, endpoint_opts)
14
19
  end
15
20
 
16
21
  return if opts[:collection_only]
@@ -23,22 +28,28 @@ module Scalingo
23
28
 
24
29
  extend ClassMethods
25
30
  resources :apps
26
- resources :account_keys
31
+ resources :account_keys, auth_api: true
27
32
  resources :addon_providers, collection_only: true
28
33
  resources :addon_categories, collection_only: true
34
+ resources :regions, collection_only: true, auth_api: true
29
35
 
30
36
  module Base
31
37
  attr_accessor :api
32
38
  attr_accessor :prefix
39
+ attr_accessor :auth_api
33
40
 
34
- def initialize(api, prefix = nil)
41
+ def initialize(api, prefix = nil, opts = {})
35
42
  self.api = api
43
+ self.auth_api = opts.fetch(:auth_api, false)
36
44
  self.prefix = prefix || self.class.name.split('::').last.underscore.pluralize
37
45
  end
38
46
 
39
47
  Request::REQUEST_METHODS.each do |method|
40
48
  define_method(method) do |path = nil, options = {}|
41
- api.send(method, "#{prefix}/#{path}", options)
49
+ req_path = prefix
50
+ req_path += "/#{path}" if !path.nil? && path != ''
51
+ options.merge!(auth_api: auth_api) if auth_api
52
+ api.send(method, req_path, options)
42
53
  end
43
54
  end
44
55
  end
@@ -47,8 +58,8 @@ module Scalingo
47
58
  include Base
48
59
  include Endpoint
49
60
 
50
- def initialize(api, prefix, data = {})
51
- Base.instance_method(:initialize).bind(self).call(api, prefix)
61
+ def initialize(api, prefix, opts = {}, data = {})
62
+ Base.instance_method(:initialize).bind(self).call(api, prefix, opts)
52
63
  OpenStruct.instance_method(:initialize).bind(self).call(data)
53
64
  end
54
65
  end
@@ -59,16 +70,16 @@ module Scalingo
59
70
  include Enumerable
60
71
 
61
72
  def all
62
- get[collection_name].map{|r| resource_class.new(self, r[find_by], r)}
73
+ get[collection_name].map { |r| resource_class.new(self, r[find_by], {}, r) }
63
74
  end
64
- alias_method :to_a, :all
75
+ alias to_a all
65
76
 
66
77
  def each
67
78
  block_given? ? all.each(&Proc.new) : all.each
68
79
  end
69
80
 
70
81
  def find(id)
71
- detect{|r| r[find_by] == id}
82
+ detect { |r| r[find_by] == id }
72
83
  end
73
84
 
74
85
  def collection_name
@@ -1,8 +1,8 @@
1
1
  module Scalingo
2
2
  module Endpoint
3
3
  class AccountKeys < Collection
4
- def initialize(api)
5
- super(api, 'account/keys')
4
+ def initialize(api, _, opts = {})
5
+ super(api, 'keys', opts)
6
6
  end
7
7
 
8
8
  def collection_name
@@ -10,7 +10,7 @@ module Scalingo
10
10
  end
11
11
 
12
12
  def create(name, content)
13
- post(nil, {key: {name: name, content: content}})
13
+ post(nil, key: { name: name, content: content })
14
14
  end
15
15
  end
16
16
  class AccountKey < Resource
@@ -20,4 +20,3 @@ module Scalingo
20
20
  end
21
21
  end
22
22
  end
23
-
@@ -4,4 +4,3 @@ module Scalingo
4
4
  end
5
5
  end
6
6
  end
7
-
@@ -4,4 +4,3 @@ module Scalingo
4
4
  end
5
5
  end
6
6
  end
7
-
@@ -2,12 +2,12 @@ module Scalingo
2
2
  module Endpoint
3
3
  class Addons < Collection
4
4
  def create(addon_provider_id, plan_id)
5
- post(nil, {addon:{addon_provider_id: addon_provider_id, plan_id: plan_id}})
5
+ post(nil, addon: { addon_provider_id: addon_provider_id, plan_id: plan_id })
6
6
  end
7
7
  end
8
8
  class Addon < Resource
9
9
  def update(plan_id)
10
- patch(nil, {addon: {plan_id: plan_id}})
10
+ patch(nil, addon: { plan_id: plan_id })
11
11
  end
12
12
 
13
13
  def destroy
@@ -16,4 +16,3 @@ module Scalingo
16
16
  end
17
17
  end
18
18
  end
19
-
@@ -0,0 +1,25 @@
1
+ module Scalingo
2
+ module Endpoint
3
+ # - container_type: can be any container type of an application (e.g. web, clock…)
4
+ # - limit: Any float value. For any resource consumption, please provide 0.1 if you need to be alerted when the consumption goes above 10%.
5
+ # - metric: e.g. RPM per container, RAM consumption…
6
+ # - notifiers: list of notifier ID that will receive the alerts (optional)
7
+ # - send_when_below: will the alert be sent when the value goes above or below the limit (optional)
8
+ # - duration_before_trigger: the alert is triggered if the value is above the limit for the specified duration. Duration is expressed in nanoseconds. (optional)
9
+ # - remind_every: send the alert at regular interval when activated (optional)
10
+ class Alerts < Collection
11
+ def create(params)
12
+ post(nil, alert: params)
13
+ end
14
+ end
15
+ class Alert < Resource
16
+ def update(params)
17
+ patch(nil, alert: params)
18
+ end
19
+
20
+ def destroy
21
+ delete
22
+ end
23
+ end
24
+ end
25
+ end
@@ -2,7 +2,7 @@ module Scalingo
2
2
  module Endpoint
3
3
  class Apps < Collection
4
4
  def create(name)
5
- post(nil, {app: {name: name}})
5
+ post(nil, app: { name: name })
6
6
  end
7
7
 
8
8
  def find_by
@@ -11,15 +11,15 @@ module Scalingo
11
11
  end
12
12
  class App < Resource
13
13
  def scale(containers)
14
- post('scale', {containers: containers})
14
+ post('scale', containers: containers)
15
15
  end
16
16
 
17
17
  def restart(*scopes)
18
- post('restart', {scope: scopes})
18
+ post('restart', scope: scopes)
19
19
  end
20
20
 
21
21
  def destroy(current_name)
22
- delete(nil, {current_name: current_name})
22
+ delete(nil, current_name: current_name)
23
23
  end
24
24
 
25
25
  def destroy!
@@ -27,14 +27,15 @@ module Scalingo
27
27
  end
28
28
 
29
29
  def rename(new_name, current_name)
30
- post('rename', {new_name: new_name, current_name: current_name})
30
+ post('rename', new_name: new_name, current_name: current_name)
31
31
  end
32
+
32
33
  def rename!(new_name)
33
34
  rename(new_name, prefix)
34
35
  end
35
36
 
36
37
  def transfer(email)
37
- patch(nil, {app: {owner: {email: email}}})
38
+ patch(nil, app: { owner: { email: email } })
38
39
  end
39
40
 
40
41
  def logs_url
@@ -46,7 +47,7 @@ module Scalingo
46
47
  end
47
48
 
48
49
  def run(command, env = {})
49
- post('run', {command: command, env: env})
50
+ post('run', command: command, env: env)
50
51
  end
51
52
 
52
53
  resources :addons
@@ -56,8 +57,10 @@ module Scalingo
56
57
  resources :domains
57
58
  resources :stats
58
59
  resources :variables
60
+ resources :notifiers
61
+ resources :alerts
62
+ resources :autoscalers
59
63
  resources :events, collection_only: true
60
64
  end
61
65
  end
62
66
  end
63
-
@@ -0,0 +1,27 @@
1
+ module Scalingo
2
+ module Endpoint
3
+ class Autoscalers < Collection
4
+ # - container_type: can be any container type of an application (e.g. web, clock…)
5
+ # - min_containers: lower limit of containers
6
+ # - max_containers: upper limit of containers
7
+ # - metric: e.g. RPM per container, RAM consumption. The list of available metrics is here.
8
+ # - target: the autoscaler will keep the metric value as close to the target as possible by scaling the application
9
+ # https://developers.scalingo.com/autoscalers
10
+ def create(params)
11
+ post(nil, autoscaler: params)
12
+ end
13
+ end
14
+ class Autoscaler < Resource
15
+ # - container_type can't be changed
16
+ # Additional fields
17
+ # - disabled: if true, autoscaler will stop manipulating container formation
18
+ def update(params)
19
+ patch(nil, autoscaler: params)
20
+ end
21
+
22
+ def destroy
23
+ delete
24
+ end
25
+ end
26
+ end
27
+ end
@@ -2,7 +2,7 @@ module Scalingo
2
2
  module Endpoint
3
3
  class Collaborators < Collection
4
4
  def create(email)
5
- post(nil, {collaborator: {email: email}})
5
+ post(nil, collaborator: { email: email })
6
6
  end
7
7
  end
8
8
  class Collaborator < Resource
@@ -12,4 +12,3 @@ module Scalingo
12
12
  end
13
13
  end
14
14
  end
15
-
@@ -9,4 +9,3 @@ module Scalingo
9
9
  end
10
10
  end
11
11
  end
12
-
@@ -3,15 +3,15 @@ module Scalingo
3
3
  class Domains < Collection
4
4
  def create(name, tlscert = nil, tlskey = nil)
5
5
  if tlskey
6
- post(nil, {domain: {name: name, tlscert: tlscert, tlskey: tlskey}})
6
+ post(nil, domain: { name: name, tlscert: tlscert, tlskey: tlskey })
7
7
  else
8
- post(nil, {domain: {name: name}})
8
+ post(nil, domain: { name: name })
9
9
  end
10
10
  end
11
11
  end
12
12
  class Domain < Resource
13
13
  def update(tlscert, tlskey)
14
- path(nil, {domain: {tlscert: tlscert, tlskey: tlskey}})
14
+ path(nil, domain: { tlscert: tlscert, tlskey: tlskey })
15
15
  end
16
16
 
17
17
  def destroy
@@ -20,4 +20,3 @@ module Scalingo
20
20
  end
21
21
  end
22
22
  end
23
-
@@ -4,4 +4,3 @@ module Scalingo
4
4
  end
5
5
  end
6
6
  end
7
-
@@ -0,0 +1,28 @@
1
+ module Scalingo
2
+ module Endpoint
3
+ # - platform_id
4
+ # - name
5
+ # - send_all_alerts (optional)
6
+ # - send_all_events (optional)
7
+ # - type_data (optional)
8
+ # - selected_event_ids (optional)
9
+ # - active (optional)
10
+ # https://developers.scalingo.com/notifiers
11
+ #
12
+ class Notifiers < Collection
13
+ def create(params)
14
+ post(nil, notifier: params)
15
+ end
16
+ end
17
+ class Notifier < Resource
18
+ # params: platform_id can't be changed
19
+ def update(params)
20
+ patch(nil, notifier: params)
21
+ end
22
+
23
+ def destroy
24
+ delete
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,6 @@
1
+ module Scalingo
2
+ module Endpoint
3
+ class Regions < Collection
4
+ end
5
+ end
6
+ end
@@ -2,16 +2,16 @@ module Scalingo
2
2
  module Endpoint
3
3
  class Variables < Collection
4
4
  def all(aliases = true)
5
- get(nil, {aliases: aliases})[collection_name]
5
+ get(nil, aliases: aliases)[collection_name]
6
6
  end
7
7
 
8
8
  def create(name, value)
9
- post(nil, {variable: {name: name, value: value}})
9
+ post(nil, variable: { name: name, value: value })
10
10
  end
11
11
  end
12
12
  class Variable < Resource
13
13
  def update(value)
14
- patch(nil, {variable: {value: value}})
14
+ patch(nil, variable: { value: value })
15
15
  end
16
16
 
17
17
  def destroy
@@ -20,4 +20,3 @@ module Scalingo
20
20
  end
21
21
  end
22
22
  end
23
-
@@ -2,9 +2,15 @@ module Scalingo
2
2
  # Custom error class for rescuing from all Scalingo errors
3
3
  class Error < StandardError; end
4
4
 
5
- # Raised when Scalingo try a connection without any token
5
+ # Raised when the client tries a connection without any token
6
6
  class MissingToken < Error; end
7
7
 
8
+ # Raised when the client tries a connection without a region defined
9
+ class MissingRegion < Error; end
10
+
11
+ # Raise if the region named used is invalid, like if it doesn't exist
12
+ class InvalidRegion < Error; end
13
+
8
14
  # Raised when Scalingo returns the HTTP status code 400
9
15
  class BadRequest < Error; end
10
16
 
@@ -0,0 +1,37 @@
1
+ require 'jwt'
2
+
3
+ module Scalingo
4
+ module JWT
5
+ attr_reader :current_jwt
6
+
7
+ private
8
+
9
+ def current_jwt
10
+ return @current_jwt if current_jwt_valid?
11
+
12
+ @current_jwt = exchange_token_jwt
13
+ return @current_jwt
14
+ end
15
+
16
+ def current_jwt_valid?
17
+ return false if @current_jwt.nil?
18
+
19
+ ::JWT.decode @current_jwt, nil, false
20
+
21
+ return true
22
+ rescue ::JWT::ExpiredSignature
23
+ return false
24
+ end
25
+
26
+ def exchange_token_jwt
27
+ jwt_connection = build_connection
28
+ jwt_connection.basic_auth('', token)
29
+ jwt_connection.url_prefix = auth_endpoint
30
+
31
+ response = jwt_connection.post do |request|
32
+ request.path = '/v1/tokens/exchange'
33
+ end
34
+ return response.body['token']
35
+ end
36
+ end
37
+ end
@@ -5,9 +5,8 @@ module Scalingo
5
5
  attr_reader :app
6
6
 
7
7
  def initialize(app)
8
- super({endpoint: ''})
9
- self.parse_json = false
10
8
  @app = app
9
+ super()
11
10
  end
12
11
 
13
12
  def dump(lines = 10)
@@ -19,15 +18,12 @@ module Scalingo
19
18
  end
20
19
 
21
20
  protected
22
- def log_token
23
- self.endpoint, log_token = app.logs_url.split('?')
24
- @log_token = log_token.split('=').last
25
- end
26
21
 
27
22
  def request(method, path, options)
28
- options.merge!(token: log_token)
23
+ endpoint, query = app.logs_url.split('?')
24
+ log_token = query.split('=').last
25
+ options.merge!(token: log_token, endpoint: endpoint)
29
26
  super(method, path, options)
30
27
  end
31
28
  end
32
29
  end
33
-
@@ -26,12 +26,10 @@ module Scalingo
26
26
 
27
27
  ws.on :message do |event|
28
28
  data = JSON.parse(event.data)
29
- if data['event'] == 'log'
30
- @callbacks.each{|c| c.call(data['log'])}
31
- end
29
+ @callbacks.each { |c| c.call(data['log']) } if data['event'] == 'log'
32
30
  end
33
31
 
34
- ws.on :close do |event|
32
+ ws.on :close do
35
33
  ws = nil
36
34
  end
37
35
 
@@ -41,8 +39,10 @@ module Scalingo
41
39
  end
42
40
 
43
41
  protected
42
+
44
43
  def url
45
- "#{app.logs_url}&stream=true"
44
+ url = app.logs_url.gsub(/^http/, 'ws')
45
+ return "#{url}&stream=true"
46
46
  end
47
47
  end
48
48
  end
@@ -0,0 +1,12 @@
1
+ module Scalingo
2
+ module RegionsCache
3
+ def region_api_endpoint(name)
4
+ @regions_cache ||= regions.all
5
+
6
+ region = @regions_cache.select { |r| r.name == name }.first
7
+ raise InvalidRegion, name if region.nil?
8
+
9
+ return "#{region['api']}/v1"
10
+ end
11
+ end
12
+ end
@@ -5,8 +5,8 @@ module Scalingo
5
5
  :post,
6
6
  :patch,
7
7
  :put,
8
- :delete
9
- ]
8
+ :delete,
9
+ ].freeze
10
10
 
11
11
  REQUEST_METHODS.each do |method|
12
12
  define_method(method) do |path, options = {}|
@@ -15,8 +15,26 @@ module Scalingo
15
15
  end
16
16
 
17
17
  protected
18
+
18
19
  def request(method, path, options)
20
+ auth_api = options.delete(:auth_api)
21
+ endpoint = options.delete(:endpoint)
22
+
23
+ connection = build_connection
24
+
25
+ if auth_api
26
+ connection.url_prefix = URI(auth_endpoint)
27
+ elsif endpoint
28
+ connection.url_prefix = endpoint
29
+ else
30
+ raise MissingRegion if !region
31
+
32
+ connection.url_prefix = URI(region_api_endpoint(region))
33
+ end
34
+
19
35
  response = connection.send(method) do |request|
36
+ request.headers['Authorization'] = "Bearer #{current_jwt}"
37
+
20
38
  case method
21
39
  when :get, :delete
22
40
  request.url(path, options)
@@ -1,4 +1,3 @@
1
1
  module Scalingo
2
- VERSION = '1.1.1'
2
+ VERSION = '2.0.0'
3
3
  end
4
-
@@ -4,21 +4,8 @@ class ConnectionTest < BaseTestCase
4
4
  test 'missing token' do
5
5
  Scalingo.token = nil
6
6
  assert_raise(Scalingo::MissingToken) do
7
- Scalingo.apps.all
7
+ client = Scalingo::Client.new(region: 'test-1')
8
+ client.apps.all
8
9
  end
9
10
  end
10
-
11
- test 'parse_json' do
12
- stub(:get, 'parse_json').to_return(body: {hello: :world}.to_json)
13
- result = Scalingo.get('parse_json')
14
- assert_equal({"hello" => "world"}, result)
15
- end
16
-
17
- test 'parse_json false' do
18
- Scalingo.parse_json = false
19
- stub(:get, 'parse_json').to_return(body: {hello: :world}.to_json)
20
- result = Scalingo.get('parse_json')
21
- assert_equal('{"hello":"world"}', result)
22
- end
23
11
  end
24
-
@@ -21,26 +21,28 @@ class EndpointBaseTest < BaseTestCase
21
21
  end
22
22
 
23
23
  Scalingo::Request::REQUEST_METHODS.each do |method|
24
- test "#{method}" do
24
+ test method.to_s do
25
+ default_opts = {}
26
+
25
27
  @base.send(method)
26
28
  assert_equal method, @api.method_called.http_method
27
- assert_equal "#{@prefix}/", @api.method_called.path
28
- assert_equal({}, @api.method_called.options)
29
+ assert_equal @prefix.to_s, @api.method_called.path
30
+ assert_equal(default_opts, @api.method_called.options)
29
31
 
30
32
  @base.send(method, 'test')
31
33
  assert_equal method, @api.method_called.http_method
32
34
  assert_equal "#{@prefix}/test", @api.method_called.path
33
- assert_equal({}, @api.method_called.options)
35
+ assert_equal(default_opts, @api.method_called.options)
34
36
 
35
- @base.send(method, nil, {a: 1})
37
+ @base.send(method, nil, a: 1)
36
38
  assert_equal method, @api.method_called.http_method
37
- assert_equal "#{@prefix}/", @api.method_called.path
38
- assert_equal({a: 1}, @api.method_called.options)
39
+ assert_equal @prefix.to_s, @api.method_called.path
40
+ assert_equal(default_opts.merge(a: 1), @api.method_called.options)
39
41
 
40
- @base.send(method, 'test', {a: 1})
42
+ @base.send(method, 'test', a: 1)
41
43
  assert_equal method, @api.method_called.http_method
42
44
  assert_equal "#{@prefix}/test", @api.method_called.path
43
- assert_equal({a: 1}, @api.method_called.options)
45
+ assert_equal(default_opts.merge(a: 1), @api.method_called.options)
44
46
  end
45
47
  end
46
48
 
@@ -49,4 +51,3 @@ class EndpointBaseTest < BaseTestCase
49
51
  assert_equal 'endpoint_base_classes', @base.prefix
50
52
  end
51
53
  end
52
-
@@ -6,7 +6,6 @@ end
6
6
  class EndpointCollectionTest < BaseTestCase
7
7
  class ApiMock
8
8
  def get(path, options = {})
9
- path = path[0..-2]
10
9
  {
11
10
  path => collection,
12
11
  }
@@ -2,7 +2,9 @@ require 'test_helper'
2
2
 
3
3
  class EndpointResourceTest < BaseTestCase
4
4
  test 'initialize' do
5
- resource = Scalingo::Endpoint::Resource.new('api', 'prefix', {hello: :world, world: :hello})
5
+ resource = Scalingo::Endpoint::Resource.new(
6
+ 'api', 'prefix', {}, hello: :world, world: :hello,
7
+ )
6
8
  assert_equal 'api', resource.api
7
9
  assert_equal 'prefix', resource.prefix
8
10
 
@@ -9,55 +9,64 @@ class RequestTest < BaseTestCase
9
9
  http_method: request.method,
10
10
  headers: request.headers,
11
11
  uri: request.uri,
12
- query: request.uri.query ? Hash[request.uri.query.split('&').map{|q| q.split('=')}] : {},
12
+ query: request.uri.query ? Hash[request.uri.query.split('&').map { |q| q.split('=') }] : {},
13
13
  path: request.uri.path[4..-1],
14
14
  )
15
- {status: 200}
15
+ { status: 200 }
16
16
  end
17
17
  end
18
18
 
19
19
  [:get, :delete].each do |sym|
20
- test "#{sym}" do
21
- Scalingo.public_send(sym, '')
20
+ test sym.to_s do
21
+ client = Scalingo::Client.new(region: 'test-1')
22
+
23
+ stub_regions('test-1')
24
+ stub_token_exchange
25
+
26
+ client.public_send(sym, '')
22
27
  assert_equal sym, @request.http_method
23
28
  assert_equal({}, @request.query)
24
- assert_equal '', @request.path
29
+ assert_equal nil, @request.path
25
30
  assert_nil @request.body
26
31
 
27
- Scalingo.public_send(sym, 'hello')
32
+ client.public_send(sym, 'hello')
28
33
  assert_equal sym, @request.http_method
29
34
  assert_equal({}, @request.query)
30
35
  assert_equal 'hello', @request.path
31
36
  assert_nil @request.body
32
37
 
33
- Scalingo.public_send(sym, 'hello', {hello: :world})
38
+ client.public_send(sym, 'hello', hello: :world)
34
39
  assert_equal sym, @request.http_method
35
- assert_equal({"hello" => "world"}, @request.query)
40
+ assert_equal({ 'hello' => 'world' }, @request.query)
36
41
  assert_equal 'hello', @request.path
37
42
  assert_nil @request.body
38
43
  end
39
44
  end
40
45
 
41
46
  [:post, :patch, :put].each do |sym|
42
- test "#{sym}" do
43
- Scalingo.public_send(sym, '')
47
+ test sym.to_s do
48
+ client = Scalingo::Client.new(region: 'test-1')
49
+
50
+ stub_token_exchange
51
+ stub_regions('test-1')
52
+
53
+ client.public_send(sym, '')
44
54
  assert_equal sym, @request.http_method
45
55
  assert_equal({}, @request.query)
46
- assert_equal '', @request.path
56
+ assert_equal nil, @request.path
47
57
  assert_equal '', @request.body
48
58
 
49
- Scalingo.public_send(sym, 'hello')
59
+ client.public_send(sym, 'hello')
50
60
  assert_equal sym, @request.http_method
51
61
  assert_equal({}, @request.query)
52
62
  assert_equal 'hello', @request.path
53
63
  assert_equal '', @request.body
54
64
 
55
- Scalingo.public_send(sym, 'hello', {hello: :world})
65
+ client.public_send(sym, 'hello', hello: :world)
56
66
  assert_equal sym, @request.http_method
57
67
  assert_equal({}, @request.query)
58
68
  assert_equal 'hello', @request.path
59
- assert_equal({hello: :world}, @request.body)
69
+ assert_equal({ hello: :world }, @request.body)
60
70
  end
61
71
  end
62
72
  end
63
-
@@ -11,15 +11,4 @@ class ScalingoTest < BaseTestCase
11
11
  test 'client' do
12
12
  assert_equal Scalingo::Client, Scalingo.client.class
13
13
  end
14
-
15
- test 'respond_to' do
16
- assert !Scalingo.methods.include?(:get)
17
- assert Scalingo.respond_to?(:get)
18
- end
19
-
20
- test 'method_missing' do
21
- stub(:get, '').to_return(status: 200)
22
- assert Scalingo.client.get('') == Scalingo.get('')
23
- end
24
14
  end
25
-
@@ -1,5 +1,6 @@
1
1
  require 'bundler/setup'
2
2
  require 'simplecov'
3
+ require 'jwt'
3
4
  require 'pry'
4
5
 
5
6
  SimpleCov.configure do
@@ -26,17 +27,63 @@ class BaseTestCase < ActiveSupport::TestCase
26
27
  WebMock.reset!
27
28
  end
28
29
 
29
- def endpoint_uri
30
- Scalingo.endpoint.split('://').join("://:#{Scalingo.token}@")
30
+ def auth_endpoint_uri
31
+ Scalingo.auth_endpoint
31
32
  end
32
33
 
33
- def stub(method, path)
34
+ def url_with_basic_auth(url)
35
+ return url.split('://').join("://:#{Scalingo.token}@")
36
+ end
37
+
38
+ def stub(method, path, auth_api: false, region: 'test-1')
39
+ uri = "https://api.#{region}.scalingo.com"
40
+ uri = auth_endpoint_uri if auth_api
34
41
  case path
35
42
  when String
36
- stub_request(method, "#{endpoint_uri}#{path}")
43
+ uri = url_with_basic_auth(uri) if path == '/tokens/exchange'
44
+ return stub_request(method, "#{uri}#{path}")
37
45
  when Regexp
38
- stub_request(method, /#{Regexp.quote(endpoint_uri)}#{path}/)
46
+ return stub_request(method, /#{Regexp.quote(uri)}#{path}/)
39
47
  end
40
48
  end
49
+
50
+ def stub_regions(region)
51
+ stub(
52
+ :get, '/regions', auth_api: true,
53
+ ).to_return(
54
+ status: 200,
55
+ headers: {
56
+ 'Content-Type' => 'application/json',
57
+ },
58
+ body: { regions: [
59
+ {
60
+ name: region,
61
+ api: "https://api.#{region}.scalingo.com",
62
+ database_api: "https://db-api.#{region}.scalingo.com",
63
+ },
64
+ ] }.to_json,
65
+ )
66
+ end
67
+
68
+ def stub_token_exchange
69
+ stub(
70
+ :post, '/tokens/exchange', auth_api: true,
71
+ ).to_return(
72
+ status: 200,
73
+ headers: {
74
+ 'Content-Type' => 'application/json',
75
+ },
76
+ body: { token: generate_test_jwt }.to_json,
77
+ )
78
+ end
79
+
80
+ def generate_test_jwt
81
+ payload = {
82
+ iss: 'Scalingo Test', iat: Time.current.utc.to_i, uuid: SecureRandom.uuid,
83
+ rnd: SecureRandom.hex, exp: (Time.now.utc.to_i + 3600),
84
+ }
85
+ token = JWT.encode payload, '0' * 100, 'HS512'
86
+ return token
87
+ end
41
88
  end
42
89
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scalingo-ruby-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leo Unbekandt
@@ -9,8 +9,28 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-01-08 00:00:00.000000000 Z
12
+ date: 2019-12-09 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '4'
21
+ - - "<"
22
+ - !ruby/object:Gem::Version
23
+ version: '6'
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ version: '4'
31
+ - - "<"
32
+ - !ruby/object:Gem::Version
33
+ version: '6'
14
34
  - !ruby/object:Gem::Dependency
15
35
  name: faraday
16
36
  requirement: !ruby/object:Gem::Requirement
@@ -20,7 +40,7 @@ dependencies:
20
40
  version: '0.7'
21
41
  - - "<="
22
42
  - !ruby/object:Gem::Version
23
- version: '0.9'
43
+ version: 0.17.0
24
44
  type: :runtime
25
45
  prerelease: false
26
46
  version_requirements: !ruby/object:Gem::Requirement
@@ -30,76 +50,70 @@ dependencies:
30
50
  version: '0.7'
31
51
  - - "<="
32
52
  - !ruby/object:Gem::Version
33
- version: '0.9'
53
+ version: 0.17.0
34
54
  - !ruby/object:Gem::Dependency
35
55
  name: faraday_middleware
36
56
  requirement: !ruby/object:Gem::Requirement
37
57
  requirements:
38
58
  - - "~>"
39
59
  - !ruby/object:Gem::Version
40
- version: '0.8'
60
+ version: '0.13'
41
61
  type: :runtime
42
62
  prerelease: false
43
63
  version_requirements: !ruby/object:Gem::Requirement
44
64
  requirements:
45
65
  - - "~>"
46
66
  - !ruby/object:Gem::Version
47
- version: '0.8'
67
+ version: '0.13'
48
68
  - !ruby/object:Gem::Dependency
49
- name: multi_json
69
+ name: faye-websocket
50
70
  requirement: !ruby/object:Gem::Requirement
51
71
  requirements:
52
72
  - - "~>"
53
73
  - !ruby/object:Gem::Version
54
- version: '1.0'
55
- - - ">="
56
- - !ruby/object:Gem::Version
57
- version: 1.0.3
74
+ version: 0.9.2
58
75
  type: :runtime
59
76
  prerelease: false
60
77
  version_requirements: !ruby/object:Gem::Requirement
61
78
  requirements:
62
79
  - - "~>"
63
80
  - !ruby/object:Gem::Version
64
- version: '1.0'
65
- - - ">="
66
- - !ruby/object:Gem::Version
67
- version: 1.0.3
81
+ version: 0.9.2
68
82
  - !ruby/object:Gem::Dependency
69
- name: faye-websocket
83
+ name: jwt
70
84
  requirement: !ruby/object:Gem::Requirement
71
85
  requirements:
72
86
  - - "~>"
73
87
  - !ruby/object:Gem::Version
74
- version: 0.9.2
88
+ version: 2.2.1
75
89
  type: :runtime
76
90
  prerelease: false
77
91
  version_requirements: !ruby/object:Gem::Requirement
78
92
  requirements:
79
93
  - - "~>"
80
94
  - !ruby/object:Gem::Version
81
- version: 0.9.2
95
+ version: 2.2.1
82
96
  - !ruby/object:Gem::Dependency
83
- name: activesupport
97
+ name: multi_json
84
98
  requirement: !ruby/object:Gem::Requirement
85
99
  requirements:
86
- - - ">="
100
+ - - "~>"
87
101
  - !ruby/object:Gem::Version
88
- version: '4'
89
- - - "<"
102
+ version: '1.0'
103
+ - - ">="
90
104
  - !ruby/object:Gem::Version
91
- version: '6'
105
+ version: 1.0.3
92
106
  type: :runtime
93
107
  prerelease: false
94
108
  version_requirements: !ruby/object:Gem::Requirement
95
109
  requirements:
96
- - - ">="
110
+ - - "~>"
97
111
  - !ruby/object:Gem::Version
98
- version: '4'
99
- - - "<"
112
+ version: '1.0'
113
+ - - ">="
100
114
  - !ruby/object:Gem::Version
101
- version: '6'
102
- description: Ruby wrapper around the web API of scalingo.io
115
+ version: 1.0.3
116
+ description: Ruby wrapper around the web API of scalingo.com
103
117
  email:
104
118
  - leo@scalingo.com
105
119
  - geoffroy@planquart.fr
@@ -107,6 +121,8 @@ executables: []
107
121
  extensions: []
108
122
  extra_rdoc_files: []
109
123
  files:
124
+ - CHANGELOG.md
125
+ - README.md
110
126
  - Rakefile
111
127
  - lib/faraday/raise_http_exception.rb
112
128
  - lib/scalingo.rb
@@ -119,17 +135,23 @@ files:
119
135
  - lib/scalingo/endpoint/addon_categories.rb
120
136
  - lib/scalingo/endpoint/addon_provider.rb
121
137
  - lib/scalingo/endpoint/addons.rb
138
+ - lib/scalingo/endpoint/alerts.rb
122
139
  - lib/scalingo/endpoint/apps.rb
140
+ - lib/scalingo/endpoint/autoscalers.rb
123
141
  - lib/scalingo/endpoint/collaborators.rb
124
142
  - lib/scalingo/endpoint/containers.rb
125
143
  - lib/scalingo/endpoint/deployments.rb
126
144
  - lib/scalingo/endpoint/domains.rb
127
145
  - lib/scalingo/endpoint/events.rb
146
+ - lib/scalingo/endpoint/notifiers.rb
147
+ - lib/scalingo/endpoint/regions.rb
128
148
  - lib/scalingo/endpoint/stats.rb
129
149
  - lib/scalingo/endpoint/variables.rb
130
150
  - lib/scalingo/error.rb
151
+ - lib/scalingo/jwt.rb
131
152
  - lib/scalingo/logs.rb
132
153
  - lib/scalingo/realtime/logs.rb
154
+ - lib/scalingo/regions_cache.rb
133
155
  - lib/scalingo/request.rb
134
156
  - lib/scalingo/version.rb
135
157
  - test/connection_test.rb
@@ -158,15 +180,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
180
  - !ruby/object:Gem::Version
159
181
  version: '0'
160
182
  requirements: []
161
- rubygems_version: 3.0.1
183
+ rubygems_version: 3.0.3
162
184
  signing_key:
163
185
  specification_version: 4
164
186
  summary: Ruby API for the awesome scalingo project !
165
187
  test_files:
166
- - test/connection_test.rb
167
- - test/endpoint_base_test.rb
188
+ - test/scalingo_test.rb
189
+ - test/request_test.rb
168
190
  - test/endpoint_collection_test.rb
169
191
  - test/endpoint_resource_test.rb
170
- - test/request_test.rb
171
- - test/scalingo_test.rb
192
+ - test/endpoint_base_test.rb
193
+ - test/connection_test.rb
172
194
  - test/test_helper.rb