Skip to content

Commit dedbd66

Browse files
ArthurTrauterGerrit Code Review
authored andcommitted
Merge "[INTERNAL][FIX] sap.ui.fl: Rebuild filteredResponse after storageResponse update" into rel-1.120
2 parents 0bf6464 + d1e0259 commit dedbd66

File tree

6 files changed

+222
-133
lines changed

6 files changed

+222
-133
lines changed

src/sap.ui.fl/src/sap/ui/fl/apply/_internal/flexState/FlexState.js

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -560,12 +560,13 @@ sap.ui.define([
560560

561561
/**
562562
* Some save operations don't require a complete new data request, so the storage response gets a live update.
563-
* This will not invalidate the DataSelectors
563+
* This will also update the runtime persistence.
564564
*
565565
* @param {string} sReference - Flex reference of the app
566566
* @param {object[]} aUpdates - All new FlexObjects in JSON format
567567
*/
568568
FlexState.updateStorageResponse = function(sReference, aUpdates) {
569+
const aFlexObjectUpdates = [];
569570
aUpdates.forEach((oUpdate) => {
570571
if (oUpdate.type === "ui2") {
571572
_mInstances[sReference].unfilteredStorageResponse.changes.ui2personalization = oUpdate.newData;
@@ -574,23 +575,55 @@ sap.ui.define([
574575
const sFileName = oUpdate.flexObject.fileName;
575576
const aUnfiltered = ObjectPath.get(vPath, _mInstances[sReference].unfilteredStorageResponse.changes);
576577
const aFiltered = ObjectPath.get(vPath, _mInstances[sReference].storageResponse.changes);
578+
const iExistingFlexObjectIdx = _mInstances[sReference].runtimePersistence.flexObjects.findIndex(
579+
(oFlexObject) => oFlexObject.getId() === sFileName
580+
);
581+
let iFilteredIdx;
582+
let iUnfilteredIdx;
583+
const oExistingFlexObject = _mInstances[sReference].runtimePersistence.flexObjects[iExistingFlexObjectIdx];
577584
switch (oUpdate.type) {
578585
case "add":
579586
aUnfiltered.push(oUpdate.flexObject);
580587
aFiltered.push(oUpdate.flexObject);
588+
if (iExistingFlexObjectIdx < 0) {
589+
throw new Error("Flex response includes unknown flex object");
590+
}
581591
break;
582592
case "delete":
583-
aFiltered.splice(aFiltered.findIndex((oFlexObject) => oFlexObject.fileName === sFileName), 1);
584-
aUnfiltered.splice(aUnfiltered.findIndex((oFlexObject) => oFlexObject.fileName === sFileName), 1);
593+
iFilteredIdx = aFiltered.findIndex((oFlexObject) => oFlexObject.fileName === sFileName);
594+
if (iFilteredIdx > -1) {
595+
aFiltered.splice(iFilteredIdx, 1);
596+
}
597+
iUnfilteredIdx = aUnfiltered.findIndex((oFlexObject) => oFlexObject.fileName === sFileName);
598+
if (iUnfilteredIdx > -1) {
599+
aUnfiltered.splice(iUnfilteredIdx, 1);
600+
}
601+
if (iExistingFlexObjectIdx >= 0) {
602+
_mInstances[sReference].runtimePersistence.flexObjects.splice(iExistingFlexObjectIdx, 1);
603+
aFlexObjectUpdates.push({ type: "removeFlexObject", updatedObject: oExistingFlexObject });
604+
}
585605
break;
586606
case "update":
587-
aFiltered.splice(aFiltered.findIndex((oFlexObject) => oFlexObject.fileName === sFileName), 1, oUpdate.flexObject);
588-
aUnfiltered.splice(aUnfiltered.findIndex((oFlexObject) => oFlexObject.fileName === sFileName), 1, oUpdate.flexObject);
607+
iFilteredIdx = aFiltered.findIndex((oFlexObject) => oFlexObject.fileName === sFileName);
608+
if (iFilteredIdx > -1) {
609+
aFiltered.splice(iFilteredIdx, 1, oUpdate.flexObject);
610+
}
611+
iUnfilteredIdx = aUnfiltered.findIndex((oFlexObject) => oFlexObject.fileName === sFileName);
612+
if (iUnfilteredIdx > -1) {
613+
aUnfiltered.splice(iUnfilteredIdx, 1, oUpdate.flexObject);
614+
}
615+
if (oExistingFlexObject && oExistingFlexObject.getState() !== States.LifecycleState.PERSISTED) {
616+
oExistingFlexObject.setResponse(oUpdate.flexObject);
617+
aFlexObjectUpdates.push({ type: "updateFlexObject", updatedObject: oExistingFlexObject });
618+
}
589619
break;
590620
default:
591621
}
592622
}
593623
});
624+
if (aFlexObjectUpdates.length > 0) {
625+
oFlexObjectsDataSelector.checkUpdate({ reference: sReference }, aFlexObjectUpdates);
626+
}
594627
};
595628

596629
FlexState.clearState = function(sReference) {

src/sap.ui.fl/test/sap/ui/fl/qunit/ChangePersistence.qunit.js

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -46,35 +46,36 @@ sap.ui.define([
4646
) {
4747
"use strict";
4848

49-
var sandbox = sinon.createSandbox();
50-
var aControls = [];
49+
const sandbox = sinon.createSandbox();
50+
const aControls = [];
51+
const sComponentName = "MyComponent";
5152

5253
function getInitialChangesMap(mPropertyBag) {
5354
return merge(DependencyHandler.createEmptyDependencyMap(), mPropertyBag);
5455
}
5556

5657
QUnit.module("sap.ui.fl.ChangePersistence", {
57-
beforeEach() {
58-
sandbox.stub(FlexState, "initialize").resolves();
58+
async beforeEach() {
5959
sandbox.stub(VariantManagementState, "getInitialChanges").returns([]);
6060
this._mComponentProperties = {
61-
name: "MyComponent"
61+
name: sComponentName
6262
};
6363
this.oChangePersistence = new ChangePersistence(this._mComponentProperties);
6464

65-
return Component.create({
65+
const oComponent = await Component.create({
6666
name: "sap.ui.fl.qunit.integration.testComponentComplex",
6767
manifest: false
68-
}).then(function(oComponent) {
69-
this._oComponentInstance = oComponent;
70-
this.oControl = new Control("abc123");
71-
aControls.push(this.oControl);
72-
this.oControlWithComponentId = new Control(oComponent.createId("abc123"));
73-
aControls.push(this.oControlWithComponentId);
74-
}.bind(this));
68+
});
69+
this._oComponentInstance = oComponent;
70+
this.oControl = new Control("abc123");
71+
aControls.push(this.oControl);
72+
this.oControlWithComponentId = new Control(oComponent.createId("abc123"));
73+
aControls.push(this.oControlWithComponentId);
74+
await FlQUnitUtils.initializeFlexStateWithData(sandbox, sComponentName);
7575
},
7676
afterEach() {
7777
sandbox.restore();
78+
FlexState.clearState(sComponentName);
7879
this._oComponentInstance.destroy();
7980
aControls.forEach(function(control) {
8081
control.destroy();
@@ -1006,7 +1007,7 @@ sap.ui.define([
10061007
assert.equal(fnGetCompEntitiesByIdMapStub.callCount, 1, "then getCompEntitiesByIdMap called once");
10071008
assert.equal(oResetChangesStub.callCount, 1, "Storage.reset is called once");
10081009
var oResetArgs = oResetChangesStub.getCall(0).args[0];
1009-
assert.equal(oResetArgs.reference, "MyComponent");
1010+
assert.equal(oResetArgs.reference, sComponentName);
10101011
assert.equal(oResetArgs.layer, Layer.CUSTOMER);
10111012
assert.equal(oResetArgs.changes.length, 3); // oCUSTOMERChange1, oCUSTOMERChange2, oMockCompVariant3
10121013
assert.equal(oResetArgs.changes[0].getId(), "oCUSTOMERChange1");
@@ -1019,7 +1020,7 @@ sap.ui.define([
10191020
});
10201021
});
10211022

1022-
QUnit.test("when calling resetChanges with selector and change type (control reset)", function(assert) {
1023+
QUnit.test("when calling resetChanges with selector and change type (control reset)", async function(assert) {
10231024
// changes for the component
10241025
var oVENDORChange1 = FlexObjectFactory.createFromFileContent({
10251026
fileType: "change",
@@ -1059,27 +1060,32 @@ sap.ui.define([
10591060
sandbox.stub(this.oChangePersistence, "getChangesForComponent").resolves(aChanges);
10601061
var aDeletedChangeContentIds = {response: [{fileName: "1"}, {fileName: "2"}]};
10611062

1062-
var oResetChangesStub = sandbox.stub(WriteStorage, "reset").resolves(aDeletedChangeContentIds);
1063-
var oUpdateStorageResponseStub = sandbox.stub(FlexState, "updateStorageResponse");
1064-
var oGetChangesFromMapByNamesStub = sandbox.stub(this.oChangePersistence, "_getChangesFromMapByNames").returns(aChanges);
1065-
1066-
return this.oChangePersistence.resetChanges(Layer.VENDOR, "", ["abc123"], ["labelChange"]).then(function() {
1067-
assert.equal(oResetChangesStub.callCount, 1, "Storage.reset is called once");
1068-
var oResetArgs = oResetChangesStub.getCall(0).args[0];
1069-
assert.equal(oResetArgs.reference, "MyComponent");
1070-
assert.equal(oResetArgs.layer, Layer.VENDOR);
1071-
assert.deepEqual(oResetArgs.selectorIds, ["abc123"]);
1072-
assert.deepEqual(oResetArgs.changeTypes, ["labelChange"]);
1073-
assert.equal(oUpdateStorageResponseStub.callCount, 1, "the FlexState is called once");
1074-
assert.deepEqual(oUpdateStorageResponseStub.args[0][1],
1075-
aChanges.map((oFlexObject) => {
1076-
return {flexObject: oFlexObject.convertToFileContent(), type: "delete"};
1077-
}),
1078-
"and with the correct names"
1079-
);
1080-
assert.equal(oGetChangesFromMapByNamesStub.callCount, 1, "the getChangesFromMapByNames is called once");
1081-
assert.deepEqual(oGetChangesFromMapByNamesStub.args[0][0], ["1", "2"], "and with the correct names");
1082-
});
1063+
const oResetChangesStub = sandbox.stub(WriteStorage, "reset").resolves(aDeletedChangeContentIds);
1064+
const oUpdateStorageResponseSpy = sandbox.spy(FlexState, "updateStorageResponse");
1065+
const oGetChangesFromMapByNamesStub = sandbox.stub(this.oChangePersistence, "_getChangesFromMapByNames").returns(aChanges);
1066+
1067+
await this.oChangePersistence.resetChanges(Layer.VENDOR, "", ["abc123"], ["labelChange"]);
1068+
1069+
assert.strictEqual(oResetChangesStub.callCount, 1, "Storage.reset is called once");
1070+
const oResetArgs = oResetChangesStub.getCall(0).args[0];
1071+
assert.strictEqual(oResetArgs.reference, sComponentName);
1072+
assert.strictEqual(oResetArgs.layer, Layer.VENDOR);
1073+
assert.deepEqual(oResetArgs.selectorIds, ["abc123"]);
1074+
assert.deepEqual(oResetArgs.changeTypes, ["labelChange"]);
1075+
assert.strictEqual(oUpdateStorageResponseSpy.callCount, 1, "the FlexState.updateStorageResponse is called once");
1076+
assert.deepEqual(oUpdateStorageResponseSpy.args[0][1],
1077+
aChanges.map((oFlexObject) => {
1078+
return {flexObject: oFlexObject.convertToFileContent(), type: "delete"};
1079+
}),
1080+
"and with the correct names"
1081+
);
1082+
assert.strictEqual(
1083+
FlexState.getFlexObjectsDataSelector().get({reference: sComponentName}).length,
1084+
0,
1085+
"then the change is also removed from the flex state"
1086+
);
1087+
assert.strictEqual(oGetChangesFromMapByNamesStub.callCount, 1, "the getChangesFromMapByNames is called once");
1088+
assert.deepEqual(oGetChangesFromMapByNamesStub.args[0][0], ["1", "2"], "and with the correct names");
10831089
});
10841090
});
10851091

src/sap.ui.fl/test/sap/ui/fl/qunit/apply/_internal/flexState/FlexState.qunit.js

Lines changed: 102 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,21 +1227,29 @@ sap.ui.define([
12271227
await FlexState.initialize({
12281228
reference: sReference
12291229
});
1230+
const oChangePersistence = ChangePersistenceFactory.getChangePersistenceForComponent(sReference);
12301231
// initial data
1231-
FlexState.updateStorageResponse(sReference, [
1232-
{type: "add", flexObject: FlexObjectFactory.createUIChange({id: "initialUIChange1"}).convertToFileContent()},
1233-
{type: "add", flexObject: FlexObjectFactory.createUIChange({id: "initialUIChange2", variantReference: "flVariant12"}).convertToFileContent()},
1234-
{type: "add", flexObject: FlexObjectFactory.createUIChange({id: "initialUIChange3", fileType: "ctrl_variant_change"}).convertToFileContent()},
1235-
{type: "add", flexObject: FlexObjectFactory.createUIChange({id: "initialUIChange4", fileType: "ctrl_variant_management_change"}).convertToFileContent()},
1236-
{type: "add", flexObject: FlexObjectFactory.createFlVariant({id: "initialFlVariant1"}).convertToFileContent()},
1237-
{type: "add", flexObject: FlexObjectFactory.createCompVariant({id: "initialCompVariant1"}).convertToFileContent()},
1238-
{type: "add", flexObject: FlexObjectFactory.createUIChange({
1232+
const aInitialChanges = [
1233+
FlexObjectFactory.createUIChange({id: "initialUIChange1"}),
1234+
FlexObjectFactory.createUIChange({id: "initialUIChange2", variantReference: "flVariant12"}),
1235+
FlexObjectFactory.createUIChange({id: "initialUIChange3", fileType: "ctrl_variant_change"}),
1236+
FlexObjectFactory.createUIChange({id: "initialUIChange4", fileType: "ctrl_variant_management_change"}),
1237+
FlexObjectFactory.createFlVariant({id: "initialFlVariant1"}),
1238+
FlexObjectFactory.createCompVariant({id: "initialCompVariant1"}),
1239+
FlexObjectFactory.createUIChange({
12391240
id: "initialUIChange5",
12401241
selector: {
12411242
persistencyKey: "foo"
12421243
}
1243-
}).convertToFileContent()}
1244-
]);
1244+
})
1245+
];
1246+
aInitialChanges.forEach(function(oFlexObject) {
1247+
oChangePersistence.addDirtyChange(oFlexObject);
1248+
});
1249+
FlexState.updateStorageResponse(sReference, aInitialChanges.map((flexObject) => ({
1250+
type: "add",
1251+
flexObject: flexObject.convertToFileContent()
1252+
})));
12451253
FlexState.rebuildFilteredResponse(sReference);
12461254
this.oUIChange = FlexObjectFactory.createUIChange({
12471255
id: "uiChange1"
@@ -1277,16 +1285,26 @@ sap.ui.define([
12771285
}
12781286
}, function() {
12791287
QUnit.test("with all operations at once", async function(assert) {
1280-
let aFlexObjects = FlexState.getFlexObjectsDataSelector().get({ reference: sReference });
1288+
const oFlexObjectsDataSelector = FlexState.getFlexObjectsDataSelector();
1289+
let aFlexObjects = oFlexObjectsDataSelector.get({ reference: sReference });
12811290
assert.strictEqual(aFlexObjects.length, 8, "initially there are 8 flexObjects");
1291+
const aNewChanges = [
1292+
this.oUIChange,
1293+
this.oVariantChange1,
1294+
this.oVariantChange2,
1295+
this.oVariantDepUIChange,
1296+
this.oFlVariant,
1297+
this.oCompVariant,
1298+
this.oCompChange
1299+
];
1300+
aNewChanges.forEach(function(oFlexObject) {
1301+
FlexState.addDirtyFlexObject(sReference, oFlexObject);
1302+
});
12821303
FlexState.updateStorageResponse(sReference, [
1283-
{type: "add", flexObject: this.oUIChange.convertToFileContent()},
1284-
{type: "add", flexObject: this.oVariantChange1.convertToFileContent()},
1285-
{type: "add", flexObject: this.oVariantChange2.convertToFileContent()},
1286-
{type: "add", flexObject: this.oVariantDepUIChange.convertToFileContent()},
1287-
{type: "add", flexObject: this.oFlVariant.convertToFileContent()},
1288-
{type: "add", flexObject: this.oCompVariant.convertToFileContent()},
1289-
{type: "add", flexObject: this.oCompChange.convertToFileContent()},
1304+
...aNewChanges.map((flexObject) => ({
1305+
type: "add",
1306+
flexObject: flexObject.convertToFileContent()
1307+
})),
12901308
{type: "ui2", newData: "ui2"}
12911309
]);
12921310
const oStorageResponse = await FlexState.getStorageResponse(sReference);
@@ -1299,25 +1317,25 @@ sap.ui.define([
12991317
assert.strictEqual(oStorageResponse.changes.variants.length, 2, "fl variant was added");
13001318
assert.strictEqual(oStorageResponse.changes.ui2personalization, "ui2", "ui2 was set");
13011319

1302-
FlexState.rebuildFilteredResponse(sReference);
1303-
aFlexObjects = FlexState.getFlexObjectsDataSelector().get({ reference: sReference });
1320+
aFlexObjects = oFlexObjectsDataSelector.get({ reference: sReference });
13041321
assert.strictEqual(aFlexObjects.length, 15, "all flexObjects are part of the DataSelector");
13051322

13061323
this.oFlVariant.setFavorite(true);
13071324
this.oCompVariant.setFavorite(true);
13081325
this.oUIChange.setContent("foo");
13091326
this.oCompChange.setContent("bar");
13101327

1328+
const oUpdateSpy = sandbox.spy(oFlexObjectsDataSelector, "checkUpdate");
1329+
const aUpdates = [this.oUIChange, this.oFlVariant, this.oCompVariant, this.oCompChange];
13111330
FlexState.updateStorageResponse(sReference, [
1312-
{type: "update", flexObject: this.oUIChange.convertToFileContent()},
1313-
{type: "update", flexObject: this.oFlVariant.convertToFileContent()},
1314-
{type: "update", flexObject: this.oCompVariant.convertToFileContent()},
1315-
{type: "update", flexObject: this.oCompChange.convertToFileContent()},
1331+
...aUpdates.map((flexObject) => ({
1332+
type: "update",
1333+
flexObject: flexObject.convertToFileContent()
1334+
})),
13161335
{type: "ui2", newData: "newUi2"}
13171336
]);
13181337
assert.strictEqual(oStorageResponse.changes.ui2personalization, "newUi2", "ui2 was set");
1319-
FlexState.rebuildFilteredResponse(sReference);
1320-
aFlexObjects = FlexState.getFlexObjectsDataSelector().get({ reference: sReference });
1338+
aFlexObjects = oFlexObjectsDataSelector.get({ reference: sReference });
13211339
assert.strictEqual(aFlexObjects.length, 15, "all flexObjects are part of the DataSelector");
13221340
assert.strictEqual(
13231341
aFlexObjects.find((oFlexObject) => oFlexObject.getId() === "uiChange1").getContent(),
@@ -1336,14 +1354,12 @@ sap.ui.define([
13361354
"bar", "the content was updated"
13371355
);
13381356

1339-
FlexState.updateStorageResponse(sReference, [
1340-
{type: "delete", flexObject: this.oVariantDepUIChange.convertToFileContent()},
1341-
{type: "delete", flexObject: this.oFlVariant.convertToFileContent()},
1342-
{type: "delete", flexObject: this.oCompVariant.convertToFileContent()},
1343-
{type: "delete", flexObject: this.oCompChange.convertToFileContent()}
1344-
]);
1345-
FlexState.rebuildFilteredResponse(sReference);
1346-
aFlexObjects = FlexState.getFlexObjectsDataSelector().get({ reference: sReference });
1357+
const aDeletes = [this.oVariantDepUIChange, this.oFlVariant, this.oCompVariant, this.oCompChange];
1358+
FlexState.updateStorageResponse(sReference, aDeletes.map((flexObject) => ({
1359+
type: "delete",
1360+
flexObject: flexObject.convertToFileContent()
1361+
})));
1362+
aFlexObjects = oFlexObjectsDataSelector.get({ reference: sReference });
13471363
assert.strictEqual(aFlexObjects.length, 11, "all remaining flexObjects are part of the DataSelector");
13481364
assert.notOk(
13491365
aFlexObjects.find((oFlexObject) => oFlexObject.getId() === this.oVariantDepUIChange.getId()),
@@ -1361,6 +1377,58 @@ sap.ui.define([
13611377
aFlexObjects.find((oFlexObject) => oFlexObject.getId() === this.oCompChange.getId()),
13621378
"the flexObject was deleted"
13631379
);
1380+
assert.ok(
1381+
oUpdateSpy.firstCall.args[1].every((oUpdateInfo, iIdx) => {
1382+
return oUpdateInfo.type === "updateFlexObject" && oUpdateInfo.updatedObject === aUpdates[iIdx];
1383+
}),
1384+
"the data selector was updated with the correct operation"
1385+
);
1386+
assert.ok(
1387+
oUpdateSpy.secondCall.args[1].every((oUpdateInfo, iIdx) => {
1388+
return oUpdateInfo.type === "removeFlexObject" && oUpdateInfo.updatedObject === aDeletes[iIdx];
1389+
}),
1390+
"the data selector was updated with the correct operation"
1391+
);
1392+
});
1393+
1394+
QUnit.test("when only ui2personalization is updated", async function(assert) {
1395+
const oUpdateFlexObjectsSpy = sandbox.spy(FlexState.getFlexObjectsDataSelector(), "checkUpdate");
1396+
await FlexState.updateStorageResponse(sReference, [
1397+
{type: "ui2", newData: "ui2"}
1398+
]);
1399+
assert.strictEqual(oUpdateFlexObjectsSpy.callCount, 0, "then the flex objects data selector is not updated");
1400+
});
1401+
1402+
QUnit.test("when adding a flex object that is not part of the runtime pertsistence", function(assert) {
1403+
assert.throws(
1404+
function() {
1405+
FlexState.updateStorageResponse(sReference, [
1406+
{type: "add", flexObject: {id: "unknownObject"}}
1407+
]);
1408+
},
1409+
"then an error is thrown"
1410+
);
1411+
});
1412+
1413+
QUnit.test("when updating the storage response", function(assert) {
1414+
FlexState.addDirtyFlexObject(sReference, this.oUIChange);
1415+
FlexState.updateStorageResponse(sReference, [
1416+
{type: "add", flexObject: this.oUIChange.convertToFileContent()}
1417+
]);
1418+
FlexState.updateStorageResponse(sReference, [
1419+
{
1420+
type: "update",
1421+
flexObject: {
1422+
...this.oUIChange.convertToFileContent(),
1423+
...{content: "bar"}
1424+
}
1425+
}
1426+
]);
1427+
assert.strictEqual(
1428+
this.oUIChange.getContent(),
1429+
"bar",
1430+
"then the content of the runtime persistence object is also updated"
1431+
);
13641432
});
13651433
});
13661434

src/sap.ui.fl/test/sap/ui/fl/qunit/write/_internal/flexState/FlexObjectState.qunit.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ sap.ui.define([
5959
changeType: "addGroup",
6060
layer: Layer.USER
6161
});
62+
oChangePersistence.addDirtyChanges([oChangeInPersistence1, oChangeInPersistence2]);
63+
// Some tests require persisted changes
6264
sandbox.stub(oChangePersistence, "getChangesForComponent").resolves([oChangeInPersistence1, oChangeInPersistence2]);
6365
}
6466

0 commit comments

Comments
 (0)