Skip to content

Commit 4cd7938

Browse files
Valodyav-excelsior
andauthored
Add support for Upsert (#223)
* - add method for DataUpsert * - add RT listeners for "upsert/bulkUpsert" data operation * - add "upsert/bulkUpsert" into TransactionsAPI * - add types definitions for "upsert/bulkUpsert" in TransactionsAPI * Add bulkUpsert and tests for upsert/bulkUpsert * Add signature for bulkUpsert * - fix tests for "bulkUpsert" Co-authored-by: Dima <vakyla98@gmail.com>
1 parent 8ab7c63 commit 4cd7938

File tree

17 files changed

+2034
-11
lines changed

17 files changed

+2034
-11
lines changed

backendless.d.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,16 @@ declare module Backendless {
968968
* @class EventHandler
969969
*/
970970
class EventHandler {
971+
addUpsertListener<T = object>(whereClause: string, callback: (obj: T) => void, onError: (error: RTSubscriptionError) => void): Backendless.EventHandler;
972+
addUpsertListener<T = object>(whereClause: string, callback: (obj: T) => void): Backendless.EventHandler;
973+
addUpsertListener<T = object>(callback: (obj: T) => void, onError: (error: RTSubscriptionError) => void): Backendless.EventHandler;
974+
addUpsertListener<T = object>(callback: (obj: T) => void): Backendless.EventHandler;
975+
976+
removeUpsertListeners(whereClause: string): Backendless.EventHandler;
977+
removeUpsertListeners(): Backendless.EventHandler;
978+
979+
removeUpsertListener<T = object>(callback: (obj: T) => void): Backendless.EventHandler;
980+
971981
addCreateListener<T = object>(whereClause: string, callback: (obj: T) => void, onError: (error: RTSubscriptionError) => void): Backendless.EventHandler;
972982
addCreateListener<T = object>(whereClause: string, callback: (obj: T) => void): Backendless.EventHandler;
973983
addCreateListener<T = object>(callback: (obj: T) => void, onError: (error: RTSubscriptionError) => void): Backendless.EventHandler;
@@ -998,6 +1008,13 @@ declare module Backendless {
9981008

9991009
removeDeleteListener<T = object>(callback: (obj: T) => void): Backendless.EventHandler;
10001010

1011+
addBulkUpsertListener(callback: (list: string[]) => void, onError: (error: RTSubscriptionError) => void): Backendless.EventHandler;
1012+
addBulkUpsertListener(callback: (list: string[]) => void): Backendless.EventHandler;
1013+
1014+
removeBulkUpsertListener(callback: (list: string[]) => void): Backendless.EventHandler;
1015+
1016+
removeBulkUpsertListeners(): Backendless.EventHandler;
1017+
10011018
addBulkCreateListener(callback: (list: string[]) => void, onError: (error: RTSubscriptionError) => void): Backendless.EventHandler;
10021019
addBulkCreateListener(callback: (list: string[]) => void): Backendless.EventHandler;
10031020

@@ -1071,7 +1088,7 @@ declare module Backendless {
10711088

10721089
constructor(name: string | Object | Function, classToTableMap: Object);
10731090

1074-
save<T = object>(obj: T | object): Promise<T>;
1091+
save<T = object>(obj: T | object, isUpsert?: boolean): Promise<T>;
10751092

10761093
deepSave<T = object>(obj: T | object): Promise<T>;
10771094

@@ -1105,6 +1122,8 @@ declare module Backendless {
11051122

11061123
bulkCreate(objects: Array<object>): Promise<Array<string>>;
11071124

1125+
bulkUpsert(objects: Array<object>): Promise<Array<string>>;
1126+
11081127
bulkUpdate(whereClause: string, changes: object): Promise<string>;
11091128

11101129
bulkDelete(where: string | Array<string> | Array<{ objectId: string, [key: string]: any }>): Promise<string>;
@@ -1256,6 +1275,9 @@ declare module Backendless {
12561275
create(object: object): OpResult;
12571276
create(tableName: string, object: object): OpResult;
12581277

1278+
upsert(object: object): OpResult;
1279+
upsert(tableName: string, object: object): OpResult;
1280+
12591281
update(object: object): OpResult;
12601282
update(tableName: string, object: object): OpResult;
12611283
update(opResult: OpResult | OpResultValueReference, changes: object): OpResult;
@@ -1270,6 +1292,9 @@ declare module Backendless {
12701292
bulkCreate(tableName: string, objects: object[]): OpResult;
12711293
bulkCreate(objects: object[]): OpResult;
12721294

1295+
bulkUpsert(tableName: string, objects: object[]): OpResult;
1296+
bulkUpsert(objects: object[]): OpResult;
1297+
12731298
bulkUpdate(tableName: string, whereClause: string, changes: object): OpResult;
12741299
bulkUpdate(tableName: string, objectIds: string[], changes: object): OpResult;
12751300
bulkUpdate(tableName: string, objects: object[], changes: object): OpResult;

src/data/rt-handlers.js

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { RTListeners } from '../rt'
22

33
const ChangesTypes = {
4-
CREATED: 'created',
5-
UPDATED: 'updated',
6-
DELETED: 'deleted',
7-
8-
BULK_CREATED: 'bulk-created',
9-
BULK_UPDATED: 'bulk-updated',
10-
BULK_DELETED: 'bulk-deleted',
4+
CREATED : 'created',
5+
UPDATED : 'updated',
6+
DELETED : 'deleted',
7+
UPSERTED: 'upserted',
8+
9+
BULK_CREATED : 'bulk-created',
10+
BULK_UPDATED : 'bulk-updated',
11+
BULK_DELETED : 'bulk-deleted',
12+
BULK_UPSERTED: 'bulk-upserted',
1113
}
1214

1315
const RelationsChangesTypes = {
@@ -20,6 +22,7 @@ const SingleChangesTypes = [
2022
ChangesTypes.CREATED,
2123
ChangesTypes.UPDATED,
2224
ChangesTypes.DELETED,
25+
ChangesTypes.UPSERTED,
2326
]
2427

2528
export default class RTHandlers extends RTListeners {
@@ -53,6 +56,22 @@ export default class RTHandlers extends RTListeners {
5356
this.removeCreateListeners(undefined, callback)
5457
}
5558

59+
addUpsertListener(whereClause, callback, onError) {
60+
this.addChangesListener(ChangesTypes.UPSERTED, whereClause, callback, onError)
61+
}
62+
63+
removeUpsertListeners(whereClause, callback) {
64+
this.removeChangesListeners(ChangesTypes.UPSERTED, whereClause, callback)
65+
}
66+
67+
removeUpsertListener(callback) {
68+
if (!callback || typeof callback !== 'function') {
69+
throw new Error('Listener Function must be passed.')
70+
}
71+
72+
this.removeUpsertListeners(undefined, callback)
73+
}
74+
5675
addUpdateListener(whereClause, callback, onError) {
5776
this.addChangesListener(ChangesTypes.UPDATED, whereClause, callback, onError)
5877
}
@@ -133,6 +152,22 @@ export default class RTHandlers extends RTListeners {
133152
this.removeBulkDeleteListeners(undefined, callback)
134153
}
135154

155+
addBulkUpsertListener(whereClause, callback, onError) {
156+
this.addChangesListener(ChangesTypes.BULK_UPSERTED, whereClause, callback, onError)
157+
}
158+
159+
removeBulkUpsertListeners() {
160+
this.removeChangesListeners(ChangesTypes.BULK_UPSERTED)
161+
}
162+
163+
removeBulkUpsertListener(callback) {
164+
if (!callback || typeof callback !== 'function') {
165+
throw new Error('Listener Function must be passed.')
166+
}
167+
168+
this.removeChangesListeners(ChangesTypes.BULK_UPSERTED, undefined, callback)
169+
}
170+
136171
addSetRelationListener(relationColumnName, parentObjects, callback, onError) {
137172
this.addRelationsChangesListener(RelationsChangesTypes.SET, relationColumnName, parentObjects, callback, onError)
138173
}

src/data/store.js

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,14 @@ export default class DataStore {
4848
return this.rtHandlers = this.rtHandlers || new RTHandlers(this)
4949
}
5050

51-
async save(object) {
51+
async save(object, isUpsert) {
52+
const url = isUpsert
53+
? this.app.urls.dataTableUpsert(this.className)
54+
: this.app.urls.dataTable(this.className)
55+
5256
return this.app.request
5357
.put({
54-
url : this.app.urls.dataTable(this.className),
58+
url,
5559
data: convertToServerRecord(object),
5660
})
5761
.then(result => this.parseResponse(result))
@@ -229,6 +233,27 @@ export default class DataStore {
229233
})
230234
}
231235

236+
async bulkUpsert(objects) {
237+
const errorMessage = 'Objects must be provided and must be an array of objects.'
238+
239+
if (!objects || !Array.isArray(objects) || !objects.length) {
240+
throw new Error(errorMessage)
241+
}
242+
243+
objects = objects.map(object => {
244+
if (!object || typeof object !== 'object' || Array.isArray(object)) {
245+
throw new Error(errorMessage)
246+
}
247+
248+
return object
249+
})
250+
251+
return this.app.request.put({
252+
url : this.app.urls.dataBulkTableUpsert(this.className),
253+
data: objects,
254+
})
255+
}
256+
232257
async bulkUpdate(condition, changes) {
233258
if (!condition || typeof condition !== 'string') {
234259
throw new Error('Condition must be provided and must be a string.')

src/unit-of-work/constants.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ export const OperationType = {
55
UPDATE_BULK : 'UPDATE_BULK',
66
DELETE : 'DELETE',
77
DELETE_BULK : 'DELETE_BULK',
8+
UPSERT : 'UPSERT',
9+
UPSERT_BULK : 'UPSERT_BULK',
810
FIND : 'FIND',
911
ADD_RELATION : 'ADD_RELATION',
1012
SET_RELATION : 'SET_RELATION',

src/unit-of-work/index.js

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,35 @@ class UnitOfWork {
219219
return this.addOperations(OperationType.FIND, tableName, payload)
220220
}
221221

222+
/**
223+
* upsert(object: object): OpResult;
224+
* upsert(tableName: string, object: object): OpResult;
225+
* **/
226+
upsert(...args) {
227+
let tableName
228+
let changes
229+
230+
if (args.length === 1) {
231+
tableName = Utils.getClassName(args[0])
232+
changes = args[0]
233+
} else if (args.length === 2) {
234+
tableName = args[0]
235+
changes = args[1]
236+
} else {
237+
throw new Error('Invalid arguments')
238+
}
239+
240+
if (!tableName || typeof tableName !== 'string') {
241+
throw new Error('Invalid arguments')
242+
}
243+
244+
if (!changes || typeof changes !== 'object' || Array.isArray(changes)) {
245+
throw new Error('Invalid arguments')
246+
}
247+
248+
return this.addOperations(OperationType.UPSERT, tableName, changes)
249+
}
250+
222251
/**
223252
* create(object: object): OpResult;
224253
* create(tableName: string, object: object): OpResult;
@@ -342,6 +371,27 @@ class UnitOfWork {
342371
return this.addOperations(OperationType.DELETE, tableName, object)
343372
}
344373

374+
/**
375+
* bulkUpsert(tableName: string, objects: object[]): OpResult;
376+
* bulkUpsert(objects: object[]): OpResult;
377+
* **/
378+
bulkUpsert(tableName, objects) {
379+
if (Array.isArray(tableName)) {
380+
objects = tableName
381+
tableName = Utils.getClassName(objects[0])
382+
}
383+
384+
if (!objects || !Array.isArray(objects)) {
385+
throw new Error('Objects must be an array of objects.')
386+
}
387+
388+
if (!tableName || typeof tableName !== 'string') {
389+
throw new Error('Table Name must be a string.')
390+
}
391+
392+
return this.addOperations(OperationType.UPSERT_BULK, tableName, objects)
393+
}
394+
345395
/**
346396
* bulkCreate(tableName: string, objects: object[]): OpResult;
347397
* bulkCreate(objects: object[]): OpResult;
@@ -600,7 +650,7 @@ export default function UnitOfWorkService(app) {
600650

601651
opResult.setOpResultId(op.opResultId)
602652
})
603-
653+
604654
return uow
605655
}
606656

src/unit-of-work/json-adapter.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ export const OperationJSONAdapter = {
4141

4242
UPDATE: (uow, { table, payload }) => uow.update.call(uow, table, resolveOpResultValueReference(uow, payload)),
4343

44+
UPSERT: (uow, { table, payload }) => uow.upsert.call(uow, table, resolveOpResultValueReference(uow, payload)),
45+
4446
UPDATE_BULK: (uow, { table, payload }) => {
4547
const args = baseBulkArgs(uow, { table, payload })
4648

@@ -59,6 +61,10 @@ export const OperationJSONAdapter = {
5961
return uow.bulkCreate.call(uow, table, resolveOpResultValueReference(uow, payload))
6062
},
6163

64+
UPSERT_BULK: (uow, { table, payload }) => {
65+
return uow.bulkUpsert.call(uow, table, resolveOpResultValueReference(uow, payload))
66+
},
67+
6268
SET_RELATION: (uow, { table, payload }) => updateRelations(uow, 'setRelation', { table, payload }),
6369

6470
DELETE_RELATION: (uow, { table, payload }) => updateRelations(uow, 'deleteRelation', { table, payload }),

src/urls.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ export default class Urls {
109109
return `${this.data()}/${tableName}`
110110
}
111111

112+
dataTableUpsert(tableName) {
113+
return `${this.data()}/${tableName}/upsert`
114+
}
115+
112116
dataTableDeepSave(tableName) {
113117
return `${this.data()}/${tableName}/deep-save`
114118
}
@@ -141,6 +145,10 @@ export default class Urls {
141145
return `${this.data()}/bulk/${tableName}`
142146
}
143147

148+
dataBulkTableUpsert(tableName) {
149+
return `${this.data()}/bulkupsert/${tableName}`
150+
}
151+
144152
dataBulkTableDelete(tableName) {
145153
return `${this.dataBulkTable(tableName)}/delete`
146154
}

0 commit comments

Comments
 (0)