scalingo 3.0.0.beta.1 → 3.0.0.beta.2
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 +4 -4
- data/.gitignore +1 -1
- data/CHANGELOG.md +13 -1
- data/README.md +41 -27
- data/lib/scalingo.rb +1 -1
- data/lib/scalingo/api/client.rb +34 -17
- data/lib/scalingo/api/endpoint.rb +1 -1
- data/lib/scalingo/api/response.rb +21 -32
- data/lib/scalingo/bearer_token.rb +11 -5
- data/lib/scalingo/billing.rb +11 -0
- data/lib/scalingo/billing/profile.rb +46 -0
- data/lib/scalingo/client.rb +52 -26
- data/lib/scalingo/configuration.rb +98 -0
- data/lib/scalingo/regional/addons.rb +2 -2
- data/lib/scalingo/regional/containers.rb +1 -1
- data/lib/scalingo/regional/events.rb +2 -2
- data/lib/scalingo/regional/logs.rb +1 -1
- data/lib/scalingo/regional/metrics.rb +1 -1
- data/lib/scalingo/regional/notifiers.rb +1 -1
- data/lib/scalingo/regional/operations.rb +9 -1
- data/lib/scalingo/version.rb +1 -1
- data/samples/billing/profile/_meta.json +23 -0
- data/samples/billing/profile/create-201.json +50 -0
- data/samples/billing/profile/create-400.json +27 -0
- data/samples/billing/profile/create-422.json +44 -0
- data/samples/billing/profile/show-200.json +41 -0
- data/samples/billing/profile/show-404.json +22 -0
- data/samples/billing/profile/update-200.json +47 -0
- data/samples/billing/profile/update-422.json +32 -0
- data/scalingo.gemspec +1 -3
- data/spec/scalingo/api/client_spec.rb +168 -0
- data/spec/scalingo/api/endpoint_spec.rb +30 -0
- data/spec/scalingo/api/response_spec.rb +285 -0
- data/spec/scalingo/auth_spec.rb +15 -0
- data/spec/scalingo/bearer_token_spec.rb +72 -0
- data/spec/scalingo/billing/profile_spec.rb +55 -0
- data/spec/scalingo/client_spec.rb +93 -0
- data/spec/scalingo/configuration_spec.rb +55 -0
- data/spec/scalingo/regional/operations_spec.rb +11 -3
- data/spec/scalingo/regional_spec.rb +14 -0
- metadata +33 -40
- data/Gemfile.lock +0 -110
- data/lib/scalingo/config.rb +0 -38
    
        data/lib/scalingo/client.rb
    CHANGED
    
    | @@ -4,24 +4,33 @@ require "faraday_middleware" | |
| 4 4 | 
             
            require "scalingo/bearer_token"
         | 
| 5 5 | 
             
            require "scalingo/errors"
         | 
| 6 6 | 
             
            require "scalingo/auth"
         | 
| 7 | 
            +
            require "scalingo/billing"
         | 
| 7 8 | 
             
            require "scalingo/regional"
         | 
| 8 9 |  | 
| 9 10 | 
             
            module Scalingo
         | 
| 10 11 | 
             
              class Client
         | 
| 11 12 | 
             
                extend Forwardable
         | 
| 12 13 |  | 
| 14 | 
            +
                attr_reader :config
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def initialize(attributes = {})
         | 
| 17 | 
            +
                  @config = Configuration.new(attributes, Scalingo.config)
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  define_regions!
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 13 22 | 
             
                ## Authentication helpers / Token management
         | 
| 14 23 | 
             
                attr_reader :token
         | 
| 15 24 |  | 
| 16 25 | 
             
                def token=(input)
         | 
| 17 | 
            -
                  @token = input.is_a?(BearerToken) ? input : BearerToken.new(input.to_s)
         | 
| 26 | 
            +
                  @token = input.is_a?(BearerToken) ? input : BearerToken.new(input.to_s, raise_on_expired: config.raise_on_expired_token)
         | 
| 18 27 | 
             
                end
         | 
| 19 28 |  | 
| 20 29 | 
             
                def authenticated?
         | 
| 21 30 | 
             
                  token.present? && !token.expired?
         | 
| 22 31 | 
             
                end
         | 
