Skip to content

Commit b640e85

Browse files
author
Matt Chadburn
committed
Split archive in to a self-container application
- Added tests - Moved S3Archive object to services.S3
1 parent cdbb17e commit b640e85

File tree

12 files changed

+218
-21
lines changed

12 files changed

+218
-21
lines changed

applications/conf/routes

-3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,3 @@ GET /$leftSide<[\w\d-]*(/[\w\d-]*)?(/[\w\d-]*)?>+$rightSide<[\w\d-]*(/[\w
3636
# Interactive paths
3737
GET /$path<[\w\d-]*(/[\w\d-]*)?/(interactive|ng-interactive)/.*>.json controllers.InteractiveController.renderInteractiveJson(path)
3838
GET /$path<[\w\d-]*(/[\w\d-]*)?/(interactive|ng-interactive)/.*> controllers.InteractiveController.renderInteractive(path)
39-
40-
# 404
41-
GET /404/$path<.*> controllers.ArchiveController.render(path)

applications/app/controllers/ArchiveController.scala renamed to archive/app/controllers/ArchiveController.scala

+4-17
Original file line numberDiff line numberDiff line change
@@ -8,38 +8,25 @@ import services.S3
88
import services.DynamoDB
99
import play.api.templates.Html
1010

11-
12-
// looks for an archived file in S3 bucket
13-
object S3Archive extends S3 {
14-
override lazy val bucket = "aws-frontend-archive"
15-
def getHtml(path: String) = get(path)
16-
}
17-
18-
1911
object ArchiveController extends Controller with Logging with ExecutionContexts {
2012

21-
// ...
2213
def isRedirect(path: String) = DynamoDB.destinationFor(path)
2314

24-
// do we have an archived copy of this resource?
25-
def existsInS3(path: String) = S3Archive.getHtml(path)
15+
def isArchived(path: String) = services.S3Archive.getHtml(path)
2616

27-
// gets
28-
def render(path: String) = Action { implicit request =>
17+
def lookup(path: String) = Action { implicit request =>
2918

3019
val destination = isRedirect(path)
3120

32-
// redirect -> 301? s3 -> 200? no -> then 404.
21+
// is it a redirect -> 301, if not is it archived -> 200, if not then 404
3322
destination match {
3423
case Some(_) => Redirect(destination.get)
3524
case _ => {
36-
37-
val s3content = existsInS3(path)
25+
val s3content = isArchived(path)
3826
s3content match {
3927
case Some(_) => Ok(s3content.get).as("text/html")
4028
case _ => NotFound
4129
}
42-
4330
}
4431
}
4532
}

archive/conf/application-logger.xml

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<configuration>
2+
3+
<contextName>frontend-archive</contextName>
4+
5+
<appender name="LOGFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
6+
<file>logs/frontend-archive.log</file>
7+
8+
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
9+
<fileNamePattern>logs/frontend-archive.log.%d{yyyy-MM-dd}.gz</fileNamePattern>
10+
<maxHistory>30</maxHistory>
11+
</rollingPolicy>
12+
13+
<encoder>
14+
<pattern>%date [%thread] %-5level %logger{36} - %msg%n%xException{3}</pattern>
15+
</encoder>
16+
</appender>
17+
18+
<appender name="AUDIT" class="ch.qos.logback.core.rolling.RollingFileAppender">
19+
<file>logs/audit.log</file>
20+
21+
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
22+
<fileNamePattern>logs/audit.log.%d{yyyy-MM-dd}.gz</fileNamePattern>
23+
<maxHistory>30</maxHistory>
24+
</rollingPolicy>
25+
26+
<encoder>
27+
<pattern>%date [%thread] %-5level %logger{36} - %msg%n%xException{3}</pattern>
28+
</encoder>
29+
</appender>
30+
31+
<root level="INFO">
32+
<appender-ref ref="LOGFILE"/>
33+
</root>
34+
35+
<logger name="common.Audit" level="TRACE">
36+
<appender-ref ref="AUDIT" />
37+
</logger>
38+
39+
</configuration>

archive/conf/application.conf

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
application : {
2+
# The secret key is used to secure cryptographics functions.
3+
# If you deploy your application to several instances be sure to use the same key!
4+
secret: ${APP_SECRET}
5+
langs: "en"
6+
}
7+
8+
# Define the Global object class for this application, defaults to Global in the root package.
9+
#global: Global
10+
11+
logger: {
12+
# Even though we configure logback using conf/logger.xml, Play still inherits a standard logback configuration
13+
# which defines 'play' and 'application' loggers which create `logs/application.log`. The default configuration
14+
# also logs ROOT at ERROR which is not useful for us.
15+
root: INFO,
16+
play: INFO,
17+
application: DEBUG
18+
}
19+
20+
# Caching for humans.txt
21+
"assets.cache./public/humans.txt"="public, max-age=900"
22+
23+
############################################################
24+
#
25+
# Threadpool config
26+
# see http://www.playframework.com/documentation/2.2.x/ThreadPools
27+
#
28+
############################################################
29+
30+
play {
31+
akka {
32+
akka.loggers = ["akka.event.Logging$DefaultLogger", "akka.event.slf4j.Slf4jLogger"]
33+
loglevel = WARNING
34+
actor {
35+
default-dispatcher = {
36+
fork-join-executor {
37+
parallelism-factor = 1.0
38+
parallelism-max = 24
39+
}
40+
}
41+
java-futures = {
42+
fork-join-executor {
43+
parallelism-factor = 1.0
44+
parallelism-max = 1
45+
}
46+
}
47+
}
48+
}
49+
}

archive/conf/deploy.json

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"packages":{
3+
"frontend-archive":{
4+
"type":"autoscaling",
5+
"apps":[ "frontend::archive" ],
6+
"data":{
7+
"secondsToWait":1200,
8+
"port":18080,
9+
"healthcheckGrace":20,
10+
"bucket":"aws-frontend-artifacts",
11+
"healthcheck_paths":[
12+
"/management/healthcheck"
13+
]
14+
}
15+
},
16+
"frontend-static":{
17+
"type":"aws-s3",
18+
"apps":[ "aws-s3" ],
19+
"data":{
20+
"bucket":"aws-frontend-static",
21+
"cacheControl":"public, max-age=315360000"
22+
}
23+
}
24+
},
25+
"recipes":{
26+
"default":{
27+
"depends" : ["staticFilesUpload", "artifactUpload", "deploy"]
28+
},
29+
"deploy":{
30+
"actionsBeforeApp": ["frontend-archive.deploy"]
31+
},
32+
"artifactUpload":{
33+
"actionsBeforeApp": ["frontend-archive.uploadArtifacts"]
34+
},
35+
"staticFilesUpload":{
36+
"actionsBeforeApp": ["frontend-static.uploadStaticFiles"]
37+
}
38+
}
39+
}

archive/conf/play.plugins

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
100:conf.SwitchBoardPlugin
2+
#1000:com.gu.management.play.InternalManagementPlugin

archive/conf/routes

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Routes
2+
# This file defines all application routes (Higher priority routes first)
3+
# ~~~~
4+
5+
# 404
6+
GET /404/$path<.*> controllers.ArchiveController.lookup(path)

archive/readme.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This frontend-archive server is for accessing content that has been archived.
2+
3+
These archive server has a dependency on s3 and dynamodb.
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package test
2+
3+
import play.api.test._
4+
import play.api.test.Helpers._
5+
import org.scalatest.Matchers
6+
import org.scalatest.FlatSpec
7+
import common.UsesElasticSearch
8+
9+
class ArchiveControllerTest extends FlatSpec with Matchers with UsesElasticSearch {
10+
11+
12+
"Archive Controller" should "return a HTTP 303 when it finds a match in DynamoDB" in Fake {
13+
val url = "www.theguardian.com/media/emailservices/article/0,,1694396,.html"
14+
val result = controllers.ArchiveController.lookup(url)(TestRequest())
15+
header("Location", result) should be(Some("http://www.theguardian.com/media/2006/jan/25/1"))
16+
status(result) should be(303)
17+
}
18+
19+
it should "return a HTTP 200 when it finds a resource in S3" in Fake {
20+
val url = "travel.theguardian.com/askatraveller/0,,345059,00.html"
21+
val result = controllers.ArchiveController.lookup(url)(TestRequest())
22+
status(result) should be(200)
23+
contentType(result) should be("text/html")
24+
}
25+
26+
it should "return a HTTP 404 when it can not find a resource" in Fake {
27+
val url = "www.theguardian.com/i/am/not/here"
28+
val result = controllers.ArchiveController.lookup(url)(TestRequest())
29+
status(result) should be(404)
30+
}
31+
32+
it should "fetch the HTML of a valid archived URL" in Fake {
33+
val result = controllers.ArchiveController.isArchived("travel.theguardian.com/askatraveller/0,,345059,00.html")
34+
result.get should include ("<title>Ask a traveller | guardian.co.uk Travel</title>")
35+
}
36+
37+
it should "return None for a invalid archive URL" in Fake {
38+
val result = controllers.ArchiveController.isArchived("not/here")
39+
result should be(None)
40+
}
41+
42+
it should "fetch the destination of a valid redirected URL" in Fake {
43+
val result = controllers.ArchiveController.isRedirect("www.theguardian.com/media/emailservices/article/0,,1694396,.html")
44+
result should be (Some("http://www.theguardian.com/media/2006/jan/25/1"))
45+
}
46+
47+
it should "return Noen for a path that has not been redirected" in Fake {
48+
val result = controllers.ArchiveController.isRedirect("not/here")
49+
result should be(None)
50+
}
51+
52+
}

archive/test/package.scala

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package test
2+
3+
import java.util.{ List => JList }
4+
import collection.JavaConversions._
5+
6+
object `package` {
7+
8+
object HtmlUnit extends EditionalisedHtmlUnit
9+
10+
object Fake extends FakeApp
11+
12+
implicit class ListString2FirstNonEmpty(list: JList[String]) {
13+
lazy val firstNonEmpty: Option[String] = list find { !_.isEmpty }
14+
}
15+
}

common/app/services/S3.scala

+5
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,8 @@ trait SecureS3Request extends implicits.Dates with Logging {
155155
}
156156

157157
object SecureS3Request extends SecureS3Request
158+
159+
object S3Archive extends S3 {
160+
override lazy val bucket = "aws-frontend-archive"
161+
def getHtml(path: String) = get(path)
162+
}

project/Frontend.scala

+4-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ object Frontend extends Build with Prototypes {
3838
val facia = application("facia").dependsOn(commonWithTests).aggregate(common)
3939
val article = application("article").dependsOn(commonWithTests).aggregate(common)
4040
val applications = application("applications").dependsOn(commonWithTests).aggregate(common)
41+
val archive = application("archive").dependsOn(commonWithTests).aggregate(common)
4142
val sport = application("sport").dependsOn(commonWithTests).aggregate(common).settings(
4243
libraryDependencies += "com.gu" %% "pa-client" % "4.0",
4344
templatesImport ++= Seq(
@@ -125,6 +126,7 @@ object Frontend extends Build with Prototypes {
125126
).dependsOn(
126127
facia,
127128
applications,
129+
archive,
128130
sport,
129131
discussion,
130132
router,
@@ -148,6 +150,7 @@ object Frontend extends Build with Prototypes {
148150
admin,
149151
identity,
150152
commercial,
151-
onward
153+
onward,
154+
archive
152155
)
153156
}

0 commit comments

Comments
 (0)