Skip to content

Commit

Permalink
[MS-670] feat: Add license. Dynamic subject generation (#46)
Browse files Browse the repository at this point in the history
* [MS-670] feat: Add license. Dynamic subject generation

* [MS-670] docs: Update readme
  • Loading branch information
piotrgrundas committed Nov 6, 2024
1 parent c8d4047 commit 9cd4ff0
Show file tree
Hide file tree
Showing 21 changed files with 111 additions and 46 deletions.
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ AWS_SECRET_ACCESS_KEY=
# One of: AWS_SES, NODE_MAILER
EMAIL_PROVIDER=NODE_MAILER

# If you want to whitelist specific domains to which the app can send emails - separated by comma.
WHITELISTED_DOMAINS=

# Used in `send-notification` endpoint for authentication if you do not want to use Saleor JWT verification.
AUTHORIZATION_TOKEN=

# Localstack only. When using AWS please comment this out
FROM_DOMAIN=mirumee.com
AWS_ENDPOINT_URL=http://host.docker.internal:4566
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ jobs:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@master
with:
role-to-assume: arn:aws:iam::414357773561:role/nimara-mailer
role-to-assume: ${{ vars.AWS_ROLE_ARN }}
aws-region: eu-central-1

- name: Copy ZIP
run: aws s3 cp artifact.zip s3://marina-artifacts/lambda/lambda-mailer-${{ env.GIT_DESCRIBE }}.zip --quiet
run: aws s3 cp artifact.zip ${{ vars.AWS_BUCKET }}/lambda-mailer-${{ env.GIT_DESCRIBE }}.zip --quiet

- name: ZIP tag
run: echo "${{ env.GIT_DESCRIBE }}"
19 changes: 17 additions & 2 deletions .release-it.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"git": {
"commitMessage": "chore(release): v${version}",
"commitMessage": "chore(release): Release v${version}",
"push": true,
"commit": true,
"tag": true,
Expand All @@ -19,7 +19,22 @@
"@release-it/conventional-changelog": {
"preset": "conventionalcommits",
"infile": "CHANGELOG.md",
"header": "# Changelog"
"header": "# Changelog",
"types": [
{ "type": "feat", "section": "Features" },
{ "type": "fix", "section": "Bug Fixes" },
{ "type": "chore", "section": "Chores" },
{ "type": "ci", "section": "CI/CD" },
{ "type": "docs", "section": "Documentation" },
{ "type": "style", "section": "Styling" },
{ "type": "refactor", "section": "Refactoring" },
{ "type": "perf", "section": "Performance" },
{ "type": "test", "section": "Testing" }
],
"parserOpts": {
"headerPattern": "^\\[(\\w+-\\d+)\\] (\\w*)(?:\\((.*)\\))?: (.*)$",
"headerCorrespondence": ["ticket", "type", "scope", "subject"]
}
}
}
}
13 changes: 13 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
BSD 3-Clause License

Copyright 2024, Mirumee Labs

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49 changes: 25 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,31 @@

<div align="center">
<h1>Nimara Mailer</h1>
<strong>TypeScript serverless app for sending emails from Saleor.</strong>
<strong>TypeScript serverless app for sending emails from <a href="https://github.com/saleor/saleor">Saleor</a>. And more!</strong>
</div>
<br />
<br />

## Local development
App designed to work in AWS cloud using lambda functions, SQS and secret manager.
It has two main components:
- [events-receiver](src/events-receiver.ts) - responsible for receiving events from Saleor (or custom ones via http) and pushing them to SQS queue.
- [email-sender](src/emails-sender.ts) - responsible for receiving events from SQS queue via lambda triggers and sending emails.

1. Setup required envs. You can copy `.env.example` to `.env` and adjust it.
Upon build, it outputs two files. Each, for one component. Both share same environment variables. When deployed to lambda, specify the following handlers:
- `events-receiver.handler`
- `emails-sender.handler`

When installing the app in Saleor, it will store it's configuration in the secret manager. Therfore, the secret should be created beforehand with empty object (`{}`) and the name should be specified in `SECRET_MANAGER_APP_CONFIG_PATH` env.

