Skip to content
This repository has been archived by the owner on Jul 26, 2021. It is now read-only.

Commit

Permalink
Merge pull request #38 from guardian/add-tip-cloud
Browse files Browse the repository at this point in the history
Add tip cloud
  • Loading branch information
Mario Galic committed Jul 30, 2018
2 parents e5cc928 + 10d09ff commit d64cc7e
Show file tree
Hide file tree
Showing 15 changed files with 557 additions and 47 deletions.
4 changes: 1 addition & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ lazy val root = (project in file(".")).settings(
name := "tip",
description := "Scala library for testing in production.",
organization := "com.gu",
scalaVersion := "2.12.4",
scalaVersion := "2.12.6",
libraryDependencies ++= dependencies,
crossScalaVersions ++= Seq("2.11.12"),
sources in doc in Compile := List(),
publishTo := Some(
if (isSnapshot.value) { Opts.resolver.sonatypeSnapshots }
else { Opts.resolver.sonatypeReleases }
),
releaseCrossBuild := true,
releasePublishArtifactsAction := PgpKeys.publishSigned.value,
releaseProcess := Seq[ReleaseStep](
checkSnapshotDependencies,
Expand Down
31 changes: 31 additions & 0 deletions cloud/tip-create-board.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const AWS = require('aws-sdk');
AWS.config.update({region: 'eu-west-1'});
const ddb = new AWS.DynamoDB.DocumentClient();

function registerBoard(sha, board, repo, commitMessage, deployTime) {
return ddb.put(
{
TableName: 'tipcloud',
Item: {
sha: sha,
board: board,
repo: repo,
commitMessage: commitMessage,
deployTime: deployTime
}
}
).promise();
}

exports.handler = (event, context, callback) => {
const body = JSON.parse(event.body);

const board = body.board;
const sha = body.sha;
const repo = body.repo;
const commitMessage = body.commitMessage;
const deployTime = body.deployTime;

registerBoard(sha, board, repo, commitMessage, deployTime)
.then(() => callback(null, {statusCode: 200, body: `{"field": "value"}`}));
};
166 changes: 166 additions & 0 deletions cloud/tip-get-board.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
const AWS = require('aws-sdk');
AWS.config.update({region: 'eu-west-1'});
const ddb = new AWS.DynamoDB.DocumentClient();

function getBoard(sha) {
return ddb.get(
{
TableName : 'tipcloud',
Key: {
sha: sha
}
}
).promise();
}

function renderBoard(data) {
return new Promise((resolve, reject) => {
const pathsWithStatusColour =
data.Item.board
.map(path => {
let colour;
if (path.verified)
colour = `style="background-color:green"`;
else
colour = `style="background-color:grey"`;

return `<span ${colour}>${path.name}</span>`;
})
.toString()
.replace (/,/g, "");

const sha = data.Item.sha;
const repo = data.Item.repo;
const commitMessage = data.Item.commitMessage.replace(/\n/g, '<br />');
const numberOfVerifiedPaths = data.Item.board.filter( path => path.verified == true).length;
const coverage = Math.round((100 * numberOfVerifiedPaths) / data.Item.board.length);
const deployTime = data.Item.deployTime
const elapsedTimeSinceDeploy = Date.now() - Date.parse(deployTime);

const linkToCommit = `https://github.com/${repo}/commit/${sha}`

const html = `
<!DOCTYPE html>
<html>
<head>
<style>
body {
background-color: whitesmoke;
color:lightgrey;
font-family: "Courier New", Courier, monospace
}
span {
display: inline-block;
border: 1px solid;
margin: 4px;
height: 70px;
width: 155px;
text-align: center;
vertical-align: middle;
font-weight: bold;
font-family: "Courier New", Courier, monospace;
}
#myProgress {
width: 100%;
background-color: grey;
}
#myBar {
width: 79%;
height: 30px;
background-color: green;
}
.barcontainer {
width: 100%;
background-color: #ddd;
}
.progressbar {
text-align: right;
padding-right: 20px;
line-height: 30px;
color: white;
}
.coverageprogress {width: ${coverage}%; background-color: #4CAF50;}
a {
color: lightgrey;
}
#container{
max-width: 990px;
margin: auto;
background: #282828;
padding: 10px;
}
</style>
</head>
<body>
<div id="container">
<h3>
<a href="${linkToCommit}">${repo} ${sha}</a>
</h3>
<p>
${commitMessage}
</p>
<hr>
<p>
Elapsed time since deploy: <time>${msToTime(elapsedTimeSinceDeploy)}</time>
</p>
<div class="barcontainer">
<div class="progressbar coverageprogress">${coverage}%</div>
</div>
<br>
${pathsWithStatusColour}
</div>
</body>
</html>
`;

const response = {
statusCode: 200,
headers: {
'Content-Type': 'text/html',
},
body: html,
};

resolve(response);
});
}

function msToTime(s) {
var ms = s % 1000;
s = (s - ms) / 1000;
var secs = s % 60;
s = (s - secs) / 60;
var mins = s % 60;
var hrs = (s - mins) / 60;

return hrs + ':' + mins + ':' + secs;
}

exports.handler = (event, context, callback) => {
const sha = event.pathParameters.sha;

getBoard(sha)
.then(data => renderBoard(data))
.then(boardAsHtml => callback(null, boardAsHtml));
};
42 changes: 42 additions & 0 deletions cloud/tip-verify-path.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const AWS = require('aws-sdk');
AWS.config.update({region: 'eu-west-1'});
const ddb = new AWS.DynamoDB.DocumentClient();

function updateBoard(dbItem) {
ddb.put(
{
TableName: 'tipcloud',
Item: dbItem
}
).promise();
}

function getBoard(sha) {
return ddb.get(
{
TableName : 'tipcloud',
Key: {
sha: sha
}
}
).promise();
}

function verifyPath(data, path) {
return new Promise((resolve, reject) => {
const index = data.Item.board.findIndex(element => element.name === path);
data.Item.board[index] = { name: path, verified: true };
resolve(data.Item);
});
}

exports.handler = (event, context, callback) => {
const body = JSON.parse(event.body);
const sha = body.sha;
const name = body.name;

getBoard(sha)
.then(data => verifyPath(data, name))
.then(dbItem => updateBoard(dbItem))
.then(() => callback(null, {statusCode: 200, body: null}));
};
2 changes: 1 addition & 1 deletion project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ object Dependencies {

lazy val scalaTest = "org.scalatest" %% "scalatest" % "3.0.4" % Test
lazy val scalaLogging = "com.typesafe.scala-logging" %% "scala-logging" % "3.8.0"
lazy val listJson = "net.liftweb" %% "lift-json" % "3.1.1"
lazy val listJson = "net.liftweb" %% "lift-json" % "3.3.0"
lazy val ficus = "com.iheart" %% "ficus" % "1.4.3"
lazy val yaml = "net.jcazevedo" %% "moultingyaml" % "0.4.0"
lazy val akka = Seq("com.typesafe.akka" %% "akka-actor" % akkaVersion,
Expand Down
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5
addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.2.2")
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0")
addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.15")
addSbtPlugin("ch.epfl.scala" %% "sbt-scalafix" % "0.5.10")
addSbtPlugin("ch.epfl.scala" %% "sbt-scalafix" % "0.6.0-M12")
48 changes: 39 additions & 9 deletions src/main/scala/com/gu/tip/Configuration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@ import com.typesafe.config.{Config, ConfigFactory}
import net.ceedubs.ficus.Ficus._
import net.ceedubs.ficus.readers.ArbitraryTypeReader._
import net.jcazevedo.moultingyaml._

import scala.util.Try
import scala.io.Source.fromFile

// $COVERAGE-OFF$

case class TipConfig(owner: String,
repo: String,
personalAccessToken: String,
label: String)
case class TipConfig(
owner: String,
repo: String,
personalAccessToken: String,
label: String,
boardSha: String = "",
commitMessage: String = "",
deployTime: String = ""
)

class TipConfigurationException(
msg: String = "Missing TiP config. Please refer to README.")
Expand All @@ -26,19 +32,33 @@ class PathConfigurationSyntaxError(
msg: String = "Bad syntax in tip.yaml. Please refer to README")
extends RuntimeException(msg)

object Configuration {
class Configuration(config: TipConfig) {
def this() {
this(
ConfigFactory
.load()
.as[Option[TipConfig]]("tip")
.getOrElse(throw new TipConfigurationException)
)
}

def this(typesafeConfig: Config) {
this(
typesafeConfig
.as[Option[TipConfig]]("tip")
.getOrElse(throw new TipConfigurationException)
)
}

object TipYamlProtocol extends DefaultYamlProtocol {
implicit val pathFormat: YamlFormat[Path] = yamlFormat2(Path)
}

import TipYamlProtocol._

val config: Config = ConfigFactory.load()
val tipConfig = config
.as[Option[TipConfig]]("tip")
.getOrElse(throw new TipConfigurationException)

def readFile(filename: String): String =
private def readFile(filename: String): String =
Option(getClass.getClassLoader.getResource(filename))
.map { path =>
fromFile(path.getPath).mkString
Expand All @@ -51,6 +71,16 @@ object Configuration {
case e: FileNotFoundException => throw new MissingPathConfigurationFile
case _ => throw new PathConfigurationSyntaxError
}.get

def cloudEnabled: Boolean = tipConfig.boardSha.nonEmpty
}

trait ConfigurationIf {
val configuration: Configuration
}

trait ConfigFromTypesafe extends ConfigurationIf {
override val configuration: Configuration = new Configuration()
}

// $COVERAGE-ON$
7 changes: 4 additions & 3 deletions src/main/scala/com/gu/tip/GitHubApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ import com.typesafe.scalalogging.LazyLogging
import net.liftweb.json._
import net.liftweb.json.DefaultFormats

trait GitHubApiIf { this: HttpClientIf =>
trait GitHubApiIf { this: HttpClientIf with ConfigurationIf =>
def getLastMergeCommitMessage: WriterT[IO, List[Log], String]
def setLabel(prNumber: String): WriterT[IO, List[Log], String]

val githubApiRoot = "https://api.github.com"
}

trait GitHubApi extends GitHubApiIf with LazyLogging { this: HttpClientIf =>
trait GitHubApi extends GitHubApiIf with LazyLogging {
this: HttpClientIf with ConfigurationIf =>

import Configuration.tipConfig._
import configuration.tipConfig._

private implicit val formats: DefaultFormats.type = DefaultFormats
/*
Expand Down
8 changes: 5 additions & 3 deletions src/main/scala/com/gu/tip/PathsActor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ class PathsActor(val paths: Map[String, EnrichedPath])
}

trait PathsActorIf {
def apply(filename: String)(implicit system: ActorSystem): ActorRef
def apply(filename: String, configuration: Configuration)(
implicit system: ActorSystem): ActorRef
}

/**
Expand All @@ -97,8 +98,9 @@ trait PathsActorIf {
*/
object PathsActor extends PathsActorIf with LazyLogging {

def apply(filename: String)(implicit system: ActorSystem): ActorRef = {
val pathList = Configuration.readPaths(filename)
override def apply(filename: String, configuration: Configuration)(
implicit system: ActorSystem): ActorRef = {
val pathList = configuration.readPaths(filename)
val paths: Map[String, EnrichedPath] =
pathList.map(path => path.name -> new EnrichedPath(path)).toMap
system.actorOf(Props[PathsActor](new PathsActor(paths)))
Expand Down
Loading

0 comments on commit d64cc7e

Please sign in to comment.