s3_website_monadic 0.0.14 → 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1e02998ffcdd53134cc43365b7d958d6e5ac84d8
4
- data.tar.gz: 06f5c5be34c990d4d8cef7fc03ed6993680a833c
3
+ metadata.gz: 68d01d8499eba5c41c9b42d0d6ead9ea5386590a
4
+ data.tar.gz: 5a6381dfa127b1f824c1ded97efe0cb390b179ec
5
5
  SHA512:
6
- metadata.gz: 8fd0bfa68863f8c6ac38bf2292deed7e6096e92625905dbf03e92af8c377e120d434e17c657361215ca692b6ba7c67d8efdf4a140b31ce8ba483916cfb5f00d0
7
- data.tar.gz: 0df2a5712cf8fcbb144f586158fe7a6560566907b1864fe6d604c4de2775c43215eb460702bcebffbcc579d283187d450e3c55c317d73472924fb6afcc6f9a4e
6
+ metadata.gz: 1e35433d2fb1ad9ce6f737dcfaba2e0b49ca02ab57ae1092e7370ede5e5b50292e19b50236dff44cf5d8ceb76460ccb25a5c1fd15b78e886954616f037ebdafc
7
+ data.tar.gz: 9bcc6e4226a57017089ddd4e65918a53eee9f3fd3bf3b2c7a2d93542de249fdb4e0c51b762875d2615168721cbcbb847aac31126d23605afe8e46ee7bedca72d
data/s3_website.gemspec CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "s3_website_monadic"
6
- s.version = "0.0.14"
6
+ s.version = "0.0.15"
7
7
  s.platform = Gem::Platform::RUBY
8
8
  s.authors = ["Lauri Lehmijoki"]
9
9
  s.email = ["lauri.lehmijoki@iki.fi"]
