cloudkit 0.9.1 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -24,7 +24,7 @@
24
24
  </div>
25
25
  <div class="meta">
26
26
  <p class="wrapper">
27
- Version 0.9.1 released. Install with <em>gem install cloudkit</em>.
27
+ Version 0.10.0 <a href="http://blog.joncrosby.me">released</a>. Install with <em>gem install cloudkit</em>.
28
28
  </p>
29
29
  </div>
30
30
  <div class="wrapper intro-row">
@@ -107,6 +107,59 @@
107
107
  <span class="definition">uri-list:</span> an array of document URIs within the given collection. If less than the total available are supplied in the uris element, additional requests can be made, specifying the offset and limit query parameters. Future versions of CloudKit may page result sets to optimize performance.
108
108
  </p>
109
109
 
110
+ <div class="uri-template">
111
+ GET /%collection%/_resolved
112
+ </div>
113
+ <p>
114
+ List the objects and metadata contained within a collection. This effectively provides the
115
+ same result as first listing the URIs using GET /%collection% followed by
116
+ performing a GET /%collection%/%uuid% for each URI.
117
+ </p>
118
+
119
+ <p>
120
+ <span class="spec-header">URI Variables:</span><br/>
121
+ <span class="definition">collection:</span> a named collection of JSON documents.
122
+ </p>
123
+
124
+ <p>
125
+ <span class="spec-header">Optional Query Parameters:</span><br/>
126
+ <span class="definition">offset:</span> 0-based starting index for the list of results. Default is 0.<br/>
127
+ <span class="definition">limit:</span> maximum number of results to return. Default is undefined, meaning unlimited.
128
+ </p>
129
+
130
+ <p>
131
+ <span class="spec-header">Response:</span><br/>
132
+ {<br/>
133
+ "total":%total-results%,<br/>
134
+ "offset":%offset-value%,<br/>
135
+ "documents":[%document-list%]<br/>
136
+ }
137
+ </p>
138
+
139
+ <p>
140
+ <span class="spec-header">Response Variables:</span><br/>
141
+ <span class="definition">total-results:</span> the total number of available results in the complete set, calculated prior applying any provided limits.<br/>
142
+ <span class="definition">offset-value:</span> integer representing the offset.<br/>
143
+ <span class="definition">document-list:</span> an array of documents and their metadata within the given collection. If less than the total available are supplied in the documents element, additional requests can be made, specifying the offset and limit query parameters. Future versions of CloudKit may page result sets to optimize performance.
144
+ </p>
145
+
146
+ <p>
147
+ <span class="spec-header">Document:</span><br/>
148
+ {<br/>
149
+ "etag":%etag%,<br/>
150
+ "last_modified":%last-modified%,<br/>
151
+ "uri":%uri-value%,<br/>
152
+ "document":%json-document%<br/>
153
+ }
154
+ </p>
155
+
156
+ <p>
157
+ <span class="spec-header">Document Variables:</span><br/>
158
+ <span class="definition">etag:</span> the value of the ETag normally returned in a response header when requesting the individual document.<br/>
159
+ <span class="definition">last-modified:</span> the value of the Last-Modified timestamp normally returned in a response header when requesting the individual document.<br/>
160
+ <span class="definition">uri-value:</span> the URI for the individual document relative to the service root.<br/>
161
+ <span class="definition">json-document:</span> a valid JSON document.
162
+ </p>
110
163
 
111
164
  <div class="uri-template">
112
165
  GET /%collection%/%uuid%
@@ -167,6 +220,62 @@
167
220
  <span class="definition">uri-list:</span> an array of document URIs within the given collection. If less than the total available are supplied in the uris element, additional requests can be made, specifying the offset and limit query parameters. Future versions of CloudKit may page result sets to optimize performance.
168
221
  </p>
169
222
 
