cloudkit 0.10.1 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/CHANGES +11 -0
  2. data/README +7 -6
  3. data/Rakefile +13 -6
  4. data/TODO +7 -5
  5. data/cloudkit.gemspec +23 -23
  6. data/doc/curl.html +2 -2
  7. data/doc/index.html +4 -6
  8. data/examples/5.ru +2 -3
  9. data/examples/TOC +1 -3
  10. data/lib/cloudkit.rb +17 -10
  11. data/lib/cloudkit/constants.rb +0 -6
  12. data/lib/cloudkit/exceptions.rb +10 -0
  13. data/lib/cloudkit/flash_session.rb +1 -3
  14. data/lib/cloudkit/oauth_filter.rb +8 -16
  15. data/lib/cloudkit/oauth_store.rb +9 -13
  16. data/lib/cloudkit/openid_filter.rb +25 -7
  17. data/lib/cloudkit/openid_store.rb +14 -17
  18. data/lib/cloudkit/request.rb +6 -1
  19. data/lib/cloudkit/service.rb +15 -15
  20. data/lib/cloudkit/store.rb +97 -284
  21. data/lib/cloudkit/store/memory_table.rb +105 -0
  22. data/lib/cloudkit/store/resource.rb +256 -0
  23. data/lib/cloudkit/store/response_helpers.rb +0 -1
  24. data/lib/cloudkit/uri.rb +88 -0
  25. data/lib/cloudkit/user_store.rb +7 -14
  26. data/spec/ext_spec.rb +76 -0
  27. data/spec/flash_session_spec.rb +20 -0
  28. data/spec/memory_table_spec.rb +86 -0
  29. data/spec/oauth_filter_spec.rb +326 -0
  30. data/spec/oauth_store_spec.rb +10 -0
  31. data/spec/openid_filter_spec.rb +64 -0
  32. data/spec/openid_store_spec.rb +101 -0
  33. data/spec/rack_builder_spec.rb +39 -0
  34. data/spec/request_spec.rb +185 -0
  35. data/spec/resource_spec.rb +291 -0
  36. data/spec/service_spec.rb +974 -0
  37. data/{test/helper.rb → spec/spec_helper.rb} +14 -2
  38. data/spec/store_spec.rb +10 -0
  39. data/spec/uri_spec.rb +93 -0
  40. data/spec/user_store_spec.rb +10 -0
  41. data/spec/util_spec.rb +11 -0
  42. metadata +37 -61
  43. data/examples/6.ru +0 -10
  44. data/lib/cloudkit/store/adapter.rb +0 -8
  45. data/lib/cloudkit/store/extraction_view.rb +0 -57
  46. data/lib/cloudkit/store/sql_adapter.rb +0 -36
  47. data/test/ext_test.rb +0 -76
  48. data/test/flash_session_test.rb +0 -22
  49. data/test/oauth_filter_test.rb +0 -331
  50. data/test/oauth_store_test.rb +0 -12
  51. data/test/openid_filter_test.rb +0 -60
  52. data/test/openid_store_test.rb +0 -12
  53. data/test/rack_builder_test.rb +0 -41
  54. data/test/request_test.rb +0 -197
  55. data/test/service_test.rb +0 -971
  56. data/test/store_test.rb +0 -93
  57. data/test/user_store_test.rb +0 -12
  58. data/test/util_test.rb +0 -13
data/CHANGES CHANGED
@@ -1,3 +1,14 @@
1
+ 0.11.0
2
+ - Added Tokyo Cabinet storage
3
+ - Added MemoryTable development-time storage
4
+ - Improved router performance
5
+ - Added CloudKit::Resource model
6
+ - Added custom URIs for OpenID bypass
7
+ - Removed internal undocumented ExtractionViews
8
+ - Removed SQL backends
9
+ - Removed Sequel and Rack::Config dependencies
10
+ - Switched test framework to RSpec
11
+
1
12
  0.10.1
2
13
  - Updated oauth and sequel gem dependencies
3
14
  - Fixed 410 responses for stale PUT operations
data/README CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  CloudKit is an Open Web JSON Appliance.
4
4
 
