Skip to content

Commit 6af27cf

Browse files
committed
Working Channel API and Broadcast API clients
1 parent d835f48 commit 6af27cf

3 files changed

Lines changed: 94 additions & 15 deletions

File tree

liveactivities/src/main/scala/com/gu/liveactivities/BroadcastApiClient.scala

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,15 @@ class BroadcastApiClient {
5656
private val startPayload: String = Json.stringify(Json.toJson(broadcastStartBodyFixture))
5757

5858
private def getAccessToken(): String = {
59-
return "invalid-token-for-testing"
59+
ChannelApiClient.authenticationToken match {
60+
case Some(token) => token
61+
case None => {
62+
val authenticationToken = generateToken()
63+
ChannelApiClient.authenticationToken = Some(authenticationToken)
64+
ChannelApiClient.issueDate = Some(new Date())
65+
authenticationToken
66+
}
67+
}
6068
}
6169

6270
private def generateToken(): String = {
@@ -71,19 +79,17 @@ class BroadcastApiClient {
7179

7280
def sendToChannel(channelId: String, expiration: Option[Instant], priority: Option[Int]): Future[String] = {
7381
println(s"Broadcasting to channel $channelId")
74-
val authToken = generateToken()
82+
val authToken = getAccessToken()
7583
println(s"Generated auth token: $authToken")
7684

7785
val request: HttpRequest = HttpRequest.newBuilder(new URI(url))
7886
.version(HttpClient.Version.HTTP_2)
7987
.header("Authorization", authToken)
8088
.header("Content-Type", mediaType)
89+
.header("apns-channel-id", channelId)
8190
.header("apns-expiration", expiration.getOrElse(Instant.now().plusSeconds(5 * 60)).getEpochSecond.toString)
8291
.header("apns-priority", priority.map(_.toString).getOrElse("1"))
8392
.header("apns-push-type", "Liveactivity")
84-
85-
// add channel id
86-
.header("apns-channel-id", channelId)
8793
.POST(HttpRequest.BodyPublishers.ofString(startPayload, charSet))
8894

8995
.timeout(Duration.ofSeconds(60))

liveactivities/src/main/scala/com/gu/liveactivities/ChannelApiClient.scala

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ import com.turo.pushy.apns.auth.ApnsSigningKey;
1313
import com.turo.pushy.apns.auth.AuthenticationToken;
1414
import java.time.Instant
1515
import java.util.Date
16+
import com.gu.liveactivities.ChannelApiClient.authenticationToken
17+
18+
object ChannelApiClient {
19+
20+
var authenticationToken: Option[String] = None
21+
22+
var issueDate: Option[Date] = None
23+
}
1624

1725
class ChannelApiClient {
1826

@@ -31,7 +39,15 @@ class ChannelApiClient {
3139
private val message = "{\"message-storage-policy\": 1, \"push-type\": \"LiveActivity\"}"
3240

3341
private def getAccessToken(): String = {
34-
return "invalid-token-for-testing"
42+
ChannelApiClient.authenticationToken match {
43+
case Some(token) => token
44+
case None => {
45+
val authenticationToken = generateToken()
46+
ChannelApiClient.authenticationToken = Some(authenticationToken)
47+
ChannelApiClient.issueDate = Some(new Date())
48+
authenticationToken
49+
}
50+
}
3551
}
3652

3753
private def generateToken(): String = {
@@ -46,9 +62,7 @@ class ChannelApiClient {
4662

4763
def createChannel(): Future[String] = {
4864
println("Creating channel")
49-
val authToken = generateToken()
50-
println(s"Generated auth token: $authToken")
51-
65+
val authToken = getAccessToken()
5266
val request: HttpRequest = HttpRequest.newBuilder(new URI(url))
5367
.version(HttpClient.Version.HTTP_2)
5468
.header("Authorization", authToken)
@@ -60,11 +74,46 @@ class ChannelApiClient {
6074
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()).whenComplete((response, err) => {
6175
if (response == null) {
6276
p.failure(err)
77+
} else if (response.statusCode() >= 200 &&
78+
response.statusCode() < 300 &&
79+
response.headers().firstValue("apns-channel-id").isPresent()) {
80+
val channelId = response.headers().firstValue("apns-channel-id").get()
81+
println(s"Channel created successfully with channel ID $channelId")
82+
p.success(channelId)
6383
} else {
64-
println(s"Received response with status code ${response.statusCode()} and body ${response.body()}")
65-
p.success(response.body())
84+
println(s"Failed to create channel with status code ${response.statusCode()} and body ${response.body()}")
85+
p.failure(new Exception(s"Failed to create channel with status code ${response.statusCode()} and body ${response.body()}"))
6686
}
6787
})
6888
p.future
6989
}
90+
91+
def closeChannel(channelId: String): Future[Unit] = {
92+
println(s"Closing channel $channelId")
93+
val authToken = getAccessToken()
94+
val request: HttpRequest = HttpRequest.newBuilder(new URI(url))
95+
.version(HttpClient.Version.HTTP_2)
96+
.header("Authorization", authToken)
97+
.header("Content-Type", mediaType)
98+
.header("apns-channel-id", channelId)
99+
.DELETE()
100+
.timeout(Duration.ofSeconds(60))
101+
.build()
102+
val p = Promise[Unit]()
103+
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()).whenComplete((response, err) => {
104+
if (response == null) {
105+
println(s"Failed to close channel $channelId due to error ${err.getMessage}")
106+
p.failure(err)
107+
} else if (response.statusCode() >= 200 &&
108+
response.statusCode() < 300) {
109+
println(s"Channel closed successfully with channel ID $channelId")
110+
p.success(())
111+
} else {
112+
println(s"Failed to close channel with status code ${response.statusCode()} and body ${response.body()}")
113+
p.failure(new Exception(s"Failed to close channel with status code ${response.statusCode()} and body ${response.body()}"))
114+
}
115+
})
116+
p.future
117+
}
118+
70119
}

liveactivities/src/main/scala/com/gu/liveactivities/ChannelManagerLambda.scala

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,39 @@ object ChannelManagerLambda {
55

66
implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global
77

8+
val channelApiClient = new ChannelApiClient()
9+
val broadcastApiClient = new BroadcastApiClient()
10+
11+
def onChannelCreated(channelId: String): Unit = {
12+
println(s"Message sent to channel with ID: $channelId")
13+
// You can add additional logic here to handle the sent message, such as logging or sending a notification.
14+
15+
broadcastApiClient.sendToChannel(channelId, None, None).onComplete {
16+
case scala.util.Success(messageId) => {
17+
println(s"Message sent successfully with message ID $messageId")
18+
onMessageSent(channelId)
19+
}
20+
case scala.util.Failure(exception) => println(s"Failed to send message to channel $channelId: ${exception.getMessage}")
21+
}
22+
}
23+
24+
25+
def onMessageSent(channelId: String): Unit = {
26+
Thread.sleep(10 * 1000) // Wait for 10 seconds before closing the channel
27+
channelApiClient.closeChannel(channelId).onComplete {
28+
case scala.util.Success(_) => println(s"Channel $channelId closed successfully")
29+
case scala.util.Failure(exception) => println(s"Failed to close channel $channelId: ${exception.getMessage}")
30+
}
31+
}
32+
833
def handleRequest(): Unit = {
9-
val client = new ChannelApiClient()
10-
val channelFuture = client.createChannel()
34+
val channelFuture = channelApiClient.createChannel()
1135
channelFuture.onComplete {
12-
case scala.util.Success(channelId) => println(s"Channel created successfully with id: $channelId")
36+
case scala.util.Success(channelId) => onChannelCreated(channelId)
1337
case scala.util.Failure(exception) => println(s"Failed to create channel: ${exception.getMessage}")
1438
}
1539
// Keep the main thread alive to allow the async operation to complete
16-
Thread.sleep(5000)
40+
Thread.sleep(30000)
1741
}
1842
}
1943

0 commit comments

Comments
 (0)