Skip to content

Commit

Permalink
feat: Add tenant invitation notification (#497)
Browse files Browse the repository at this point in the history
  • Loading branch information
wojcikmat authored Mar 11, 2024
1 parent c4d4653 commit bcb633f
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 5 deletions.
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

0 comments on commit bcb633f

Please sign in to comment.