223
+ <div class="uri-template">
224
+ GET /%collection%/%uuid%/versions/_resolved
225
+ </div>
226
+ <p>
227
+ List the objects and metadata contained within a collection of document versions.
228
+ This effectively provides the same result as first listing the URIs using
229
+ GET /%collection%/%uuid%/versions followed by performing a
230
+ GET /%collection%/%uuid%/versions/%etag% for each URI.
231
+ </p>
232
+
233
+ <p>
234
+ <span class="spec-header">URI Variables:</span><br/>
235
+ <span class="definition">collection:</span> a named collection of documents.<br/>
236
+ <span class="definition">uuid:</span> the unique key for a JSON document.
237
+ </p>
238
+
239
+ <p>
240
+ <span class="spec-header">Optional Query Parameters:</span><br/>
241
+ <span class="definition">offset:</span> 0-based starting index for the list of results. Default is 0.<br/>
242
+ <span class="definition">limit:</span> maximum number of results to return. Default is undefined, meaning unlimited.
243
+ </p>
244
+
245
+ <p>
246
+ <span class="spec-header">Response:</span><br/>
247
+ {<br/>
248
+ "total":%total-results%,<br/>
249
+ "offset":%offset-value%,<br/>
250
+ "documents":[%document-list%]<br/>
251
+ }
252
+ </p>
253
+
254
+ <p>
255
+ <span class="spec-header">Response Variables:</span><br/>
256
+ <span class="definition">total-results:</span> the total number of available results in the complete set, calculated prior applying any provided limits.<br/>
257
+ <span class="definition">offset-value:</span> integer representing the offset.<br/>
258
+ <span class="definition">document-list:</span> an array of documents and their metadata within the given collection. If less than the total available are supplied in the documents element, additional requests can be made, specifying the offset and limit query parameters. Future versions of CloudKit may page result sets to optimize performance.
259
+ </p>
260
+
261
+ <p>
262
+ <span class="spec-header">Document:</span><br/>
263
+ {<br/>
264
+ "etag":%etag%,<br/>
265
+ "last_modified":%last-modified%,<br/>
266
+ "uri":%uri-value%,<br/>
267
+ "document":%json-document%<br/>
268
+ }
269
+ </p>
270
+
271
+ <p>
272
+ <span class="spec-header">Document Variables:</span><br/>
273
+ <span class="definition">etag:</span> the value of the ETag normally returned in a response header when requesting the individual document.<br/>
274
+ <span class="definition">last-modified:</span> the value of the Last-Modified timestamp normally returned in a response header when requesting the individual document.<br/>
275
+ <span class="definition">uri-value:</span> the URI for the individual document relative to the service root.<br/>
276
+ <span class="definition">json-document:</span> a valid JSON document.
277
+ </p>
278
+
170
279
  <div class="uri-template">
171
280
  GET /%collection%/%uuid%/versions/%etag%
172
281
  </div>
@@ -3,4 +3,4 @@ require 'cloudkit'
3
3
  use Rack::Session::Pool
4
4
  use CloudKit::OpenIDFilter
5
5
  use CloudKit::Service, :collections => [:notes]
6
- run lambda{|env| [200, {'Content-Type' => 'text/html'}, ['HELLO']]}
6
+ run lambda{|env| [200, {'Content-Type' => 'text/html', 'Content-Length' => '5'}, ['HELLO']]}
@@ -2,4 +2,4 @@ $:.unshift File.expand_path(File.dirname(__FILE__)) + '/../lib'
2
2
  require 'cloudkit'
3
3
  use CloudKit::OAuthFilter
4
4
  use CloudKit::Service, :collections => [:notes]
5
- run lambda{|env| [200, {'Content-Type' => 'text/html'}, ['HELLO']]}
5
+ run lambda{|env| [200, {'Content-Type' => 'text/html', 'Content-Length' => '5'}, ['HELLO']]}
@@ -1,10 +1,10 @@
1
1
  $:.unshift File.expand_path(File.dirname(__FILE__)) + '/../lib'
2
2
  require 'cloudkit'
3
3
  use Rack::Config do |env|
4
- env['cloudkit.storage.uri'] = 'sqlite://example.db'
4
+ env[CLOUDKIT_STORAGE_URI] = 'sqlite://example.db'
5
5
  end
6
6
  use Rack::Session::Pool
7
7
  use CloudKit::OAuthFilter
8
8
  use CloudKit::OpenIDFilter
