Skip to content

Commit

Permalink
Merge branch 'main' into update/sbt-1.9.6
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored Oct 5, 2023
2 parents f48cffa + a564c8c commit 5ba79f0
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 36 deletions.
16 changes: 16 additions & 0 deletions .dwollaci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ stages:
sbt test
filesToStash:
- '**'
deployDevInt:
awsAccount: dwolla-sandbox
nodeLabel: nvm-sbt-deployer
secrets:
CLOUDFLARE_AUTH_KEY: scheduled-maintenance/cloudflare-api-key
CLOUDFLARE_AUTH_EMAIL: scheduled-maintenance/cloudflare-email-address
CLOUDFLARE_ACCOUNT_ID: scheduled-maintenance/cloudflare-account
CLOUDFLARE_ZONE_ID: scheduled-maintenance/cloudflare-zone
steps:
- |
set +x
. ${NVM_DIR}/nvm.sh --no-use
nvm install
set -x
npm install -g serverless serverless-cloudflare-workers
sbt deploy
# deployProd:
# nodeLabel: nvm-sbt-deployer
# secrets:
Expand Down
4 changes: 3 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,14 @@ lazy val `scheduled-maintenance` = (project in file("core"))
"org.scala-js" %%% "scala-js-macrotask-executor" % "1.0.0",
"org.scala-js" %%% "scalajs-java-securerandom" % "1.0.0",
"org.typelevel" %% "jawn-parser" % "1.0.0" % Compile,
"org.scalameta" %%% "munit" % "0.7.25" % Test,
"org.scalameta" %%% "munit" % "0.7.5" % Test,
"io.circe" %%% "circe-parser" % circeV % Test,
"org.scala-js" %% "scalajs-env-jsdom-nodejs" % "1.0.0"
)
},
scalaJSUseMainModuleInitializer := true,
Test / scalaJSLinkerConfig ~= { _.withModuleKind(ModuleKind.CommonJSModule) },
Test / jsEnv := new org.scalajs.jsenv.nodejs.NodeJSEnv(),
Test / npmDependencies ++= Seq(
"cross-fetch" -> "3.1.4",
),
Expand Down
23 changes: 17 additions & 6 deletions core/src/main/scala/com/dwolla/scheduledmaintenance/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,32 @@ package com.dwolla.scheduledmaintenance

import dev.holt.javatime.literals.offsetDateTime
import io.circe.literal._
import org.scalajs.dom.{FetchEvent, Request, Response, ResponseInit}
import org.scalajs.dom._
import stubs.Globals

import java.time.format.DateTimeFormatter
import java.time.{OffsetDateTime, ZoneOffset}
import scala.annotation.nowarn
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.scalajs.js
import scala.scalajs.js.JSConverters.JSRichFutureNonThenable

