Skip to content

Commit 9315673

Browse files
committed
Issue #152: Allow objects with immutable properties to be added to an array
This bug erroneously rejected cases where a new object could not be added to an array for an existing document if the document definition specifies that one or more properties of the object are immutable. Also applied to hashtables with immutable values nested in an array.
1 parent 3cc9d56 commit 9315673

4 files changed

Lines changed: 445 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
All notable changes to this project will be documented in this file.
33
This project adheres to [Semantic Versioning](http://semver.org/).
44

5+
## [Unreleased]
6+
### Fixed
7+
- [#152](https://github.com/Kashoo/synctos/issues/152): Cannot append a new object with immutable properties to an array
8+
59
## [1.9.2] - 2017-10-02
610
### Fixed
711
- [#149](https://github.com/Kashoo/synctos/issues/149): Permissions for add operations sometimes applied to other operation types

etc/sync-function-validation-module.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -438,11 +438,8 @@ function() {
438438
for (var elementIndex = 0; elementIndex < itemValue.length; elementIndex++) {
439439
var elementName = '[' + elementIndex + ']';
440440
var elementValue = itemValue[elementIndex];
441-
442-
var oldElementValue;
443-
if (!isValueNullOrUndefined(oldItemValue) && elementIndex < oldItemValue.length) {
444-
oldElementValue = oldItemValue[elementIndex];
445-
}
441+
var oldElementValue =
442+
(!isValueNullOrUndefined(oldItemValue) && elementIndex < oldItemValue.length) ? oldItemValue[elementIndex] : null;
446443

447444
itemStack.push({
448445
itemName: elementName,
Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
var testHelper = require('../etc/test-helper.js');
2+
var errorFormatter = testHelper.validationErrorFormatter;
3+
4+
describe('Immutable nested properties:', function() {
5+
beforeEach(function() {
6+
testHelper.initSyncFunction('build/sync-functions/test-immutable-nested-properties-sync-function.js');
7+
});
8+
9+
describe('when an object with an immutable property is nested in an array', function() {
10+
it('prevent modification of immutable properties for objects that already exist', function() {
11+
var doc = {
12+
_id: 'myDoc',
13+
type: 'objectNestedInArrayDoc',
14+
elementList: [
15+
{ id: 'my-element-1', content: 'new-content-1' }, // This element has not changed
16+
{ id: 'my-element-3', content: 'new-content-3' }, // This element was originally at index 2 of the array
17+
{ id: 'my-element-2', content: 'new-content-2' }, // This element was originally at index 1 of the array
18+
{ id: 'new-element-4', content: 'new-content-4' } // Brand new element
19+
]
20+
};
21+
22+
var oldDoc = {
23+
_id: 'myDoc',
24+
type: 'objectNestedInArrayDoc',
25+
elementList: [
26+
{ id: 'my-element-1', content: 'old-content-1' },
27+
{ id: 'my-element-2', content: 'old-content-2' },
28+
{ id: 'my-element-3', content: 'old-content-3' }
29+
]
30+
};
31+
32+
testHelper.verifyDocumentNotReplaced(
33+
doc,
34+
oldDoc,
35+
'objectNestedInArrayDoc',
36+
[ errorFormatter.immutableItemViolation('elementList[1].id'), errorFormatter.immutableItemViolation('elementList[2].id') ]);
37+
});
38+
39+
it('allow modification as long as immutable properties do not change', function() {
40+
var doc = {
41+
_id: 'myDoc',
42+
type: 'objectNestedInArrayDoc',
43+
elementList: [
44+
{ id: 'my-element-1', content: 'new-content-1' },
45+
{ id: 'my-element-2', content: 'new-content-2' },
46+
{ id: 'new-element-3', content: 'new-content-3' } // Brand new element
47+
]
48+
};
49+
50+
var oldDoc = {
51+
_id: 'myDoc',
52+
type: 'objectNestedInArrayDoc',
53+
elementList: [
54+
{ id: 'my-element-1', content: 'old-content-1' },
55+
{ id: 'my-element-2', content: 'old-content-2' }
56+
]
57+
};
58+
59+
testHelper.verifyDocumentReplaced(doc, oldDoc);
60+
});
61+
});
62+
63+
describe('when an object with an immutable property is nested in a hashtable', function() {
64+
it('prevent modification of immutable properties for objects that already exist', function() {
65+
var doc = {
66+
_id: 'myDoc',
67+
type: 'objectNestedInHashtableDoc',
68+
hash: {
69+
one: { id: 'my-element-3', content: 'new-content-3' }, // This element was originally for key "three"
70+
two: { id: 'my-element-2', content: 'new-content-2' }, // This element has not changed
71+
three: { id: 'my-element-1', content: 'new-content-1' }, // This element was originally for key "one"
72+
four: { id: 'new-element-4', content: 'new-content-4' } // Brand new element
73+
}
74+
};
75+
76+
var oldDoc = {
77+
_id: 'myDoc',
78+
type: 'objectNestedInHashtableDoc',
79+
hash: {
80+
one: { id: 'my-element-1', content: 'old-content-1' },
81+
two: { id: 'my-element-2', content: 'old-content-2' },
82+
three: { id: 'my-element-3', content: 'old-content-3' }
83+
}
84+
};
85+
86+
testHelper.verifyDocumentNotReplaced(
87+
doc,
88+
oldDoc,
89+
'objectNestedInHashtableDoc',
90+
[ errorFormatter.immutableItemViolation('hash[one].id'), errorFormatter.immutableItemViolation('hash[three].id') ]);
91+
});
92+
93+
it('allow modification as long as immutable properties do not change', function() {
94+
var doc = {
95+
_id: 'myDoc',
96+
type: 'objectNestedInHashtableDoc',
97+
hash: {
98+
one: { id: 'my-element-1', content: 'new-content-1' },
99+
two: { id: 'my-element-2', content: 'new-content-2' },
100+
three: { id: 'new-element-3', content: 'new-content-3' } // Brand new element
101+
}
102+
};
103+
104+
var oldDoc = {
105+
_id: 'myDoc',
106+
type: 'objectNestedInHashtableDoc',
107+
hash: {
108+
one: { id: 'my-element-1', content: 'old-content-1' },
109+
two: { id: 'my-element-2', content: 'old-content-2' }
110+
}
111+
};
112+
113+
testHelper.verifyDocumentReplaced(doc, oldDoc);
114+
});
115+
});
116+
117+
describe('when an object with an immutable property is nested in an object', function() {
118+
it('prevent modification of immutable properties for objects that already exist', function() {
119+
var doc = {
120+
_id: 'myDoc',
121+
type: 'objectNestedInObjectDoc',
122+
object: {
123+
value: { id: 'new-element', content: 'new-content' }
124+
}
125+
};
126+
127+
var oldDoc = {
128+
_id: 'myDoc',
129+
type: 'objectNestedInObjectDoc',
130+
object: {
131+
value: { id: 'old-element', content: 'old-content' }
132+
}
133+
};
134+
135+
testHelper.verifyDocumentNotReplaced(
136+
doc,
137+
oldDoc,
138+
'objectNestedInObjectDoc',
139+
errorFormatter.immutableItemViolation('object.value.id'));
140+
});
141+
142+
it('allow modification as long as immutable properties do not change', function() {
143+
var doc = {
144+
_id: 'myDoc',
145+
type: 'objectNestedInObjectDoc',
146+
object: {
147+
value: { id: 'my-element', content: 'new-content' }
148+
}
149+
};
150+
151+
var oldDoc = {
152+
_id: 'myDoc',
153+
type: 'objectNestedInObjectDoc',
154+
object: {
155+
value: { id: 'my-element', content: 'new-content' }
156+
}
157+
};
158+
159+
testHelper.verifyDocumentReplaced(doc, oldDoc);
160+
});
161+
});
162+
163+
describe('when a hashtable with immutable values is nested in an array', function() {
164+
it('prevent modification of immutable values for hashtables that already exist', function() {
165+
var doc = {
166+
_id: 'myDoc',
167+
type: 'hashtableNestedInArrayDoc',
168+
elementList: [
169+
{ value: 2 }, // This element was originally at index 1 of the array
170+
{ value: 1 }, // This element was originally at index 0 of the array
171+
{ value: 3 }, // This element has not changed
172+
{ value: 4 } // Brand new element
173+
]
174+
};
175+
176+
var oldDoc = {
177+
_id: 'myDoc',
178+
type: 'hashtableNestedInArrayDoc',
179+
elementList: [
180+
{ value: 1 },
181+
{ value: 2 },
182+
{ value: 3 }
183+
]
184+
};
185+
186+
testHelper.verifyDocumentNotReplaced(
187+
doc,
188+
oldDoc,
189+
'hashtableNestedInArrayDoc',
190+
[
191+
errorFormatter.immutableItemViolation('elementList[1][value]'),
192+
errorFormatter.immutableItemViolation('elementList[0][value]')
193+
]);
194+
});
195+
196+
it('allow modification as long as immutable values do not change', function() {
197+
var doc = {
198+
_id: 'myDoc',
199+
type: 'hashtableNestedInArrayDoc',
200+
elementList: [
201+
{ value: 1 },
202+
{ value: 2 },
203+
{ value: 3 } // Brand new element
204+
]
205+
};
206+
207+
var oldDoc = {
208+
_id: 'myDoc',
209+
type: 'hashtableNestedInArrayDoc',
210+
elementList: [
211+
{ value: 1 },
212+
{ value: 2 }
213+
]
214+
};
215+
216+
testHelper.verifyDocumentReplaced(doc, oldDoc);
217+
});
218+
});
219+
220+
describe('when a hashtable with immutable values is nested in an object', function() {
221+
it('prevent modification of immutable values for hashtables that already exist', function() {
222+
var doc = {
223+
_id: 'myDoc',
224+
type: 'hashtableNestedInObjectDoc',
225+
object: {
226+
hash: { value: 2 }
227+
}
228+
};
229+
230+
var oldDoc = {
231+
_id: 'myDoc',
232+
type: 'hashtableNestedInObjectDoc',
233+
object: {
234+
hash: { value: 1 }
235+
}
236+
};
237+
238+
testHelper.verifyDocumentNotReplaced(
239+
doc,
240+
oldDoc,
241+
'hashtableNestedInObjectDoc',
242+
errorFormatter.immutableItemViolation('object.hash[value]'));
243+
});
244+
245+
it('allow modification as long as immutable values do not change', function() {
246+
var doc = {
247+
_id: 'myDoc',
248+
type: 'hashtableNestedInObjectDoc',
249+
object: {
250+
hash: { value: 1 }
251+
}
252+
};
253+
254+
var oldDoc = {
255+
_id: 'myDoc',
256+
type: 'hashtableNestedInObjectDoc',
257+
object: {
258+
hash: { value: 1 }
259+
}
260+
};
261+
262+
testHelper.verifyDocumentReplaced(doc, oldDoc);
263+
});
264+
});
265+
266+
describe('when a hashtable with immutable values is nested in a hashtable', function() {
267+
it('prevent modification of immutable values for hashtables that already exist', function() {
268+
var doc = {
269+
_id: 'myDoc',
270+
type: 'hashtableNestedInHashtableDoc',
271+
hash: {
272+
one: { value: 2 }, // This element was originally for key "two"
273+
two: { value: 1 }, // This element was originally for key "one"
274+
three: { value: 3 }, // This element has not changed
275+
four: { value: 4 } // Brand new element
276+
}
277+
};
278+
279+
var oldDoc = {
280+
_id: 'myDoc',
281+
type: 'hashtableNestedInHashtableDoc',
282+
hash: {
283+
one: { value: 1 },
284+
two: { value: 2 },
285+
three: { value: 3 }
286+
}
287+
};
288+
289+
testHelper.verifyDocumentNotReplaced(
290+
doc,
291+
oldDoc,
292+
'hashtableNestedInHashtableDoc',
293+
[
294+
errorFormatter.immutableItemViolation('hash[one][value]'),
295+
errorFormatter.immutableItemViolation('hash[two][value]')
296+
]);
297+
});
298+
299+
it('allow modification as long as immutable values do not change', function() {
300+
var doc = {
301+
_id: 'myDoc',
302+
type: 'hashtableNestedInHashtableDoc',
303+
hash: {
304+
one: { value: 1 },
305+
two: { value: 2 },
306+
three: { value: 3 } // Brand new element
307+
}
308+
};
309+
310+
var oldDoc = {
311+
_id: 'myDoc',
312+
type: 'hashtableNestedInHashtableDoc',
313+
hash: {
314+
one: { value: 1 },
315+
two: { value: 2 }
316+
}
317+
};
318+
319+
testHelper.verifyDocumentReplaced(doc, oldDoc);
320+
});
321+
});
322+
});

0 commit comments

Comments
 (0)