Skip to content

Commit

Permalink
fix(api): handle session.cancelled when session not being saved serve…
Browse files Browse the repository at this point in the history
…r-side
  • Loading branch information
lukashroch committed Sep 2, 2024
1 parent 673c1c0 commit 1e984f5
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 67 deletions.
4 changes: 0 additions & 4 deletions apps/api/__tests__/integration/surveys/clear-session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,6 @@ export default () => {
await suite.sharedTests.assertMissingAuthorization('delete', invalidUrl, { bearer: 'respondent' });
});

it(`should return 404 when session record doesn't exist`, async () => {
await suite.sharedTests.assertMissingRecord('delete', url, { bearer: 'respondent' });
});

it('should return 204 and no content', async () => {
await UserSurveySession.create(input);

Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/http/routers/survey-respondent.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,10 @@ export function surveyRespondent() {

return { status: 200, body: undefined };
},
clearSession: async ({ params: { slug }, req }) => {
clearSession: async ({ params: { slug, sessionId }, req }) => {
const { userId } = req.scope.cradle.user;

await req.scope.cradle.surveyService.clearSession(slug, userId);
await req.scope.cradle.surveyService.clearSession(slug, userId, sessionId);

return { status: 204, body: undefined };
},
Expand Down
11 changes: 1 addition & 10 deletions apps/api/src/jobs/surveys/survey-event-notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import axios from 'axios';
import type { IoC } from '@intake24/api/ioc';
import type { WebhookNotification } from '@intake24/common/types';
import { NotFoundError } from '@intake24/api/http/errors';
import { submissionScope, Survey, SurveySubmission, UserSurveySession } from '@intake24/db';
import { submissionScope, Survey, SurveySubmission } from '@intake24/db';

import NotificationJob from '../notification-job';

Expand All @@ -25,15 +25,6 @@ export default class SurveyEventNotification extends NotificationJob<'SurveyEven
this.logger.debug('Job finished.');
}

private async getSession() {
const { sessionId, surveyId, userId } = this.params;
const session = await UserSurveySession.findOne({ where: { id: sessionId, surveyId, userId } });
if (!session)
throw new NotFoundError('Session not found');

return undefined;
}

private async getSubmission(submissionId: string) {
const submission = await SurveySubmission.findByPk(submissionId, submissionScope());
if (!submission)
Expand Down
78 changes: 35 additions & 43 deletions apps/api/src/services/survey/survey.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type {
SurveyRatingRequest,
SurveyUserInfoResponse,
} from '@intake24/common/types/http';
import type { FindOptions, Includeable, SubmissionScope } from '@intake24/db';
import type { FindOptions, SubmissionScope } from '@intake24/db';
import { ForbiddenError, NotFoundError } from '@intake24/api/http/errors';
import { jwt } from '@intake24/api/util';
import { strongPassword } from '@intake24/common/security';
Expand Down Expand Up @@ -307,26 +307,22 @@ function surveyService({
throw new NotFoundError();

const { id: surveyId, notifications, session } = survey;
const { uxSessionId } = sessionData;

await UserSurveySession.destroy({ where: { userId, surveyId } });

if (session.store) {
await UserSurveySession.create({ id: sessionData.uxSessionId, userId, surveyId, sessionData });
await UserSurveySession.create({ id: uxSessionId, userId, surveyId, sessionData });
}

if (notifications.length && notifications.some(({ type }) => type === 'survey.session.started')
) {
await scheduler.jobs.addJob({
type: 'SurveyEventNotification',
userId,
params: {
type: 'survey.session.started',
sessionId: sessionData.uxSessionId,
surveyId,
userId,
},
});
}
if (!notifications.some(({ type }) => type === 'survey.session.started'))
return;

await scheduler.jobs.addJob({
type: 'SurveyEventNotification',
userId,
params: { type: 'survey.session.started', sessionId: uxSessionId, surveyId, userId },
});
};

/**
Expand Down Expand Up @@ -361,30 +357,22 @@ function surveyService({
*
* @param {string} slug
* @param {string} userId
* @param {string} [clientSessionId]
* @returns
*/
const clearSession = async (slug: string, userId: string) => {
const clearSession = async (slug: string, userId: string, clientSessionId?: string) => {
const survey = await Survey.findBySlug(slug, { attributes: ['id', 'notifications'] });
if (!survey)
throw new NotFoundError();

const session = await UserSurveySession.findOne({ where: { userId, surveyId: survey.id } });
if (!session)
throw new NotFoundError();

const {
id: sessionId,
sessionData: { submissionTime },
} = session;
await session.destroy();

if (
submissionTime
|| !survey.notifications.length
|| !survey.notifications.some(({ type }) => type === 'survey.session.cancelled')
) {
const hasNotifications = survey.notifications.some(({ type }) => type === 'survey.session.cancelled');
const sessionId = session?.id ?? clientSessionId;
await session?.destroy();

if (session?.sessionData.submissionTime || !hasNotifications || !sessionId)
return;
}

await scheduler.jobs.addJob({
type: 'SurveyEventNotification',
Expand Down Expand Up @@ -418,26 +406,24 @@ function surveyService({
const redirectPrompts = surveyScheme.prompts.submission.filter(
(prompt): prompt is Prompts['redirect-prompt'] => prompt.component === 'redirect-prompt',
);
if (!redirectPrompts.length)
const size = redirectPrompts.length;
if (!size)
return null;

const identifiers = redirectPrompts.map(({ identifier }) => identifier);

const include: Includeable[] = [
{ association: 'aliases', where: { surveyId }, required: false },
{ association: 'customFields', where: { name: identifiers }, required: false },
];

const user = await User.findByPk(userId, { attributes: ['id'], include });
const user = await User.findByPk(userId, {
attributes: ['id'],
include: [
{ association: 'aliases', where: { surveyId }, required: false },
{ association: 'customFields', where: { name: identifiers }, required: false },
],
});
if (!user)
throw new NotFoundError();

const { aliases = [], customFields = [] } = user;

const size = redirectPrompts.length;
if (!size)
return null;

const urls = redirectPrompts.reduce<string | null | Record<string, string>>(
(acc, { id, identifier, url }) => {
let identifierValue: string | null;
Expand Down Expand Up @@ -519,8 +505,14 @@ function surveyService({
};

const getSearchSettings = async (slug: string) => {
const survey = await Survey.findBySlug(slug, { attributes: ['searchSettings'], include: ['locale'] });
if (!survey || !survey.locale)
const survey = await Survey.findBySlug(slug, {
attributes: ['searchSettings'],
include: [{
association: 'locale',
attributes: ['code'],
}],
});
if (!survey?.locale)
throw new NotFoundError();

return {
Expand Down
8 changes: 4 additions & 4 deletions apps/survey/src/services/survey.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,15 @@ export default {
},

startUserSession: async (surveyId: string, session: SurveyState) => {
await http.post<SurveyUserSessionResponse>(`surveys/${surveyId}/session`, { session });
await http.post(`surveys/${surveyId}/session`, { session });
},

saveUserSession: async (surveyId: string, session: SurveyState) => {
await http.put<SurveyUserSessionResponse>(`surveys/${surveyId}/session`, { session });
await http.put(`surveys/${surveyId}/session`, { session });
},

clearUserSession: async (surveyId: string) => {
await http.delete(`surveys/${surveyId}/session`);
clearUserSession: async (surveyId: string, sessionId?: string) => {
await http.delete(sessionId ? `surveys/${surveyId}/session/${sessionId}` : `surveys/${surveyId}/session`);
},

submit: async (surveyId: string, submission: SurveyState): Promise<SurveySubmissionResponse> => {
Expand Down
8 changes: 5 additions & 3 deletions apps/survey/src/stores/survey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,8 +315,10 @@ export const useSurvey = defineStore('survey', {
},

async cancelRecall() {
const { uxSessionId } = this.data;

this.clearState();
await this.clearUserSession();
await this.clearUserSession(uxSessionId);
},

setState(payload: CurrentSurveyState) {
Expand Down Expand Up @@ -408,13 +410,13 @@ export const useSurvey = defineStore('survey', {
await surveyService.saveUserSession(this.parameters.slug, this.data);
},

async clearUserSession() {
async clearUserSession(sessionId?: string) {
if (!this.parameters) {
console.error(`Survey parameters not loaded. Cannot clear user session.`);
return;
}

await surveyService.clearUserSession(this.parameters.slug);
await surveyService.clearUserSession(this.parameters.slug, sessionId);
},

async submitRecall() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const surveyRespondent = initContract().router({
},
clearSession: {
method: 'DELETE',
path: '/surveys/:slug/session',
path: '/surveys/:slug/session/:sessionId?',
body: null,
responses: {
204: z.undefined(),
Expand Down

0 comments on commit 1e984f5

Please sign in to comment.