@@ -61,7 +61,7 @@ object CloudFront {
61
61
  type CloudFrontClientProvider = (Config) => AmazonCloudFront
62
62
 
63
63
  case class SuccessfulInvalidation(invalidatedItemsCount: Int) extends SuccessReport {
64
- def reportMessage = s"Invalidated ${countToString(invalidatedItemsCount, "item")} on CloudFront"
64
+ def reportMessage = s"Invalidated ${invalidatedItemsCount ofType "item"} on CloudFront"
65
65
  }
66
66
 
67
67
  case class FailedInvalidation(error: Throwable) extends FailureReport {
@@ -71,18 +71,36 @@ object CloudFront {
71
71
  def awsCloudFrontClient(config: Config) =
72
72
  new AmazonCloudFrontClient(new BasicAWSCredentials(config.s3_id, config.s3_secret))
73
73
 
74
- def toInvalidationBatches(pushSuccessReports: Seq[PushSuccessReport])(implicit config: Config): Seq[InvalidationBatch] =
75
- pushSuccessReports
76
- .filter(needsInvalidation) // Assume that redirect objects are never cached.
77
- .map(toInvalidationPath)
78
- .map (applyInvalidateRootSetting)
74
+ def toInvalidationBatches(pushSuccessReports: Seq[PushSuccessReport])(implicit config: Config): Seq[InvalidationBatch] = {
75
+ val invalidationPaths: Seq[String] = {
76
+ def withDefaultPathIfNeeded(paths: Seq[String]) = {
77
+ // This is how we support the Default Root Object @ CloudFront (http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DefaultRootObject.html)
78
+ // We do this more accurately by fetching the distribution config (http://docs.aws.amazon.com/AmazonCloudFront/latest/APIReference/GetConfig.html)
79
+ // and reading the Default Root Object from there.
80
+ val containsPotentialDefaultRootObject = paths
81
+ .exists(
82
+ _
83
+ .replaceFirst("^/", "") // S3 keys do not begin with a slash
84
+ .contains("/") == false // See if the S3 key is a top-level key (i.e., it is not within a directory)
85
+ )
86
+ if (containsPotentialDefaultRootObject) paths :+ "/" else paths
87
+ }
88
+ val paths = pushSuccessReports
89
+ .filter(needsInvalidation) // Assume that redirect objects are never cached.
90
+ .map(toInvalidationPath)
91
+ .map (applyInvalidateRootSetting)
92
+ withDefaultPathIfNeeded(paths)
93
+ }
94
+
95
+ invalidationPaths
79
96
  .grouped(1000) // CloudFront supports max 1000 invalidations in one request (http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html#InvalidationLimits)
80
97
  .map { batchKeys =>
81
98
  new InvalidationBatch() withPaths
82
99
  (new Paths() withItems batchKeys withQuantity batchKeys.size) withCallerReference
83
- s"s3_website gem ${System.currentTimeMillis()}"
100
+ s"s3_website gem ${System.currentTimeMillis()}"
84
101
  }
85
102
  .toSeq
103
+ }
86
104
 
87
105
  def applyInvalidateRootSetting(path: String)(implicit config: Config) =
88
106
  if (config.cloudfront_invalidate_root.exists(_ == true))
@@ -156,11 +156,11 @@ object Push {
156
156
  "There was nothing to push."
157
157
  case PushCounts(updates, newFiles, failures, redirects, deletes) =>
158
158
  val reportClauses: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer()
159
- if (updates > 0) reportClauses += s"Updated ${countToString(updates, "file")}."
160
- if (newFiles > 0) reportClauses += s"Created ${countToString(newFiles, "file")}."
161
- if (failures > 0) reportClauses += s"${countToString(failures, "operation")} failed." // This includes both failed uploads and deletes.
162
- if (redirects > 0) reportClauses += s"Applied ${countToString(redirects, "redirect")}."
163
- if (deletes > 0) reportClauses += s"Deleted ${countToString(deletes, "file")}."
159
+ if (updates > 0) reportClauses += s"Updated ${updates ofType "file"}."
160
+ if (newFiles > 0) reportClauses += s"Created ${newFiles ofType "file"}."
161
+ if (failures > 0) reportClauses += s"${failures ofType "operation"} failed." // This includes both failed uploads and deletes.
162
+ if (redirects > 0) reportClauses += s"Applied ${redirects ofType "redirect"}."
163
+ if (deletes > 0) reportClauses += s"Deleted ${deletes ofType "file"}."
164
164
  reportClauses.mkString(" ")
165
165
  }
166
166
 
@@ -51,8 +51,12 @@ package object website {
51
51
  httpStatusCode.exists(c => c >= 400 && c < 500)
52
52
  }
53
53
 
54
- def countToString(count: Int, singular: String) = {
55
- def plural = s"${singular}s"
56
- s"$count ${if (count > 1) plural else singular}"
54
+ implicit class NumReport(val num: Int) extends AnyVal {
55
+ def ofType(itemType: String) = countToString(num, itemType)
56
+
57
+ private def countToString(count: Int, singular: String) = {
58
+ def plural = s"${singular}s"
59
+ s"$count ${if (count > 1) plural else singular}"
60
+ }
57
61
  }
58
62
  }
@@ -136,11 +136,11 @@ class S3WebsiteSpec extends Specification {
136
136
  "invalidate the updated CloudFront items" in new SiteDirectory with MockAWS {
137
137
  implicit val site = siteWithFiles(
138
138
  config = defaultConfig.copy(cloudfront_distribution_id = Some("EGM1J2JJX9Z")),
139
- localFiles = "test.css" :: "articles/index.html" :: Nil
139
+ localFiles = "css/test.css" :: "articles/index.html" :: Nil
140
140
  )
141
- setOutdatedS3Keys("test.css", "articles/index.html")
141
+ setOutdatedS3Keys("css/test.css", "articles/index.html")
142
142
  Push.pushSite
143
- sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq.sorted must equalTo(("/test.css" :: "/articles/index.html" :: Nil).sorted)
143
+ sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq.sorted must equalTo(("/css/test.css" :: "/articles/index.html" :: Nil).sorted)
144
144
  }
145
145
 
146
146
  "not send CloudFront invalidation requests on new objects" in new SiteDirectory with MockAWS {
@@ -191,23 +191,37 @@ class S3WebsiteSpec extends Specification {
191
191
  Push.pushSite
192
192
  sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq.sorted must equalTo(("/articles/arnold's%20file.html" :: Nil).sorted)
193
193
  }
194
+
195
+ /*
196
+ * Because CloudFront supports Default Root Objects (http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DefaultRootObject.html),
197
+ * we have to guess
198
+ */
199
+ "invalidate the root object '/' if a top-level object is updated or deleted" in new SiteDirectory with MockAWS {
200
+ implicit val site = siteWithFiles(
201
+ config = defaultConfig.copy(cloudfront_distribution_id = Some("EGM1J2JJX9Z")),
202
+ localFiles = "maybe-index.html" :: Nil
203
+ )
204
+ setOutdatedS3Keys("maybe-index.html")
205
+ Push.pushSite
206
+ sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq.sorted must equalTo(("/" :: "/maybe-index.html" :: Nil).sorted)
207
+ }
194
208
  }
195
209
 
196
210
  "cloudfront_invalidate_root: true" should {
197
211
  "convert CloudFront invalidation paths with the '/index.html' suffix into '/'" in new SiteDirectory with MockAWS {
198
212
  implicit val site = siteWithFiles(
199
213
  config = defaultConfig.copy(cloudfront_distribution_id = Some("EGM1J2JJX9Z"), cloudfront_invalidate_root = Some(true)),
200
- localFiles = "index.html" :: "articles/index.html" :: Nil
214
+ localFiles = "articles/index.html" :: Nil
201
215
  )
202
- setOutdatedS3Keys("index.html", "articles/index.html")
216
+ setOutdatedS3Keys("articles/index.html")
203
217
  Push.pushSite
204
- sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq.sorted must equalTo(("/" :: "/articles/" :: Nil).sorted)
218
+ sentInvalidationRequest.getInvalidationBatch.getPaths.getItems.toSeq.sorted must equalTo(("/articles/" :: Nil).sorted)
205
219
  }
206
220
  }
207
221
 
208
222
  "a site with over 1000 items" should {
209
223
  "split the CloudFront invalidation requests into batches of 1000 items" in new SiteDirectory with MockAWS {
210
- val files = (1 to 1002).map { i => s"file-$i"}
224
+ val files = (1 to 1002).map { i => s"lots-of-files/file-$i"}
211
225
  implicit val site = siteWithFiles(
212
226
  config = defaultConfig.copy(cloudfront_distribution_id = Some("EGM1J2JJX9Z")),
213
227
  localFiles = files
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: s3_website_monadic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.14
4
+ version: 0.0.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lauri Lehmijoki
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-08 00:00:00.000000000 Z
11
+ date: 2014-05-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk