Skip to content
This repository was archived by the owner on Jul 27, 2020. It is now read-only.

Commit 46372c9

Browse files
committed
feature(db): add upsert
1 parent dd2d11d commit 46372c9

File tree

4 files changed

+101
-36
lines changed

4 files changed

+101
-36
lines changed

karma.conf.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module.exports = function(config) {
77

88
// base path that will be used to resolve all patterns (eg. files, exclude)
99
basePath: '',
10-
10+
1111
plugins: [ require('./spec/support/tsc-preprocessor'), 'karma-jasmine', 'karma-chrome-launcher'],
1212
// frameworks to use
1313
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
@@ -22,7 +22,7 @@ module.exports = function(config) {
2222
{pattern: 'node_modules/angular2/src/**/*.js', included: false},
2323
{pattern: 'node_modules/angular2/*.js', included: false},
2424
{pattern: 'node_modules/rxjs/**/*.js', included: false},
25-
25+
2626
{pattern: 'src/**/*', included: false},
2727
{pattern: 'spec/**/*', included: false},
2828
],
@@ -71,7 +71,7 @@ module.exports = function(config) {
7171

7272
// Continuous Integration mode
7373
// if true, Karma captures browsers, runs the tests and exits
74-
singleRun: argv.ci,
74+
singleRun: false,
7575

7676
// Concurrency level
7777
// how many browser should be started simultaneous

spec/db_spec.ts

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,25 @@ import * as DB from '../src/database';
22

33
import {Injector, provide} from 'angular2/core';
44

5-
jasmine.DEFAULT_TIMEOUT_INTERVAL = 2000;
5+
jasmine.DEFAULT_TIMEOUT_INTERVAL = 2000;
66

77
const todoAppSchema:DB.DBSchema = {
88
version: 1,
99
name: 'todo_app',
1010
stores: {
1111
'todos': {autoIncrement: true},
1212
'categories': {autoIncrement: true},
13-
'friends': {autoIncrement: true}
13+
'friends': {autoIncrement: true},
14+
'users': {autoIncrement: true, primaryKey: 'userID'}
1415
}
1516
}
1617

1718

1819
//cleanup function
1920
const deleteDatabase = (done) => {
20-
21+
2122
let del = indexedDB.deleteDatabase(todoAppSchema.name);
22-
23+
2324
del.onerror = (err) => {
2425
del.onblocked = undefined;
2526
console.error(err);
@@ -31,68 +32,94 @@ const deleteDatabase = (done) => {
3132
}
3233

3334
describe('database functionality', () => {
34-
35+
3536
let idb:DB.Database;
3637
let dbBackend;
3738
let injector:Injector;
38-
39+
3940
beforeEach(() => {
4041
injector = Injector.resolveAndCreate([
4142
DB.DB_PROVIDERS,
4243
DB.provideDB(todoAppSchema)
4344
]);
44-
45+
4546
idb = injector.get(DB.Database);
4647
});
47-
48+
4849
beforeAll((done) => {
4950
deleteDatabase(done);
5051
});
51-
52+
5253
it('should instantiate a DB', () => {
5354
expect(idb).toBeDefined();
5455
});
55-
56+
5657
it('should open successfully', (done) => {
5758
let openReq = idb.open(todoAppSchema.name);
58-
59+
5960
openReq.subscribe(db => {
6061
expect(db).toBeDefined();
61-
expect(db.objectStoreNames.length).toBe(3);
62+
expect(db.objectStoreNames.length).toBe(4);
6263
done();
6364
});
6465
});
65-
66+
6667
it('should insert some data', (done) => {
6768
idb.insert('todos',[{name: 'todo1'}, {name: 'todo2'}])
6869
.toArray()
6970
.subscribe(results => {
70-
expect(results[0]).toEqual({$key: 1, record: {name: 'todo1'}});
71-
expect(results[1]).toEqual({$key: 2, record: {name: 'todo2'}});
71+
expect(results[0]).toEqual({$key: 1, name: 'todo1'});
72+
expect(results[1]).toEqual({$key: 2, name: 'todo2'});
7273
done()
7374
}, err => {
7475
console.error(err);
7576
done(err);
7677
});
7778
});
78-
79+
7980
it('should insert some more data', (done) => {
8081
idb.insert('todos',[{name: 'todo3'}, {name: 'todo4'}])
8182
.toArray()
8283
.subscribe(results => {
83-
expect(results[0]).toEqual({$key: 3, record: {name: 'todo3'}});
84-
expect(results[1]).toEqual({$key: 4, record: {name: 'todo4'}});
84+
expect(results[0]).toEqual({$key: 3, name: 'todo3'});
85+
expect(results[1]).toEqual({$key: 4, name: 'todo4'});
86+
done()
87+
}, err => {
88+
console.error(err);
89+
done(err);
90+
});
91+
});
92+
93+
it('should update existing data', (done) => {
94+
idb.insert('todos',[{$key: 3, name: 'todo3++'}, {$key: 4, name: 'todo4++'}])
95+
.toArray()
96+
.subscribe(results => {
97+
expect(results[0]).toEqual({$key: 3, name: 'todo3++'});
98+
expect(results[1]).toEqual({$key: 4, name: 'todo4++'});
8599
done()
86100
}, err => {
87101
console.error(err);
88102
done(err);
89103
});
90104
});
91-
105+
106+
it('should insert some more data with a primary key', (done) => {
107+
idb.insert('users',[{userID: 'user1'}, {userID: 'user2'}])
108+
.toArray()
109+
.subscribe(results => {
110+
expect(results[0]).toEqual({userID: 'user1'});
111+
expect(results[1]).toEqual({userID: 'user2'});
112+
done()
113+
}, err => {
114+
console.error(err);
115+
done(err);
116+
});
117+
});
118+
92119
it('should broadcast notifications on insert', (done) => {
93-
120+
94121
let notificationCount = 0;
95-
122+
96123
idb.changes.subscribe(notif => {
97124
notificationCount++;
98125
});
@@ -103,21 +130,35 @@ describe('database functionality', () => {
103130
done();
104131
});
105132
});
106-
133+
107134
it('should get a record by key', (done) => {
108135
let found;
109-
idb.get('todos', 3)
136+
idb.get('todos', 2)
110137
.subscribe(record => {
111138
found = record;
112139
}, err => {
113140
console.error(err);
114141
done(err);
115142
}, () => {
116-
expect(found).toEqual({name: 'todo3'});
143+
expect(found).toEqual({name: 'todo2'});
117144
done()
118145
});
119146
});
120-
147+
148+
it('should get a record by primaryKey', (done) => {
149+
let found;
150+
idb.get('users', 'user1')
151+
.subscribe(record => {
152+
found = record;
153+
}, err => {
154+
console.error(err);
155+
done(err);
156+
}, () => {
157+
expect(found).toEqual({userID: 'user1'});
158+
done()
159+
});
160+
});
161+
121162
it('should iterate records', (done) => {
122163
let found;
123164
idb.query('todos').toArray()
@@ -131,7 +172,7 @@ describe('database functionality', () => {
131172
done()
132173
});
133174
});
134-
175+
135176
it('should iterate records with a predicate fn', (done) => {
136177
let found;
137178
idb.query('todos', (rec) => rec.name === 'todo5').toArray()
@@ -145,6 +186,6 @@ describe('database functionality', () => {
145186
done()
146187
});
147188
});
148-
189+
149190
});
150191

src/database.ts

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {Subscriber} from 'rxjs/Subscriber';
33
import {Subject} from 'rxjs/Subject';
44
import {OpaqueToken, Inject, provide} from 'angular2/core';
55
import 'rxjs/add/operator/mergeMap'
6+
import 'rxjs/add/operator/map'
67
import 'rxjs/add/operator/do'
78
import 'rxjs/add/operator/toArray'
89
import 'rxjs/add/observable/fromArray'
@@ -49,6 +50,15 @@ export class Database {
4950
this._idb = idbBackend;
5051
}
5152

53+
private _mapRecord(objectSchema: DBStore){
54+
return (dbResponseRec: any) => {
55+
if(!objectSchema.primaryKey){
56+
dbResponseRec.record['$key'] = dbResponseRec['$key'];
57+
}
58+
return dbResponseRec.record;
59+
}
60+
}
61+
5262
private _upgradeDB(observer, db:IDBDatabase){
5363
for(var storeName in this._schema.stores){
5464
if(db.objectStoreNames.contains(storeName)){
@@ -61,7 +71,7 @@ export class Database {
6171
}
6272

6373
private _createObjectStore(db:IDBDatabase, key:string, schema:DBStore){
64-
let objectStore = db.createObjectStore(key, {autoIncrement: true});
74+
let objectStore = db.createObjectStore(key, {autoIncrement: true, keyPath: schema.primaryKey});
6575
}
6676

6777
open(dbName:string, version:number = 1, upgradeHandler?:DBUpgradeHandler):Observable<IDBDatabase> {
@@ -119,14 +129,16 @@ export class Database {
119129
}
120130

121131
insert(storeName:string, records:any[], notify:boolean = true){
122-
return this.executeWrite(storeName, 'add', records)
132+
return this.executeWrite(storeName, 'put', records)
123133
.do(payload => notify ? this.changes.next({type: DB_INSERT, payload }) : ({}));
124134
}
125135

126136
get(storeName:string, key:any){
127137
return this.open(this._schema.name)
128138
.mergeMap(db => {
129139
return new Observable(txnObserver => {
140+
const recordSchema = this._schema.stores[storeName];
141+
const mapper = this._mapRecord(recordSchema);
130142
const txn = db.transaction([storeName], IDB_TXN_READ);
131143
const objectStore = txn.objectStore(storeName);
132144

@@ -202,6 +214,8 @@ export class Database {
202214
return this.open(this._schema.name)
203215
.mergeMap(db => {
204216
return new Observable(txnObserver => {
217+
const recordSchema = this._schema.stores[storeName];
218+
const mapper = this._mapRecord(recordSchema);
205219
const txn = db.transaction([storeName], IDB_TXN_READWRITE);
206220
const objectStore = txn.objectStore(storeName);
207221

@@ -213,10 +227,19 @@ export class Database {
213227

214228
const makeRequest = (record) => {
215229
return new Observable(reqObserver => {
216-
let req = objectStore[actionType](record);
230+
let req;
231+
if(recordSchema.primaryKey){
232+
req = objectStore[actionType](record);
233+
}
234+
else {
235+
let $key = record['$key'];
236+
let $record = Object.assign({}, record);
237+
delete $record.key;
238+
req = objectStore[actionType]($record, $key);
239+
}
217240
req.addEventListener(IDB_SUCCESS, () => {
218-
let $key = req.result
219-
reqObserver.next({$key, record});
241+
let $key = req.result;
242+
reqObserver.next(mapper({$key, record}));
220243
});
221244
req.addEventListener(IDB_ERROR, (err) => {
222245
reqObserver.error(err);

tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"module": "commonjs",
77
"moduleResolution": "node",
88
"outDir": "dist",
9-
"declaration": true
9+
"declaration": true,
10+
"inlineSourceMap": true
1011
},
1112
"files": [
1213
"typings/browser.d.ts",

0 commit comments

Comments
 (0)