9
9
  use CloudKit::Service, :collections => [:notes]
10
- run lambda{|env| [200, {'Content-Type' => 'text/html'}, ['HELLO']]}
10
+ run lambda{|env| [200, {'Content-Type' => 'text/html', 'Content-Length' => '5'}, ['HELLO']]}
@@ -1,10 +1,10 @@
1
1
  $:.unshift File.expand_path(File.dirname(__FILE__)) + '/../lib'
2
2
  require 'cloudkit'
3
3
  use Rack::Config do |env|
4
- env['cloudkit.storage.uri'] = 'mysql://user:pass@localhost/cloudkit_example'
4
+ env[CLOUDKIT_STORAGE_URI] = 'mysql://user:pass@localhost/cloudkit_example'
5
5
  end
6
6
  use Rack::Session::Pool
7
7
  use CloudKit::OAuthFilter
8
8
  use CloudKit::OpenIDFilter
9
9
  use CloudKit::Service, :collections => [:notes]
10
- run lambda{|env| [200, {'Content-Type' => 'text/html'}, ['HELLO']]}
10
+ run lambda{|env| [200, {'Content-Type' => 'text/html', 'Content-Length' => '5'}, ['HELLO']]}
@@ -14,6 +14,7 @@ require 'oauth/consumer'
14
14
  require 'oauth/request_proxy/rack_request'
15
15
  require 'oauth/server'
16
16
  require 'oauth/signature'
17
+ require 'cloudkit/constants'
17
18
  require 'cloudkit/util'
18
19
  require 'cloudkit/store/adapter'
19
20
  require 'cloudkit/store/extraction_view'
@@ -27,12 +28,17 @@ require 'cloudkit/oauth_store'
27
28
  require 'cloudkit/openid_filter'
28
29
  require 'cloudkit/openid_store'
29
30
  require 'cloudkit/rack/builder'
30
- require 'cloudkit/rack/lint'
31
31
  require 'cloudkit/rack/router'
32
32
  require 'cloudkit/request'
33
33
  require 'cloudkit/service'
34
34
  require 'cloudkit/user_store'
35
35
 
36
+ include CloudKit::Constants
37
+
38
+ module CloudKit
39
+ VERSION = '0.10.0'
40
+ end
41
+
36
42
  class Object
37
43
 
38
44
  # Execute a method if it exists.
@@ -0,0 +1,40 @@
1
+ module CloudKit
2
+ module Constants
3
+
4
+ # The key used to store the authenticated user.
5
+ CLOUDKIT_AUTH_KEY = 'cloudkit.user'.freeze
6
+
7
+ # The key used to indicate the presence of auth in a stack.
8
+ CLOUDKIT_AUTH_PRESENCE = 'cloudkit.auth'.freeze
9
+
10
+ # The key used to store auth challenge headers shared between
11
+ # OpenID and OAuth middleware.
12
+ CLOUDKIT_AUTH_CHALLENGE = 'cloudkit.challenge'.freeze
13
+
14
+ # The 'via' key used to announce and track upstream middleware.
15
+ CLOUDKIT_VIA = 'cloudkit.via'.freeze
16
+
17
+ # The key used to store the 'flash' in the session.
18
+ CLOUDKIT_FLASH = 'cloudkit.flash'.freeze
19
+
20
+ # The 'via' key for the OAuth filter.
21
+ CLOUDKIT_OAUTH_FILTER_KEY = 'cloudkit.filter.oauth'.freeze
22
+
23
+ # The 'via' key for the OpenID filter.
24
+ CLOUDKIT_OPENID_FILTER_KEY = 'cloudkit.filter.openid'.freeze
25
+
26
+ # The key used to store the shared storage URI for the stack.
27
+ CLOUDKIT_STORAGE_URI = 'cloudkit.storage.uri'.freeze
28
+
29
+ # The key for the login URL used in OpenID and OAuth middleware
30
+ # components.
31
+ CLOUDKIT_LOGIN_URL = 'cloudkit.filter.openid.url.login'.freeze
32
+
33
+ # The key for the logout URL used in OpenID and OAuth middleware
34
+ # components.
35
+ 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
+ end
40
+ end
@@ -31,11 +31,11 @@ module CloudKit
31
31
 
