Skip to content

Commit 379e4c4

Browse files
authored
Merge pull request #1740 from guardian/live/add-pa-polling
Ship PA match events from football lambda to live activities test q.
2 parents a35d502 + afc8a30 commit 379e4c4

7 files changed

Lines changed: 318 additions & 11 deletions

File tree

build.sbt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ lazy val football = lambda("football", "football")
317317
"io.netty" % "netty-codec-http" % nettyVersion,
318318
"io.netty" % "netty-codec-http2" % nettyVersion,
319319
"io.netty" % "netty-common" % nettyVersion,
320+
"software.amazon.awssdk" % "eventbridge" % "2.20.0"
320321
),
321322
excludeDependencies ++= Seq(
322323
ExclusionRule("org.playframework", "play-ahc-ws_2.13"),
@@ -466,6 +467,7 @@ lazy val liveactivities = lambda("liveactivities", "liveactivities", Some("com.g
466467
libraryDependencies ++= Seq(
467468
"com.turo" % "pushy" % "0.13.10",
468469
"com.squareup.okhttp3" % "okhttp" % okHttpVersion,
470+
"software.amazon.awssdk" % "eventbridge" % "2.20.0"
469471
),
470472
excludeDependencies ++= Seq(
471473
ExclusionRule("org.playframework", "play-ahc-ws_2.13"),

cdk/lib/__snapshots__/footballnotificationslambda.test.ts.snap

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,26 @@ exports[`The FootballNotificationsLambda stack matches the snapshot for CODE 1`]
458458
],
459459
},
460460
},
461+
{
462+
"Action": "events:PutEvents",
463+
"Effect": "Allow",
464+
"Resource": {
465+
"Fn::Join": [
466+
"",
467+
[
468+
"arn:aws:events:",
469+
{
470+
"Ref": "AWS::Region",
471+
},
472+
":",
473+
{
474+
"Ref": "AWS::AccountId",
475+
},
476+
":event-bus/liveactivities-eventbus-CODE",
477+
],
478+
],
479+
},
480+
},
461481
{
462482
"Action": [
463483
"dynamodb:PutItem",
@@ -1015,6 +1035,26 @@ exports[`The FootballNotificationsLambda stack matches the snapshot for PROD 1`]
10151035
],
10161036
},
10171037
},
1038+
{
1039+
"Action": "events:PutEvents",
1040+
"Effect": "Allow",
1041+
"Resource": {
1042+
"Fn::Join": [
1043+
"",
1044+
[
1045+
"arn:aws:events:",
1046+
{
1047+
"Ref": "AWS::Region",
1048+
},
1049+
":",
1050+
{
1051+
"Ref": "AWS::AccountId",
1052+
},
1053+
":event-bus/liveactivities-eventbus-PROD",
1054+
],
1055+
],
1056+
},
1057+
},
10181058
{
10191059
"Action": [
10201060
"dynamodb:PutItem",

cdk/lib/__snapshots__/liveactivities.test.ts.snap

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,75 @@ exports[`The LiveActivities stack matches the snapshot for CODE 1`] = `
1818
},
1919
},
2020
"Resources": {
21+
"EventsD32975C2": {
22+
"Properties": {
23+
"Description": "CODE event routing for live activities",
24+
"Name": "liveactivities-eventbus-CODE",
25+
"Tags": [
26+
{
27+
"Key": "gu:cdk:version",
28+
"Value": "TEST",
29+
},
30+
{
31+
"Key": "gu:repo",
32+
"Value": "guardian/mobile-n10n",
33+
},
34+
{
35+
"Key": "Stack",
36+
"Value": "mobile-notifications",
37+
},
38+
{
39+
"Key": "Stage",
40+
"Value": "CODE",
41+
},
42+
],
43+
},
44+
"Type": "AWS::Events::EventBus",
45+
},
46+
"liveGameEventsTargetingA85C7B8B": {
47+
"Properties": {
48+
"Description": "Deliver live match events in CODE to liveGameTestingQueue",
49+
"EventBusName": {
50+
"Ref": "EventsD32975C2",
51+
},
52+
"EventPattern": {
53+
"source": [
54+
"football-lambda",
55+
],
56+
},
57+
"State": "DISABLED",
58+
"Tags": [
59+
{
60+
"Key": "gu:cdk:version",
61+
"Value": "TEST",
62+
},
63+
{
64+
"Key": "gu:repo",
65+
"Value": "guardian/mobile-n10n",
66+
},
67+
{
68+
"Key": "Stack",
69+
"Value": "mobile-notifications",
70+
},
71+
{
72+
"Key": "Stage",
73+
"Value": "CODE",
74+
},
75+
],
76+
"Targets": [
77+
{
78+
"Arn": {
79+
"Fn::GetAtt": [
80+
"liveactivitiesfootballlivegamesCODE6EBEFF14",
81+
"Arn",
82+
],
83+
},
84+
"Id": "Target0",
85+
},
86+
],
87+
},
88+
"Type": "AWS::Events::Rule",
89+
},
2190
"liveactivitiesbroadcastlambdaC288C367": {
2291
"DependsOn": [
2392
"liveactivitiesbroadcastlambdaServiceRoleDefaultPolicyB2BCF85A",
@@ -561,6 +630,75 @@ exports[`The LiveActivities stack matches the snapshot for CODE 1`] = `
561630
"Type": "AWS::DynamoDB::Table",
562631
"UpdateReplacePolicy": "Retain",
563632
},
633+
"liveactivitiesfootballlivegamesCODE6EBEFF14": {
634+
"DeletionPolicy": "Delete",
635+
"Properties": {
636+
"MessageRetentionPeriod": 604800,
637+
"QueueName": "liveactivities-football-live-games-CODE",
638+
"Tags": [
639+
{
640+
"Key": "gu:cdk:version",
641+
"Value": "TEST",
642+
},
643+
{
644+
"Key": "gu:repo",
645+
"Value": "guardian/mobile-n10n",
646+
},
647+
{
648+
"Key": "Stack",
649+
"Value": "mobile-notifications",
650+
},
651+
{
652+
"Key": "Stage",
653+
"Value": "CODE",
654+
},
655+
],
656+
},
657+
"Type": "AWS::SQS::Queue",
658+
"UpdateReplacePolicy": "Delete",
659+
},
660+
"liveactivitiesfootballlivegamesCODEPolicy00A91D6E": {
661+
"Properties": {
662+
"PolicyDocument": {
663+
"Statement": [
664+
{
665+
"Action": [
666+
"sqs:SendMessage",
667+
"sqs:GetQueueAttributes",
668+
"sqs:GetQueueUrl",
669+
],
670+
"Condition": {
671+
"ArnEquals": {
672+
"aws:SourceArn": {
673+
"Fn::GetAtt": [
674+
"liveGameEventsTargetingA85C7B8B",
675+
"Arn",
676+
],
677+
},
678+
},
679+
},
680+
"Effect": "Allow",
681+
"Principal": {
682+
"Service": "events.amazonaws.com",
683+
},
684+
"Resource": {
685+
"Fn::GetAtt": [
686+
"liveactivitiesfootballlivegamesCODE6EBEFF14",
687+
"Arn",
688+
],
689+
},
690+
},
691+
],
692+
"Version": "2012-10-17",
693+
},
694+
"Queues": [
695+
{
696+
"Ref": "liveactivitiesfootballlivegamesCODE6EBEFF14",
697+
},
698+
],
699+
},
700+
"Type": "AWS::SQS::QueuePolicy",
701+
},
564702
},
565703
}
566704
`;

cdk/lib/footballnotificationslambda.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import { GuAlarm } from '@guardian/cdk/lib/constructs/cloudwatch';
33
import type { GuStackProps } from '@guardian/cdk/lib/constructs/core';
44
import { GuStack } from '@guardian/cdk/lib/constructs/core';
55
import type { App } from 'aws-cdk-lib';
6-
import { Tags } from 'aws-cdk-lib';
7-
import { Duration } from 'aws-cdk-lib';
6+
import { Duration, Tags } from 'aws-cdk-lib';
87
import {
98
ComparisonOperator,
109
Metric,
@@ -63,6 +62,15 @@ export class FootballNotificationsLambda extends GuStack {
6362
}),
6463
);
6564

65+
footballnotificationslambda.addToRolePolicy(
66+
new PolicyStatement({
67+
actions: ['events:PutEvents'],
68+
resources: [
69+
`arn:aws:events:${region}:${account}:event-bus/liveactivities-eventbus-${stage}`,
70+
],
71+
}),
72+
);
73+
6674
const dynamoTableName = `${stack}-football-notifications-${stage}`;
6775

6876
const dynamoTable = new Table(this, 'DynamoTable', {

cdk/lib/liveactivities.ts

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@ import { GuLambdaFunction } from '@guardian/cdk/lib/constructs/lambda';
44
import type { App } from 'aws-cdk-lib';
55
import { Duration, Tags } from 'aws-cdk-lib';
66
import { AttributeType, BillingMode, Table } from 'aws-cdk-lib/aws-dynamodb';
7+
import { EventBus, Rule } from 'aws-cdk-lib/aws-events';
8+
import { SqsQueue } from 'aws-cdk-lib/aws-events-targets';
79
import { Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam';
810
import { Runtime } from 'aws-cdk-lib/aws-lambda';
11+
import { Queue } from 'aws-cdk-lib/aws-sqs';
912

1013
export class LiveActivities extends GuStack {
1114
constructor(scope: App, id: string, props: GuStackProps) {
1215
super(scope, id, props);
1316

14-
const { stack, stage } = this;
17+
const { stack, stage, region, account } = this;
1518
const app = 'liveactivities';
1619

1720
const dynamoTableName = `${stack}-liveactivities-${stage}`;
@@ -56,7 +59,7 @@ export class LiveActivities extends GuStack {
5659
actions: ['ssm:GetParametersByPath'],
5760
effect: Effect.ALLOW,
5861
resources: [
59-
`arn:aws:ssm:${this.region}:${this.account}:parameter/notifications/${this.stage}/liveactivities/ios`,
62+
`arn:aws:ssm:${region}:${account}:parameter/notifications/${stage}/liveactivities/ios`,
6063
],
6164
}),
6265
);
@@ -94,9 +97,37 @@ export class LiveActivities extends GuStack {
9497
actions: ['ssm:GetParametersByPath'],
9598
effect: Effect.ALLOW,
9699
resources: [
97-
`arn:aws:ssm:${this.region}:${this.account}:parameter/notifications/${this.stage}/liveactivities/ios`,
100+
`arn:aws:ssm:${region}:${account}:parameter/notifications/${stage}/liveactivities/ios`,
98101
],
99102
}),
100103
);
104+
105+
//////////// EVENTBUS INFRASTRUCTURE //////////////
106+
107+
const eventBus = new EventBus(this, 'Events', {
108+
eventBusName: `${app}-eventbus-${stage}`,
109+
description: `${stage} event routing for live activities`,
110+
});
111+
112+
// Development SQS to capture and inspect events from PA polling during development
113+
const liveGameTestingQueue = new Queue(
114+
this,
115+
`${app}-football-live-games-${stage}`,
116+
{
117+
queueName: `${app}-football-live-games-${stage}`,
118+
retentionPeriod: Duration.days(7),
119+
},
120+
);
121+
122+
new Rule(this, 'liveGameEventsTargeting', {
123+
eventBus: eventBus,
124+
description: `Deliver live match events in ${stage} to liveGameTestingQueue`,
125+
eventPattern: {
126+
source: ['football-lambda'],
127+
// detailType: ['football-match-events-with-articleId'],
128+
},
129+
enabled: false, // only enable if we want to inspect events in the development queue
130+
targets: [new SqsQueue(liveGameTestingQueue)],
131+
});
101132
}
102133
}

football/src/main/scala/com/gu/mobile/notifications/football/Lambda.scala

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import java.util.concurrent.TimeUnit
66
import com.amazonaws.regions.Regions
77
import com.amazonaws.services.dynamodbv2.{AmazonDynamoDBAsync, AmazonDynamoDBAsyncClientBuilder}
88
import com.gu.contentapi.client.GuardianContentClient
9-
import com.gu.mobile.notifications.football.lib.{ArticleSearcher, DynamoDistinctCheck, EventConsumer, EventFilter, FootballData, NotificationHttpProvider, NotificationSender, NotificationsApiClient, PaFootballClient, SyntheticMatchEventGenerator}
9+
import com.gu.mobile.notifications.football.lib.{ArticleSearcher, DynamoDistinctCheck, EventConsumer, EventFilter, FootballData, LiveActivityPusher, NotificationHttpProvider, NotificationSender, NotificationsApiClient, PaFootballClient, SyntheticMatchEventGenerator}
1010
import com.gu.mobile.notifications.football.notificationbuilders.MatchStatusNotificationBuilder
1111
import play.api.libs.json.Json
1212

@@ -52,6 +52,8 @@ object Lambda extends Logging {
5252

5353
lazy val matchStatusNotificationBuilder = new MatchStatusNotificationBuilder(configuration.mapiHost)
5454

55+
lazy val liveActivityPusher = new LiveActivityPusher()
56+
5557
lazy val eventConsumer = new EventConsumer(matchStatusNotificationBuilder)
5658

5759
lazy val distinctCheck = new DynamoDistinctCheck(dynamoDBClient, tableName)
@@ -87,11 +89,14 @@ object Lambda extends Logging {
8789

8890
logContainer()
8991

90-
val processing = footballData.pollFootballData(getZonedDateTime())
91-
.flatMap(articleSearcher.tryToMatchWithCapiArticle)
92-
.map(_.flatMap(eventConsumer.eventsToNotifications))
93-
.flatMap(eventFilter.filterNotifications)
94-
.flatMap(notificationSender.sendNotifications)
92+
val processing = for {
93+
footballDataResult <- footballData.pollFootballData(getZonedDateTime())
94+
articleMatches <- articleSearcher.tryToMatchWithCapiArticle(footballDataResult)
95+
_ = liveActivityPusher.pushToEventbus(articleMatches)
96+
notifications = articleMatches.flatMap(eventConsumer.eventsToNotifications)
97+
filteredNotifications <- eventFilter.filterNotifications(notifications) // todo what filtering is happening.
98+
result <- notificationSender.sendNotifications(filteredNotifications)
99+
} yield result
95100

96101
// we're in a lambda so we do need to block the main thread until processing is finished
97102
val result = Await.ready(processing, Duration(40, TimeUnit.SECONDS))

0 commit comments

Comments
 (0)