| 23 32 |  | 
| 24 | 
            -
                def authenticate_with(access_token: nil, bearer_token: nil,  | 
| 33 | 
            +
                def authenticate_with(access_token: nil, bearer_token: nil, expires_at: nil)
         | 
| 25 34 | 
             
                  if !access_token && !bearer_token
         | 
| 26 35 | 
             
                    raise ArgumentError, "You must supply one of `access_token` or `bearer_token`"
         | 
| 27 36 | 
             
                  end
         | 
| @@ -30,18 +39,19 @@ module Scalingo | |
| 30 39 | 
             
                    raise ArgumentError, "You cannot supply both `access_token` and `bearer_token`"
         | 
| 31 40 | 
             
                  end
         | 
| 32 41 |  | 
| 33 | 
            -
                  if  | 
| 34 | 
            -
                    raise ArgumentError, "` | 
| 42 | 
            +
                  if expires_at && !bearer_token
         | 
| 43 | 
            +
                    raise ArgumentError, "`expires_at` can only be used with `bearer_token`. `access_token` have a 1 hour expiration."
         | 
| 35 44 | 
             
                  end
         | 
| 36 45 |  | 
| 37 46 | 
             
                  if access_token
         | 
| 38 | 
            -
                    expiration = Time.now +  | 
| 47 | 
            +
                    expiration = Time.now + config.exchanged_token_validity
         | 
| 39 48 | 
             
                    response = auth.tokens.exchange(access_token)
         | 
| 40 49 |  | 
| 41 50 | 
             
                    if response.successful?
         | 
| 42 51 | 
             
                      self.token = BearerToken.new(
         | 
| 43 52 | 
             
                        response.data[:token],
         | 
| 44 | 
            -
                         | 
| 53 | 
            +
                        expires_at: expiration,
         | 
| 54 | 
            +
                        raise_on_expired: config.raise_on_expired_token,
         | 
| 45 55 | 
             
                      )
         | 
| 46 56 | 
             
                    end
         | 
| 47 57 |  | 
| @@ -49,35 +59,33 @@ module Scalingo | |
| 49 59 | 
             
                  end
         | 
| 50 60 |  | 
| 51 61 | 
             
                  if bearer_token
         | 
| 52 | 
            -
                    self.token =  | 
| 62 | 
            +
                    self.token = if expires_at
         | 
| 63 | 
            +
                      token = bearer_token.is_a?(BearerToken) ? bearer_token.value : bearer_token.to_s
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                      BearerToken.new(
         | 
| 66 | 
            +
                        token,
         | 
| 67 | 
            +
                        expires_at: expires_at,
         | 
| 68 | 
            +
                        raise_on_expired: config.raise_on_expired_token,
         | 
| 69 | 
            +
                      )
         | 
| 70 | 
            +
                    else
         | 
| 71 | 
            +
                      bearer_token
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    true
         | 
| 53 75 | 
             
                  end
         | 
| 54 76 | 
             
                end
         | 
| 55 77 |  | 
| 56 78 | 
             
                ## API clients
         | 
| 57 79 | 
             
                def auth
         | 
| 58 | 
            -
                  @auth ||= Auth.new(self,  | 
| 80 | 
            +
                  @auth ||= Auth.new(self, config.auth)
         | 
| 59 81 | 
             
                end
         | 
| 60 82 |  | 
| 61 | 
            -
                def  | 
| 62 | 
            -
                   | 
| 83 | 
            +
                def billing
         | 
| 84 | 
            +
                  @billing ||= Billing.new(self, config.billing)
         | 
| 63 85 | 
             
                end
         | 
| 64 86 |  | 
| 65 | 
            -
                 | 
| 66 | 
            -
                   | 
| 67 | 
            -
                    raise ArgumentError, "Scalingo: no url configured for region #{region}"
         | 
| 68 | 
            -
                  end
         | 
| 69 | 
            -
             | 
| 70 | 
            -
                  define_method(region) do
         | 
| 71 | 
            -
                    region_client = instance_variable_get :"@#{region}"
         | 
| 72 | 
            -
             | 
| 73 | 
            -
                    unless region_client
         | 
| 74 | 
            -
                      region_client = Regional.new(self, Scalingo.config.urls[region])
         | 
| 75 | 
            -
             | 
| 76 | 
            -
                      instance_variable_set :"@#{region}", region_client
         | 
| 77 | 
            -
                    end
         | 
| 78 | 
            -
             | 
| 79 | 
            -
                    region_client
         | 
| 80 | 
            -
                  end
         | 
| 87 | 
            +
                def region(name = nil)
         | 
| 88 | 
            +
                  public_send(name || config.default_region)
         | 
| 81 89 | 
             
                end
         | 
| 82 90 |  | 
| 83 91 | 
             
                ## Delegations
         | 
| @@ -101,5 +109,23 @@ module Scalingo | |
| 101 109 | 
             
                def_delegator :region, :notifiers
         | 
| 102 110 | 
             
                def_delegator :region, :operations
         | 
| 103 111 | 
             
                def_delegator :region, :scm_repo_links
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                private
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                def define_regions!
         | 
| 116 | 
            +
                  config.regions.each_pair do |region, _|
         | 
| 117 | 
            +
                    define_singleton_method(region) do
         | 
| 118 | 
            +
                      region_client = instance_variable_get :"@#{region}"
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                      unless region_client
         | 
| 121 | 
            +
                        region_client = Regional.new(self, config.regions.public_send(region))
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                        instance_variable_set :"@#{region}", region_client
         | 
| 124 | 
            +
                      end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                      region_client
         | 
| 127 | 
            +
                    end
         | 
| 128 | 
            +
                  end
         | 
| 129 | 
            +
                end
         | 
| 104 130 | 
             
              end
         | 
| 105 131 | 
             
            end
         | 
| @@ -0,0 +1,98 @@ | |
| 1 | 
            +
            require "active_support/core_ext/numeric/time"
         | 
| 2 | 
            +
            require "scalingo/version"
         | 
| 3 | 
            +
            require "ostruct"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Scalingo
         | 
| 6 | 
            +
              class Configuration
         | 
| 7 | 
            +
                ATTRIBUTES = [
         | 
| 8 | 
            +
                  # URL to the authentication API
         | 
| 9 | 
            +
                  :auth,
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  # URL to the billing API
         | 
| 12 | 
            +
                  :billing,
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  # List of regions under the form {"region_id": root_url}
         | 
| 15 | 
            +
                  :regions,
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  # Default region. Must match a key in `regions`
         | 
| 18 | 
            +
                  :default_region,
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  # Wether to use https or http
         | 
| 21 | 
            +
                  :https,
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  # Configure the User Agent header
         | 
| 24 | 
            +
                  :user_agent,
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  # For how long is a bearer token considered valid (it will raise passed this delay).
         | 
| 27 | 
            +
                  # Set to nil to never raise.
         | 
| 28 | 
            +
                  :exchanged_token_validity,
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  # Raise an exception when trying to use an authenticated connection without a bearer token set
         | 
| 31 | 
            +
                  # Having this setting to true prevents performing requests that would fail due to lack of authentication headers.
         | 
| 32 | 
            +
                  :raise_on_missing_authentication,
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  # Raise an exception when the bearer token in use is supposed to be invalid
         | 
| 35 | 
            +
                  :raise_on_expired_token,
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  # These headers will be added to every request. Individual methods may override them.
         | 
| 38 | 
            +
                  # This should be a hash or a callable object that returns a hash.
         | 
| 39 | 
            +
                  :additional_headers
         | 
| 40 | 
            +
                ]
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                ATTRIBUTES.each { |attr| attr_accessor(attr) }
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                def self.default
         | 
| 45 | 
            +
                  new(
         | 
| 46 | 
            +
                    auth: "https://auth.scalingo.com/v1",
         | 
| 47 | 
            +
                    billing: "https://cashmachine.scalingo.com",
         | 
| 48 | 
            +
                    regions: {
         | 
| 49 | 
            +
                      agora_fr1: "https://api.agora-fr1.scalingo.com/v1",
         | 
| 50 | 
            +
                      osc_fr1: "https://api.osc-fr1.scalingo.com/v1",
         | 
| 51 | 
            +
                      osc_secnum_fr1: "https://api.osc-secnum-fr1.scalingo.com/v1"
         | 
| 52 | 
            +
                    },
         | 
| 53 | 
            +
                    default_region: :osc_fr1,
         | 
| 54 | 
            +
                    user_agent: "Scalingo Ruby Client v#{Scalingo::VERSION}",
         | 
| 55 | 
            +
                    exchanged_token_validity: 1.hour,
         | 
| 56 | 
            +
                    raise_on_missing_authentication: true,
         | 
| 57 | 
            +
                    raise_on_expired_token: false,
         | 
| 58 | 
            +
                    additional_headers: {},
         | 
| 59 | 
            +
                  )
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                def initialize(attributes = {}, parent = nil)
         | 
| 63 | 
            +
                  ATTRIBUTES.each do |name|
         | 
| 64 | 
            +
                    public_send("#{name}=", attributes.fetch(name, parent&.public_send(name)))
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                def regions=(input)
         | 
| 69 | 
            +
                  if input.is_a?(OpenStruct)
         | 
| 70 | 
            +
                    @regions = input
         | 
| 71 | 
            +
                    return
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  if input.is_a?(Hash)
         | 
| 75 | 
            +
                    @regions = OpenStruct.new(input)
         | 
| 76 | 
            +
                    return
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  raise ArgumentError, "wrong type for argument"
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                def default_region=(input)
         | 
| 83 | 
            +
                  input = input.to_sym
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  raise ArgumentError, "No regions named `#{input}`" unless regions.respond_to?(input)
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                  @default_region = input
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
              end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
              def self.config
         | 
| 92 | 
            +
                @config ||= Configuration.default
         | 
| 93 | 
            +
              end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
              def self.configure
         | 
| 96 | 
            +
                yield config
         | 
| 97 | 
            +
              end
         | 
| 98 | 
            +
            end
         | 
| @@ -83,7 +83,7 @@ module Scalingo | |
| 83 83 | 
             
                def categories(headers = nil, &block)
         | 
| 84 84 | 
             
                  data = nil
         | 
| 85 85 |  | 
| 86 | 
            -
                  response = connection( | 
| 86 | 
            +
                  response = connection(fallback_to_guest: true).get(
         | 
| 87 87 | 
             
                    "addon_categories",
         | 
| 88 88 | 
             
                    data,
         | 
| 89 89 | 
             
                    headers,
         | 
| @@ -96,7 +96,7 @@ module Scalingo | |
| 96 96 | 
             
                def providers(headers = nil, &block)
         | 
| 97 97 | 
             
                  data = nil
         | 
| 98 98 |  | 
| 99 | 
            -
                  response = connection( | 
| 99 | 
            +
                  response = connection(fallback_to_guest: true).get(
         | 
| 100 100 | 
             
                    "addon_providers",
         | 
| 101 101 | 
             
                    data,
         | 
| 102 102 | 
             
                    headers,
         | 
| @@ -29,7 +29,7 @@ module Scalingo | |
| 29 29 | 
             
                def types(headers = nil, &block)
         | 
| 30 30 | 
             
                  data = nil
         | 
| 31 31 |  | 
| 32 | 
            -
                  response = connection( | 
| 32 | 
            +
                  response = connection(fallback_to_guest: true).get(
         | 
| 33 33 | 
             
                    "event_types",
         | 
| 34 34 | 
             
                    data,
         | 
| 35 35 | 
             
                    headers,
         | 
| @@ -42,7 +42,7 @@ module Scalingo | |
| 42 42 | 
             
                def categories(headers = nil, &block)
         | 
| 43 43 | 
             
                  data = nil
         | 
| 44 44 |  | 
| 45 | 
            -
                  response = connection( | 
| 45 | 
            +
                  response = connection(fallback_to_guest: true).get(
         | 
| 46 46 | 
             
                    "event_categories",
         | 
| 47 47 | 
             
                    data,
         | 
| 48 48 | 
             
                    headers,
         | 
| @@ -3,10 +3,18 @@ require "scalingo/api/endpoint" | |
| 3 3 | 
             
            module Scalingo
         | 
| 4 4 | 
             
              class Regional::Operations < API::Endpoint
         | 
| 5 5 | 
             
                def find(app_id, operation_id, headers = nil, &block)
         | 
| 6 | 
            +
                  get(
         | 
| 7 | 
            +
                    "apps/#{app_id}/operations/#{operation_id}",
         | 
| 8 | 
            +
                    headers,
         | 
| 9 | 
            +
                    &block
         | 
| 10 | 
            +
                  )
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def get(url, headers = nil, &block)
         | 
| 6 14 | 
             
                  data = nil
         | 
| 7 15 |  | 
| 8 16 | 
             
                  response = connection.get(
         | 
| 9 | 
            -
                     | 
| 17 | 
            +
                    url,
         | 
| 10 18 | 
             
                    data,
         | 
| 11 19 | 
             
                    headers,
         | 
| 12 20 | 
             
                    &block
         | 
    
        data/lib/scalingo/version.rb
    CHANGED
    
    
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            {
         | 
| 2 | 
            +
              "id": "882d7733-923b-40dc-88e6-f1324e48c42a",
         | 
| 3 | 
            +
              "create": {
         | 
| 4 | 
            +
                "valid": {
         | 
| 5 | 
            +
                  "name": "Billing profile",
         | 
| 6 | 
            +
                  "address_line1": "1, rue de la paix",
         | 
| 7 | 
            +
                  "address_zip": "75001",
         | 
| 8 | 
            +
                  "address_city": "Paris",
         | 
| 9 | 
            +
                  "address_country": "France"
         | 
| 10 | 
            +
                },
         | 
| 11 | 
            +
                "invalid": {
         | 
| 12 | 
            +
                  "some": "attribute"
         | 
| 13 | 
            +
                }
         | 
| 14 | 
            +
              },
         | 
| 15 | 
            +
              "update": {
         | 
| 16 | 
            +
                "valid": {
         | 
| 17 | 
            +
                  "address_city": "New Somecity"
         | 
| 18 | 
            +
                },
         | 
| 19 | 
            +
                "invalid": {
         | 
| 20 | 
            +
                  "address_country": "not a country"
         | 
| 21 | 
            +
                }
         | 
| 22 | 
            +
              }
         | 
| 23 | 
            +
            }
         | 
| @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            {
         | 
| 2 | 
            +
              "path": "/profiles",
         | 
| 3 | 
            +
              "method": "post",
         | 
| 4 | 
            +
              "request": {
         | 
| 5 | 
            +
                "headers": {
         | 
| 6 | 
            +
                  "Authorization": "Bearer the-bearer-token",
         | 
| 7 | 
            +
                  "Content-Type": "application/json"
         | 
| 8 | 
            +
                },
         | 
| 9 | 
            +
                "json_body": {
         | 
| 10 | 
            +
                  "profile": {
         | 
| 11 | 
            +
                    "name": "Billing profile",
         | 
| 12 | 
            +
                    "address_line1": "1, rue de la paix",
         | 
| 13 | 
            +
                    "address_zip": "75001",
         | 
| 14 | 
            +
                    "address_city": "Paris",
         | 
| 15 | 
            +
                    "address_country": "France"
         | 
| 16 | 
            +
                  }
         | 
| 17 | 
            +
                }
         | 
| 18 | 
            +
              },
         | 
| 19 | 
            +
              "response": {
         | 
| 20 | 
            +
                "status": 201,
         | 
| 21 | 
            +
                "headers": {
         | 
| 22 | 
            +
                  "Date": "Fri, 12 Jun 2020 15:46:11 GMT",
         | 
| 23 | 
            +
                  "Content-Type": "application/json; charset=utf-8",
         | 
| 24 | 
            +
                  "Transfer-Encoding": "chunked",
         | 
| 25 | 
            +
                  "Connection": "keep-alive",
         | 
| 26 | 
            +
                  "Cache-Control": "no-cache"
         | 
| 27 | 
            +
                },
         | 
| 28 | 
            +
                "json_body": {
         | 
| 29 | 
            +
                  "profile": {
         | 
| 30 | 
            +
                    "id": "882d7733-923b-40dc-88e6-f1324e48c42a",
         | 
| 31 | 
            +
                    "name": "Billing profile",
         | 
| 32 | 
            +
                    "email": null,
         | 
| 33 | 
            +
                    "address_line1": "1, rue de la paix",
         | 
| 34 | 
            +
                    "address_line2": null,
         | 
| 35 | 
            +
                    "address_city": "Paris",
         | 
| 36 | 
            +
                    "balance": 0,
         | 
| 37 | 
            +
                    "address_zip": "75001",
         | 
| 38 | 
            +
                    "address_state": null,
         | 
| 39 | 
            +
                    "address_country": "FR",
         | 
| 40 | 
            +
                    "vat_number": null,
         | 
| 41 | 
            +
                    "company": null,
         | 
| 42 | 
            +
                    "payment_method": "sepa",
         | 
| 43 | 
            +
                    "created_at": "2020-05-29T13:01:46.217Z",
         | 
| 44 | 
            +
                    "updated_at": "2020-06-12T15:45:43.535Z",
         | 
| 45 | 
            +
                    "credit": 0,
         | 
| 46 | 
            +
                    "stripe_payment_method": null
         | 
| 47 | 
            +
                  }
         | 
| 48 | 
            +
                }
         | 
| 49 | 
            +
              }
         | 
| 50 | 
            +
            }
         | 
| @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            {
         | 
| 2 | 
            +
              "path": "/profiles",
         | 
| 3 | 
            +
              "method": "post",
         | 
| 4 | 
            +
              "request": {
         | 
| 5 | 
            +
                "headers": {
         | 
| 6 | 
            +
                  "Authorization": "Bearer the-bearer-token",
         | 
| 7 | 
            +
                  "Content-Type": "application/json"
         | 
| 8 | 
            +
                },
         | 
| 9 | 
            +
                "json_body": {
         | 
| 10 | 
            +
                  "profile": {
         | 
| 11 | 
            +
                  }
         | 
| 12 | 
            +
                }
         | 
| 13 | 
            +
              },
         | 
| 14 | 
            +
              "response": {
         | 
| 15 | 
            +
                "status": 400,
         | 
| 16 | 
            +
                "headers": {
         | 
| 17 | 
            +
                  "Date": "Fri, 12 Jun 2020 15:46:10 GMT",
         | 
| 18 | 
            +
                  "Content-Type": "application/json; charset=utf-8",
         | 
| 19 | 
            +
                  "Transfer-Encoding": "chunked",
         | 
| 20 | 
            +
                  "Connection": "keep-alive",
         | 
| 21 | 
            +
                  "Cache-Control": "no-cache"
         | 
| 22 | 
            +
                },
         | 
| 23 | 
            +
                "json_body": {
         | 
| 24 | 
            +
                  "error": "profile is required"
         | 
| 25 | 
            +
                }
         | 
| 26 | 
            +
              }
         | 
| 27 | 
            +
            }
         | 
| @@ -0,0 +1,44 @@ | |
| 1 | 
            +
            {
         | 
| 2 | 
            +
              "path": "/profiles",
         | 
| 3 | 
            +
              "method": "post",
         | 
| 4 | 
            +
              "request": {
         | 
| 5 | 
            +
                "headers": {
         | 
| 6 | 
            +
                  "Authorization": "Bearer the-bearer-token",
         | 
| 7 | 
            +
                  "Content-Type": "application/json"
         | 
| 8 | 
            +
                },
         | 
| 9 | 
            +
                "json_body": {
         | 
| 10 | 
            +
                  "profile": {
         | 
| 11 | 
            +
                    "some": "attribute"
         | 
| 12 | 
            +
                  }
         | 
| 13 | 
            +
                }
         | 
| 14 | 
            +
              },
         | 
| 15 | 
            +
              "response": {
         | 
| 16 | 
            +
                "status": 422,
         | 
| 17 | 
            +
                "headers": {
         | 
| 18 | 
            +
                  "Date": "Fri, 12 Jun 2020 15:46:11 GMT",
         | 
| 19 | 
            +
                  "Content-Type": "application/json; charset=utf-8",
         | 
| 20 | 
            +
                  "Transfer-Encoding": "chunked",
         | 
| 21 | 
            +
                  "Connection": "keep-alive",
         | 
| 22 | 
            +
                  "Cache-Control": "no-cache"
         | 
| 23 | 
            +
                },
         | 
| 24 | 
            +
                "json_body": {
         | 
| 25 | 
            +
                  "errors": {
         | 
| 26 | 
            +
                    "name": [
         | 
| 27 | 
            +
                      "can't be blank"
         | 
| 28 | 
            +
                    ],
         | 
| 29 | 
            +
                    "address_line1": [
         | 
| 30 | 
            +
                      "can't be blank"
         | 
| 31 | 
            +
                    ],
         | 
| 32 | 
            +
                    "address_zip": [
         | 
| 33 | 
            +
                      "can't be blank"
         | 
| 34 | 
            +
                    ],
         | 
| 35 | 
            +
                    "address_city": [
         | 
| 36 | 
            +
                      "can't be blank"
         | 
| 37 | 
            +
                    ],
         | 
| 38 | 
            +
                    "address_country": [
         | 
| 39 | 
            +
                      "can't be blank"
         | 
| 40 | 
            +
                    ]
         | 
| 41 | 
            +
                  }
         | 
| 42 | 
            +
                }
         | 
| 43 | 
            +
              }
         | 
| 44 | 
            +
            }
         |