Skip to content

Commit

Permalink
Add version attribute into JSON property and use it in Serializer fix #…
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewtelnov committed Jan 19, 2024
1 parent de7a5d8 commit 7f8fe7c
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 32 deletions.
4 changes: 4 additions & 0 deletions src/base-interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,4 +391,8 @@ export interface IPlainDataOptions {
}
export interface ILoadFromJSONOptions {
validatePropertyValues?: boolean;
}
export interface ISaveToJSONOptions {
storeDefaults?: boolean;
version?: string;
}
6 changes: 3 additions & 3 deletions src/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from "./jsonobject";
import { settings } from "./settings";
import { ItemValue } from "./itemvalue";
import { IElement, IFindElement, IProgressInfo, ISurvey, ILoadFromJSONOptions } from "./base-interfaces";
import { IElement, IFindElement, IProgressInfo, ISurvey, ILoadFromJSONOptions, ISaveToJSONOptions } from "./base-interfaces";
import { ExpressionRunner } from "./conditions";
import { surveyLocalization } from "./surveyStrings";
import { ConsoleWarnings } from "./console-warnings";
Expand Down Expand Up @@ -402,8 +402,8 @@ export class Base {
* Returns a JSON object that corresponds to the current SurveyJS object.
* @see fromJSON
*/
public toJSON(): any {
return new JsonObject().toJsonObject(this);
public toJSON(options?: ISaveToJSONOptions): any {
return new JsonObject().toJsonObject(this, options);
}
/**
* Assigns a new configuration to the current SurveyJS object. This configuration is taken from a passed JSON object.
Expand Down
19 changes: 19 additions & 0 deletions src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,25 @@ export class Helpers {
}
return val;
}
public static compareVerions(ver1: string, ver2: string): number {
if(!ver1 && !ver2) return 0;
const ver1Ar = ver1.split(".");
const ver2Ar = ver2.split(".");
const len1 = ver1Ar.length;
const len2 = ver2Ar.length;
for(let i = 0; i < len1 && i < len2; i ++) {
const str1 = ver1Ar[i];
const str2 = ver2Ar[i];
if(str1.length === str2.length) {
if(str1 !== str2) {
return str1 < str2 ? -1 : 1;
}
} else {
return str1.length < str2.length ? -1 : 1;
}
}
return len1 === len2 ? 0 : (len1 < len2 ? -1 : 1);
}
}
if (!(<any>String.prototype)["format"]) {
(<any>String.prototype)["format"] = function() {
Expand Down
73 changes: 46 additions & 27 deletions src/jsonobject.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { surveyLocalization } from "./surveyStrings";
import { Base, ComputedUpdater } from "./base";
import { Helpers, HashTable } from "./helpers";
import { ILoadFromJSONOptions } from "./base-interfaces";
import { ILoadFromJSONOptions, ISaveToJSONOptions } from "./base-interfaces";

export interface IPropertyDecoratorOptions<T = any> {
defaultValue?: T;
Expand Down Expand Up @@ -202,6 +202,7 @@ export class JsonObjectProperty implements IObject {
"className",
"alternativeName",
"layout",
"version",
"classNamePart",
"baseClassName",
"defaultValue",
Expand Down Expand Up @@ -263,6 +264,7 @@ export class JsonObjectProperty implements IObject {
public minValue: any;
private dataListValue: Array<string>;
public layout: string;
public version: string;
public onSerializeValue: (obj: any) => any;
public onGetValue: (obj: any) => any;
public onSettingValue: (obj: any, value: any) => any;
Expand Down Expand Up @@ -455,6 +457,18 @@ export class JsonObjectProperty implements IObject {
public set visible(val: boolean) {
this.visibleValue = val;
}
public isAvailableInVersion(ver: string): boolean {
if(!!this.alternativeName) return true;
return this.isAvailableInVersionCore(ver);
}
public getSerializedName(ver: string): string {
if(!this.alternativeName) return this.name;
return this.isAvailableInVersionCore(ver) ? this.name : this.alternativeName;
}
private isAvailableInVersionCore(ver: string): boolean {
if(!ver || !this.version) return true;
return Helpers.compareVerions(this.version, ver) <= 0;
}
public get isLocalizable(): boolean {
return this.isLocalizableValue != null ? this.isLocalizableValue : false;
}
Expand Down Expand Up @@ -914,6 +928,9 @@ export class JsonMetadataClass {
if (propInfo.layout) {
prop.layout = propInfo.layout;
}
if (propInfo.version) {
prop.version = propInfo.version;
}
if (propInfo.dependsOn) {
this.addDependsOnProperties(prop, propInfo.dependsOn);
}
Expand Down Expand Up @@ -1566,8 +1583,8 @@ export class JsonObject {
public errors = new Array<JsonError>();
public lightSerializing: boolean = false;
public options: ILoadFromJSONOptions;
public toJsonObject(obj: any, storeDefaults = false): any {
return this.toJsonObjectCore(obj, null, storeDefaults);
public toJsonObject(obj: any, options?: ISaveToJSONOptions | boolean): any {
return this.toJsonObjectCore(obj, null, options);
}
public toObject(jsonObj: any, obj: any, options?: ILoadFromJSONOptions): void {
this.toObjectCore(jsonObj, obj, options);
Expand Down Expand Up @@ -1616,7 +1633,7 @@ export class JsonObject {
public toJsonObjectCore(
obj: any,
property: JsonObjectProperty,
storeDefaults = false
options?: ISaveToJSONOptions | boolean
): any {
if (!obj || !obj.getType) return obj;
if (typeof obj.getData === "function") return obj.getData();
Expand All @@ -1626,17 +1643,24 @@ export class JsonObject {
obj.getType()
);
}
const storeDefaults = options === true;
if(!options || options === true) {
options = { };
}
if(storeDefaults) {
options.storeDefaults = storeDefaults;
}
this.propertiesToJson(
obj,
Serializer.getProperties(obj.getType()),
result,
storeDefaults
options
);
this.propertiesToJson(
obj,
this.getDynamicProperties(obj),
result,
storeDefaults
options
);
return result;
}
Expand All @@ -1663,40 +1687,35 @@ export class JsonObject {
obj: any,
properties: Array<JsonObjectProperty>,
json: any,
storeDefaults = false
options: ISaveToJSONOptions
) {
for (var i: number = 0; i < properties.length; i++) {
this.valueToJson(obj, json, properties[i], storeDefaults);
this.valueToJson(obj, json, properties[i], options);
}
}
public valueToJson(
obj: any,
result: any,
property: JsonObjectProperty,
storeDefaults = false
): void {
if (
property.isSerializable === false ||
(property.isLightSerializable === false && this.lightSerializing)
)
return;
var value = property.getSerializableValue(obj);
if (!storeDefaults && property.isDefaultValueByObj(obj, value)) return;
public valueToJson(obj: any, result: any, prop: JsonObjectProperty, options?: ISaveToJSONOptions): void {
if(!options) options = {};
if (prop.isSerializable === false || (prop.isLightSerializable === false && this.lightSerializing)) return;
if(options.version && !prop.isAvailableInVersion(options.version)) return;
var value = prop.getSerializableValue(obj);
if (!options.storeDefaults && prop.isDefaultValueByObj(obj, value)) return;
if (this.isValueArray(value)) {
var arrValue = [];
for (var i = 0; i < value.length; i++) {
arrValue.push(this.toJsonObjectCore(value[i], property, storeDefaults));
arrValue.push(this.toJsonObjectCore(value[i], prop, options));
}
value = arrValue.length > 0 ? arrValue : null;
} else {
value = this.toJsonObjectCore(value, property, storeDefaults);
value = this.toJsonObjectCore(value, prop, options);
}
if(value === undefined || value === null) return;
const name = prop.getSerializedName(options.version);
var hasValue =
typeof obj["getPropertyValue"] === "function" &&
obj["getPropertyValue"](property.name, null) !== null;
if ((storeDefaults && hasValue) || !property.isDefaultValueByObj(obj, value)) {
if (!Serializer.onSerializingProperty || !Serializer.onSerializingProperty(obj, property, value, result)) {
result[property.name] = this.removePosOnValueToJson(property, value);
obj["getPropertyValue"](name, null) !== null;
if ((options.storeDefaults && hasValue) || !prop.isDefaultValueByObj(obj, value)) {
if (!Serializer.onSerializingProperty || !Serializer.onSerializingProperty(obj, prop, value, result)) {
result[name] = this.removePosOnValueToJson(prop, value);
}
}
}
Expand Down
15 changes: 14 additions & 1 deletion tests/helperstests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -527,4 +527,17 @@ QUnit.test("base.equals", function(assert) {
assert.equal(Helpers.isTwoValueEquals(q1, q3), true, "#2");
assert.equal(Helpers.isTwoValueEquals(q1, q4), false, "#3");
assert.equal(Helpers.isTwoValueEquals(q1, q5), false, "#4");
});
});
QUnit.test("compareVersions", function(assert) {
assert.equal(Helpers.compareVerions("", ""), 0, "#1");
assert.equal(Helpers.compareVerions("1", ""), 1, "#2");
assert.equal(Helpers.compareVerions("", "1"), -1, "#3");
assert.equal(Helpers.compareVerions("1.2.3", "1.2.3"), 0, "#4");
assert.equal(Helpers.compareVerions("1.201.31", "1.201.31"), 0, "#5");
assert.equal(Helpers.compareVerions("1.201.31", "1.90.31"), 1, "#6");
assert.equal(Helpers.compareVerions("1.90.31", "1.201.31"), -1, "#7");
assert.equal(Helpers.compareVerions("1", "1.2.3"), -1, "#8");
assert.equal(Helpers.compareVerions("1.2.3", "1"), 1, "#9");
assert.equal(Helpers.compareVerions("1.2", "1.2.3"), -1, "#10");
assert.equal(Helpers.compareVerions("1.2.3", "1.2"), 1, "#11");
});
45 changes: 44 additions & 1 deletion tests/jsonobjecttests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1846,8 +1846,10 @@ QUnit.test(
assert.equal(
json["readOnly"],
false,
"default value for readOnly proeprty has been serialzied successfully"
"default value for readOnly property has been serialzied successfully"
);
const jsonKeys = Object.keys(json);
assert.equal(jsonKeys.indexOf("validators"), -1, "no validators");

q2.readOnly = true;
new JsonObject().toObject(json, q2);
Expand Down Expand Up @@ -3148,3 +3150,44 @@ QUnit.test("Check existing pos", function (assert) {
assert.deepEqual(json, { name: "question1", testProperty: { someProperty: "bbb" } }, "no pos in json");
Serializer.removeProperty("question", "testProperty");
});
QUnit.test("Versions in property", function (assert) {
const prop = Serializer.addProperty("question", { name: "testProperty", version: "1.9.127" });
assert.equal(prop.version, "1.9.127", "version is set correclty");
assert.equal(prop.isAvailableInVersion("1.9.5"), false, "#1");
assert.equal(prop.isAvailableInVersion("1.9.200"), true, "#2");
assert.equal(prop.isAvailableInVersion("2"), true, "#3");
assert.equal(prop.isAvailableInVersion("1"), false, "#4");
assert.equal(prop.isAvailableInVersion("1.9.127"), true, "#5");
assert.equal(prop.isAvailableInVersion("1.9.126"), false, "#6");
assert.equal(prop.isAvailableInVersion("1.9.128"), true, "#7");
assert.equal(prop.isAvailableInVersion(""), true, "#8");
Serializer.removeProperty("question", "testProperty");
});
QUnit.test("Versions in new property serialization", function (assert) {
Serializer.addProperty("question", { name: "testProperty", version: "1.9.127" });
const question = new QuestionTextModel("q1");
question.testProperty = "abc";
assert.deepEqual(question.toJSON(), { name: "q1", testProperty: "abc" }, "#1");
assert.deepEqual(question.toJSON({ version: "1.9.127" }), { name: "q1", testProperty: "abc" }, "#2");
assert.deepEqual(question.toJSON({ version: "1.9.126" }), { name: "q1" }, "#3");
Serializer.removeProperty("question", "testProperty");
});
QUnit.test("Versions & alternative name", function (assert) {
const prop = Serializer.addProperty("question", { name: "testProperty", version: "1.9.127", alternativeName: "testProp" });

assert.equal(prop.isAvailableInVersion("1.9.5"), true, "isAvailableInVersion: #1");
assert.equal(prop.isAvailableInVersion("1.9.200"), true, "isAvailableInVersion: #2");
assert.equal(prop.isAvailableInVersion(""), true, "isAvailableInVersion: #3");
assert.equal(prop.getSerializedName("1.9.5"), "testProp", "getSerializedName: #1");
assert.equal(prop.getSerializedName("1.9.200"), "testProperty", "getSerializedName: #2");
assert.equal(prop.getSerializedName(""), "testProperty", "getSerializedName: #3");

const question = new QuestionTextModel("q1");
question.testProperty = "abc";
assert.deepEqual(question.toJSON(), { name: "q1", testProperty: "abc" }, "#1");
assert.deepEqual(question.toJSON({ version: "1.9.127" }), { name: "q1", testProperty: "abc" }, "#2");
assert.deepEqual(question.toJSON({ version: "1.9.128" }), { name: "q1", testProperty: "abc" }, "#3");
assert.deepEqual(question.toJSON({ version: "1.9.126" }), { name: "q1", testProp: "abc" }, "#4");
assert.deepEqual(question.toJSON({ version: "1" }), { name: "q1", testProp: "abc" }, "#5");
Serializer.removeProperty("question", "testProperty");
});

0 comments on commit 7f8fe7c

Please sign in to comment.