Skip to content

Commit ab21e1c

Browse files
committed
bridge: put targetValue into statement only on save
This moves the mutation of the statementState to only after successful persistence (and round trip) to the server, allowing us to attempt a retry in the future. Consequently, the originalStatement state became obsolete and could be removed. Detection of changed state (by user input) happens by comparison between targetValue and the contents of statementModule. applyStringDataValue would not need to be an actual store action (maybe it is even somewhat confusing) and it can move out when implementing the different patch strategies. One downside of state update after successful save only: As actions are not tracked in vue dev tools[0], this makes for poor visibility of what is going on - the "mutations" are performed only on a copy of the statements state before submitting the change, which is not visible in the otherwise very helpful browser extension. I'm unsure about the value of the applyStringDataValue integration tests as they don't integrate a lot. [0] vuejs/devtools-v6#179 Bug: T238662 Change-Id: Id0a9f656ee06f15fb814e6ecd724b934c50bf6ad
1 parent 351f8c4 commit ab21e1c

File tree

17 files changed

+375
-403
lines changed

17 files changed

+375
-403
lines changed

client/data-bridge/dist/data-bridge.common.js

Lines changed: 89 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -13544,18 +13544,6 @@ function (_Mutations) {
1354413544
value: function setStatements(payload) {
1354513545
external_commonjs_vue2_commonjs2_vue2_amd_vue2_root_vue2_default.a.set(this.state, payload.entityId, clone(payload.statements));
1354613546
}
13547-
}, {
13548-
key: "setDataValue",
13549-
value: function setDataValue(payload) {
13550-
var snak = payload.path.resolveSnakInStatement(this.state);
13551-
external_commonjs_vue2_commonjs2_vue2_amd_vue2_root_vue2_default.a.set(snak, 'datavalue', payload.value);
13552-
}
13553-
}, {
13554-
key: "setSnakType",
13555-
value: function setSnakType(payload) {
13556-
var snak = payload.path.resolveSnakInStatement(this.state);
13557-
snak.snaktype = payload.value;
13558-
}
1355913547
}]);
1356013548

1356113549
return StatementMutations;
@@ -13580,6 +13568,7 @@ var SnakActionErrors;
1358013568

1358113569

1358213570