32
32
  def call(env)
33
33
  @@lock.synchronize do
34
- @@store = OAuthStore.new(env[storage_uri_key])
34
+ @@store = OAuthStore.new(env[CLOUDKIT_STORAGE_URI])
35
35
  end unless @@store
36
36
 
37
37
  request = Request.new(env)
38
- request.announce_auth(oauth_filter_key)
38
+ request.announce_auth(CLOUDKIT_OAUTH_FILTER_KEY)
39
39
  return xrds_location(request) if oauth_disco_draft2_xrds?(request)
40
40
  return @app.call(env) if request.path_info == '/'
41
41
 
@@ -99,7 +99,7 @@ module CloudKit
99
99
  @@store.put(
100
100
  "/cloudkit_oauth_request_tokens/#{token_id}",
101
101
  :json => request_token)
102
- [201, {'Content-Type' => 'text/html'}, ["oauth_token=#{token_id}&oauth_token_secret=#{secret}"]]
102
+ Rack::Response.new("oauth_token=#{token_id}&oauth_token_secret=#{secret}", 201).finish
103
103
  end
104
104
 
105
105
  def request_authorization(request)
@@ -186,7 +186,7 @@ module CloudKit
186
186
  @@store.delete(
187
187
  "/cloudkit_oauth_request_tokens/#{request[:oauth_token]}",
188
188
  :etag => request_token_response.etag)
189
- [201, {'Content-Type' => 'text/html'}, ["oauth_token=#{token_id}&oauth_token_secret=#{secret}"]]
189
+ Rack::Response.new("oauth_token=#{token_id}&oauth_token_secret=#{secret}", 201).finish
190
190
  end
191
191
 
192
192
  def inject_user_or_challenge(request)
@@ -222,11 +222,11 @@ module CloudKit
222
222
  end
223
223
 
224
224
  def inject_challenge(request)
225
- request.env[challenge_key] = challenge_headers(request)
225
+ request.env[CLOUDKIT_AUTH_CHALLENGE] = challenge_headers(request)
226
226
  end
227
227
 
228
- def challenge(request, message)
229
- [401, challenge_headers(request), [message || '']]
228
+ def challenge(request, message='')
229
+ Rack::Response.new(message, 401, challenge_headers(request)).finish
230
230
  end
231
231
 
232
232
  def challenge_headers(request)
@@ -243,7 +243,7 @@ module CloudKit
243
243
 
244
244
  def login_redirect(request)
245
245
  request.session['return_to'] = request.url if request.session
246
- [302, {'Location' => request.login_url, 'Content-Type' => 'text/html'}, []]
246
+ Rack::Response.new([], 302, {'Location' => request.login_url}).finish
247
247
  end
248
248
 
249
249
  def load_user_from_session(request)
@@ -264,7 +264,7 @@ module CloudKit
264
264
 
265
265
  def xrds_location(request)
266
266
  # Current OAuth Discovery Draft 2 / XRDS-Simple 1.0, Section 5.1.2
267
- [200, {'X-XRDS-Location' => "#{request.scheme}://#{request.env['HTTP_HOST']}/oauth"}, []]
267
+ Rack::Response.new([], 200, {'X-XRDS-Location' => "#{request.scheme}://#{request.env['HTTP_HOST']}/oauth"}).finish
268
268
  end
269
269
 
270
270
  def get_descriptor(request)
@@ -1,5 +1,5 @@
1
1
  module CloudKit
2
-
2
+
3
3
  # An OAuthStore is a thin abstraction around CloudKit::Store, providing
4
4
  # consistent collection names, and allowing automatic migrations in later
5
5
  # releases if needed.
@@ -35,10 +35,6 @@ module CloudKit
35
35
  @@store.delete(uri, options)
36
36
  end
37
37
 
38
- def resolve_uris(uris) #:nodoc:
39
- @@store.resolve_uris(uris)
40
- end
41
-
42
38
  def reset! #:nodoc:
