spark_api 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -15,6 +15,49 @@ Installation
15
15
  Usage Examples
16
16
  ------------------------
17
17
 
18
+ #### Ruby Script: OAuth 2
19
+ # initialize the gem with your OAuth 2 key/secret.
20
+ # See also: script/oauth2_example.rb
21
+ # api_key, api_secret, and callback are all required.
22
+ # The following options are required:
23
+ # - api_key: Your OAuth 2 client key
24
+ # - api-secret: Your OAuth 2 client secret
25
+ # - callback: Your OAuth 2 redirect_uri, which the end user will be redirected
26
+ # to after authorizing your application to access their data.
27
+ # - auth_endpoint: The URI to redirect the user's web browser to, in order for them to
28
+ # authorize your application to access their data.
29
+ # other options and their defaults:
30
+ # - endpoint: 'https://api.sparkapi.com'
31
+ # - version: 'v1'
32
+ # - ssl: true
33
+ # - user_agent: 'Spark API Ruby Gem'
34
+ SparkApi.configure do |config|
35
+ config.authentication_mode = SparkApi::Authentication::OAuth2
36
+ config.api_key = "YOUR_CLIENT_ID"
37
+ config.api_secret = "YOUR_CLIENT_SECRET"
38
+ config.callback = "YOUR_REDIRECT_URI"
39
+ config.auth_endpoint = "https://developers.sparkplatform.com/oauth2"
40
+ config.endpoint = 'https://developers.sparkapi.com'
41
+ end
42
+
43
+ # Code is retrieved from the method. client.authenticator.authorization_url
44
+ # See script/oauth2_example.rb for more details.
45
+
46
+
47
+ SparkApi.client.oauth2_provider.code = "CODE_FROM_ABOVE_URI"
48
+ SparkApi.client.authenticate
49
+
50
+ # Alternatively, if you've already received an access token, you may
51
+ # do the following instead of the above two lines:
52
+ #SparkApi.client.session = SparkApi::Authentication::OAuthSession.new "access_token"=> "ACCESS_TOKEN",
53
+ # "refresh_token" => "REFRESH_TOKEN", "expires_in" => 86400
54
+
55
+ # mixin the models so you can use them without prefix
56
+ include SparkApi::Models
57
+
58
+ # Grab your listings!
59
+ my_listings = Listing.my()
60
+
18
61
  #### Ruby Script
19
62
  # initialize the gem with your key/secret
20
63
  # api_key and _api_secret are the only required settings
@@ -62,8 +105,10 @@ Authentication is handled transparently by the request framework in the gem, so
62
105
  #### API Authentication (Default)
63
106
  Usually supplied for a single user, this authentication mode is the simplest, and is setup as the default. The example usage above demonstrates how to get started using this authentication mode.
64
107
 
65
- #### OAuth2 Authentication
66
- Authentication mode the separates application and user authentication. This mode requires further setup which is described in _lib/spark_api/authentication/oauth2.rb_
108
+ #### OAuth2 Authorization (Preferred)
109
+ Authorization mode the separates application and user authentication. This mode requires the end user to be redirected to Spark Platform's auth endpoint. See "script/oauth2_example.rb" for an example.
110
+
111
+ Read more about Spark Platform's OAuth 2 flow <a href="http://sparkplatform.com/docs/authentication/oauth2_authentication">here</a>.
67
112
 
68
113
  Error Codes
69
114
  ---------------------
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.1
1
+ 1.0.2
@@ -182,6 +182,7 @@ module SparkApi
182
182
  require 'spark_api/authentication/oauth2_impl/grant_type_code'
183
183
  require 'spark_api/authentication/oauth2_impl/grant_type_password'
184
184
  require 'spark_api/authentication/oauth2_impl/password_provider'
185
+ require 'spark_api/authentication/oauth2_impl/simple_provider'
185
186
 
186
187
  # Loads a provider class from a string
187
188
  def self.load_provider(string, args={})
