-
Notifications
You must be signed in to change notification settings - Fork 24
Conditional Fetch requests support #39
base: master
Are you sure you want to change the base?
Changes from all commits
46fd9fa
4bbc75b
47b43d4
88d1bca
f7eddda
aeeebdc
2d60100
2625e73
8ebfe8d
fb6fe91
d6e814c
e051ade
c5f5169
fed1fc2
7733e08
9a6bd02
07a62f5
3fc4795
11ac7d0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,9 +17,10 @@ | |
package com.scalapenos.riak | ||
package internal | ||
|
||
private[riak] trait RiakUriSupport { | ||
import spray.http.Uri | ||
import spray.http.Uri._ | ||
private[riak] trait RiakHttpSupport { | ||
import spray.http.{ Uri, HttpHeader, HttpHeaders, EntityTag }, HttpHeaders._, Uri._ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer consistent wildcard imports instead of explicit imports but that's a personal thing and I'm not religious about it. |
||
import DateTimeSupport._ | ||
import RiakBucket._ | ||
|
||
// ========================================================================== | ||
// Query Parameters | ||
|
@@ -37,6 +38,22 @@ private[riak] trait RiakUriSupport { | |
def query = ("returnbody", s"$returnBody") +: Query.Empty | ||
} | ||
|
||
// ========================================================================== | ||
// Conditional Request Parameters Support | ||
// ========================================================================== | ||
|
||
implicit class ConditionalHttpRequestParam(conditionalParam: ConditionalRequestParam) { | ||
def asHttpHeader: HttpHeader = { | ||
conditionalParam match { | ||
case IfModifiedSince(date) ⇒ `If-Modified-Since`(toSprayDateTime(date)) | ||
case IfUnmodifiedSince(date) ⇒ `If-Unmodified-Since`(toSprayDateTime(date)) | ||
case IfMatch(eTag) ⇒ RawHeader("If-Match", eTag.value) // TODO this `If-Match`(EntityTag(eTag)) doesn't work as spray escapes double quotes in ETag value | ||
case IfNotMatch(eTag) ⇒ RawHeader("If-None-Match", eTag.value) // TODO this `If-None-Match`(EntityTag(eTag)) doesn't work as spray escapes double quotes in ETag value | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The TODO comments are slightly confusing. |
||
case _ ⇒ throw new IllegalArgumentException("Unknown conditional request param: cannot convert to HTTP header.") | ||
} | ||
} | ||
} | ||
|
||
// ========================================================================== | ||
// URL building and Query Parameters | ||
// ========================================================================== | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,9 @@ | |
|
||
package com.scalapenos.riak | ||
|
||
import com.scalapenos.riak.RiakBucket.{ IfMatch, IfModifiedSince, IfNotMatch, IfUnmodifiedSince } | ||
import org.joda.time.DateTime | ||
|
||
class RiakBucketSpec extends RiakClientSpecification with RandomKeySupport with RandomBucketSupport { | ||
|
||
"A RiakBucket" should { | ||
|
@@ -100,5 +103,129 @@ class RiakBucketSpec extends RiakClientSpecification with RandomKeySupport with | |
|
||
fetched should beNone | ||
} | ||
|
||
// ============================================================================ | ||
// Conditional requests tests | ||
// ============================================================================ | ||
|
||
"not return back a stored value if 'If-None-Match' condition does not hold for a requested data" in { | ||
val bucket = randomBucket | ||
val key = randomKey | ||
|
||
val storedValue = bucket.storeAndFetch(key, "value").await | ||
|
||
val eTag = storedValue.etag | ||
|
||
bucket.fetch(key, IfNotMatch(eTag)).await must beNone | ||
} | ||
|
||
"return back a stored value if 'If-None-Match' condition holds for requested data" in { | ||
val bucket = randomBucket | ||
val key = randomKey | ||
|
||
val storedValue = bucket.storeAndFetch(key, "value").await | ||
|
||
bucket.fetch(key, IfNotMatch(randomKey)).await must beSome(storedValue) | ||
} | ||
|
||
"not return back a stored value if 'If-Match' condition does not hold for a requested data" in { | ||
val bucket = randomBucket | ||
val key = randomKey | ||
|
||
bucket.storeAndFetch(key, "value").await | ||
|
||
bucket.fetch(key, IfMatch(randomKey)).await must beNone | ||
} | ||
|
||
"return back a stored value if 'If-Match' condition holds for requested data" in { | ||
val bucket = randomBucket | ||
val key = randomKey | ||
|
||
val storedValue = bucket.storeAndFetch(key, "value").await | ||
|
||
val eTag = storedValue.etag | ||
|
||
bucket.fetch(key, IfMatch(eTag)).await must beSome(storedValue) | ||
} | ||
|
||
"not return back a stored value if 'If-Modified-Since' condition does not hold for a requested data" in { | ||
val bucket = randomBucket | ||
val key = randomKey | ||
|
||
val storedValue = bucket.storeAndFetch(key, "value").await | ||
|
||
// Fetch if the value has been modified after store operation | ||
bucket.fetch(key, IfModifiedSince(storedValue.lastModified.plusMillis(1))).await must beNone | ||
} | ||
|
||
"return back a stored value if 'If-Modified-Since' condition holds for requested data" in { | ||
val bucket = randomBucket | ||
val key = randomKey | ||
|
||
val storedValue = bucket.storeAndFetch(key, "value").await | ||
|
||
// Fetch if the value has been modified since before the store operation | ||
bucket.fetch(key, IfModifiedSince(storedValue.lastModified.minusMinutes(5))).await must beSome(storedValue) | ||
} | ||
|
||
"not return back a stored value if 'If-Unmodified-Since' condition does not hold for a requested data" in { | ||
val bucket = randomBucket | ||
val key = randomKey | ||
|
||
val storedValue = bucket.storeAndFetch(key, "value").await | ||
|
||
// Fetch if the value has not been modified since before the store operation | ||
bucket.fetch(key, IfUnmodifiedSince(storedValue.lastModified.minusMinutes(5))).await must beNone | ||
} | ||
|
||
"return back a stored value if 'If-Unmodified-Since' condition holds for requested data" in { | ||
val bucket = randomBucket | ||
val key = randomKey | ||
|
||
val storedValue = bucket.storeAndFetch(key, "value").await | ||
|
||
// Fetch if the value has not been modified since after the store operation | ||
bucket.fetch(key, IfUnmodifiedSince(storedValue.lastModified.plusMinutes(5))).await must beSome(storedValue) | ||
} | ||
|
||
// Combining multiple request conditions | ||
|
||
"support multiple conditional request parameters" in { | ||
val bucket = randomBucket | ||
val key = randomKey | ||
|
||
val storedValue = bucket.storeAndFetch(key, "value").await | ||
|
||
// Fetch a value that hasn't been modified since after the store operation (this condition holds) | ||
// only if it has a different tag (this condition doesn't hold) | ||
bucket.fetch(key, | ||
IfUnmodifiedSince(storedValue.lastModified.plusMillis(1)), | ||
IfNotMatch(storedValue.etag) | ||
).await must beNone | ||
|
||
// Fetch a value if it has the same ETag (this condition holds) | ||
// has been modified since before the store operation (this condition also holds) | ||
bucket.fetch(key, | ||
IfMatch(storedValue.etag), | ||
IfModifiedSince(storedValue.lastModified.minusMillis(1)) | ||
).await must beSome(storedValue) | ||
|
||
// Fetch a value if it has the same ETag (this condition holds) | ||
// and has been modified since after the store operation (this condition doesn't hold) | ||
bucket.fetch(key, | ||
IfMatch(storedValue.etag), | ||
IfModifiedSince(storedValue.lastModified.plusMillis(1)) | ||
).await must beNone | ||
|
||
bucket.fetch(key, | ||
// Repeating the same conditional parameter doesn't change the behaviour | ||
IfNotMatch(storedValue.etag), | ||
IfNotMatch(storedValue.etag)).await must beNone | ||
|
||
bucket.fetch(key, | ||
// Repeating the same conditional parameter doesn't change the behaviour | ||
IfModifiedSince(storedValue.lastModified.minusMinutes(5)), | ||
IfModifiedSince(storedValue.lastModified.minusMinutes(5))).await must beSome(storedValue) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might be better expressed as separate tests. |
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a way to distinguish between the not-found and not-modified case? Perhaps in a different method to retain backwards compatibility.