scalingo-ruby-api 1.1.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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