43
39
  @@store.reset!
44
40
  end
@@ -20,13 +20,13 @@ module CloudKit
20
20
 
21
21
  def call(env)
22
22
  @@lock.synchronize do
23
- @@store = OpenIDStore.new(env[storage_uri_key])
24
- @users = UserStore.new(env[storage_uri_key])
23
+ @@store = OpenIDStore.new(env[CLOUDKIT_STORAGE_URI])
24
+ @users = UserStore.new(env[CLOUDKIT_STORAGE_URI])
25
25
  @@store.get_association('x') rescue nil # refresh sqlite3
26
26
  end unless @@store
27
27
 
28
28
  request = Request.new(env)
29
- request.announce_auth(openid_filter_key)
29
+ request.announce_auth(CLOUDKIT_OPENID_FILTER_KEY)
30
30
 
31
31
  case request
32
32
  when r(:get, request.login_url); request_login(request)
@@ -37,18 +37,18 @@ module CloudKit
37
37
  if (root_request?(request) || valid_auth_key?(request) || logged_in?(request))
38
38
  @app.call(env)
39
39
  else
40
- if request.env[challenge_key]
40
+ if request.env[CLOUDKIT_AUTH_CHALLENGE]
41
41
  store_location(request)
42
42
  erb(
43
43
  request,
44
44
  :openid_login,
45
- request.env[challenge_key].merge('Content-Type' => 'text/html'),
45
+ request.env[CLOUDKIT_AUTH_CHALLENGE].merge('Content-Type' => 'text/html'),
46
46
  401)
47
- elsif !request.via.include?(oauth_filter_key)
47
+ elsif !request.via.include?(CLOUDKIT_OAUTH_FILTER_KEY)
48
48
  store_location(request)
49
49
  login_redirect(request)
50
50
  else
51
- [500, {}, ['server misconfigured']]
51
+ Rack::Response.new('server misconfigured', 500).finish
52
52
  end
53
53
  end
54
54
  end
@@ -63,7 +63,7 @@ module CloudKit
63
63
  json = JSON.generate(user)
64
64
  @users.put(user_uri, :etag => result.etag, :json => json)
65
65
 
66
- request.env[auth_key] = nil
66
+ request.env[CLOUDKIT_AUTH_KEY] = nil
67
67
  request.flash['info'] = 'You have been logged out.'
