Skip to content

Commit

Permalink
Merge 61c142a into 0d74c31
Browse files Browse the repository at this point in the history
  • Loading branch information
johnduffell committed Jun 24, 2019
2 parents 0d74c31 + 61c142a commit fff1bd4
Show file tree
Hide file tree
Showing 17 changed files with 385 additions and 23 deletions.
14 changes: 13 additions & 1 deletion build.sbt
Expand Up @@ -141,6 +141,12 @@ lazy val `effects-ses` = all(project in file("lib/effects-ses"))
libraryDependencies ++= Seq(logging, awsSES)
)

lazy val `effects-cloudwatch` = all(project in file("lib/effects-cloudwatch"))
.dependsOn(testDep)
.settings(
libraryDependencies ++= Seq(logging, awsCloudwatch)
)

val effectsDepIncludingTestFolder: ClasspathDependency = effects % "compile->compile;test->test"

lazy val `zuora-reports` = all(project in file("lib/zuora-reports"))
Expand Down Expand Up @@ -174,11 +180,13 @@ lazy val root = all(project in file(".")).enablePlugins(RiffRaffArtifact).aggreg
`new-product-api`,
`effects-sqs`,
`effects-ses`,
`effects-cloudwatch`,
`sf-datalake-export`,
`zuora-datalake-export`,
`batch-email-sender`,
`braze-to-salesforce-file-upload`,
`holiday-stop-processor`,
`metric-push-api`
).dependsOn(zuora, handler, effectsDepIncludingTestFolder, `effects-sqs`, testDep)