@@ -0,0 +1,22 @@
1
+ module SparkApi
2
+ module Authentication
3
+ class SimpleProvider < BaseOAuth2Provider
4
+ def initialize(credentials)
5
+ super(credentials)
6
+ @grant_type = :authorization_code
7
+ end
8
+
9
+ def load_session
10
+ @session
11
+ end
12
+
13
+ def save_session session
14
+ @session = session
15
+ end
16
+
17
+ def destroy_session
18
+ @session = nil
19
+ end
20
+ end
21
+ end
22
+ end
@@ -7,6 +7,9 @@ module SparkApi
7
7
  include Connection
8
8
  include Authentication
9
9
  include Request
10
+
11
+ require File.expand_path('../configuration/oauth2_configurable', __FILE__)
12
+ include Configuration::OAuth2Configurable
10
13
 
11
14
  attr_accessor :authenticator
12
15
  attr_accessor *Configuration::VALID_OPTION_KEYS
@@ -0,0 +1,30 @@
1
+ module SparkApi
2
+ module Configuration
3
+ module OAuth2Configurable
4
+ def convert_to_oauth2?
5
+ self.authentication_mode == SparkApi::Authentication::OAuth2 &&
6
+ self.oauth2_provider.nil?
7
+ end
8
+
9
+ def oauth2_enabled?
10
+ self.authentication_mode == SparkApi::Authentication::OAuth2
11
+ end
12
+
13
+ def oauthify!
14
+ self.oauth2_provider = SparkApi::Authentication::SimpleProvider.new(
15
+ :access_uri => grant_uri,
16
+ :client_id => self.api_key,
17
+ :client_secret => self.api_secret,
18
+ :authorization_uri => self.auth_endpoint,
19
+ :redirect_uri => self.callback
20
+ )
21
+ end
22
+
23
+ def grant_uri
24
+ e = self.endpoint.gsub(/\/+$/,"")
25
+ v = self.version.gsub(/\/+/,"/")
26
+ "#{e}/#{v}/oauth2/grant"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -2,7 +2,9 @@ module SparkApi
2
2
  module Configuration
3
3
 
4
4
  # valid configuration options
5
- VALID_OPTION_KEYS = [:api_key, :api_secret, :api_user, :endpoint, :user_agent, :version, :ssl, :oauth2_provider, :authentication_mode].freeze
5
+ VALID_OPTION_KEYS = [:api_key, :api_secret, :api_user, :endpoint,
6
+ :user_agent, :version, :ssl, :oauth2_provider, :authentication_mode,
7
+ :auth_endpoint, :callback].freeze
6
8
  OAUTH2_KEYS = [:authorization_uri, :access_uri, :client_id, :client_secret,
7
9
  # Requirements for authorization_code grant type
8
10
  :redirect_uri,
@@ -11,11 +13,15 @@ module SparkApi
11
13
  ]
12
14
 
13
15
  require File.expand_path('../configuration/yaml', __FILE__)
16
+ require File.expand_path('../configuration/oauth2_configurable', __FILE__)
17
+
18
+ include OAuth2Configurable
14
19
 
15
20
  DEFAULT_API_KEY = nil
16
21
  DEFAULT_API_SECRET = nil
17
22
  DEFAULT_API_USER = nil
18
23
  DEFAULT_ENDPOINT = 'https://api.sparkapi.com'
24
+ DEFAULT_AUTH_ENDPOINT = 'https://sparkplatform.com/openid' # Ignored for Spark API Auth
19
25
  DEFAULT_VERSION = 'v1'
20
26
  DEFAULT_USER_AGENT = "Spark API Ruby Gem #{VERSION}"
21
27
  DEFAULT_SSL = true
@@ -25,7 +31,8 @@ module SparkApi
25
31
 
26
32
  attr_accessor *VALID_OPTION_KEYS
27
33
  def configure
28
- yield self
34
+ yield(self)
35
+ oauthify! if convert_to_oauth2?
29
36
  end
30
37
 
31
38
  def self.extended(base)
@@ -43,6 +50,7 @@ module SparkApi
43
50
  self.api_secret = DEFAULT_API_SECRET
44
51
  self.api_user = DEFAULT_API_USER
45
52
  self.authentication_mode = SparkApi::Authentication::ApiAuth