5
- More specifically, CloudKit provides RESTful JSON storage with optional OpenID and OAuth support, including OAuth Discovery. Stored entities are versioned. Services manage their own storage and do not require schema updates when models change.
5
+ CloudKit provides schema-free, auto-versioned, RESTful JSON storage with optional OpenID and OAuth support, including OAuth Discovery.
6
6
 
7
- CloudKit is Rack middleware and as such can be used on its own or alongside other Rack-based applications or middleware components like Rails, Merb or Sinatra.
7
+ CloudKit is Rack middleware. It can be used on its own or alongside other Rack-based applications or middleware components like Rails, Merb or Sinatra.
8
8
 
9
9
  ===Install
10
10
 
@@ -35,12 +35,11 @@ An explicit version of example 2, minus the default developer landing page:
35
35
  use CloudKit::Service, :collections => [:notes, :todos]
36
36
  run lambda {|env| [200, {'Content-Type' => 'text/html', 'Content-Length' => '5'}, ['HELLO']]}
37
37
 
38
- The same as above, using MySQL:
38
+ The same as above, using Tokyo Cabinet:
39
39
 
40
40
  require 'cloudkit'
41
- use Rack::Config do |env|
42
- env[CLOUDKIT_STORAGE_URI] = 'mysql://user:pass@localhost/cloudkit_example'
43
- end
41
+ require 'rufus/tokyo' # gem install rufus-tokyo
42
+ CloudKit.setup_storage_adapter(Rufus::Tokyo::Table.new('cloudkit.tdb'))
44
43
  use Rack::Pool::Session
45
44
  use CloudKit::OAuthFilter
46
45
  use CloudKit::OpenIDFilter
@@ -59,6 +58,8 @@ Google Group: http://groups.google.com/group/cloudkit
59
58
 
60
59
  Source: http://github.com/jcrosby/cloudkit
61
60
 
61
+ IRC: #cloudkit on freenode
62
+
62
63
  ===License
63
64
 
64
65
  Copyright (c) 2008, 2009 Jon Crosby http://joncrosby.me
data/Rakefile CHANGED
@@ -1,13 +1,20 @@
1
1
  require 'rake/clean'
2
+ require 'spec/rake/spectask'
2
3
 
3
4
  CLEAN.include 'doc/api'
4
5
 
5
- task :default => :test
6
+ task :default => :spec
6
7
 
7
- desc 'Run specs'
8
- task :test => FileList['test/*_test.rb'] do |t|
9
- suite = t.prerequisites.map{|f| "-r#{f.chomp('.rb')}"}.join(' ')
10
- sh "ruby -Ilib:test #{suite} -e ''", :verbose => false
8
+ desc "Run all examples (or a specific spec with TASK=xxxx)"
9
+ Spec::Rake::SpecTask.new('spec') do |t|
10
+ t.spec_opts = ["-c"]
11
+ t.spec_files = begin
12
+ if ENV["TASK"]
13
+ ENV["TASK"].split(',').map { |task| "spec/**/#{task}_spec.rb" }
14
+ else
15
+ FileList['spec/**/*_spec.rb']
16
+ end
17
+ end
11
18
  end
12
19
 
13
20
  desc 'Generate rdoc'
@@ -25,7 +32,7 @@ task :rdoc do
25
32
  --exclude=cloudkit.gemspec
