Skip to content

Commit cec0e7d

Browse files
committed
- add Backendless.Expression
1 parent e2cb225 commit cec0e7d

File tree

10 files changed

+420
-1
lines changed

10 files changed

+420
-1
lines changed

backendless.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,7 @@ declare module Backendless {
16431643
update(opResult: OpResult | OpResultValueReference, changes: object): OpResult;
16441644
update(opResult: OpResult | OpResultValueReference, propertyName: string, propertyValue: OpResultValueReference): OpResult;
16451645
update(opResult: OpResult | OpResultValueReference, propertyName: string, propertyValue: number | string | boolean): OpResult;
1646+
update(opResult: OpResult | OpResultValueReference, propertyName: string, expression: Expression): OpResult;
16461647

16471648
delete(opResult: OpResult | OpResultValueReference): OpResult;
16481649
delete(object: object): OpResult;
@@ -1803,6 +1804,10 @@ declare module Backendless {
18031804

18041805
execute(): Promise<UnitOfWorkResult>
18051806
}
1807+
1808+
class Expression {
1809+
constructor(value: string);
1810+
}
18061811
}
18071812

18081813
declare module 'backendless' {

src/data/store.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Utils from '../utils'
2+
import Expression from '../expression'
23
import RTHandlers from './rt-handlers'
34
import DataQueryBuilder from './data-query-builder'
45
import LoadRelationsQueryBuilder from './load-relations-query-builder'
@@ -393,7 +394,8 @@ const convertToServerRecord = (() => {
393394
} else if (
394395
source[prop] && typeof source[prop] === 'object'
395396
&& !(source[prop] instanceof Geometry)
396-
&& !(source[prop] instanceof JSONUpdateBuilder)) {
397+
&& !(source[prop] instanceof JSONUpdateBuilder)
398+
&& !(source[prop] instanceof Expression)) {
397399

398400
if (source[prop] instanceof Date) {
399401
target[prop] = source[prop].getTime()

src/expression.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export default class BackendlessExpression {
2+
constructor(value) {
3+
if (!value || typeof value !== 'string') {
4+
throw new Error('The Backendless.Expression class can be initialized with non empty string value only')
5+
}
6+
7+
this.value = value
8+
}
9+
10+
toJSON() {
11+
return {
12+
___class: 'BackendlessExpression',
13+
value : this.value
14+
}
15+
}
16+
}

src/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Request from 'backendless-request'
33
import APIRequest from './request'
44
import Urls from './urls'
55
import Utils from './utils'
6+
import Expression from './expression'
67

78
const DEFAULT_PROPS = {
89
appId : null,
@@ -503,6 +504,10 @@ class Backendless {
503504
return this.Messaging.EmailEnvelope
504505
}
505506

507+
get Expression() {
508+
return Expression
509+
}
510+
506511
///--------BACKWARD COMPATIBILITY-------///
507512
///-------------------------------------///
508513
}

test/tsd.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@ function testDataStoreClass() {
464464
}
465465

466466
promiseObject = dataStore.save(item);
467+
promiseObject = dataStore.save({age: new Backendless.Expression('age + 1')});
467468
promiseObject = dataStore.save(item, true);
468469
promiseObject = dataStore.save(item, false);
469470
promisePerson = dataStore.save<Person>(person);
@@ -579,6 +580,7 @@ function testPersistence() {
579580

580581
promiseObject = Backendless.Data.save('model', {});
581582
promiseObject = Backendless.Data.save(dataStore, {});
583+
promiseObject = Backendless.Data.save(dataStore, {age: new Backendless.Expression('age + 1')});
582584

583585
promiseObject = Backendless.Data.deepSave('model', {});
584586
promiseObject = Backendless.Data.deepSave(dataStore, {});
@@ -689,6 +691,7 @@ function testData() {
689691

690692
promiseObject = Backendless.Data.save('model', {});
691693
promiseObject = Backendless.Data.save(dataStore, {});
694+
promiseObject = Backendless.Data.save(dataStore, {age: new Backendless.Expression('age + 1')});
692695

693696
promiseObject = Backendless.Data.deepSave('model', {});
694697
promiseObject = Backendless.Data.deepSave(dataStore, {});
@@ -1221,6 +1224,7 @@ function testBulkOperations() {
12211224
resultPromiseListOfString = dataStore.bulkUpsert([{}, {}, {}]);
12221225

12231226
resultPromiseString = dataStore.bulkUpdate('where clause string', {foo: 'bar'});
1227+
resultPromiseString = dataStore.bulkUpdate('where clause string', {age: new Backendless.Expression('age + 1')});
12241228

12251229
resultPromiseString = dataStore.bulkDelete('where clause string');
12261230
resultPromiseString = dataStore.bulkDelete(['objectId1', {objectId: 'objectId1'}, 123]);
@@ -2245,12 +2249,14 @@ async function testBaseTransactions() {
22452249
opResult = uow.update(personInst);
22462250
opResult = uow.update(personClassName, personObj);
22472251
opResult = uow.update(opResult, personObj);
2252+
opResult = uow.update(opResult, {age: new Backendless.Expression('age + 1')});
22482253
opResult = uow.update(opResult, propertyName, propertyValueObj);
22492254
opResult = uow.update(opResultValueReference, changesObj);
22502255
opResult = uow.update(opResultValueReference, propertyName, opResultValueReference);
22512256
opResult = uow.update(opResultValueReference, propertyName, 123);
22522257
opResult = uow.update(opResultValueReference, propertyName, 'str');
22532258
opResult = uow.update(opResultValueReference, propertyName, true);
2259+
opResult = uow.update(opResultValueReference, propertyName, new Backendless.Expression('age + 1'));
22542260
///
22552261
opResult = uow.delete(opResult);
22562262
opResult = uow.delete(opResultValueReference);
@@ -2268,6 +2274,7 @@ async function testBaseTransactions() {
22682274
opResult = uow.bulkUpdate(personClassName, [personObj, personObj, personObj], changesObj);
22692275
opResult = uow.bulkUpdate(personClassName, [personInst, personInst, personInst], changesObj);
22702276
opResult = uow.bulkUpdate(opResult, changesObj);
2277+
opResult = uow.bulkUpdate(opResult, {age: new Backendless.Expression('age + 1')});
22712278
///
22722279
opResult = uow.bulkDelete(personClassName, [changesObj, changesObj, changesObj]);
22732280
opResult = uow.bulkDelete([personInst, personInst, personInst]);

test/unit/specs/data/bulk.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,21 @@ describe('<Data> Bulk Operations', function() {
8585
expect(result1).to.be.equal(fakeResult)
8686
})
8787

88+
it('updates objects with expression', async () => {
89+
const req1 = prepareMockRequest(fakeResult)
90+
91+
const result1 = await dataStore.bulkUpdate('foo>123', { foo: 333, age: new Backendless.Expression('age + 1') })
92+
93+
expect(req1).to.deep.include({
94+
method : 'PUT',
95+
path : `${APP_PATH}/data/bulk/${tableName}?where=foo%3E123`,
96+
headers: { 'Content-Type': 'application/json' },
97+
body : { foo: 333, age: { ___class: 'BackendlessExpression', value: 'age + 1' } }
98+
})
99+
100+
expect(result1).to.be.equal(fakeResult)
101+
})
102+
88103
it('fails when condition is invalid', async () => {
89104
const errorMsg = 'Condition must be provided and must be a string.'
90105

test/unit/specs/data/crud.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,31 @@ describe('<Data> CRUD', function() {
330330
expect(result1).to.not.equal(savedObject)
331331
})
332332

333+
it('updates object with expression', async () => {
334+
const savedObject = getTestObjectWithId()
335+
savedObject.updatedProp = 'new-value'
336+
337+
const req1 = prepareMockRequest({ ...savedObject })
338+
339+
const result1 = await Backendless.Data.of(tableName).save({
340+
...savedObject,
341+
age: new Backendless.Expression('age + 1')
342+
})
343+
344+
expect(req1).to.deep.include({
345+
method : 'PUT',
346+
path : `${APP_PATH}/data/${tableName}`,
347+
headers: { 'Content-Type': 'application/json' },
348+
body : {
349+
...savedObject,
350+
age: { ___class: 'BackendlessExpression', value: 'age + 1' },
351+
}
352+
})
353+
354+
expect(result1).to.be.eql(savedObject)
355+
expect(result1).to.not.equal(savedObject)
356+
})
357+
333358
it('creates object instance', async () => {
334359
const savedObject = getTestObjectWithId()
335360
savedObject.updatedProp = 'new-value'
@@ -349,6 +374,32 @@ describe('<Data> CRUD', function() {
349374
expect(result1).to.be.instanceof(ClassPerson)
350375
expect(result1).to.not.equal(savedObject)
351376
})
377+
378+
it('creates object instance with expression', async () => {
379+
const savedObject = getTestObjectWithId()
380+
savedObject.updatedProp = 'new-value'
381+
382+
const req1 = prepareMockRequest({ ...savedObject })
383+
384+
const result1 = await Backendless.Data.of(ClassPerson).save({
385+
...savedObject,
386+
age: new Backendless.Expression('age + 1')
387+
})
388+
389+
expect(req1).to.deep.include({
390+
method : 'PUT',
391+
path : `${APP_PATH}/data/ClassPerson`,
392+
headers: { 'Content-Type': 'application/json' },
393+
body : {
394+
...savedObject,
395+
age: { ___class: 'BackendlessExpression', value: 'age + 1' },
396+
}
397+
})
398+
399+
expect(result1).to.be.eql(savedObject)
400+
expect(result1).to.be.instanceof(ClassPerson)
401+
expect(result1).to.not.equal(savedObject)
402+
})
352403
})
353404

354405
describe('Upsert', () => {
@@ -371,6 +422,32 @@ describe('<Data> CRUD', function() {
371422
expect(result1).to.be.instanceof(ClassPerson)
372423
expect(result1).to.not.equal(savedObject)
373424
})
425+
426+
it('updates object with expression', async () => {
427+
const savedObject = getTestObjectWithId()
428+
savedObject.updatedProp = 'new-value'
429+
430+
const req1 = prepareMockRequest({ ...savedObject })
431+
432+
const result1 = await Backendless.Data.of(ClassPerson).save({
433+
...savedObject,
434+
age: new Backendless.Expression('age + 1')
435+
}, true)
436+
437+
expect(req1).to.deep.include({
438+
method : 'PUT',
439+
path : `${APP_PATH}/data/ClassPerson/upsert`,
440+
headers: { 'Content-Type': 'application/json' },
441+
body : {
442+
...savedObject,
443+
age: { ___class: 'BackendlessExpression', value: 'age + 1' },
444+
}
445+
})
446+
447+
expect(result1).to.be.eql(savedObject)
448+
expect(result1).to.be.instanceof(ClassPerson)
449+
expect(result1).to.not.equal(savedObject)
450+
})
374451
})
375452

376453
describe('Remove', () => {

test/unit/specs/namespace.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,4 +389,29 @@ describe('Namespace', function() {
389389
})
390390

391391
})
392+
393+
describe('Expressions', () => {
394+
395+
it('converts to JSON', () => {
396+
const expression = new Backendless.Expression('age + 1')
397+
398+
expect(JSON.stringify(expression)).to.equal('{"___class":"BackendlessExpression","value":"age + 1"}')
399+
})
400+
401+
it('should fail if value is invalid', () => {
402+
const errorMsg = 'The Backendless.Expression class can be initialized with non empty string value only'
403+
404+
expect(() => new Backendless.Expression()).to.throw(errorMsg)
405+
expect(() => new Backendless.Expression('')).to.throw(errorMsg)
406+
expect(() => new Backendless.Expression(123)).to.throw(errorMsg)
407+
expect(() => new Backendless.Expression(0)).to.throw(errorMsg)
408+
expect(() => new Backendless.Expression(null)).to.throw(errorMsg)
409+
expect(() => new Backendless.Expression(true)).to.throw(errorMsg)
410+
expect(() => new Backendless.Expression(false)).to.throw(errorMsg)
411+
expect(() => new Backendless.Expression({})).to.throw(errorMsg)
412+
expect(() => new Backendless.Expression([])).to.throw(errorMsg)
413+
expect(() => new Backendless.Expression(_ => _)).to.throw(errorMsg)
414+
})
415+
})
416+
392417
})

0 commit comments

Comments
 (0)