53
+ self.auth_endpoint = DEFAULT_AUTH_ENDPOINT
46
54
  self.endpoint = DEFAULT_ENDPOINT
47
55
  self.oauth2_provider = DEFAULT_OAUTH2
48
56
  self.user_agent = DEFAULT_USER_AGENT
@@ -2,7 +2,7 @@ module SparkApi
2
2
  module Models
3
3
  class Listing < Base
4
4
  extend Finders
5
- attr_accessor :photos, :videos, :virtual_tours, :documents
5
+ attr_accessor :photos, :videos, :virtual_tours, :documents, :open_houses, :tour_of_homes
6
6
  attr_accessor :constraints
7
7
  self.element_name="listings"
8
8
  DATA_MASK = "********"
@@ -14,9 +14,10 @@ module SparkApi
14
14
  @virtual_tours = []
15
15
  @documents = []
16
16
  @constraints = []
17
+ #@tour_of_homes = []
17
18
 
18
19
  if attributes.has_key?('StandardFields')
19
- pics, vids, tours, docs = attributes['StandardFields'].values_at('Photos','Videos', 'VirtualTours', 'Documents')
20
+ pics, vids, tours, docs, ohouses, tourhomes = attributes['StandardFields'].values_at('Photos','Videos', 'VirtualTours', 'Documents', 'OpenHouses', 'TourOfHomes')
20
21
  end
21
22
 
22
23
  if pics != nil
@@ -39,6 +40,18 @@ module SparkApi
39
40
  attributes['StandardFields'].delete('Documents')
40
41
  end
41
42
 
43
+ if ohouses != nil
44
+ @open_houses = []
45
+ ohouses.collect { |ohouse| @open_houses.push(OpenHouse.new(ohouse)) }
46
+ attributes['StandardFields'].delete('OpenHouses')
47
+ end
48
+
49
+ if tourhomes != nil
50
+ @tour_of_homes = []
51
+ tourhomes.collect { |tourhome| @tour_of_homes.push(TourOfHome.new(tourhome)) }
52
+ attributes['StandardFields'].delete('TourOfHomes')
53
+ end
54
+
42
55
  super(attributes)
43
56
  end
44
57
 
@@ -60,18 +73,18 @@ module SparkApi
60
73
  end
61
74
 
62
75
  def self.nearby(latitude, longitude, arguments={})
63
- nearby_args = {:Lat => latitude, :Lon => longitude}.merge(arguments)
76
+ nearby_args = {:_lat => latitude, :_lon => longitude}.merge(arguments)
64
77
  collect(connection.get("/listings/nearby", nearby_args))
65
78
  end
66
79
 
67
80
  def tour_of_homes(arguments={})
81
+ @tour_of_homes ||= TourOfHome.find_by_listing_key(self.Id, arguments)
68
82
  return @tour_of_homes unless @tour_of_homes.nil?
69
- @tour_of_homes = TourOfHome.find_by_listing_key(self.Id, arguments)
70
83
  end
71
84
 
72
85
  def open_houses(arguments={})
86
+ @open_houses ||= OpenHouse.find_by_listing_key(self.Id, arguments)
73
87
  return @open_houses unless @open_houses.nil?
74
- @open_houses = OpenHouse.find_by_listing_key(self.Id, arguments)
75
88
  end
76
89
 
77
90
  def my_notes
@@ -16,6 +16,11 @@ module SparkApi
16
16
  attributes['StartTime'] = Time.parse("#{date}T#{attributes['StartTime']}") unless attributes['StartTime'].nil?
17
17
  attributes['EndTime'] = Time.parse("#{date}T#{attributes['EndTime']}") unless attributes['EndTime'].nil?
18
18
  end
19
+
20
+ if attributes["Comments"].nil?
21
+ attributes["Comments"] = ""
22
+ end
23
+
19
24
  super(attributes)
20
25
  end
