-
Notifications
You must be signed in to change notification settings - Fork 4
Working with SNS
The AWS Simple Notification Service is a fully managed pub/sub messaging service for coordinating the delivery of messages to subscribing endpoints and clients. SNS necessary notification ie. event driven systems. When something is worth notifying about ie. an notification event has occurred then you need the SNS service.
The following seed project can be used as example on how to use SNS:
Components use SNS need to add the following library to build.sbt:
libraryDependencies += "com.amazonaws" % "aws-java-sdk-sns" % "1.11.255"
Lambdas automatically have access to SNS when the execution policy allows it and the default client is used:
import com.amazonaws.services.sns.{AmazonSNS, AmazonSNSClientBuilder}
val sns: AmazonSNS = AmazonSNSClientBuilder.defaultClient()
Most resources in an AWS account have a flat namespace, this means that resources like SNS topics must have a unique name inside a single account. To be able to deploy multiple component in an account that use the same SNS topic name, the project uses a 'scope'.
A 'scope' works like a 'namespace' or 'package' in programming languages, it allows to reuse a name by prefixing it with a unique prefix.
sbt-sam manages resource names for us. It uses the sbt name setting and samStage setting to define a scope. All resources are prefixed with [name]-[stage]-[resource-name]. For example, a topic called 'person' would become name-stage-person.
A scoped topic name is useful, in the source code we can refer to the normal name 'person' and the topic will be automatically scoped by the settings of the project.
The setting name is obvious, it is the project name, the setting samStage defines a unique stage for the component. The stage can be an arbitrary text, and defines only a scope for the component. At first glance one would use the scope to define 'dev', 'acc', 'prod' environments. The stage is also useful when defining a single component, that can be composed together. For example, when defining a data-lake, consisting of multiple components, a component has the same project name, but the data lake consists of multiple 'stages', for example, 'part1', 'part2', 'part3' etc.
The com.github.dnvriend.lambda.SamContext
, a case class that is always available inside a Lambda, provides methods that can be used to determine the scoped topic name from its short name.
def handle(request: HttpRequest, ctx: SamContext): HttpResponse {
val topicName = "HelloWorld"
val topic: String = ctx.snsTopicArn(topicName)
sns.publish(topic, "Hello World")
}
SNS is an event source in AWS serverless. This means that a Lambda can handle events generated by SNS:
@SNSConf(topic = "person-received")
class PersonReceivedPublished extends SNSEventHandler {
override def handle(events: List[SNSEvent], ctx: SamContext): Unit = {
val repo = new PersonRepository("people", ctx)
events.foreach { event =>
val person = event.messageAs[Person]
val id = repo.id
repo.put(id, person.copy(name = person.name + "-sns-" + person.id))
}
}
}
When the component defines a topic called 'person-received' in conf/sam.conf
, then you can refer to the topic by just annotating a SNSEventHandler with @SNSConf
and the topic name. The content of conf/sam.conf
is:
topics {
personReceived {
name = "person-received"
display-name = "person-received"
export = false
}
}
A topic can also be exported by another component by setting export = true
in sam.conf
. When you want to use a topic that is exported by another component, you need to use the following syntax to refer to that topic:
@SNSConf(topic = "import:component-name-export-topic:person-received")
class PersonReceivedPublished extends SNSEventHandler {
override def handle(events: List[SNSEvent], ctx: SamContext): Unit = {
val repo = new PersonRepository("people", ctx)
events.foreach { event =>
val person = event.messageAs[Person]
val id = repo.id
repo.put(id, person.copy(name = person.name + "-sns-" + person.id))
}
}
}
The import
syntax consists of a component name that exports the topic, and the name of the topic. Also, you can only import resources from components from the same stage as your own component, so if you're component is deployed in the dev
stage, you can only import resources from components that are also deployed in the dev
stage. The scoped-resources
naming convention assures this behavior.