```
SALEOR_URL=<e.g. https://your.eu.saleor.cloud>
STATIC_URL=<where from emails static images will be served>
SQS_QUEUE_URL=
If you want to change supported events, you can do it in the [const](src/const.ts) file editing `SALEOR_EVENTS` or `CUSTOM_EVENTS` constants.

AWS_ACCESS_KEY_ID=
AWS_REGION=
AWS_SECRET_ACCESS_KEY=
SECRET_MANAGER_APP_CONFIG_PATH=<secret manager config name>
```
When adding new events, remember to update [TEMPLATES_MAP](src/lib/emails/const.ts) constant with the proper template and extract email function.

---

## Local development

1. Setup required envs. You can copy `.env.example` to `.env` and adjust it.
2. [`nvm use`](https://github.com/nvm-sh/nvm) - to set proper node version.
3. [`pnpm install`](https://pnpm.io/installation) - to install dependencies.
4. `pnpm dev` - to start the app.
Expand All @@ -40,18 +46,6 @@
Alternatively, you can use docker to run the app.

1. Setup required envs. You can copy `.env.example` to `.env` and adjust it.

```
SALEOR_URL=<e.g. https://your.eu.saleor.cloud>
STATIC_URL=<where from emails static images will be served>
SQS_QUEUE_URL=
AWS_ACCESS_KEY_ID=
AWS_REGION=
AWS_SECRET_ACCESS_KEY=
SECRET_MANAGER_APP_CONFIG_PATH=<secret manager config name>
```

2. `docker compose build` - build the app.
3. `docker compose run --rm --service-ports app` - run the app.

Expand Down Expand Up @@ -229,3 +223,10 @@ or
```
$ pnpm test:watch
```

<br>
<div align="center"> <strong>Crafted with ❤️ by Mirumee Software</strong>

[[email protected]](mailto:[email protected])

</div>
2 changes: 1 addition & 1 deletion src/emails-sender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export const handler = Sentry.wrapHandler(

await sender.send({
html,
subject: template.Subject,
subject: template.getSubject(data),
});

logger.info("Email sent successfully.", { toEmail, event });
Expand Down
5 changes: 3 additions & 2 deletions src/emails/templates/custom/CustomEventEmail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type CustomEventEmailProps = CustomEventData<
>;

const CustomEventEmail = ({
data: { channel, email, name },
data: { channel, name },
}: CustomEventEmailProps) => {
return (
<Layout channel={channel} previewText="Custom event">
Expand All @@ -34,6 +34,7 @@ const previewProps: CustomEventEmailProps = {
};

CustomEventEmail.PreviewProps = previewProps;
CustomEventEmail.Subject = "Custom event";

CustomEventEmail.getSubject = (data: CustomEventEmailProps) => "Custom event";

export default CustomEventEmail;
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ const previewProps: AccountChangeEmailRequestedEmailProps = {
};

AccountChangeEmailRequestedEmail.PreviewProps = previewProps;
AccountChangeEmailRequestedEmail.Subject = "Account change email requested";

AccountChangeEmailRequestedEmail.getSubject = (
data: AccountChangeEmailRequestedEmailProps
) => "Account change email requested";

export default AccountChangeEmailRequestedEmail;
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ const previewProps: AccountConfirmationRequestedEmailProps = {
};

AccountConfirmationRequestedEmail.PreviewProps = previewProps;
AccountConfirmationRequestedEmail.Subject =
"Account confirmation requested email";

AccountConfirmationRequestedEmail.getSubject = (
data: AccountConfirmationRequestedEmailProps
) => "Account confirmation requested email";

export default AccountConfirmationRequestedEmail;
4 changes: 3 additions & 1 deletion src/emails/templates/saleor/AccountConfirmedEmail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ const previewProps: AccountConfirmedEmailProps = {
};

AccountConfirmedEmail.PreviewProps = previewProps;
AccountConfirmedEmail.Subject = "Account confirmed";

AccountConfirmedEmail.getSubject = (data: AccountConfirmedEmailProps) =>
"Account confirmed";

export default AccountConfirmedEmail;
5 changes: 4 additions & 1 deletion src/emails/templates/saleor/AccountDeleteRequestedEmail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ const previewProps: AccountDeleteRequestedEmailProps = {
};

AccountDeleteRequestedEmail.PreviewProps = previewProps;
AccountDeleteRequestedEmail.Subject = "Account delete requested";

AccountDeleteRequestedEmail.getSubject = (
data: AccountDeleteRequestedEmailProps
) => "Account delete requested";

export default AccountDeleteRequestedEmail;
4 changes: 3 additions & 1 deletion src/emails/templates/saleor/AccountDeletedEmail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ const previewProps: AccountDeletedEmailProps = {
};

AccountDeletedEmail.PreviewProps = previewProps;
AccountDeletedEmail.Subject = "Account Deleted";

AccountDeletedEmail.getSubject = (data: AccountDeletedEmailProps) =>
"Account Deleted";

export default AccountDeletedEmail;
4 changes: 3 additions & 1 deletion src/emails/templates/saleor/AccountEmailChangedEmail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ const previewProps: AccountEmailChangedEmailProps = {
};

AccountEmailChangedEmail.PreviewProps = previewProps;
AccountEmailChangedEmail.Subject = "Account email changed";

AccountEmailChangedEmail.getSubject = (data: AccountEmailChangedEmailProps) =>
"Account email changed";

export default AccountEmailChangedEmail;
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ const previewProps: AccountSetPasswordRequestedEmailProps = {
};

AccountSetPasswordRequestedEmail.PreviewProps = previewProps;
AccountSetPasswordRequestedEmail.Subject = "Account set password requested";

AccountSetPasswordRequestedEmail.getSubject = (
data: AccountSetPasswordRequestedEmailProps
) => "Account set password requested";

export default AccountSetPasswordRequestedEmail;
4 changes: 3 additions & 1 deletion src/emails/templates/saleor/FulfillmentCreatedEmail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ const previewProps: FulfillmentCreatedEmailProps = {
};

FulfillmentCreatedEmail.PreviewProps = previewProps;
FulfillmentCreatedEmail.Subject = "Fulfillment updated";

FulfillmentCreatedEmail.getSubject = (data: FulfillmentCreatedEmailProps) =>
"Fulfillment updated";

export default FulfillmentCreatedEmail;
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ const previewProps: FulfillmentTrackingNumberUpdatedEmailProps = {
};

FulfillmentTrackingNumberUpdatedEmail.PreviewProps = previewProps;
FulfillmentTrackingNumberUpdatedEmail.Subject = "Tracking number updated";

FulfillmentTrackingNumberUpdatedEmail.getSubject = (
data: FulfillmentTrackingNumberUpdatedEmailProps
) => "Tracking number updated";

export default FulfillmentTrackingNumberUpdatedEmail;
4 changes: 3 additions & 1 deletion src/emails/templates/saleor/GiftCardSentEmail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ const previewProps: GiftCardSentEmailProps = {
};

GiftCardSentEmail.PreviewProps = previewProps;
GiftCardSentEmail.Subject = "Gift card sent";

GiftCardSentEmail.getSubject = (data: GiftCardSentEmailProps) =>
"Gift card sent";

export default GiftCardSentEmail;
4 changes: 3 additions & 1 deletion src/emails/templates/saleor/OrderCancelledEmail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ const previewProps: OrderCancelledEmailProps = {
};

OrderCancelledEmail.PreviewProps = previewProps;
OrderCancelledEmail.Subject = "Order cancelled";

OrderCancelledEmail.getSubject = (data: OrderCancelledEmailProps) =>
"Order cancelled";

export default OrderCancelledEmail;
3 changes: 2 additions & 1 deletion src/emails/templates/saleor/OrderCreatedEmail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ const previewProps: OrderCreatedEmailProps = {
};

OrderCreatedEmail.PreviewProps = previewProps;
OrderCreatedEmail.Subject = "Order placed";

OrderCreatedEmail.getSubject = (data: OrderCreatedEmailProps) => "Order placed";

export default OrderCreatedEmail;
4 changes: 3 additions & 1 deletion src/emails/templates/saleor/OrderRefundedEmail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ const previewProps: OrderRefundedEmailProps = {
};

OrderRefundedEmail.PreviewProps = previewProps;
OrderRefundedEmail.Subject = "Order refunded";

OrderRefundedEmail.getSubject = (data: OrderRefundedEmailProps) =>
"Order refunded";

export default OrderRefundedEmail;
2 changes: 1 addition & 1 deletion src/lib/emails/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const extractEmailFromCustomEvent = (data: { email: string }) => data.email;
export const TEMPLATES_MAP: {
[key in EmailEventType]?: {
extractFn: (data: any) => string;
template: ComponentType<any> & { Subject: string };
template: ComponentType<any> & { getSubject: (data: any) => string };
};
} = {
order_created: {
Expand Down

0 comments on commit 9cd4ff0

Please sign in to comment.