lazy val `identity-backfill` = all(project in file("handlers/identity-backfill")) // when using the "project identity-backfill" command it uses the lazy val name
Expand Down Expand Up @@ -240,7 +248,11 @@ lazy val `braze-to-salesforce-file-upload` = all(project in file("handlers/braze
lazy val `holiday-stop-processor` = all(project in file("handlers/holiday-stop-processor"))
.enablePlugins(RiffRaffArtifact)
.dependsOn(`holiday-stops`, effects)


lazy val `metric-push-api` = all(project in file("handlers/metric-push-api"))
.enablePlugins(RiffRaffArtifact)
.dependsOn()

// ==== END handlers ====

initialize := {
Expand Down
Expand Up @@ -205,8 +205,8 @@ class DigitalSubscriptionExpiryStepsTest extends FlatSpec with Matchers {

def verifyResponse(actualResponse: ApiResponse, expectedStatus: String, expectedBody: String) = {
val expectedReponseBodyJson = Json.parse(expectedBody)
val actualResponseBodyJson = Json.parse(actualResponse.body)
(actualResponse.statusCode, actualResponseBodyJson).shouldBe((expectedStatus, expectedReponseBodyJson))
val actualResponseBodyJson = actualResponse.body.map(Json.parse)
(actualResponse.statusCode, actualResponseBodyJson).shouldBe((expectedStatus, Some(expectedReponseBodyJson)))
}

val expectedBadRequestResponseBody =
Expand Down
Expand Up @@ -51,14 +51,14 @@ class IdentityBackfillStepsTest extends FlatSpec with Matchers {
(_, _) => GenericError("error")
}(Option(SFContactId("sfContactId")), IdentityId("123"))

result.body should include("updateBuyersIdentityId multiple errors updating 123: (sfContactId,error)")
result.body.get should include("updateBuyersIdentityId multiple errors updating 123: (sfContactId,error)")
}

"updateZuoraBillingAccountsIdentityId" should "propagate errors" in {
val ReturnWithResponse(result) = IdentityBackfillSteps.updateZuoraBillingAccountsIdentityId {
(_, _) => GenericError("error")
}(Set(AccountId("accountId1"), AccountId("accountId2")), IdentityId("123"))

result.body should include("updateZuoraBillingAccountsIdentityId multiple errors updating 123: (accountId1,error), (accountId2,error)")
result.body.get should include("updateZuoraBillingAccountsIdentityId multiple errors updating 123: (accountId1,error), (accountId2,error)")
}
}
Expand Up @@ -80,17 +80,17 @@ class PreReqCheckTest extends FlatSpec with Matchers {
val ReturnWithResponse(result) = PreReqCheck
.checkSfContactsSyncable(_ => ReturnWithResponse(errorResponse))(Set(SFAccountId("crmId")))

result.body should include("Bad request: foo")
result.body.get should include("Bad request: foo")
result shouldNot be(errorResponse)
result.body should include("is not syncable for the following reasons:")
result.body.get should include("is not syncable for the following reasons:")
}

"checkSfContactsSyncable" should "ReturnWithResponse if more than one CRM account" in {
val errorResponse = ApiGatewayResponse.badRequest("foo")
val ReturnWithResponse(result) = PreReqCheck
.checkSfContactsSyncable(_ => ReturnWithResponse(errorResponse))(Set(SFAccountId("crmId1"), SFAccountId("crmId2")))
result.body should include("more than one CRM account")
result.body shouldNot include("foo")
result.body.get should include("more than one CRM account")
result.body.get shouldNot include("foo")
}

"checkSfContactsSyncable" should "continue processing if syncable" in {
Expand All @@ -115,7 +115,7 @@ class PreReqCheckTest extends FlatSpec with Matchers {
)(EmailAddress("email@gu.com"))

result.statusCode shouldBe "400"
result.body should include("identity ids found in zuora")
result.body.get should include("identity ids found in zuora")

}

Expand All @@ -141,7 +141,7 @@ class PreReqCheckTest extends FlatSpec with Matchers {
)(EmailAddress("email@gu.com"))

result.statusCode shouldBe "400"
result.body should include("multiple CRM ids found for")
result.body.get should include("multiple CRM ids found for")

}

Expand All @@ -150,7 +150,7 @@ class PreReqCheckTest extends FlatSpec with Matchers {
val ReturnWithResponse(result) = PreReqCheck.validateZuoraAccountsFound(ClientSuccess(Nil))(EmailAddress("email@gu.com"))

result.statusCode shouldBe "400"
result.body should include("no zuora accounts found for")
result.body.get should include("no zuora accounts found for")

}

Expand Down
5 changes: 5 additions & 0 deletions handlers/metric-push-api/README.md
@@ -0,0 +1,5 @@
# metric-push-api

This lambda takes http requests from the client side for never events e.g.
fail to render. This would be the form of an img tag or similar.
It will then push a metric to cloudwatch which we can then alarm on.
10 changes: 10 additions & 0 deletions handlers/metric-push-api/build.sbt
@@ -0,0 +1,10 @@
// "Any .sbt files in foo, say foo/build.sbt, will be merged with the build definition for the entire build, but scoped to the hello-foo project."
// https://www.scala-sbt.org/0.13/docs/Multi-Project.html
name := "metric-push-api"
description:= "HTTP API to push a metric to cloudwatch so we can alarm on errors"

riffRaffPackageType := assembly.value
riffRaffUploadArtifactBucket := Option("riffraff-artifact")
riffRaffUploadManifestBucket := Option("riffraff-builds")
riffRaffManifestProjectName := "MemSub::Membership Admin::Metric Push API"
riffRaffArtifactResources += (file("handlers/metric-push-api/cfn.yaml"), "cfn/cfn.yaml")
140 changes: 140 additions & 0 deletions handlers/metric-push-api/cfn.yaml
@@ -0,0 +1,140 @@
AWSTemplateFormatVersion: "2010-09-09"
Description: HTTP API to push a metric to cloudwatch so we can alarm on errors

Parameters:
Stage:
Description: Stage name
Type: String
AllowedValues:
- PROD
- CODE
Default: CODE

Conditions:
CreateProdMonitoring: !Equals [ !Ref Stage, PROD ]

Mappings:
StageMap:
CODE:
ApiName: metric-push-api-api-CODE
DomainName: metric-push-api-code.membership.guardianapis.com
PROD:
ApiName: metric-push-api-api-PROD
DomainName: metric-push-api-prod.membership.guardianapis.com

Resources:

MetricPushProxyResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref MetricPushAPI
ParentId: !GetAtt [MetricPushAPI, RootResourceId]
PathPart: metric-push-api
DependsOn: MetricPushAPI

MetricPushMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
ApiKeyRequired: false
RestApiId: !Ref MetricPushAPI
ResourceId: !Ref MetricPushProxyResource
HttpMethod: GET
Integration:
Type: MOCK
IntegrationResponses:
- StatusCode: 204
ResponseParameters:
method.response.header.Cache-control: '''no-cache'''
MethodResponses:
- StatusCode: 204
ResponseParameters:
method.response.header.Cache-control: true
DependsOn:
- MetricPushAPI
- MetricPushProxyResource

MetricPushAPI:
Type: "AWS::ApiGateway::RestApi"
Properties:
Description: This called when we have a set of SF accounts that all belong to one person (email) to merge them
Name: !FindInMap [StageMap, !Ref Stage, ApiName]

MetricPushAPIStage:
Type: AWS::ApiGateway::Stage
Properties:
Description: Stage for metric-push-api-api
RestApiId: !Ref MetricPushAPI
DeploymentId: !Ref MetricPushAPIDeployment
StageName: !Sub ${Stage}
MethodSettings:
- ResourcePath: '/*'
HttpMethod: '*'
LoggingLevel: ERROR
DataTraceEnabled: true
DependsOn: MetricPushMethod

MetricPushAPIDeployment:
Type: AWS::ApiGateway::Deployment
Properties:
Description: Deploys metric-push-api-api into an environment/stage
RestApiId: !Ref MetricPushAPI
DependsOn: MetricPushMethod

MetricPushDomainName:
Type: "AWS::ApiGateway::DomainName"
Properties:
RegionalCertificateArn: # only for *.membership.guardianapis.com
!Sub arn:aws:acm:${AWS::Region}:${AWS::AccountId}:certificate/c1efc564-9ff8-4a03-be48-d1990a3d79d2
DomainName: !FindInMap [ StageMap, !Ref Stage, DomainName ]
EndpointConfiguration:
Types:
- REGIONAL

MetricPushBasePathMapping:
Type: "AWS::ApiGateway::BasePathMapping"
Properties:
RestApiId: !Ref MetricPushAPI
DomainName: !Ref MetricPushDomainName
Stage: !Sub ${Stage}
DependsOn:
- MetricPushAPI
- MetricPushAPIStage
- MetricPushDomainName

MetricPushDNSRecord:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneName: membership.guardianapis.com.
Name: !FindInMap [ StageMap, !Ref Stage, DomainName ]
Comment: !Sub CNAME for metric-push-api API ${Stage}
Type: CNAME
TTL: '120'
ResourceRecords:
- !GetAtt [ MetricPushDomainName, RegionalDomainName ]
DependsOn:
- MetricPushDomainName

5xxApiAlarm:
Type: AWS::CloudWatch::Alarm
Condition: CreateProdMonitoring
Properties:
AlarmActions:
- !Sub arn:aws:sns:${AWS::Region}:${AWS::AccountId}:subscriptions_dev
AlarmName:
!Sub
- 5XX rate from ${ApiName}
- { ApiName: !FindInMap [StageMap, !Ref Stage, ApiName] }
ComparisonOperator: GreaterThanThreshold
Dimensions:
- Name: ApiName
Value: !FindInMap [StageMap, !Ref Stage, ApiName]
- Name: Stage
Value: !Sub ${Stage}
EvaluationPeriods: 1
MetricName: 5XXError
Namespace: AWS/ApiGateway
Period: 3600
Statistic: Sum
Threshold: 5
TreatMissingData: notBreaching
11 changes: 11 additions & 0 deletions handlers/metric-push-api/riff-raff.yaml
@@ -0,0 +1,11 @@
stacks:
- membership
regions:
- eu-west-1
deployments:

cfn:
type: cloud-formation
app: metric-push-api
parameters:
templatePath: cfn.yaml
7 changes: 7 additions & 0 deletions handlers/metric-push-api/src/main/resources/log4j.properties
@@ -0,0 +1,7 @@
log = .
log4j.rootLogger = INFO, LAMBDA

# Define the LAMBDA Appender
log4j.appender.LAMBDA=com.amazonaws.services.lambda.runtime.log4j.LambdaAppender
log4j.appender.LAMBDA.layout=org.apache.log4j.PatternLayout
log4j.appender.LAMBDA.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %l - %m%n
Expand Up @@ -107,7 +107,7 @@ class ContributionStepsTest extends FlatSpec with Matchers {

val actual = Await.result(futureActual, 30 seconds)
actual.statusCode should be("200")
actual.body jsonMatchesFormat expectedOutput
actual.body.get jsonMatchesFormat expectedOutput
}

}
Expand Up @@ -98,7 +98,7 @@ class PaperStepsTest extends FlatSpec with Matchers {

val actual = Await.result(futureActual, 30 seconds)
actual.statusCode should be("200")
actual.body jsonMatchesFormat expectedOutput
actual.body.get jsonMatchesFormat expectedOutput
}

}

0 comments on commit fff1bd4

Please sign in to comment.