This is a library for building communication gateways.
Download the library
from maven central.
- Gateways are responsible for sending communications to users.
- For a given user ID, the gateway should resolve the user and content objects, generate the message, and send it to the user.
- This library allows business to use their existing customer databases and data models to send communications without having to change their existing systems.
- The library uses a history database to store the hash of the message contents with the runID to prevent duplicate
messages from being sent to the same user.
- By default, this is an AWS DynamoDB database but this can be overridden, see the implementation section for more information.
Gateways should expose endpoints that accept and return JSON payloads outlined in the swagger specification. for gateways.
- gateways should always expose POST endpoints
- The endpoint path doesn't matter as you can create the gateway in the gateway-api and specify the path there to match the one that the gateway exposes.
- It must accept the JSON payload in the format specified in the swagger specification.
- It must return a JSON payload in the format specified in the swagger specification.
- Requests must be sent with a header
x-worker-api-key
that matches the one set in the configuration section to be accepted
See the interface diagram
for more information on the design of the gateway library with an "Email Gateway" as an example.
See the sequence diagram to understand the communication
between the gateway and systems it connects to
See the activity diagram to understand
decision logic in the gateway library
Add the library to your projects dependencies
from maven central.
See the following example gateways for reference.
- Email Gateway - An example gateway that sends emails using Sendgrid
- SMS Gateway - An example gateway that sends SMS messages using Twilio
- Mock Gateway - An example gateway that pretends to send a message and always returns a 200 response for any given user ID
Ensure you have the following installed and configured locally:
- Java 21
- Maven 3.8.7 or newer
- Spring Boot 3.2.4 or newer
- History database (AWS DynamoDB by default) with
the schema defined here
- Deploy one using the Terragrunt library
- Add this library to your project dependencies
- Add lombok to your project dependencies
- Create or include your user and content models
- The content model must implement the
Content
interface and either be an entity or POJO for storing the information to send in the communication or generate the message with. (See the email gateway for an example) - The user model is for holding the information about the user to send the communication to. Note that it should have some kind of identifier, but the fields created here are up to you. (See the email gateway for an example)
- The content model must implement the
- Configuring the properties class.
- Create a public class that extends the
GatewayProperties
interface and use your User and Content models as the generic arguments. - Annotate the top of the class with
@SuperBuilder
from lombok - It should look like this with no body:
@SuperBuilder public class EmailGatewayProperties extends GatewayProperties<EmailUser, EmailContent> { }
- Create a public class that extends the
- Resolving user and content objects from a user ID.
This class is responsible for resolving the user and content objects from the user ID passed in the request payload.- Create a public class that implements the
UserContentService
interface and use your User and Content models as the generic arguments. - Implement the getUserAndContent method to resolve the user and content objects from the user ID.
- If you can resolve the content and user objects from just the user ID then you don't need to extend
the
GetUserAndContent
interface. See the sms gateway for an example. - If you need to use separate methods to resolve the user and content objects then extend the
GetUserAndContent
interface and implement thegetUser
andgetContent
methods. See the email gateway for an example. - If you can't resolve the user by ID then throw a
ResourceNotFoundException
- Create a public class that implements the
- Creating a service class to generate and send the message contents.
This class is responsible for generating the message content and sending it to the user via your internal methods or third party APIs.- Create a public class that implements the
ContentDeliveryService
interface use the User and Content models as the generic arguments.
Create a new class like this every time you want to generate a different type of message e.g. one for a monthly report and one for a weekly report that has different message templates.
See the email gateway for an example. - Implement the
sendContent
method that takes the User and Content objects and sends the message to the user. - Throw a
ContentDeliveryException
if there is an error sending the message.
- Create a public class that implements the
- Creating a controller to accept a request
This class configures the endpoints that the gateway will expose to accept requests.
I recommend creating a new class for each message format you want to send. E.g. one for a weekly report and one from a monthly report
See the email gateway for an example.- Create a public class that implements the
GatewayController
interface and use your User and Content models as the generic arguments. - add the
@Controller
,@ComponentScan("<base package name here>")
, and optionally a request mapping annotation to the top of this class - add a private final field for the properties object you created in step 4
- add a private final field for the
CommunicationGatewayService
object provided by this library, use your User and Content models as the generic arguments - Create a constructor that takes the following arguments:
- The delivery service object you created in step 6
- The user content service you created in step 5
DefaultCommunicationHistoryAccessProvider
object provided by this library (see section 8 below about using your own history database)- The
CommunicationGatewayService
provided by this library and use your User and Content models as the generic arguments - Initialise the properties object by calling the .builder() method on the class and setting
the
userContentService
,contendDeliveryService
andcommunicationHistoryAccessProvider
fields with the objects you created in steps 5 and 6 respectively.
- Implement the
processGatewayRequest
method to accept the request payload and return the response payload.- This should only need to include this line:
return GatewayController.sendCommunication(gatewayRequest, emailGatewayProperties, communicationGatewayService);
- This should only need to include this line:
- Create a public class that implements the
- (Optional) Create your own history access provider
This class is responsible for checking if the message has already been sent and storing sent messages.
See configuration to disable the default dynamoDB client being initialised when running the gateway. The database must have the schema defined here.- Create a public class that implements the
CommunicationHistoryAccessProvider
interface - Implement the methods:
- getPreviousCommunicationByMessageHash
- removeCommunicationHistoryByMessageHash
- storeCommunication
- Inject this dependency into your controller class and pass it to the properties object in the constructor to set
it as the
communicationHistoryAccessProvider
parameter.
- Create a public class that implements the
This section describes the configuration options that must be set in the application.properties or application.yml file of your project implementing this library
Caution
Disabling the cors and csrf to false should NOT be used in a live production environment, it should only be used when testing.
All properties set must be under the base prefix: io.github.cameronward301.communication-scheduler.gateway-library
Application Property | Description | Default Value | Required |
---|---|---|---|
.worker.apiKey | The API key that must be present in the header of all requests being sent to this gateway. | Y | |
.default-communication-history-access-provider.table-name | The table name of the DynamoDB table to connect to | Y if using default access provider | |
.default-communication-history-access-provider.region | The AWS region of the DynamoDB table | Y if using default access provider | |
.default-communication-history-access-provider.enabled | Only include if you want to set to false to use your own implementation | true | N |
.security.cors.enabled | Set to false for testing purposes to disable | true | N |
.security.csrf.enabled | Set to false for testing purposes to disable | true | N |
Environment Variable | Description | Default Value | Required |
---|---|---|---|
AWS_ACCESS_KEY_ID | The AWS key id to connect to DynamoDB | Y if using default access provider | |
AWS_SECRET_ACCESS_KEY | The AWS secret key to connect to DynamoDB | Y if using default access provider |
- To run the unit tests, run
mvn test
in the project directory - See the specific gateway implementations and integration testing project for example unit tests and integration tests.
- This is deployed to Maven with the command
mvn clean deploy