object Main {
@nowarn("cat=other")
def main(args: Array[String]): Unit =
Globals.addEventListener("fetch",
(event: FetchEvent) => event.respondWith(handleRequest(event.request))
(event: FetchEvent) => event.respondWith(handleRequest(event.request).toJSPromise)
)

//noinspection SameParameterValue
private def formatForHttpHeader(odt: OffsetDateTime): String =
odt.toInstant.atOffset(ZoneOffset.UTC).format(DateTimeFormatter.RFC_1123_DATE_TIME)

private[scheduledmaintenance] def handleRequest(req: Request): Response =
private[scheduledmaintenance] def handleRequest(req: Request): Future[Response] = {
if(Option(req.headers.get("Access")).exists(_.contains("<secret-code>")))
return fetch(req).toFuture

if (Option(req.headers.get("Accept")).exists(_.contains("text/html")))
buildResponse("text/html",
"""<!doctype html>
Expand Down Expand Up @@ -137,17 +145,20 @@ object Main {
"code": "ScheduledMaintenance",
"message": "Services are temporarily unavailable while we perform scheduled maintenance"
}""".noSpaces)
}

private def buildResponse(contentType: String, body: String): Response =
new Response(
private def buildResponse(contentType: String, body: String): Future[Response] = {
Future.successful(new Response(
body,
new ResponseInit {
status = 503
statusText = "Service Unavailable (scheduled maintenance)"
headers = js.Dictionary(
"content-type" -> contentType,
"Retry-After" -> formatForHttpHeader(offsetDateTime"""2021-05-16T00:00:00-05:00"""),
"Retry-After" -> formatForHttpHeader(offsetDateTime"""2023-10-16T22:00:00-05:00"""),
)
}
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import dev.holt.javatime.literals._
import io.circe.literal._
import io.circe.parser.parse
import org.scalajs.dom.{Headers, HttpMethod, Request, RequestInit}
import org.scalajs.macrotaskexecutor.MacrotaskExecutor.Implicits._

import java.time.Instant
import java.time.format.DateTimeFormatter
import org.scalajs.macrotaskexecutor.MacrotaskExecutor.Implicits._
import scala.scalajs.js

class ScheduledMaintenanceResponseTest extends munit.FunSuite with FetchPolyfill {
Expand All @@ -19,35 +19,39 @@ class ScheduledMaintenanceResponseTest extends munit.FunSuite with FetchPolyfill

test("the response should be a 503 status code") {
val output = Main.handleRequest(defaultRequest)

assert(output.status == 503)
assert(output.statusText == "Service Unavailable (scheduled maintenance)")
output.map { res =>
assert(res.status == 503)
assert(res.statusText == "Service Unavailable (scheduled maintenance)")
}
}

test("the response should contain an appropriate JSON body") {
val output = Main.handleRequest(defaultRequest)

output.text().toFuture.map { body =>
assert(parse(body) == Right(
json"""{
"code": "ScheduledMaintenance",
"message": "Services are temporarily unavailable while we perform scheduled maintenance"
}"""))
output.map { res =>
res.text().toFuture.map { body =>
assert(parse(body) == Right(
json"""{
"code": "ScheduledMaintenance",
"message": "Services are temporarily unavailable while we perform scheduled maintenance"
}"""))
}
}
}

test("the response should contain an appropriate Retry-After header") {
val expected = offsetDateTime"""2021-05-16T00:00:00-05:00""".toInstant
val expected = offsetDateTime"""2023-10-16T22:00:00-05:00""".toInstant

val output = Main.handleRequest(defaultRequest)

val actual: Instant =
Option(output.headers.get("Retry-After"))
.map(DateTimeFormatter.RFC_1123_DATE_TIME.parse)
.map(Instant.from)
.orNull
output.map { res =>
val actual: Instant =
Option(res.headers.get("Retry-After"))
.map(DateTimeFormatter.RFC_1123_DATE_TIME.parse)
.map(Instant.from)
.orNull

assert(expected == actual)
assert(expected == actual)
}
}

test("if the request asks for HTML, give it HTML") {
Expand All @@ -60,7 +64,30 @@ class ScheduledMaintenanceResponseTest extends munit.FunSuite with FetchPolyfill

val output = Main.handleRequest(req)

assert(output.status == 503)
assert(output.headers.get("Content-type") == "text/html")
output.map { res =>
assert(res.status == 503)
assert(res.headers.get("Content-type") == "text/html")
}
}

// test("if request headers include Access: <secret-code>, allow request") {
// val req = new Request("https://hydragents.xyz/test", new RequestInit() {
// method = HttpMethod.GET
// headers = new Headers(js.Dictionary(
// "Access" -> "<secret-code>"
// ))
// })
//
// val output = Main.handleRequest(req)
//
// output.map { res =>
// println(res.status)
// println(res.ok)
//// println(res.statusText)
//
// assert(res.status != 503)
// assert(res.ok)
//// assert(res.status == 200)
// }
// }
}
10 changes: 1 addition & 9 deletions serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,4 @@ functions:
script: ${env:ARTIFACT_PATH}
events:
- http:
url: api.dwolla.com/*
- http:
url: dashboard.dwolla.com/*
- http:
url: accounts.dwolla.com/*
- http:
url: partners.dwolla.com/*
- http:
url: transfer.dwolla.com/*
url: hydragents.xyz/*

0 comments on commit 5ba79f0

Please sign in to comment.