68
68
  response = Rack::Response.new(
69
69
  [],
@@ -86,7 +86,7 @@ module CloudKit
86
86
  end
87
87
 
88
88
  redirect_url = response.redirect_url(base_url(request), full_url(request))
89
- [302, {'Location' => redirect_url, 'Content-Type' => 'text/html'}, []]
89
+ Rack::Response.new([], 302, {'Location' => redirect_url}).finish
90
90
  end
91
91
 
92
92
  def complete_openid_login(request)
@@ -142,7 +142,7 @@ module CloudKit
142
142
  end
143
143
 
144
144
  def login_redirect(request)
145
- [302, {'Location' => request.login_url, 'Content-Type' => 'text/html'}, []]
145
+ Rack::Response.new([], 302, {'Location' => request.login_url}).finish
146
146
  end
147
147
 
148
148
  def base_url(request)
@@ -172,7 +172,7 @@ module CloudKit
172
172
  end
173
173
 
174
174
  def valid_auth_key?(request)
175
- request.env[auth_key] && request.env[auth_key] != ''
175
+ request.env[CLOUDKIT_AUTH_KEY] && request.env[CLOUDKIT_AUTH_KEY] != ''
176
176
  end
177
177
 
178
178
  def openid_consumer(request)
@@ -8,9 +8,9 @@ module Rack #:nodoc:
8
8
  def to_app
9
9
  default_app = lambda do |env|
10
10
  if (env['PATH_INFO'] == '/')
11
- [200, {'Content-Type' => 'text/html'}, [welcome]]
11
+ Rack::Response.new(welcome).finish
12
12
  else
13
- [404, {'Content-Type' => 'text/html'}, []]
13
+ Rack::Response.new('not found', 404).finish
14
14
  end
15
15
  end
16
16
  @ins << default_app if @last_cloudkit_id == @ins.last.object_id
@@ -51,7 +51,7 @@ doc = <<HTML
51
51
  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
52
52
  <head>
53
53
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
54
- <title>CloudKit via cURL</title>
54
+ <title>CloudKit</title>
55
55
  <style type="text/css">
56
56
  body {
57
57
  font-family: 'Helvetica', 'Arial', san-serif;
@@ -14,6 +14,12 @@ module CloudKit
14
14
  @cloudkit_params ||= cloudkit_params.merge(oauth_header_params)
15
15
  end
16
16
 
17
+ # Return the JSON content from the request body
18
+ def json
19
+ self.body.rewind
20
+ self.body.read
21
+ end
22
+
17
23
  # Return true if method, path, and required_params match.
18
24
  def match?(method, path, required_params=[])
19
25
  (request_method == method) &&
@@ -78,7 +84,7 @@ module CloudKit
78
84
  # not use the header because the transition from one piece of middleware to
79
85
  # the next does not use HTTP.
80
86
  def via
81
- @env[via_key].split(', ') rescue []
87
+ @env[CLOUDKIT_VIA].split(', ') rescue []
82
88
  end
83
89
 
84
90
  # Return parsed contents of an If-Match header.
@@ -99,29 +105,29 @@ module CloudKit
99
105
  # Add a via entry to the Rack environment.
100
106
  def inject_via(key)
101
107
  items = via << key
102
- @env[via_key] = items.join(', ')
108
+ @env[CLOUDKIT_VIA] = items.join(', ')
103
109
  end
104
110
 
105
111
  # Return the current user URI.
106
112
  def current_user
107
- return nil unless @env[auth_key] && @env[auth_key] != ''
108
- @env[auth_key]
113
+ return nil unless @env[CLOUDKIT_AUTH_KEY] && @env[CLOUDKIT_AUTH_KEY] != ''
114
+ @env[CLOUDKIT_AUTH_KEY]
109
115
  end
110
116
 
111
117
  # Set the current user URI.
112
118
  def current_user=(user)
113
- @env[auth_key] = user
119
+ @env[CLOUDKIT_AUTH_KEY] = user
114
120
  end
115
121
 
116
122
  # Return true if authentication is being used.
117
123
  def using_auth?
118
- @env[auth_presence_key] != nil
124
+ @env[CLOUDKIT_AUTH_PRESENCE] != nil
119
125
  end
120
126
 
121
127
  # Report to downstream middleware that authentication is in use.
122
128
  def announce_auth(via)
123
129
  inject_via(via)
124
- @env[auth_presence_key] = 1
130
+ @env[CLOUDKIT_AUTH_PRESENCE] = 1
125
131
  end
126
132
 
127
133
  # Return the session associated with this request.
@@ -133,27 +139,27 @@ module CloudKit
133
139
  # environment so the OpenID and OAuth middleware can cooperate during the
134
140
  # token authorization step in the OAuth flow.
135
141
  def login_url
136
- @env[login_url_key] || '/login'
142
+ @env[CLOUDKIT_LOGIN_URL] || '/login'
137
143
  end
138
144
 
139
145
  # Set the login url for this request.
140
146
  def login_url=(url)
141
- @env[login_url_key] = url
147
+ @env[CLOUDKIT_LOGIN_URL] = url
142
148
  end
143
149
 
144
150
  # Return the logout URL for this request.
145
151
  def logout_url
146
- @env[logout_url_key] || '/logout'
152
+ @env[CLOUDKIT_LOGOUT_URL] || '/logout'
147
153
  end
148
154
 
149
155
  # Set the logout URL for this request.
150
156
  def logout_url=(url)
151
- @env[logout_url_key] = url
157
+ @env[CLOUDKIT_LOGOUT_URL] = url
152
158
  end
153
159
 
154
160
  # Return the flash session for this request.
155
161
  def flash
156
- session[flash_key] ||= CloudKit::FlashSession.new
162
+ session[CLOUDKIT_FLASH] ||= CloudKit::FlashSession.new
157
163
  end
158
164
  end
159
165
  end