26
33
  --exclude=templates/*
27
34
  --exclude=examples/*
28
- --exclude=test/*
35
+ --exclude=spec/*
29
36
  --exclude=doc/index.html
30
37
  --exclude=doc/curl.html
31
38
  --exclude=doc/rest-api.html
data/TODO CHANGED
@@ -3,19 +3,21 @@
3
3
  - oauth token management
4
4
  - oauth consumer registration
5
5
  - acls i.e. allowed methods per user per uri
6
- - complete user data export
7
- - method filtering on collections
8
- - js functions as observers (validation, mapping, etc.)
6
+ - jsonpath
7
+ - jsonquery
8
+ - jsonschema
9
+ - user lookup via a block for integration with existing stores
9
10
  - custom templates for openid / oauth
10
11
 
11
12
  Backlog
13
+ - method filtering on collections
14
+ - js functions as observers (validation, mapping, etc.)
15
+ - complete user data export
12
16
  - batch updates (post, put, delete)
13
17
  - expect header/100-continue
14
18
  - deployable gem + admin app
15
19
  - cappuccino adapter
16
20
  - sproutcore adapter
17
- - tokyocabinet
18
- - ldap adapter for UserStore
19
21
  - titanium middleware
20
22
  version/upgrade middleware
21
23
  rake automation
data/cloudkit.gemspec CHANGED
@@ -2,8 +2,8 @@ Gem::Specification.new do |s|
2
2
  s.specification_version = 2 if s.respond_to? :specification_version=
3
3
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
4
  s.name = "cloudkit"
5
- s.version = "0.10.1"
6
- s.date = "2008-01-27"
5
+ s.version = "0.11.0"
6
+ s.date = "2008-03-09"
7
7
  s.summary = "An Open Web JSON Appliance."
8
8
  s.description = "An Open Web JSON Appliance."
9
9
  s.authors = ["Jon Crosby"]
@@ -29,10 +29,10 @@ Gem::Specification.new do |s|
29
29
  examples/3.ru
30
30
  examples/4.ru
31
31
  examples/5.ru
32
- examples/6.ru
33
32
  examples/TOC
34
33
  lib/cloudkit.rb
35
34
  lib/cloudkit/constants.rb
35
+ lib/cloudkit/exceptions.rb
36
36
  lib/cloudkit/flash_session.rb
37
37
  lib/cloudkit/oauth_filter.rb
38
38
  lib/cloudkit/oauth_store.rb
@@ -43,42 +43,42 @@ Gem::Specification.new do |s|
43
43
  lib/cloudkit/request.rb
44
44
  lib/cloudkit/service.rb
45
45
  lib/cloudkit/store.rb
46
- lib/cloudkit/store/adapter.rb
47
- lib/cloudkit/store/extraction_view.rb
46
+ lib/cloudkit/store/memory_table.rb
47
+ lib/cloudkit/store/resource.rb
48
48
  lib/cloudkit/store/response.rb
49
49
  lib/cloudkit/store/response_helpers.rb
50
- lib/cloudkit/store/sql_adapter.rb
51
50
  lib/cloudkit/templates/authorize_request_token.erb
52
51
  lib/cloudkit/templates/oauth_descriptor.erb
53
52
  lib/cloudkit/templates/oauth_meta.erb
54
53
  lib/cloudkit/templates/openid_login.erb
55
54
  lib/cloudkit/templates/request_authorization.erb
56
55
  lib/cloudkit/templates/request_token_denied.erb
56
+ lib/cloudkit/uri.rb
57
57
  lib/cloudkit/user_store.rb
58
58
  lib/cloudkit/util.rb
59
- test/ext_test.rb
60
- test/flash_session_test.rb
61
- test/helper.rb
62
- test/oauth_filter_test.rb
63
- test/oauth_store_test.rb
64
- test/openid_filter_test.rb
65
- test/openid_store_test.rb
66
- test/rack_builder_test.rb
67
- test/request_test.rb
68
- test/service_test.rb
69
- test/store_test.rb
70
- test/user_store_test.rb
71
- test/util_test.rb
59
+ spec/ext_spec.rb
60
+ spec/flash_session_spec.rb
61
+ spec/memory_table_spec.rb
62
+ spec/oauth_filter_spec.rb
63
+ spec/oauth_store_spec.rb
64
+ spec/openid_filter_spec.rb
65
+ spec/openid_store_spec.rb
66
+ spec/rack_builder_spec.rb
67
+ spec/request_spec.rb
68
+ spec/resource_spec.rb
69
+ spec/service_spec.rb
70
+ spec/spec_helper.rb
71
+ spec/store_spec.rb
72
+ spec/uri_spec.rb
73
+ spec/user_store_spec.rb
74
+ spec/util_spec.rb
72
75
  ]
73
- s.test_files = s.files.select {|path| path =~ /^test\/.*_test.rb/}
76
+ s.test_files = s.files.select {|path| path =~ /^spec\/.*_spec.rb/}
74
77
  s.rubyforge_project = "cloudkit"
75
78
  s.rubygems_version = "1.1.1"
76
79
  s.add_dependency 'rack', '~> 0.9'
77
- s.add_dependency 'rack-config', '>= 0.9'
78
80
  s.add_dependency 'uuid', '= 2.0.1'
79
- s.add_dependency 'sequel', '~> 2.9'
80
81
  s.add_dependency 'oauth', '~> 0.3'
81
82
  s.add_dependency 'ruby-openid', '= 2.1.2'
82
83
  s.add_dependency 'json', '= 1.1.3'
83
- s.add_dependency 'sqlite3-ruby', '= 1.2.4'
84
84
  end
data/doc/curl.html CHANGED
@@ -38,13 +38,13 @@ If you haven't already installed the gem:
38
38
  </p>
39
39
 
40
40
  <p>
41
- If you already have the gem, make sure you're running the latest version (0.10.0):
41
+ If you already have the gem, make sure you're running the latest version (0.11.0):
42
42
  <div class="code">
43
43
  $ gem list cloudkit<br/>
44
44
  cloudkit (0.9.1) &lt;-- need to upgrade<br/>
45
45
  $ gem update cloudkit<br/>
46
46
  $ gem list cloudkit<br/>
47
- cloudkit (0.10.0, 0.9.1) &lt;-- 0.10.0 is now in the list
47
+ cloudkit (0.11.0, 0.9.1) &lt;-- 0.11.0 is now in the list
48
48
  </div>
49
49
  </p>
50
50
 
data/doc/index.html CHANGED
@@ -24,7 +24,7 @@
24
24
  </div>
25
25
  <div class="meta">
26
26
  <p class="wrapper">
27
- Version 0.10.1 released. Install with <em>gem install cloudkit</em>.
27
+ Version 0.11 released with Tokyo Cabinet support. Install with <em>gem install cloudkit</em>.
28
28
  </p>
29
29
  </div>
30
30
  <div class="wrapper intro-row">
@@ -36,13 +36,11 @@
36
36
  </div>
37
37
  <div class="column-two">
38
38
  <p>
39
- CloudKit provides RESTful JSON storage with optional OpenID and OAuth
40
- support, including OAuth Discovery. Stored entities are versioned.
41
- Services manage their own storage and do not require schema updates
42
- when models change.
39
+ CloudKit provides schema-free, auto-versioned, RESTful JSON storage with optional OpenID and OAuth
40
+ support, including OAuth Discovery.
43
41
  </p>
44
42
  <p>
45
- CloudKit is Rack middleware and as such can be used on its own or
43
+ CloudKit is Rack middleware. It can be used on its own or
46
44
  alongside other Rack-based applications or middleware components such
47
45
  as Rails, Merb or Sinatra.
48
46
  </p>
data/examples/5.ru CHANGED
@@ -1,8 +1,7 @@
1
1
  $:.unshift File.expand_path(File.dirname(__FILE__)) + '/../lib'
2
2
  require 'cloudkit'
3
- use Rack::Config do |env|
4
- env[CLOUDKIT_STORAGE_URI] = 'sqlite://example.db'
5
- end
3
+ require 'rufus/tokyo' # gem install rufus-tokyo
4
+ CloudKit.setup_storage_adapter(Rufus::Tokyo::Table.new('cloudkit.tdb'))
6
5
  use Rack::Session::Pool
7
6
  use CloudKit::OAuthFilter
8
7
  use CloudKit::OpenIDFilter
data/examples/TOC CHANGED
@@ -12,6 +12,4 @@ removed.
12
12
 
13
13
  4. Notes with OAuth - Same as #1 using only OAuth.
14
14
 
15
- 5. SQLite Notes - Same as #2 with a SQLite file store.
16
-
17
- 6. MySQL Notes - Same as #2 with MySQL store.
15
+ 5. Tokyo Notes - Same as #2 with a Tokyo Cabinet Table store.
data/lib/cloudkit.rb CHANGED
@@ -3,23 +3,22 @@ require 'erb'
3
3
  require 'json'
4
4
  require 'md5'
5
5
  require 'openid'
6
- require 'sequel'
7
6
  require 'time'
8
7
  require 'uuid'
9
8
  require 'rack'
10
- require 'rack/config'
11
9
  require 'oauth'
12
10
  require 'oauth/consumer'
13
11
  require 'oauth/request_proxy/rack_request'
14
12
  require 'oauth/server'
15
13
  require 'oauth/signature'
16
14
  require 'cloudkit/constants'
15
+ require 'cloudkit/exceptions'
17
16
  require 'cloudkit/util'
18
- require 'cloudkit/store/adapter'
19
- require 'cloudkit/store/extraction_view'
17
+ require 'cloudkit/uri'
18
+ require 'cloudkit/store/memory_table'
19
+ require 'cloudkit/store/resource'
20
20
  require 'cloudkit/store/response'
21
21
  require 'cloudkit/store/response_helpers'
22
- require 'cloudkit/store/sql_adapter'
23
22
  require 'cloudkit/store'
24
23
  require 'cloudkit/flash_session'
25
24
  require 'cloudkit/oauth_filter'
@@ -35,7 +34,19 @@ require 'cloudkit/user_store'
35
34
  include CloudKit::Constants
36
35
 
37
36
  module CloudKit
38
- VERSION = '0.10.1'
37
+ VERSION = '0.11.0'
38
+
39
+ # Sets up the storage adapter. Defaults to development-time
40
+ # CloudKit::MemoryTable. Also supports Rufus Tokyo Table instances. See the
41
+ # examples directory for a demonstration.
42
+ def self.setup_storage_adapter(adapter_instance=nil)
43
+ @storage_adapter = adapter_instance || CloudKit::MemoryTable.new
44
+ end
45
+
46
+ # Return the shared storage adapter.
47
+ def self.storage_adapter
48
+ @storage_adapter
49
+ end
39
50
  end
40
51
 
41
52
  class Object
@@ -52,7 +63,6 @@ class Hash
52
63
  # Hash.
53
64
  def filter_merge!(other={})
54
65
  other.each_pair{|k,v| self.merge!(k => v) unless v.nil?}
55
-
56
66
  self
57
67
  end
58
68
 
@@ -61,7 +71,6 @@ class Hash
61
71
  if self.has_key? oldkey
62
72
  self[newkey] = self.delete(oldkey)
63
73
  end
64
-
65
74
  nil
66
75
  end
67
76
 
@@ -69,7 +78,6 @@ class Hash
69
78
  def excluding(*keys)
70
79
  trimmed = self.dup
71
80
  keys.each{|k| trimmed.delete(k)}
72
-
73
81
  trimmed
74
82
  end
75
83
  end
@@ -79,7 +87,6 @@ class Array
79
87
  # Return a new Array, excluding the specified list of values.
80
88
  def excluding(*keys)
81
89
  trimmed = self.dup
82
-
83
90
  trimmed - keys
84
91
  end
85
92
  end
@@ -23,9 +23,6 @@ module CloudKit
23
23
  # The 'via' key for the OpenID filter.
24
24
  CLOUDKIT_OPENID_FILTER_KEY = 'cloudkit.filter.openid'.freeze
25
25
 
26
- # The key used to store the shared storage URI for the stack.
27
- CLOUDKIT_STORAGE_URI = 'cloudkit.storage.uri'.freeze
28
-
29
26
  # The key for the login URL used in OpenID and OAuth middleware
30
27
  # components.
31
28
  CLOUDKIT_LOGIN_URL = 'cloudkit.filter.openid.url.login'.freeze
@@ -33,8 +30,5 @@ module CloudKit
33
30
  # The key for the logout URL used in OpenID and OAuth middleware
34
31
  # components.
35
32
  CLOUDKIT_LOGOUT_URL = 'cloudkit.filter.openid.url.logout'.freeze
36
-
37
- # The outer namespace key for the JSON store.
38
- CLOUDKIT_STORE = :cloudkit_json_store.freeze
39
33
  end
40
34
  end
@@ -0,0 +1,10 @@
1
+ module CloudKit
2
+
3
+ # HistoricalIntegrityViolation exceptions are raised when an attempt is made
4
+ # to modify an archived or deleted version of a resource.
5
+ class HistoricalIntegrityViolation < Exception; end
6
+
7
+ # InvalidURIFormat exceptions are raised during attempts to get or generate
8
+ # cannonical URIs from non-collection or non-resource URIs.
9
+ class InvalidURIFormat < Exception; end
10
+ end
@@ -14,9 +14,7 @@ module CloudKit
14
14
 
15
15
  # Access a value, then forget it.
16
16
  def [](k)
17
- v = @values[k]
18
- @values[k] = nil
19
- v
17
+ @values.delete(k)
20
18
  end
21
19
  end
22
20
  end
@@ -31,7 +31,7 @@ module CloudKit
31
31
 
32
32
  def call(env)
33
33
  @@lock.synchronize do
34
- @@store = OAuthStore.new(env[CLOUDKIT_STORAGE_URI])
34
+ @@store = OAuthStore.new
35
35
  end unless @@store
36
36
 
37
37
  request = Request.new(env)
@@ -82,8 +82,7 @@ module CloudKit
82
82
  def create_request_token(request)
83
83
  return challenge(request, 'invalid nonce') unless valid_nonce?(request)
84
84
 
85
- consumer_result = @@store.get(
86
- "/cloudkit_oauth_consumers/#{request[:oauth_consumer_key]}")
85
+ consumer_result = @@store.get("/cloudkit_oauth_consumers/#{request[:oauth_consumer_key]}")
87
86
  unless consumer_result.status == 200
88
87
  return challenge(request, 'invalid consumer')
89
88
  end
@@ -105,8 +104,7 @@ module CloudKit
105
104
  def request_authorization(request)
106
105
  return login_redirect(request) unless request.current_user
107
106
 
108
- request_token_result = @@store.get(
109
- "/cloudkit_oauth_request_tokens/#{request[:oauth_token]}")
107
+ request_token_result = @@store.get("/cloudkit_oauth_request_tokens/#{request[:oauth_token]}")
110
108
  unless request_token_result.status == 200
111
109
  return challenge(request, 'invalid request token')
112
110
  end
@@ -118,8 +116,7 @@ module CloudKit
118
116
  def authorize_request_token(request)
119
117
  return login_redirect(request) unless request.current_user
120
118
 
121
- request_token_response = @@store.get(
122
- "/cloudkit_oauth_request_tokens/#{request.last_path_element}")
119
+ request_token_response = @@store.get("/cloudkit_oauth_request_tokens/#{request.last_path_element}")
123
120
  request_token = request_token_response.parsed_content
124
121
  if request_token['authorized_at']
125
122
  return challenge(request, 'invalid request token')
@@ -138,8 +135,7 @@ module CloudKit
138
135
  def deny_request_token(request)
139
136
  return login_redirect(request) unless request.current_user
140
137
 
141
- request_token_response = @@store.get(
142
- "/cloudkit_oauth_request_tokens/#{request.last_path_element}")
138
+ request_token_response = @@store.get("/cloudkit_oauth_request_tokens/#{request.last_path_element}")
143
139
  @@store.delete(
144
140
  "/cloudkit_oauth_request_tokens/#{request.last_path_element}",
145
141
  :etag => request_token_response.etag)
@@ -149,15 +145,13 @@ module CloudKit
149
145
  def create_access_token(request)
150
146
  return challenge(request, 'invalid nonce') unless valid_nonce?(request)
151
147
 
152
- consumer_response = @@store.get(
153
- "/cloudkit_oauth_consumers/#{request[:oauth_consumer_key]}")
148
+ consumer_response = @@store.get("/cloudkit_oauth_consumers/#{request[:oauth_consumer_key]}")
154
149
  unless consumer_response.status == 200
155
150
  return challenge(request, 'invalid consumer')
156
151
  end
157
152
 
158
153
  consumer = consumer_response.parsed_content
159
- request_token_response = @@store.get(
160
- "/cloudkit_oauth_request_tokens/#{request[:oauth_token]}")
154
+ request_token_response = @@store.get("/cloudkit_oauth_request_tokens/#{request[:oauth_token]}")
161
155
  unless request_token_response.status == 200
162
156
  return challenge(request, 'invalid request token')
163
157
  end
@@ -180,9 +174,7 @@ module CloudKit
180
174
  :consumer_key => request[:oauth_consumer_key],
181
175
  :consumer_secret => consumer['secret'],
182
176
  :user_id => request_token['user_id'])
183
- @@store.put(
184
- "/cloudkit_oauth_tokens/#{token_id}",
185
- :json => token_data)
177
+ @@store.put("/cloudkit_oauth_tokens/#{token_id}", :json => token_data)
186
178
  @@store.delete(
187
179
  "/cloudkit_oauth_request_tokens/#{request[:oauth_token]}",
188
180
  :etag => request_token_response.etag)