13571+
1358313572
var actions_StatementActions =
1358413573
/*#__PURE__*/
1358513574
function (_Actions) {
@@ -13601,12 +13590,13 @@ function (_Actions) {
1360113590
return Promise.resolve();
1360213591
}
1360313592
}, {
13604-
key: "setStringDataValue",
13605-
value: function setStringDataValue(payloadDataValue) {
13593+
key: "applyStringDataValue",
13594+
value: function applyStringDataValue(payloadDataValue) {
1360613595
var _this = this;
1360713596

1360813597
return new Promise(function (resolve) {
13609-
var snak = payloadDataValue.path.resolveSnakInStatement(_this.state);
13598+
var state = clone(_this.state);
13599+
var snak = payloadDataValue.path.resolveSnakInStatement(state);
1361013600

1361113601
if (snak === null) {
1361213602
throw new Error(storeActionErrors_SnakActionErrors.NO_SNAK_FOUND);
@@ -13618,19 +13608,11 @@ function (_Actions) {
1361813608

1361913609
if (typeof payloadDataValue.value.value !== 'string') {
1362013610
throw new Error(storeActionErrors_SnakActionErrors.WRONG_PAYLOAD_VALUE_TYPE);
13621-
} // TODO put more validation here
13622-
13623-
13624-
var payloadSnakType = {
13625-
path: payloadDataValue.path,
13626-
value: 'value'
13627-
};
13628-
13629-
_this.commit('setSnakType', payloadSnakType);
13630-
13631-
_this.commit('setDataValue', payloadDataValue);
13611+
}
1363213612

13633-
resolve();
13613+
snak.snaktype = 'value';
13614+
snak.datavalue = payloadDataValue.value;
13615+
resolve(state);
1363413616
});
1363513617
}
1363613618
}]);
@@ -13813,12 +13795,12 @@ function (_Actions) {
1381313795
}
1381413796
}, {
1381513797
key: "entitySave",
13816-
value: function entitySave() {
13798+
value: function entitySave(statements) {
1381713799
var _this2 = this;
1381813800

1381913801
var entityRevision = new EntityRevision_EntityRevision({
1382013802
id: this.state.id,
13821-
statements: this.statementsModule.state[this.state.id]
13803+
statements: statements
1382213804
}, this.state.baseRevision);
1382313805
return this.store.$services.get('writingEntityRepository').saveEntity(entityRevision).then(function (entityRevision) {
1382413806
return _this2.dispatch('entityWrite', entityRevision);
@@ -13970,7 +13952,7 @@ function (_Actions) {
1397013952
var _postEntityLoad = _asyncToGenerator(
1397113953
/*#__PURE__*/
1397213954
regeneratorRuntime.mark(function _callee2() {
13973-
var state, path, originalStatement;
13955+
var state, path;
1397413956
return regeneratorRuntime.wrap(function _callee2$(_context2) {
1397513957
while (1) {
1397613958
switch (_context2.prev = _context2.next) {
@@ -13982,10 +13964,9 @@ function (_Actions) {
1398213964

1398313965
case 4:
1398413966
if (this.getters.applicationStatus !== definitions_ApplicationStatus.ERROR) {
13985-
originalStatement = state[NS_STATEMENTS][path.entityId][path.propertyId][path.index];
13986-
this.commit('setOriginalStatement', originalStatement);
1398713967
this.commit('setTargetValue', // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13988-
originalStatement.mainsnak.datavalue);
13968+
state[NS_STATEMENTS][path.entityId][path.propertyId][path.index] // TODO use getter
13969+
.mainsnak.datavalue);
1398913970
this.commit('setApplicationStatus', definitions_ApplicationStatus.READY);
1399013971
}
1399113972

@@ -14083,8 +14064,6 @@ function (_Actions) {
1408314064
}, {
1408414065
key: "setTargetValue",
1408514066
value: function setTargetValue(dataValue) {
14086-
var _this3 = this;
14087-
1408814067
if (this.state.applicationStatus !== definitions_ApplicationStatus.READY) {
1408914068
this.commit('addApplicationErrors', [{
1409014069
type: ErrorTypes.APPLICATION_LOGIC_ERROR,
@@ -14096,46 +14075,85 @@ function (_Actions) {
1409614075
}
1409714076

1409814077
this.commit('setTargetValue', dataValue);
14099-
var state = this.state;
14100-
var path = new MainSnakPath_MainSnakPath(state[NS_ENTITY].id, state.targetProperty, 0);
14101-
return this.statementModule.dispatch('setStringDataValue', {
14102-
path: path,
14103-
value: dataValue
14104-
}).catch(function (error) {
14105-
_this3.commit('addApplicationErrors', [{
14106-
type: ErrorTypes.APPLICATION_LOGIC_ERROR,
14107-
info: error
14108-
}]);
14109-
14110-
throw error;
14111-
});
14078+
return Promise.resolve();
1411214079
}
1411314080
}, {
1411414081
key: "saveBridge",
14115-
value: function saveBridge() {
14116-
var _this4 = this;
14082+
value: function () {
14083+
var _saveBridge = _asyncToGenerator(
14084+
/*#__PURE__*/
14085+
regeneratorRuntime.mark(function _callee3() {
14086+
var _this3 = this;
1411714087

14118-
if (this.state.applicationStatus !== definitions_ApplicationStatus.READY) {
14119-
this.commit('addApplicationErrors', [{
14120-
type: ErrorTypes.APPLICATION_LOGIC_ERROR,
14121-
info: {
14122-
stack: new Error().stack
14088+
var state, entityId, path, statements;
14089+
return regeneratorRuntime.wrap(function _callee3$(_context3) {
14090+
while (1) {
14091+
switch (_context3.prev = _context3.next) {
14092+
case 0:
14093+
if (!(this.state.applicationStatus !== definitions_ApplicationStatus.READY)) {
14094+
_context3.next = 3;
14095+
break;
14096+
}
14097+
14098+
this.commit('addApplicationErrors', [{
14099+
type: ErrorTypes.APPLICATION_LOGIC_ERROR,
14100+
info: {
14101+
stack: new Error().stack
14102+
}
14103+
}]);
14104+
return _context3.abrupt("return", Promise.reject(null));
14105+
14106+
case 3:
14107+
state = this.state;
14108+
entityId = state[NS_ENTITY].id;
14109+
path = new MainSnakPath_MainSnakPath(entityId, state.targetProperty, 0);
14110+
_context3.prev = 6;
14111+
_context3.next = 9;
14112+
return this.statementModule.dispatch('applyStringDataValue', {
14113+
path: path,
14114+
value: state.targetValue
14115+
});
14116+
14117+
case 9:
14118+
statements = _context3.sent;
14119+
_context3.next = 16;
14120+
break;
14121+
14122+
case 12:
14123+
_context3.prev = 12;
14124+
_context3.t0 = _context3["catch"](6);
14125+
this.commit('addApplicationErrors', [{
14126+
type: ErrorTypes.APPLICATION_LOGIC_ERROR,
14127+
info: _context3.t0
14128+
}]);
14129+
throw _context3.t0;
14130+
14131+
case 16:
14132+
return _context3.abrupt("return", this.entityModule.dispatch('entitySave', statements[entityId]).catch(function (error) {
14133+
_this3.commit('addApplicationErrors', [{
14134+
type: ErrorTypes.SAVING_FAILED,
14135+
info: error
14136+
}]);
14137+
14138+
throw error;
14139+
}).then(function () {
14140+
return _this3.dispatch('postEntityLoad');
14141+
}));
14142+
14143+
case 17:
14144+
case "end":
14145+
return _context3.stop();
14146+
}
1412314147
}
14124-
}]);
14125-
return Promise.reject(null);
14126-
}
14148+
}, _callee3, this, [[6, 12]]);
14149+
}));
1412714150

14128-
return this.entityModule.dispatch('entitySave').catch(function (error) {
14129-
_this4.commit('addApplicationErrors', [{
14130-
type: ErrorTypes.SAVING_FAILED,
14131-
info: error
14132-
}]);
14151+
function saveBridge() {
14152+
return _saveBridge.apply(this, arguments);
14153+
}
1413314154

14134-
throw error;
14135-
}).then(function () {
14136-
return _this4.dispatch('postEntityLoad');
14137-
});
14138-
}
14155+
return saveBridge;
14156+
}()
1413914157
}, {
1414014158
key: "addError",
1414114159
value: function addError(errors) {
@@ -14209,22 +14227,22 @@ function (_Getters) {
1420914227
return statements.references ? statements.references : [];
1421014228
}
1421114229
}, {
14212-
key: "isTargetStatementModified",
14230+
key: "isTargetValueModified",
1421314231
get: function get() {
1421414232
if (this.state.applicationStatus !== definitions_ApplicationStatus.READY) {
1421514233
return false;
1421614234
}
1421714235

1421814236
var initState = this.state;
1421914237
var entityId = initState[NS_ENTITY].id;
14220-
return !deep_equal_default()(this.state.originalStatement, initState[NS_STATEMENTS][entityId][this.state.targetProperty][0], {
14238+
return !deep_equal_default()(this.state.targetValue, initState[NS_STATEMENTS][entityId][this.state.targetProperty][0].mainsnak.datavalue, {
1422114239
strict: true
1422214240
});
1422314241
}
1422414242
}, {
1422514243
key: "canSave",
1422614244
get: function get() {
14227-
return this.state.editDecision !== null && this.getters.isTargetStatementModified;
14245+
return this.state.editDecision !== null && this.getters.isTargetValueModified;
1422814246
}
1422914247
}, {
1423014248
key: "applicationStatus",
@@ -14316,11 +14334,6 @@ function (_Mutations) {
1431614334
value: function setTargetLabel(label) {
1431714335
this.state.targetLabel = label;
1431814336
}
14319-
}, {
14320-
key: "setOriginalStatement",
14321-
value: function setOriginalStatement(revision) {
14322-
this.state.originalStatement = clone(revision);
14323-
}
1432414337
}, {
1432514338
key: "addApplicationErrors",
1432614339
value: function addApplicationErrors(errors) {
@@ -14370,7 +14383,6 @@ var state_BaseState = function BaseState() {
1437014383
this.editFlow = '';
1437114384
this.entityTitle = '';
1437214385
this.originalHref = '';
14373-
this.originalStatement = null;
1437414386
this.pageTitle = '';
1437514387
this.targetLabel = null;
1437614388
this.targetProperty = '';

client/data-bridge/src/store/Application.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { ValidApplicationStatus } from '@/definitions/ApplicationStatus';
66
import Term from '@/datamodel/Term';
77
import { WikibaseRepoConfiguration } from '@/definitions/data-access/WikibaseRepoConfigRepository';
88
import DataValue from '@/datamodel/DataValue';
9-
import Statement from '@/datamodel/Statement';
109
import ApplicationError from '@/definitions/ApplicationError';
1110
import EditDecision from '@/definitions/EditDecision';
1211
import { StatementState } from '@/store/statements';
@@ -19,7 +18,6 @@ interface Application {
1918
editFlow: string;
2019
entityTitle: string;
2120
originalHref: string;
22-
originalStatement: Statement|null;
2321
pageTitle: string;
2422
targetLabel: Term|null;
2523
targetProperty: string;

client/data-bridge/src/store/actions.ts

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,11 @@ RootActions
105105

106106
await this.dispatch( 'validateEntityState', path );
107107
if ( this.getters.applicationStatus !== ApplicationStatus.ERROR ) {
108-
const originalStatement = state[ NS_STATEMENTS ][ path.entityId ][ path.propertyId ][ path.index ];
109-
110-
this.commit(
111-
'setOriginalStatement',
112-
originalStatement,
113-
);
114108
this.commit(
115109
'setTargetValue',
116110
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
117-
originalStatement.mainsnak.datavalue!,
111+
state[ NS_STATEMENTS ][ path.entityId ][ path.propertyId ][ path.index ] // TODO use getter
112+
.mainsnak.datavalue!,
118113
);
119114

120115
this.commit(
@@ -201,36 +196,41 @@ RootActions
201196

202197
this.commit( 'setTargetValue', dataValue );
203198

199+
return Promise.resolve();
200+
}
201+
202+
public async saveBridge(): Promise<void> {
203+
if ( this.state.applicationStatus !== ApplicationStatus.READY ) {
204+
this.commit( 'addApplicationErrors', [ {
205+
type: ErrorTypes.APPLICATION_LOGIC_ERROR,
206+
info: { stack: ( new Error() ).stack },
207+
} ] );
208+
return Promise.reject( null );
209+
}
210+
204211
const state = this.state as InitializedApplicationState;
212+
const entityId = state[ NS_ENTITY ].id;
205213
const path = new MainSnakPath(
206-
state[ NS_ENTITY ].id,
214+
entityId,
207215
state.targetProperty,
208216
0,
209217
);
210218

211-
return this.statementModule.dispatch( 'setStringDataValue',
212-
{
219+
let statements;
220+
try {
221+
statements = await this.statementModule.dispatch( 'applyStringDataValue', {
213222
path,
214-
value: dataValue,
215-
} ).catch( ( error: Error ) => {
223+
value: state.targetValue!, // eslint-disable-line @typescript-eslint/no-non-null-assertion
224+
} );
225+
} catch ( error ) {
216226
this.commit( 'addApplicationErrors', [ {
217227
type: ErrorTypes.APPLICATION_LOGIC_ERROR,
218228
info: error,
219229
} ] );
220230
throw error;
221-
} );
222-
}
223-
224-
public saveBridge(): Promise<void> {
225-
if ( this.state.applicationStatus !== ApplicationStatus.READY ) {
226-
this.commit( 'addApplicationErrors', [ {
227-
type: ErrorTypes.APPLICATION_LOGIC_ERROR,
228-
info: { stack: ( new Error() ).stack },
229-
} ] );
230-
return Promise.reject( null );
231231
}
232232

233-
return this.entityModule.dispatch( 'entitySave' )
233+
return this.entityModule.dispatch( 'entitySave', statements[ entityId ] )
234234
.catch( ( error: Error ) => {
235235
this.commit( 'addApplicationErrors', [ { type: ErrorTypes.SAVING_FAILED, info: error } ] );
236236
throw error;

client/data-bridge/src/store/entity/actions.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { EntityState } from '@/store/entity';
55
import { Actions, Context, Getters } from 'vuex-smart-module';
66
import { EntityMutations } from '@/store/entity/mutations';
77
import { statementModule } from '@/store/statements';
8+
import StatementMap from '@/datamodel/StatementMap';
89

910
export class EntityActions extends Actions<EntityState, Getters<EntityState>, EntityMutations, EntityActions> {
1011
private store!: Store<Application>;
@@ -23,11 +24,13 @@ export class EntityActions extends Actions<EntityState, Getters<EntityState>, En
2324
.then( ( entityRevision: EntityRevision ) => this.dispatch( 'entityWrite', entityRevision ) );
2425
}
2526

26-
public entitySave(): Promise<void> {
27+
public entitySave(
28+
statements: StatementMap,
29+
): Promise<void> {
2730
const entityRevision = new EntityRevision(
2831
{
2932
id: this.state.id,
30-
statements: this.statementsModule.state[ this.state.id ],
33+
statements,
3134
},
3235
this.state.baseRevision,
3336
);

0 commit comments

Comments
 (0)