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

Implement notification about 64k post limit when uploading to surveyj… #8155

Merged
merged 4 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
91 changes: 43 additions & 48 deletions src/dxSurveyService.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import { settings } from "./settings";
import { surveyLocalization } from "./surveyStrings";

const surveyIOSite = "surveyjs.io";
const surveyIOMaxPostSize = 65536;
/**
* The class contains methods to work with api.surveyjs.io service.
*/
export class dxSurveyService {
public locale: string;
public static get serviceUrl(): string {
return settings.web.surveyServiceUrl;
}
public static set serviceUrl(val: string) {
settings.web.surveyServiceUrl = val;
}
constructor() {}
public loadSurvey(
surveyId: string,
onLoad: (success: boolean, result: string, response: any) => void
) {
public loadSurvey(surveyId: string,
onLoad: (success: boolean, result: string, response: any) => void): void {
var xhr = new XMLHttpRequest();
xhr.open(
"GET",
dxSurveyService.serviceUrl + "/getSurvey?surveyId=" + surveyId
this.serviceUrl + "/getSurvey?surveyId=" + surveyId
);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onload = function () {
Expand All @@ -26,20 +28,12 @@
};
xhr.send();
}
public getSurveyJsonAndIsCompleted(
surveyId: string,
clientId: string,
onLoad: (
success: boolean,
surveyJson: any,
result: string,
response: any
) => void
) {
public getSurveyJsonAndIsCompleted(surveyId: string, clientId: string,
onLoad: (success: boolean, surveyJson: any, result: string, response: any) => void): void {
var xhr = new XMLHttpRequest();
xhr.open(
"GET",
dxSurveyService.serviceUrl +
this.serviceUrl +
"/getSurveyAndIsCompleted?surveyId=" +
surveyId +
"&clientId=" +
Expand All @@ -54,56 +48,57 @@
};
xhr.send();
}
public sendResult(
postId: string,
result: JSON,
public canSendResult(result: JSON): boolean {
if(!this.isSurveJSIOService) return true;
const str = JSON.stringify(result);
return str.length < surveyIOMaxPostSize;
}
public get isSurveJSIOService(): boolean {
return this.serviceUrl.indexOf(surveyIOSite) >= 0;
Dismissed Show dismissed Hide dismissed
}
public sendResult(postId: string, result: JSON,
onSendResult: (success: boolean, response: any, request?: any) => void,
clientId: string = null, isPartialCompleted: boolean = false): void {
if(!this.canSendResult(result)) {
onSendResult(false, surveyLocalization.getString("savingExceedSize", this.locale), undefined);
} else {
this.sendResultCore(postId, result, onSendResult, clientId, isPartialCompleted);
}
}
protected sendResultCore(postId: string, result: JSON,
onSendResult: (success: boolean, response: any, request?: any) => void,
clientId: string = null,
isPartialCompleted: boolean = false
) {
clientId: string = null, isPartialCompleted: boolean = false): void {
var xhr = new XMLHttpRequest();
xhr.open("POST", dxSurveyService.serviceUrl + "/post/");
xhr.open("POST", this.serviceUrl + "/post/");
xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
var data = { postId: postId, surveyResult: JSON.stringify(result) };
if (clientId) (<any>data)["clientId"] = clientId;
if (isPartialCompleted) (<any>data)["isPartialCompleted"] = true;
var dataStringify: string = JSON.stringify(data);
var self = this;
xhr.onload = xhr.onerror = function () {
if (!onSendResult) return;
onSendResult(xhr.status === 200, xhr.response, xhr);
};
xhr.send(dataStringify);
}
public sendFile(
postId: string,
file: File,
onSendFile: (success: boolean, response: any) => void
) {
public sendFile(postId: string, file: File,
onSendFile: (success: boolean, response: any) => void): void {
var xhr = new XMLHttpRequest();
xhr.onload = xhr.onerror = function () {
if (!onSendFile) return;
onSendFile(xhr.status == 200, JSON.parse(xhr.response));
};
xhr.open("POST", dxSurveyService.serviceUrl + "/upload/", true);
xhr.open("POST", this.serviceUrl + "/upload/", true);
var formData = new FormData();
formData.append("file", file);
formData.append("postId", postId);
xhr.send(formData);
}
public getResult(
resultId: string,
name: string,
onGetResult: (
success: boolean,
data: any,
dataList: Array<any>,
response: any
) => void
) {
public getResult(resultId: string, name: string,
onGetResult: (success: boolean, data: any, dataList: Array<any>, response: any) => void): void {
var xhr = new XMLHttpRequest();
var data = "resultId=" + resultId + "&name=" + name;
xhr.open("GET", dxSurveyService.serviceUrl + "/getResult?" + data);
xhr.open("GET", this.serviceUrl + "/getResult?" + data);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
var self = this;
xhr.onload = function () {
Expand All @@ -121,14 +116,11 @@
};
xhr.send();
}
public isCompleted(
resultId: string,
clientId: string,
onIsCompleted: (success: boolean, result: string, response: any) => void
) {
public isCompleted(resultId: string, clientId: string,
onIsCompleted: (success: boolean, result: string, response: any) => void): void {
var xhr = new XMLHttpRequest();
var data = "resultId=" + resultId + "&clientId=" + clientId;
xhr.open("GET", dxSurveyService.serviceUrl + "/isCompleted?" + data);
xhr.open("GET", this.serviceUrl + "/isCompleted?" + data);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
var self = this;
xhr.onload = function () {
Expand All @@ -140,4 +132,7 @@
};
xhr.send();
}
private get serviceUrl(): string {
return dxSurveyService.serviceUrl || "";
}
}
1 change: 1 addition & 0 deletions src/localization/english.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export var englishStrings = {
savingData: "The results are being saved on the server...",
savingDataError: "An error occurred and we could not save the results.",
savingDataSuccess: "The results were saved successfully!",
savingExceedSize: "Your response is exceed the 64K. Please reduce the size of your file(s) or contact an owner of the survey",
saveAgainButton: "Try again",
timerMin: "min",
timerSec: "sec",
Expand Down
38 changes: 14 additions & 24 deletions src/survey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5307,11 +5307,8 @@ export class SurveyModel extends SurveyElementCore
protected createSurveyService(): dxSurveyService {
return new dxSurveyService();
}
protected uploadFilesCore(
name: string,
files: File[],
uploadingCallback: (data: any | Array<any>, errors?: any | Array<any>,) => any
) {
protected uploadFilesCore(name: string, files: File[],
uploadingCallback: (data: any | Array<any>, errors?: any | Array<any>,) => any): void {
var responses: Array<any> = [];
files.forEach((file) => {
if (uploadingCallback) uploadingCallback("uploading", file);
Expand Down Expand Up @@ -5928,11 +5925,7 @@ export class SurveyModel extends SurveyElementCore
* @param clientId A respondent identifier (e-mail or other unique ID). This ID ensures that the respondent does not pass the same survey twice.
* @param isPartial Pass `true` to save partial survey results (see [Continue an Incomplete Survey](https://surveyjs.io/form-library/documentation/handle-survey-results-continue-incomplete)).
*/
public sendResult(
postId: string = null,
clientId: string = null,
isPartial: boolean = false
) {
public sendResult(postId: string = null, clientId: string = null, isPartial: boolean = false): void {
if (!this.isEditMode) return;
if (isPartial && this.onPartialSend) {
this.onPartialSend.fire(this, null);
Expand All @@ -5946,26 +5939,23 @@ export class SurveyModel extends SurveyElementCore
this.clientId = clientId;
}
if (isPartial && !this.clientId) return;
var self = this;
if (this.surveyShowDataSaving) {
const service = this.createSurveyService();
service.locale = this.getLocale();
const showSaving = this.surveyShowDataSaving || (!isPartial && service.isSurveJSIOService);
if (showSaving) {
this.setCompletedState("saving", "");
}
this.createSurveyService().sendResult(
postId,
this.data,
function (success: boolean, response: any, request: any) {
if (self.surveyShowDataSaving) {
service.sendResult(postId, this.data,
(success: boolean, response: any, request: any) => {
if (showSaving || service.isSurveJSIOService) {
if (success) {
self.setCompletedState("success", "");
this.setCompletedState("success", "");
} else {
self.setCompletedState("error", response);
this.setCompletedState("error", response);
}
}
self.onSendResult.fire(self, {
success: success,
response: response,
request: request,
});
const options = { success: success, response: response, request: request };
this.onSendResult.fire(this, options);
},
this.clientId,
isPartial
Expand Down
1 change: 1 addition & 0 deletions tests/entries/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export * from "../question_matrixdropdownbasetests";
export * from "../question_paneldynamic_tests";
export * from "../surveyserializationtests"; //
export * from "../surveytests"; //
export * from "../surveyServiceTests"; //
export * from "../surveyWindowTests"; //
export * from "../surveywidthmodetests"; //
export * from "../surveytriggertests"; //
Expand Down
95 changes: 95 additions & 0 deletions tests/surveyServiceTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { SurveyModel } from "../src/survey";
import { dxSurveyService } from "../src/dxSurveyService";

export default QUnit.module("SurveyServiceTests");

class SurveyServiceTester extends dxSurveyService {
private onSendResult: any;
public doSendResult(success: boolean, response: any, request?: any): void {
if(!!this.onSendResult) {
this.onSendResult(success, response, request);
this.onSendResult = undefined;
}
}
public get isSavingData(): boolean {
return !!this.onSendResult;
}
protected sendResultCore(postId: string, result: JSON,
onSendResult: (success: boolean, response: any, request?: any) => void,
clientId: string = null, isPartialCompleted: boolean = false): void {
this.onSendResult = onSendResult;
}
}

class SurveyModelTester extends SurveyModel {
public serviceTester: SurveyServiceTester;
public notiferList: Array<any> = [];
constructor(jsonObj: any = null) {
super(jsonObj);
}
protected createSurveyService(): dxSurveyService {
this.serviceTester = new SurveyServiceTester();
return this.serviceTester;
}
public notify(message: string, type: string, showActions: boolean = false): void {
this.notiferList.push({ message: message, type: type, showActions: showActions });
}
}

QUnit.test("Always show data saving for surveyjs io service, no errors", function(assert) {
const json = {
surveyPostId: "abc",
elements: [{ type: "comment", name: "q1", defaultValue: 1 }]
};
const survey = new SurveyModelTester(json);
survey.getQuestionByName("q1").value = "0123456789";
survey.doComplete();
assert.equal(survey.serviceTester.isSavingData, true, "The data is saving");
assert.equal(survey.serviceTester.isSurveJSIOService, true, "We are saving data into the surveyjs service");
assert.equal(survey.completedStateText, "The results are being saved on the server...", "do saving #1");
assert.equal(survey.notiferList.length, 1, "notiferList.length #1");
assert.equal(survey.notiferList[0].type, "saving", "notiferList[0].type #1");
survey.serviceTester.doSendResult(true, "", "");
assert.equal(survey.completedStateText, "The results were saved successfully!", "do saving #2");
assert.equal(survey.notiferList.length, 2, "notiferList.length #2");
assert.equal(survey.notiferList[1].type, "success", "notiferList[1].type #2");
});
QUnit.test("Always show data saving for surveyjs io service, with errors", function(assert) {
const json = {
surveyPostId: "abc",
elements: [{ type: "comment", name: "q1" }]
};
const survey = new SurveyModelTester(json);
survey.getQuestionByName("q1").value = "0123456789";
survey.doComplete();
assert.equal(survey.serviceTester.isSavingData, true, "The data is saving");
assert.equal(survey.serviceTester.isSurveJSIOService, true, "We are saving data into the surveyjs service");
assert.equal(survey.completedStateText, "The results are being saved on the server...", "do saving #1");
assert.equal(survey.notiferList.length, 1, "notiferList.length #1");
assert.equal(survey.notiferList[0].type, "saving", "notiferList[0].type #1");
survey.serviceTester.doSendResult(false, "We have an error on the server", "");
assert.equal(survey.completedStateText, "We have an error on the server", "do saving #2");
assert.equal(survey.notiferList.length, 2, "notiferList.length #2");
assert.equal(survey.notiferList[1].type, "error", "notiferList[1].type #2");
});
QUnit.test("Try to save a lot of data into the service", function(assert) {
const json = {
surveyPostId: "abc",
elements: [{ type: "comment", name: "q1" }]
};
const survey = new SurveyModelTester(json);
let largeVal = "";
for(let i = 0; i < 6556; i ++) {
largeVal += "0123456789";
}
survey.getQuestionByName("q1").value = largeVal;
survey.doComplete();
const errorText = "Your response is exceed the 64K. Please reduce the size of your file(s) or contact an owner of the survey";
assert.equal(survey.serviceTester.isSurveJSIOService, true, "We are saving data into the surveyjs service");
assert.equal(survey.serviceTester.isSavingData, false, "The data is not saving. We got an error stright away");
assert.equal(survey.completedStateText, errorText, "try to save");
assert.equal(survey.notiferList.length, 2, "notiferList.length");
assert.equal(survey.notiferList[0].type, "saving", "notiferList[0].type");
assert.equal(survey.notiferList[1].type, "error", "notiferList[1].type");
assert.equal(survey.notiferList[1].message, errorText, "notiferList[1].type");
});