Files
formbricks-formbricks/packages/lib/responseQueue.ts
tyjkerr a35d573884 feat: add single use survey links (#742)
* add uniqueResponseId to link survey

* add uniqueResponseId to survey response

* add singUseId to database and link survey

* add singleUseId to api

* add single use option in survey response options

* add single use to getSurvey

* add getResponseBySingleUseId

* add ZSurveySingleUse schema to survey schema

* add logic to check if link with suid has response

* pass singleUseId as props, revert SWR changes

* generation of single-use url in LinkSurveyModal

* add singleUseId to SingleResponseCard

* update SurveyInactive for invalid link

* add suId to ZResponse schema

* fix typo in SurveyInactive

* update ResponseOptionCard

* add suId to response select

* add default message for SurveyLinkUsed

* update logic to render SurveyLinkUsed

* add comment for suId in prisma schema

* fix types

* refresh server component on save survey

* update logic

* fix build errors

* fix prisma schema

* add db migration

* update wording

* add singleUseId to localstorage

* fix survey link used over thank you

* add suid to people responses

* fix preview and copy link on surveys page.

* update text and icon for link survey modal

* check survey not finished before setting question

* update show surveylink used logic

* add zodtype to prisma

* fix logic to render last question answered/stored

* add better comments

* update default message for single use surveys

* add LinkSingleUseSurveyModal

* add guard before getting response with suid

* fix build error

* add default message for link used page

* add key and group imports

* add suId encryption and validation

* make survey url encryption optional

* fix build errors

* move singleUseId to server side in surveyList

* added validation to getResponseBySingleUseId service

* restored env var names

* import FORMBRICKS_ENCRYPTION_KEY from constants

* check if encryption environment variable is set, add length validation for env variable

---------

Co-authored-by: Ty Kerr <tykerr@Tys-MacBook-Pro.local>
Co-authored-by: Johannes <johannes@formbricks.com>
Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
Co-authored-by: Dhruwang <dhruwangjariwala18@gmail.com>
2023-10-03 18:26:13 +02:00

103 lines
3.3 KiB
TypeScript

import { TResponseUpdate } from "@formbricks/types/v1/responses";
import { createResponse, updateResponse } from "./client/response";
import SurveyState from "./surveyState";
import { markDisplayResponded } from "./client/display";
interface QueueConfig {
apiHost: string;
retryAttempts: number;
onResponseSendingFailed?: (responseUpdate: TResponseUpdate) => void;
setSurveyState?: (state: SurveyState) => void;
personId?: string;
}
export class ResponseQueue {
private queue: TResponseUpdate[] = [];
private config: QueueConfig;
private surveyState: SurveyState;
private isRequestInProgress = false;
constructor(config: QueueConfig, surveyState: SurveyState) {
this.config = config;
this.surveyState = surveyState;
}
add(responseUpdate: TResponseUpdate) {
// update survey state
this.surveyState.accumulateResponse(responseUpdate);
if (this.config.setSurveyState) {
this.config.setSurveyState(this.surveyState);
}
// add response to queue
this.queue.push(responseUpdate);
this.processQueue();
}
async processQueue() {
if (this.isRequestInProgress) return;
if (this.queue.length === 0) return;
this.isRequestInProgress = true;
const responseUpdate = this.queue[0];
let attempts = 0;
while (attempts < this.config.retryAttempts) {
const success = await this.sendResponse(responseUpdate);
if (success) {
this.queue.shift(); // remove the successfully sent response from the queue
break; // exit the retry loop
}
console.error("Formbricks: Failed to send response. Retrying...", attempts);
attempts++;
}
if (attempts >= this.config.retryAttempts) {
// Inform the user after 2 failed attempts
console.error("Failed to send response after 2 attempts.");
// If the response is finished and thus fails finally, inform the user
if (this.surveyState.responseAcc.finished && this.config.onResponseSendingFailed) {
this.config.onResponseSendingFailed(this.surveyState.responseAcc);
}
this.queue.shift(); // remove the failed response from the queue
}
this.isRequestInProgress = false;
this.processQueue(); // process the next item in the queue if any
}
async sendResponse(responseUpdate: TResponseUpdate): Promise<boolean> {
try {
if (this.surveyState.responseId !== null) {
await updateResponse(responseUpdate, this.surveyState.responseId, this.config.apiHost);
} else {
const response = await createResponse(
{
...responseUpdate,
surveyId: this.surveyState.surveyId,
personId: this.config.personId || null,
singleUseId: this.surveyState.singleUseId || null,
},
this.config.apiHost
);
if (this.surveyState.displayId) {
markDisplayResponded(this.surveyState.displayId, this.config.apiHost);
}
this.surveyState.updateResponseId(response.id);
if (this.config.setSurveyState) {
this.config.setSurveyState(this.surveyState);
}
}
return true;
} catch (error) {
console.error(error);
return false;
}
}
// update surveyState
updateSurveyState(surveyState: SurveyState) {
this.surveyState = surveyState;
}
}