21
26
 
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+
4
+ Bundler.require(:default, "development") if defined?(Bundler)
5
+
6
+ path = File.expand_path(File.dirname(__FILE__) + "/../lib/")
7
+ $LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
8
+ require path + '/spark_api'
9
+
10
+ SparkApi.logger.info("Hello!")
11
+
12
+ SparkApi.configure do |config|
13
+ config.authentication_mode = SparkApi::Authentication::OAuth2
14
+ config.api_key = "YOUR_CLIENT_ID"
15
+ config.api_secret = "YOUR_CLIENT_SECRET"
16
+ config.callback = "YOUR_REDIRECT_URI"
17
+ config.version = "v1"
18
+ config.endpoint = "https://developers.sparkapi.com"
19
+ config.auth_endpoint = "https://developers.sparkplatform.com/oauth2"
20
+ end
21
+
22
+ client = SparkApi.client
23
+
24
+
25
+ # Step 1:
26
+ # To get your code to post to /v1/oauth2/grant, send the end user to this URI, replacing the all-capped strings with
27
+ # the CGI-escaped credentials for your key:
28
+ # https://developers.sparkplatform.com/oauth2?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI
29
+ # When the user has finished, they will land at:
30
+ # YOUR_REDIRECT_URI?code=CODE.
31
+ puts "Go here and log in to get your code: #{client.authenticator.authorization_url}"
32
+
33
+ # Step 2: Uncomment the following, and add your code in place of CODE_FROM_ABOVE_URI
34
+ # Hold on to the tokens. Unless you lose them, you can now pass in these
35
+ # values until the access_token expires.
36
+ #client.oauth2_provider.code = "CODE_FROM_ABOVE_URI"
37
+ #client.authenticate
38
+ #puts "Access Token: #{client.session.access_token}, Refresh Token: #{client.session.refresh_token}"
39
+
40
+
41
+ # Step 4: Comment out Step 3, and uncomment the following.
42
+ # Pass in your access_token and refresh_token to make authenticated requests to the API
43
+ #client.session = SparkApi::Authentication::OAuthSession.new "access_token"=> "ACCESS_TOKEN",
44
+ # "refresh_token" => "REFRESH_TOKEN", "expires_in" => 86400
45
+
46
+
47
+ # Step 3a and 4a: Uncomment with Step 3 and 4.
48
+ # Make requests for authorized listing data
49
+ #list = client.get '/contacts'
50
+ #puts "client: #{list.inspect}"
51
+ #list = SparkApi::Models::Contact.get
52
+ #puts "model: #{list.inspect}"
53
+
54
+
55
+
56
+
@@ -10,6 +10,7 @@ describe SparkApi::Client, "Client config" do
10
10
  SparkApi.api_key.should be_nil
11
11
  SparkApi.api_secret.should be_nil
12
12
  SparkApi.version.should match("v1")
13
+ SparkApi.auth_endpoint.should match("sparkplatform.com/openid")
13
14
  SparkApi.endpoint.should match("api.sparkapi.com")
14
15
  SparkApi.user_agent.should match(/Spark API Ruby Gem .*/)
15
16
  SparkApi.api_key = "my_api_key"
@@ -20,18 +21,53 @@ describe SparkApi::Client, "Client config" do
20
21
  describe "instance config" do
21
22
  it "should return a properly configured client" do
22
23
  client = SparkApi::Client.new(:api_key => "key_of_wade",
23
- :api_secret => "TopSecret",
24
- :api_user => "1234",
25
- :endpoint => "http://api.wade.dev.fbsdata.com")
24
+ :api_secret => "TopSecret",
25
+ :api_user => "1234",
26
+ :auth_endpoint => "https://login.wade.dev.fbsdata.com",
27
+ :endpoint => "http://api.wade.dev.fbsdata.com")
26
28
 
27
29
  client.api_key.should match("key_of_wade")
28
30
  client.api_secret.should match("TopSecret")
29
31
  client.api_user.should match("1234")
32
+ client.auth_endpoint.should match("https://login.wade.dev.fbsdata.com")
30
33
  client.endpoint.should match("http://api.wade.dev.fbsdata.com")
31
34
  client.version.should match("v1")
32
35
  end
33
36
  end
34
37
 
