Skip to content

Commit

Permalink
Add invite generation, and fix using invites during user registration
Browse files Browse the repository at this point in the history
  • Loading branch information
MelissaAutumn committed May 23, 2024
1 parent f99ed4f commit 0a0c9ba
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 48 deletions.
7 changes: 6 additions & 1 deletion backend/src/appointment/routes/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,12 @@ def fxa_callback(
))

# Use the invite code after we've created the new subscriber
repo.invite.use_code(db, code, subscriber.id)
used = repo.invite.use_code(db, invite_code, subscriber.id)

# This shouldn't happen, but just in case!
if not used:
repo.subscriber.delete(db, subscriber)
raise HTTPException(500, l10n('unknown-error'))
elif not subscriber:
subscriber = fxa_subscriber

Expand Down
15 changes: 0 additions & 15 deletions backend/src/appointment/routes/invite.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,6 @@ def generate_invite_codes(n: int, db: Session = Depends(get_db), _admin: Subscri
return repo.invite.generate_codes(db, n)


@router.put("/redeem/{code}")
def use_invite_code(code: str, db: Session = Depends(get_db)):
raise NotImplementedError

"""endpoint to create a new subscriber and update the corresponding invite"""
if not repo.invite.code_exists(db, code):
raise validation.InviteCodeNotFoundException()
if not repo.invite.code_is_available(db, code):
raise validation.InviteCodeNotAvailableException()
# TODO: get email from admin panel
email = '[email protected]'
subscriber = repo.subscriber.create(db, schemas.SubscriberBase(email=email, username=email))
return repo.invite.use_code(db, code, subscriber.id)


@router.put("/revoke/{code}")
def revoke_invite_code(code: str, db: Session = Depends(get_db), admin: Subscriber = Depends(get_admin_subscriber)):
"""endpoint to revoke a given invite code and mark in unavailable, needs admin permissions"""
Expand Down
1 change: 1 addition & 0 deletions backend/test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from factory.schedule_factory import make_schedule # noqa: F401
from factory.slot_factory import make_appointment_slot # noqa: F401
from factory.subscriber_factory import make_subscriber, make_basic_subscriber, make_pro_subscriber # noqa: F401
from factory.invite_factory import make_invite

# Load our env
load_dotenv(find_dotenv(".env.test"))
Expand Down
7 changes: 5 additions & 2 deletions backend/test/integration/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,19 @@ def test_fxa_login(self, with_client):
assert 'url' in data
assert data.get('url') == FXA_CLIENT_PATCH.get('authorization_url')

def test_fxa_callback(self, with_db, with_client, monkeypatch):
def test_fxa_callback(self, with_db, with_client, monkeypatch, make_invite):
"""Test that our callback function correctly handles the session states, and creates a new subscriber"""
os.environ['AUTH_SCHEME'] = 'fxa'

state = 'a1234'

invite = make_invite()

monkeypatch.setattr('starlette.requests.HTTPConnection.session', {
'fxa_state': state,
'fxa_user_email': FXA_CLIENT_PATCH.get('subscriber_email'),
'fxa_user_timezone': 'America/Vancouver'
'fxa_user_timezone': 'America/Vancouver',
'fxa_user_invite_code': invite.code,
})

response = with_client.get(
Expand Down
7 changes: 5 additions & 2 deletions frontend/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"bookingSuccessfullyRequested": "Booking request sent",
"copiedToClipboard": "Copied to clipboard",
"eventWasCreated": "Event created in your calendar.",
"invitationGenerated": "The invite codes were generated successfully.",
"invitationWasSent": "An invitation was sent to your email.",
"invitationWasSentContext": "An invitation was sent to {email}.",
"messageWasNotSent": "Your message couldn't be sent. Please try again.",
Expand All @@ -76,12 +77,12 @@
"slotIsAvailableAgain": "Time slot now available for bookings."
},
"label": {
"admin-invite-codes-panel": "Invites",
"admin-subscriber-panel": "Subscribers",
"12hAmPm": "12h AM/PM",
"24h": "24h",
"DDMMYYYY": "DD/MM/YYYY",
"MMDDYYYY": "MM/DD/YYYY",
"admin-invite-codes-panel": "Invites",
"admin-subscriber-panel": "Subscribers",
"account": "Account",
"accountData": "Download your account data",
"accountDeletion": "Delete your account",
Expand All @@ -94,6 +95,7 @@
"allAppointments": "All bookings",
"allDay": "All day",
"allFutureAppointments": "All future bookings",
"amountOfCodes": "Amount of codes to generate",
"appearance": "Appearance",
"appointmentCreationError": "Oh no! Something went wrong.",
"appointmentDetails": "Details",
Expand Down Expand Up @@ -162,6 +164,7 @@
"filter": "Filter",
"general": "General",
"generalDetails": "Details",
"generate": "Generate",
"generateZoomLink": "Generate Zoom Meeting",
"goBack": "Go Back",
"google": "Google",
Expand Down
65 changes: 37 additions & 28 deletions frontend/src/views/admin/InviteCodePanelView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,19 @@
<template v-slot:footer>
<div class="flex w-1/3 flex-col gap-4 text-center md:w-full md:flex-row md:text-left">
<label class="flex flex-col gap-4 md:flex-row md:items-center md:gap-0">
<span>{{ t('label.enterEmailToInvite') }}</span>
<span>{{ t('label.amountOfCodes') }}</span>
<input
class="mx-4 w-60 rounded-md text-sm"
type="email"
placeholder="e.g. [email protected]"
v-model="inviteEmail"
type="number"
v-model="generateCodeAmount"
:disabled="loading"
enterkeyhint="send"
@keyup.enter="sendInvite"
enterkeyhint="done"
@keyup.enter="generateInvites"
/>
</label>
<primary-button :disabled="loading" @click="sendInvite">
<icon-send />
{{ t('label.send') }}
<primary-button :disabled="loading" @click="generateInvites">
<icon-send/>
{{ t('label.generate') }}
</primary-button>
</div>
</template>
Expand Down Expand Up @@ -77,7 +76,7 @@ const dj = inject('dayjs');
const invites = ref([]);
const displayPage = ref(false);
const inviteEmail = ref('');
const generateCodeAmount = ref(null);
const loading = ref(true);
const pageError = ref('');
const pageNotification = ref('');
Expand Down Expand Up @@ -164,19 +163,31 @@ const filters = [
* @returns {*}
*/
fn: (selectedKey, mutableDataList) => {
if (selectedKey === 'all') {
return mutableDataList;
}
if (selectedKey === 'revoked') {
return mutableDataList.filter((data) => (data.status.value === 'Revoked'));
}
return mutableDataList.filter((data) => {
if (data.status.value === 'Revoked') {
return false;
}
switch (selectedKey) {
case 'all':
return null;
case 'revoked':
return mutableDataList.filter((data) => (data.status.value === 'Revoked'));
case 'used':
return mutableDataList.filter((data) => {
if (data.status.value === 'Revoked') {
return false;
}
return selectedKey === 'used' && data.subscriber_id.value !== 'Unused';
});
return data.subscriber_id.value !== 'Unused';
});
case 'unused':
return mutableDataList.filter((data) => {
if (data.status.value === 'Revoked') {
return false;
}
return data.subscriber_id.value === 'Unused';
});
default:
break;
}
return null;
},
},
];
Expand Down Expand Up @@ -211,14 +222,12 @@ const revokeInvite = async (code) => {
}
};
const sendInvite = async () => {
const generateInvites = async () => {
loading.value = true;
pageError.value = '';
pageNotification.value = '';
const response = await call('invite/send').post({
email: inviteEmail.value,
}).json();
const response = await call(`invite/generate/${generateCodeAmount.value}`).post().json();
const { data, error } = response;
Expand All @@ -233,8 +242,8 @@ const sendInvite = async () => {
pageError.value = data.value?.detail?.message;
}
} else {
pageNotification.value = t('info.invitationWasSentContext', { email: inviteEmail.value });
inviteEmail.value = '';
pageNotification.value = t('info.invitationGenerated');
generateCodeAmount.value = null;
await refresh();
}
Expand Down

0 comments on commit 0a0c9ba

Please sign in to comment.