Skip to content

Commit

Permalink
Merge 2b60632 into ee3011a
Browse files Browse the repository at this point in the history
  • Loading branch information
frj committed Sep 2, 2019
2 parents ee3011a + 2b60632 commit c8ced06
Show file tree
Hide file tree
Showing 12 changed files with 187 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class DigitalSubscriptionExpiryStepsTest extends FlatSpec with Matchers {
""".stripMargin

val actual = digitalSubscriptionExpirySteps.steps(ApiGatewayRequest(None, None, Some(request), None))
val actual = digitalSubscriptionExpirySteps.steps(ApiGatewayRequest(None, None, Some(request), None, None, None))

actual.shouldBe(successfulResponseFromZuora)
}
Expand All @@ -107,7 +107,7 @@ class DigitalSubscriptionExpiryStepsTest extends FlatSpec with Matchers {
""".stripMargin

val actual = digitalSubscriptionExpirySteps.steps(ApiGatewayRequest(None, None, Some(request), None))
val actual = digitalSubscriptionExpirySteps.steps(ApiGatewayRequest(None, None, Some(request), None, None, None))

verifyResponse(
actualResponse = actual,
Expand All @@ -125,7 +125,7 @@ class DigitalSubscriptionExpiryStepsTest extends FlatSpec with Matchers {
""".stripMargin

val actual = digitalSubscriptionExpirySteps.steps(ApiGatewayRequest(None, None, Some(request), None))
val actual = digitalSubscriptionExpirySteps.steps(ApiGatewayRequest(None, None, Some(request), None, None, None))

val expectedResponseBody =
"""{
Expand Down Expand Up @@ -155,7 +155,7 @@ class DigitalSubscriptionExpiryStepsTest extends FlatSpec with Matchers {
""".stripMargin

val actual = digitalSubscriptionExpirySteps.steps(ApiGatewayRequest(None, None, Some(request), None))
val actual = digitalSubscriptionExpirySteps.steps(ApiGatewayRequest(None, None, Some(request), None, None, None))

verifyResponse(
actualResponse = actual,
Expand All @@ -168,7 +168,7 @@ class DigitalSubscriptionExpiryStepsTest extends FlatSpec with Matchers {

val request = "{}"

val actual = digitalSubscriptionExpirySteps.steps(ApiGatewayRequest(None, None, Some(request), None))
val actual = digitalSubscriptionExpirySteps.steps(ApiGatewayRequest(None, None, Some(request), None, None, None))

verifyResponse(
actualResponse = actual,
Expand All @@ -186,7 +186,7 @@ class DigitalSubscriptionExpiryStepsTest extends FlatSpec with Matchers {
""".stripMargin

val actual = digitalSubscriptionExpirySteps.steps(ApiGatewayRequest(None, None, Some(request), None))
val actual = digitalSubscriptionExpirySteps.steps(ApiGatewayRequest(None, None, Some(request), None, None, None))

val expectedResponseBody =
"""{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.gu.holiday_stops

import java.io.{InputStream, OutputStream}
import java.time.LocalDate

import com.amazonaws.services.lambda.runtime.Context
import com.gu.effects.{GetFromS3, RawEffects}
import com.gu.salesforce.SalesforceAuthenticate.SFAuthConfig
import com.gu.salesforce.SalesforceClient
import com.gu.salesforce.holiday_stops.SalesforceHolidayStopRequestsDetail.{HolidayStopRequestId, ProductName, SubscriptionName}
import com.gu.salesforce.holiday_stops.SalesforceHolidayStopRequest._
import com.gu.salesforce.holiday_stops.SalesforceHolidayStopRequestsDetail.{HolidayStopRequestId, ProductName, SubscriptionName}
import com.gu.salesforce.holiday_stops.SalesforceSFSubscription.SubscriptionForSubscriptionNameAndContact._
import com.gu.salesforce.holiday_stops.{SalesforceHolidayStopRequest, SalesforceSFSubscription}
import com.gu.util.Logging
Expand All @@ -22,9 +23,8 @@ import com.gu.util.resthttp.JsonHttp.StringHttpRequest
import com.gu.util.resthttp.RestRequestMaker.BodyAsString
import com.gu.util.resthttp.{HttpOp, JsonHttp}
import okhttp3.{Request, Response}
import java.time.LocalDate

import play.api.libs.json.{Format, Json, Reads}
import scalaz.{-\/, \/, \/-}

object Handler extends Logging {

Expand All @@ -45,28 +45,65 @@ object Handler extends Logging {
)
)

def operationForEffects(
response: Request => Response,
stage: Stage,
fetchString: StringFromS3,
): ApiGatewayOp[Operation] = {
val POTENTIAL_PROXY_RESOURCE_PATH = "/potential"
val GET_ALL_AND_CREATE_PROXY_RESOURCE_REGEX = """/hsr.*""".r

def operationForEffects(response: Request => Response, stage: Stage,
fetchString: StringFromS3): ApiGatewayOp[Operation] = {

val loadConfig = LoadConfigModule(stage, fetchString)

for {
sfAuthConfig <- loadConfig[SFAuthConfig].toApiGatewayOp("load sfAuth config")
sfClient <- SalesforceClient(response, sfAuthConfig).value.toDisjunction.toApiGatewayOp("authenticate with SalesForce")
} yield Operation.noHealthcheck( // checking connectivity to SF is sufficient healthcheck so no special steps required
request => (request.httpMethod match { // TODO will need to match against path params too to support edit endpoint
case Some("GET") => request.queryStringParameters match {
case Some(_) => stepsForPotentialHolidayStop _
case None => stepsToListExisting _
request => validateRequestAndCreateSteps(request)(request, sfClient)
)
}

private def validateRequestAndCreateSteps(request: ApiGatewayRequest) = {
(for {
httpMethod <- validateMethod(request.httpMethod)
path <- validatePath(request.path)
} yield createSteps(httpMethod, path)).fold(
{ errorMessage: String =>
badrequest(errorMessage) _
},
identity
)
}

private def createSteps(httpMethod: String, path: String) = {
path match {
case POTENTIAL_PROXY_RESOURCE_PATH =>
httpMethod match {
case "GET" => stepsForPotentialHolidayStop _
case _ => unsupported _
}
case GET_ALL_AND_CREATE_PROXY_RESOURCE_REGEX() =>
httpMethod match {
case "GET" => stepsToListExisting _
case "POST" => stepsToCreate _
case "DELETE" => stepsToDelete _
case _ => unsupported _
}
case Some("POST") => stepsToCreate _
case Some("DELETE") => stepsToDelete _
case _ => unsupported _
})(request, sfClient))
case _ =>
notfound _
}
}

private def validateMethod(method: Option[String]): String \/ String = {
method match {
case Some(method) => \/-(method)
case None => -\/("Http method is required")
}
}

private def validatePath(path: Option[String]): String \/ String = {
path match {
case Some(method) => \/-(method)
case None => -\/("Path is required")
}
}

val HEADER_IDENTITY_ID = "x-identity-id"
Expand All @@ -79,6 +116,7 @@ object Handler extends Logging {
}).toApiGatewayOp(s"either '$HEADER_IDENTITY_ID' header OR '$HEADER_SALESFORCE_CONTACT_ID' (one is required)")

case class PotentialHolidayStopParams(startDate: LocalDate, endDate: LocalDate)

def stepsForPotentialHolidayStop(req: ApiGatewayRequest, unused: SfClient): ApiResponse = {
implicit val formatLocalDateAsSalesforceDate: Format[LocalDate] = SalesforceHolidayStopRequest.formatLocalDateAsSalesforceDate
implicit val readsPotentialHolidayStopParams: Reads[PotentialHolidayStopParams] = Json.reads[PotentialHolidayStopParams]
Expand Down Expand Up @@ -130,6 +168,7 @@ object Handler extends Logging {
}

case class DeletePathParams(subscriptionName: SubscriptionName, holidayStopRequestId: HolidayStopRequestId)

def stepsToDelete(req: ApiGatewayRequest, sfClient: SfClient): ApiResponse = {

val lookupOp = SalesforceHolidayStopRequest.LookupByContactAndOptionalSubscriptionName(sfClient.wrapWith(JsonHttp.getWithParams))
Expand All @@ -147,4 +186,9 @@ object Handler extends Logging {
def unsupported(req: ApiGatewayRequest, sfClient: HttpOp[StringHttpRequest, BodyAsString]): ApiResponse =
ApiGatewayResponse.badRequest("UNSUPPORTED HTTP METHOD")

def notfound(req: ApiGatewayRequest, sfClient: HttpOp[StringHttpRequest, BodyAsString]): ApiResponse =
ApiGatewayResponse.notFound("Not Found")

def badrequest(message: String)(req: ApiGatewayRequest, sfClient: HttpOp[StringHttpRequest, BodyAsString]): ApiResponse =
ApiGatewayResponse.badRequest(message)
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package com.gu.holiday_stops

import org.scalatest.{FlatSpec, Matchers}
import com.gu.effects.{FakeFetchString, SFTestEffects, TestingRawEffects}
import com.gu.holiday_stops.Handler._
import com.gu.salesforce.holiday_stops.SalesforceSFSubscription.SubscriptionForSubscriptionNameAndContact._
import com.gu.util.apigateway.ApiGatewayRequest
import com.gu.util.config.Stage
import com.gu.util.reader.Types.ApiGatewayOp.{ContinueProcessing, ReturnWithResponse}
import org.scalatest.Inside.inside
import org.scalatest.{FlatSpec, Matchers}
import play.api.libs.json.{JsSuccess, Json}

class HandlerTest extends FlatSpec with Matchers {

Expand All @@ -21,7 +26,67 @@ class HandlerTest extends FlatSpec with Matchers {
Handler.extractContactFromHeaders(Some(Map(
HEADER_SALESFORCE_CONTACT_ID -> expectedSfContactIdCoreValue
))) shouldBe ContinueProcessing(Right(SalesforceContactId(expectedSfContactIdCoreValue)))
}
it should "calculate potential holiday stop dates" in {
inside(
Handler.operationForEffects(
testEffects.response,
Stage("DEV"),
FakeFetchString.fetchString
).map { operation =>
operation
.steps(potentialIssueDateRequest("Guardian Weekly xxx", "2019-01-01", "2019-02-01"))
}
) {
case ContinueProcessing(response) =>
response.statusCode should equal("200")
inside(Json.fromJson[Array[String]](Json.parse(response.body))) {
case JsSuccess(dates, _) =>
dates should contain inOrderOnly (
"2019-01-04", "2019-01-11", "2019-01-18", "2019-01-25", "2019-02-01"
)
}
}
}
it should "return bad request if method is missing" in {
inside(
Handler.operationForEffects(testEffects.response, Stage("DEV"), FakeFetchString.fetchString)
.map(_.steps(ApiGatewayRequest(None, None, None, None, None, None)))
) {
case ContinueProcessing(response) =>
response.statusCode should equal("400")
response.body should equal("""{
| "message" : "Bad request: Http method is required"
|}""".stripMargin)
}
}
it should "return bad request if path is missing" in {
inside(
Handler.operationForEffects(testEffects.response, Stage("DEV"), FakeFetchString.fetchString)
.map(_.steps(ApiGatewayRequest(Some("GET"), None, None, None, None, None)))
) {
case ContinueProcessing(response) =>
response.statusCode should equal("400")
response.body should equal("""{
| "message" : "Bad request: Path is required"
|}""".stripMargin)
}
}

private def potentialIssueDateRequest(productPrefix: String, startDate: String, endDate: String) = {
ApiGatewayRequest(
Some("GET"),
Some(Map("startDate" -> startDate, "endDate" -> endDate)),
None,
Some(Map("x-product-name-prefix" -> productPrefix)),
None,
Some("/potential")
)
}

val testEffects = new TestingRawEffects(
postResponses = Map(
SFTestEffects.authSuccess
)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class ContributionStepsTest extends FlatSpec with Matchers {
addContribution = fakeAddContributionSteps,
addPaperSub = dummySteps,
addDigipackSub = dummySteps
)(ApiGatewayRequest(None, None, Some(Json.stringify(requestInput)), None, None))
)(ApiGatewayRequest(None, None, Some(Json.stringify(requestInput)), None, None, None))

val actual = Await.result(futureActual, 30 seconds)
actual.statusCode should be("200")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class PaperStepsTest extends FlatSpec with Matchers {
addContribution = dummySteps,
addPaperSub = fakeAddVoucherSteps,
addDigipackSub = dummySteps
)(ApiGatewayRequest(None, None, Some(Json.stringify(requestInput)), None, None))
)(ApiGatewayRequest(None, None, Some(Json.stringify(requestInput)), None, None, None))

val actual = Await.result(futureActual, 30 seconds)
actual.statusCode should be("200")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class EndToEndTest extends FlatSpec with Matchers {
| "accountId":"sfacc"
|}
""".stripMargin
val input = ApiGatewayRequest(None, None, Some(body), None, None)
val input = ApiGatewayRequest(None, None, Some(body), None, None, None)

val (responseString, requests) = getResultAndRequests(input)

Expand Down
28 changes: 28 additions & 0 deletions lib/effects/src/test/scala/com/gu/effects/SFTestEffects.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.gu.effects

import com.gu.effects.TestingRawEffects.{HTTPResponse, POSTRequest}

object SFTestEffects {

val authSuccessResponseBody: String =
s"""
|{
| "access_token": "tokentoken",
| "instance_url": "https://instance.url",
| "id": "https://id.sf.com/id/idididididid/ididididid",
| "token_type": "Bearer",
| "issued_at": "101010101010",
| "signature": "bW9uc3RlcnMhCg=="
|}
""".stripMargin

//stubs successful auth when provided with SF credentials provided in com.gu.effects.FakeFetchString
val authSuccess = (
POSTRequest(
"/services/oauth2/token",
"client_id=clientsfclient&client_secret=clientsecretsfsecret&username=usernamesf" +
"&password=passSFpasswordtokentokenSFtoken&grant_type=password"
),
HTTPResponse(200, authSuccessResponseBody)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ case class ApiGatewayRequest(
queryStringParameters: Option[Map[String, String]],
body: Option[String],
headers: Option[Map[String, String]],
pathParameters: Option[JsValue] = None
pathParameters: Option[JsValue] = None,
path: Option[String]
) extends LazyLogging {

def queryParamsAsCaseClass[A]()(implicit reads: Reads[A]): ApiGatewayOp[A] = {
Expand Down
Loading

0 comments on commit c8ced06

Please sign in to comment.