38
+ describe "oauth2 instance configuration" do
39
+ let(:oauth2_client) do
40
+ SparkApi::Client.new(:api_key => "key_of_wade",
41
+ :api_secret => "TopSecret",
42
+ :callback => "http://wade.dev.fbsdata.com/callback",
43
+ :auth_endpoint => "https://login.wade.dev.fbsdata.com",
44
+ :endpoint => "http://api.wade.dev.fbsdata.com",
45
+ :authentication_mode => SparkApi::Authentication::OAuth2)
46
+ end
47
+
48
+ it "should convert the configuration to oauth2 when specified" do
49
+ oauth2_client.oauthify!
50
+ oauth2_client.oauth2_provider.should be_a(SparkApi::Authentication::SimpleProvider)
51
+ end
52
+
53
+ it "should say oauth2_enabled? when it is" do
54
+ oauth2_client.oauth2_enabled?().should be_true
55
+ end
56
+
57
+ it "should say oauth2_enabled? is false" do
58
+ client = SparkApi::Client.new(:api_key => "key_of_wade",
59
+ :api_secret => "TopSecret",
60
+ :callback => "http://wade.dev.fbsdata.com/callback",
61
+ :auth_endpoint => "https://login.wade.dev.fbsdata.com",
62
+ :endpoint => "http://api.wade.dev.fbsdata.com")
63
+ client.oauth2_enabled?().should be_false
64
+ end
65
+
66
+ it "should properly build a grant_uri from the endpoint" do
67
+ oauth2_client.grant_uri.should eq("http://api.wade.dev.fbsdata.com/v1/oauth2/grant")
68
+ end
69
+ end
70
+
35
71
  describe "block config" do
36
72
  it "should correctly set up the client" do
37
73
  SparkApi.configure do |config|
@@ -49,7 +85,20 @@ describe SparkApi::Client, "Client config" do
49
85
  SparkApi.version.should match("veleventy")
50
86
  SparkApi.endpoint.should match("test.api.sparkapi.com")
51
87
  SparkApi.user_agent.should match("my useragent")
88
+ SparkApi.oauth2_enabled?().should be_false
89
+ end
52
90
 
91
+ it "should correctly set up the client for oauth2" do
92
+ SparkApi.configure do |config|
93
+ config.api_key = "my_key"
94
+ config.api_secret = "my_secret"
95
+ config.callback = "http://wade.dev.fbsdata.com/callback",
96
+ config.auth_endpoint = "https://login.wade.dev.fbsdata.com",
97
+ config.endpoint = "test.api.sparkapi.com"
98
+ config.user_agent = "my useragent"
99
+ config.authentication_mode = SparkApi::Authentication::OAuth2
100
+ end
101
+ SparkApi.oauth2_enabled?().should be_true
53
102
  end
54
103
 
55
104
  it "should reset" do
@@ -44,6 +44,13 @@ describe Listing do
44
44
  "Uri1280"=>"http=>//devresize.flexmls.com/fgo/1280x1024/true/20101115201631519737000000-o.jpg",
45
45
  "UriThumb"=>"http=>//images.dev.fbsdata.com/fgo/20101115201631519737000000-t.jpg",
46
46
  "Uri640"=>"http=>//devresize.flexmls.com/fgo/640x480/true/20101115201631519737000000-o.jpg"
47
+ }],
48
+ "OpenHouses" => [{
49
+ 'ResourceUri'=>"/v1/listings/20060412165917817933000000/openhouses/20101127153422574618000000",
50
+ 'Id'=>"20060412165917817933000000",
51
+ 'Date'=>"10/01/2010",
52
+ 'StartTime'=>"09:00:00-07:00",
53
+ 'EndTime'=>"12:00:00-07:00"
47
54
  }]
48
55
  },
49
56
  "Id"=>"20080619000032866372000000"
@@ -250,17 +257,18 @@ describe Listing do
250
257
  l.photos.length.should == 0
251
258
  l.documents.length.should == 0
252
259
  end
