Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add tenant invitation email notification #497

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/backend/apps/multitenancy/email_serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from rest_framework import serializers


class TenantInvitationEmailSerializer(serializers.Serializer):
token = serializers.CharField()
tenant_membership_id = serializers.CharField()
11 changes: 11 additions & 0 deletions packages/backend/apps/multitenancy/notifications.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import logging

from common import emails
from . import email_serializers

logger = logging.getLogger(__name__)


class TenantInvitationEmail(emails.Email):
name = 'TENANT_INVITATION'
serializer_class = email_serializers.TenantInvitationEmailSerializer
13 changes: 8 additions & 5 deletions packages/backend/apps/multitenancy/services/membership.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from graphql_relay import to_global_id
from django.contrib.auth import get_user_model

from ..models import Tenant, TenantMembership
from ..constants import TenantUserRole
from ..tokens import tenant_invitation_token
from ..notifications import TenantInvitationEmail

User = get_user_model()

Expand All @@ -18,11 +20,12 @@ def create_tenant_membership(
user=user, tenant=tenant, role=role, invitee_email_address=invitee_email_address, is_accepted=is_accepted
)
if not is_accepted:
# TODO: Change printing token below to email notification
print( # noqa
tenant_invitation_token.make_token(
user_email=invitee_email_address if invitee_email_address else user.email, tenant_membership=membership
)
token = tenant_invitation_token.make_token(
user_email=invitee_email_address if invitee_email_address else user.email, tenant_membership=membership
)
TenantInvitationEmail(
to=user.email if user else invitee_email_address,
data={'tenant_membership_id': to_global_id("TenantMembershipType", membership.id), 'token': token},
).send()

return membership
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as TrialExpiresSoon from './trialExpiresSoon';
import * as UserExport from './userExport';
import * as UserExportAdmin from './userExportAdmin';

import * as TenantInvitation from './tenantInvitation';
//<-- INJECT EMAIL TEMPLATE IMPORT -->

export const templates: Record<EmailTemplateType, EmailTemplateDefinition> = {
Expand All @@ -15,5 +16,6 @@ export const templates: Record<EmailTemplateType, EmailTemplateDefinition> = {
[EmailTemplateType.TRIAL_EXPIRES_SOON]: TrialExpiresSoon,
[EmailTemplateType.USER_EXPORT]: UserExport,
[EmailTemplateType.USER_EXPORT_ADMIN]: UserExportAdmin,
[EmailTemplateType.TENANT_INVITATION]: TenantInvitation,
//<-- INJECT EMAIL TEMPLATE -->
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './tenantInvitation.component';
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useGenerateAbsoluteLocalePath } from '@sb/webapp-core/hooks';
import { RoutesConfig } from '@sb/webapp-core/config/routes';
import { FormattedMessage } from 'react-intl';

import { EmailComponentProps } from '../../types';
import { Button, Layout } from '../../base';


export type TenantInvitationProps = EmailComponentProps & {
token: string
tenantMembershipId: string
};

export const Template = ({token, tenantMembershipId}: TenantInvitationProps) => {
const generateLocalePath = useGenerateAbsoluteLocalePath();
const url = generateLocalePath(RoutesConfig.home);

return (
<Layout
title={<FormattedMessage defaultMessage="You have a new tenant invitation" id="Email / TenantInvitation / Title" />}
text={
<FormattedMessage
defaultMessage="Click button below to accept or decline new invitation.<br />Token: {token}<br />MembershipId: {tenantMembershipId}"
id="Email / TenantInvitation / Text"
values={{ token: token, tenantMembershipId: tenantMembershipId }}
/>
}
>
<Button linkTo={url}>
<FormattedMessage defaultMessage="Button" id="Email / TenantInvitation / Link label" />
</Button>
</Layout>
);
};

export const Subject = () => <FormattedMessage defaultMessage="You have a new tenant invitation" id="Email / TenantInvitation / Subject" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { StoryFn } from '@storybook/react';

import { EmailTemplateType } from '../../types';
import { EmailStory } from '../../emailStory/emailStory.component';
import {
Template as TenantInvitationEmail,
Subject as TenantInvitationSubject,
TenantInvitationProps,
} from './tenantInvitation.component';

const Template: StoryFn<TenantInvitationProps> = (args) => (
<EmailStory type={EmailTemplateType.TENANT_INVITATION} subject={<TenantInvitationSubject />} emailData={args}>
<TenantInvitationEmail {...args} />
</EmailStory>
);

export default {
title: 'Emails/TenantInvitation',
component: TenantInvitationEmail,
};

export const Primary = Template.bind({});
Primary.args = {};
1 change: 1 addition & 0 deletions packages/webapp-libs/webapp-emails/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export enum EmailTemplateType {
TRIAL_EXPIRES_SOON = 'TRIAL_EXPIRES_SOON',
USER_EXPORT = 'USER_EXPORT',
USER_EXPORT_ADMIN = 'USER_EXPORT_ADMIN',
TENANT_INVITATION = 'TENANT_INVITATION',
//<-- INJECT EMAIL TYPE -->
}

Expand Down
Loading