253
-
254
- on_get_it "should return tour of homes" do
255
- stub_api_get("/listings/20060725224713296297000000", 'listings/no_subresources.json')
256
- stub_api_get("/listings/20060725224713296297000000/tourofhomes", 'listings/tour_of_homes.json')
257
-
258
- l = Listing.find('20060725224713296297000000')
259
- l.tour_of_homes().length.should == 2
260
- l.videos.length.should == 0
261
- l.photos.length.should == 0
262
- l.documents.length.should == 0
263
- end
260
+
261
+ ## TourOfHomes: Not implemented yet ##
262
+ #on_get_it "should return tour of homes" do
263
+ #stub_api_get("/listings/20060725224713296297000000", 'listings/no_subresources.json')
264
+ #stub_api_get("/listings/20060725224713296297000000/tourofhomes", 'listings/tour_of_homes.json')
265
+
266
+ #l = Listing.find('20060725224713296297000000')
267
+ #l.tour_of_homes().length.should == 2
268
+ #l.videos.length.should == 0
269
+ #l.photos.length.should == 0
270
+ #l.documents.length.should == 0
271
+ #end
264
272
 
265
273
  on_get_it "should return permissions" do
266
274
  stub_api_get("/listings/20060725224713296297000000", 'listings/with_permissions.json', { :_expand => "Permissions" })
@@ -306,7 +314,7 @@ describe Listing do
306
314
  context "/listings/nearby", :support do
307
315
  on_get_it "should return nearby homes" do
308
316
  stub_api_get("/listings/nearby",
309
- 'listings/no_subresources.json', {:Lat => "45.45", :Lon => "-93.98"})
317
+ 'listings/no_subresources.json', {:_lat => "45.45", :_lon => "-93.98"})
310
318
  l = Listing.nearby(45.45, -93.98)
311
319
  l.length.should == 1
312
320
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spark_api
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
- - 1
10
- version: 1.0.1
9
+ - 2
10
+ version: 1.0.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Brandon Hornseth
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2012-04-30 00:00:00 Z
19
+ date: 2012-07-11 00:00:00 Z
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
22
  version_requirements: &id001 !ruby/object:Gem::Requirement
@@ -195,22 +195,14 @@ dependencies:
195
195
  version_requirements: &id011 !ruby/object:Gem::Requirement
196
196
  none: false
197
197
  requirements:
198
- - - ">="
198
+ - - "="
199
199
  - !ruby/object:Gem::Version
200
- hash: 11
200
+ hash: 1
201
201
  segments:
202
202
  - 1
203
203
  - 7
204
- - 0
205
- version: 1.7.0
206
- - - <
207
- - !ruby/object:Gem::Version
208
- hash: 55
209
- segments:
210
- - 1
211
- - 8
212
- - 0
213
- version: 1.8.0
204
+ - 5
205
+ version: 1.7.5
214
206
  requirement: *id011
215
207
  prerelease: false
216
208
  name: webmock
@@ -307,6 +299,7 @@ files:
307
299
  - lib/spark_api/authentication/oauth2_impl/grant_type_code.rb
308
300
  - lib/spark_api/authentication/oauth2_impl/grant_type_code.rb~
309
301
  - lib/spark_api/authentication/oauth2_impl/grant_type_refresh.rb
302
+ - lib/spark_api/authentication/oauth2_impl/simple_provider.rb
310
303
  - lib/spark_api/authentication/oauth2_impl/grant_type_password.rb~
311
304
  - lib/spark_api/authentication/oauth2_impl/grant_type_password.rb
312
305
  - lib/spark_api/authentication/oauth2_impl/grant_type_refresh.rb~
@@ -396,12 +389,14 @@ files:
396
389
  - lib/spark_api/configuration.rb~
397
390
  - lib/spark_api/configuration.rb
398
391
  - lib/spark_api/configuration/yaml.rb~
392
+ - lib/spark_api/configuration/oauth2_configurable.rb
399
393
  - lib/spark_api/configuration/yaml.rb
400
394
  - lib/spark_api.rb
401
395
  - script/console~
402
396
  - script/console
403
397
  - script/example.rb
404
398
  - script/example.rb~
399
+ - script/oauth2_example.rb
405
400
  - spec/fixtures/oauth2_error.json
406
401
  - spec/fixtures/errors/failure_with_constraint.json
407
402
  - spec/fixtures/errors/expired.json