Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MLIBZ-2120: Add support for specifying a storage adapter #213

Merged
merged 5 commits into from Jan 26, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/kinvey-angular-sdk/src/bundle.js
Expand Up @@ -23,7 +23,8 @@ export {
AuthorizationGrant,
CustomEndpoint,
DataStore,
DataStoreType, SyncOperation,
DataStoreType,
SyncOperation,
LiveService,
Files,
Log,
Expand Down
3 changes: 2 additions & 1 deletion packages/kinvey-angular2-sdk/src/bundle.js
Expand Up @@ -23,7 +23,8 @@ export {
AuthorizationGrant,
CustomEndpoint,
DataStore,
DataStoreType, SyncOperation,
DataStoreType,
SyncOperation,
LiveService,
Files,
Log,
Expand Down
3 changes: 2 additions & 1 deletion packages/kinvey-html5-sdk/src/bundle.js
Expand Up @@ -22,7 +22,8 @@ export {
AuthorizationGrant,
CustomEndpoint,
DataStore,
DataStoreType, SyncOperation,
DataStoreType,
SyncOperation,
LiveService,
Files,
Log,
Expand Down
3 changes: 2 additions & 1 deletion packages/kinvey-nativescript-sdk/src/bundle.ts
Expand Up @@ -22,7 +22,8 @@ export {
AuthorizationGrant,
CustomEndpoint,
DataStore,
DataStoreType, SyncOperation,
DataStoreType,
SyncOperation,
LiveService,
Files,
Log,
Expand Down
3 changes: 2 additions & 1 deletion packages/kinvey-phonegap-sdk/src/bundle.js
Expand Up @@ -22,7 +22,8 @@ export {
AuthorizationGrant,
CustomEndpoint,
DataStore,
DataStoreType, SyncOperation,
DataStoreType,
SyncOperation,
LiveService,
Files,
Log,
Expand Down
8 changes: 7 additions & 1 deletion src/core/client.js
Expand Up @@ -137,6 +137,11 @@ export class Client {
*/
this.defaultTimeout = isNumber(config.defaultTimeout) && config.defaultTimeout >= 0 ? config.defaultTimeout : DEFAULT_TIMEOUT;

/**
* @type {?string[]}
*/
this.storageAdapters = config.storageAdapters;

/**
* @private
*/
Expand Down Expand Up @@ -236,7 +241,8 @@ export class Client {
appSecret: this.appSecret,
masterSecret: this.masterSecret,
encryptionKey: this.encryptionKey,
appVersion: this.appVersion
appVersion: this.appVersion,
storageAdapters: this.storageAdapters
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/core/index.js
Expand Up @@ -9,7 +9,7 @@ export { Files } from './files';
export { Log } from './log';
export { Metadata } from './metadata';
export { Query } from './query';
export { Properties } from './request';
export { Properties, StorageAdapter } from './request';
export { User } from './user';
export {
ActiveUserError,
Expand Down
20 changes: 11 additions & 9 deletions src/core/request/cache.js
@@ -1,12 +1,13 @@
import url from 'url';
import cloneDeep from 'lodash/cloneDeep';
import { KinveyError } from '../errors';
import { Query } from '../query';
import { Aggregation } from '../aggregation';
import { isDefined } from '../utils';
import { Request } from './request';
import { KinveyResponse } from './response';
import { CacheRack } from './rack';
import url from 'url';
import cloneDeep from 'lodash/cloneDeep';
import { KinveyError } from '../errors';
import { Query } from '../query';
import { Aggregation } from '../aggregation';
import { isDefined } from '../utils';
import { Request } from './request';
import { KinveyResponse } from './response';
import { CacheRack } from './rack';
import { StorageAdapter } from './middleware/storage';

export class CacheRequest extends Request {
constructor(options = {}) {
Expand Down Expand Up @@ -100,6 +101,7 @@ export class CacheRequest extends Request {
obj.collection = this.collection;
obj.entityId = this.entityId;
obj.encryptionKey = this.client ? this.client.encryptionKey : undefined;
obj.storageAdapters = this.client ? this.client.storageAdapters : undefined;
return obj;
}
}
8 changes: 4 additions & 4 deletions src/core/request/middleware/cache.js
Expand Up @@ -8,13 +8,13 @@ export class CacheMiddleware extends Middleware {
super(name);
}

loadStorage(name) {
return new Storage(name);
loadStorage(name, storageAdapters) {
return new Storage(name, storageAdapters);
}

handle(request) {
const { method, body, appKey, collection, entityId, encryptionKey } = request;
const storage = this.loadStorage(appKey, encryptionKey);
const { method, body, appKey, collection, entityId, encryptionKey, storageAdapters } = request;
const storage = this.loadStorage(appKey, storageAdapters, encryptionKey);
let promise;

if (method === 'GET') {
Expand Down
29 changes: 26 additions & 3 deletions src/core/request/middleware/storage/index.js
Expand Up @@ -9,8 +9,13 @@ import { MemoryAdapter } from './memory';

const queue = new Queue(1, Infinity);

export const StorageAdapter = {
Memory: 'Memory'
};
Object.freeze(StorageAdapter);

export class Storage {
constructor(name) {
constructor(name, storageAdapters = [StorageAdapter.Memory]) {
if (!name) {
throw new KinveyError('Unable to create a Storage instance without a name.');
}
Expand All @@ -20,12 +25,30 @@ export class Storage {
}

this.name = name;

if (!Array.isArray(storageAdapters)) {
storageAdapters = [storageAdapters];
}
this.storageAdapters = storageAdapters;
}

loadAdapter() {
return MemoryAdapter.load(this.name)
return this.storageAdapters.reduce((promise, storageAdapter) => {
return promise.then((adapter) => {
if (adapter) {
return adapter;
}

switch (storageAdapter) {
case StorageAdapter.Memory:
return MemoryAdapter.load(this.name);
default:
return null;
}
});
}, Promise.resolve())
.then((adapter) => {
if (!isDefined(adapter)) {
if (!adapter) {
return Promise.reject(new KinveyError('Unable to load a storage adapter.'));
}

Expand Down
4 changes: 2 additions & 2 deletions src/html5/cache.js
Expand Up @@ -2,7 +2,7 @@ import { CacheMiddleware } from '../core/request';
import { Html5Storage } from './storage';

export class Html5CacheMiddleware extends CacheMiddleware {
loadStorage(name) {
return new Html5Storage(name);
loadStorage(name, storageAdapters) {
return new Html5Storage(name, storageAdapters);
}
}
1 change: 1 addition & 0 deletions src/html5/index.js
@@ -1 +1,2 @@
export { StorageAdapter } from './storage';
export * from './kinvey';
55 changes: 29 additions & 26 deletions src/html5/storage/index.js
@@ -1,38 +1,41 @@
import { Storage } from '../../core/request';
import { Storage, StorageAdapter as CoreStorageAdapter } from '../../core/request';
import { IndexedDBAdapter } from './indexeddb';
import { WebSQLAdapter } from './websql';
import { LocalStorageAdapter, SessionStorageAdapter } from './webstorage';

export class Html5Storage extends Storage {
loadAdapter() {
return WebSQLAdapter.load(this.name)
.then((adapter) => {
if (!adapter) {
return IndexedDBAdapter.load(this.name);
}
export const StorageAdapter = Object.assign({}, CoreStorageAdapter, {
IndexedDB: 'IndexedDB',
LocalStorage: 'LocalStorage',
SessionStorage: 'SessionStorage',
WebSQL: 'WebSQL'
});
Object.freeze(StorageAdapter);

return adapter;
})
.then((adapter) => {
if (!adapter) {
return LocalStorageAdapter.load(this.name);
}
export class Html5Storage extends Storage {
constructor(name, storageAdapters = [StorageAdapter.WebSQL, StorageAdapter.IndexedDB, StorageAdapter.LocalStorage, StorageAdapter.SessionStorage, StorageAdapter.Memory]) {
super(name, storageAdapters);
}

return adapter;
})
.then((adapter) => {
if (!adapter) {
return SessionStorageAdapter.load(this.name);
loadAdapter() {
return this.storageAdapters.reduce((promise, storageAdapter) => {
return promise.then((adapter) => {
if (adapter) {
return adapter;
}

return adapter;
})
.then((adapter) => {
if (!adapter) {
return super.loadAdapter();
switch (storageAdapter) {
case StorageAdapter.IndexedDB:
return IndexedDBAdapter.load(this.name);
case StorageAdapter.LocalStorage:
return LocalStorageAdapter.load(this.name);
case StorageAdapter.SessionStorage:
return SessionStorageAdapter.load(this.name);
case StorageAdapter.WebSQL:
return WebSQLAdapter.load(this.name);
default:
return super.loadAdapter();
}

return adapter;
});
}, Promise.resolve());
}
}
87 changes: 23 additions & 64 deletions src/nativescript/cache/index.ts
@@ -1,82 +1,41 @@
import { isDefined, isEmpty } from '../../core/utils';
import { Middleware, RequestMethod, StatusCode, Storage as CoreStorage } from '../../core/request';
import { CacheMiddleware as CoreCacheMiddleware, Storage as CoreStorage, StorageAdapter as CoreStorageAdapter } from '../../core/request';
import { sqLite } from './sqlite';

export const StorageAdapter = Object.assign({}, CoreStorageAdapter, {
SQLite: 'SQLite'
});
Object.freeze(StorageAdapter);

class Storage extends CoreStorage {
name: string;
storageAdapters: Array<string>;

constructor(name: string) {
super(name);
constructor(name, storageAdapters = [StorageAdapter.SQLite, StorageAdapter.Memory]) {
super(name, storageAdapters);
}

loadAdapter() {
return sqLite.load(this.name)
.then((adapter) => {
if (!isDefined(adapter)) {
return super.loadAdapter();
return this.storageAdapters.reduce((promise, storageAdapter) => {
return promise.then((adapter) => {
if (adapter) {
return adapter;
}

return adapter;
switch (storageAdapter) {
case StorageAdapter.SQLite:
return sqLite.load(this.name);
default:
return super.loadAdapter();
}
});
}, Promise.resolve());
}
}

export class CacheMiddleware extends Middleware {
constructor(name = 'NativeScript Cache Middleware') {
super(name);
}

handle(request: any) {
const {
method,
body,
appKey,
collection,
entityId,
encryptionKey
} = request;
const storage = new Storage(appKey);
let promise;

if (method === RequestMethod.GET) {
if (entityId) {
promise = storage.findById(collection, entityId);
} else {
promise = storage.find(collection);
}
} else if (method === RequestMethod.POST || method === RequestMethod.PUT) {
if (entityId === '_group') {
promise = storage.find(collection);
} else {
promise = storage.save(collection, body);
}
} else if (method === RequestMethod.DELETE) {
if (!collection) {
promise = storage.clear();
} else {
promise = storage.removeById(collection, entityId);
}
}

return promise
.then((data) => {
const response = {
statusCode: method === 'POST' ? StatusCode.Created : StatusCode.Ok,
data: data
};

if (method === 'POST' && entityId === '_group') {
response.statusCode = StatusCode.Ok;
}

if (!data || isEmpty(data)) {
response.statusCode = StatusCode.Empty;
}

return response;
})
.catch(error => ({ statusCode: error.code }))
.then(response => ({ response: response }));
export class CacheMiddleware extends CoreCacheMiddleware {
loadStorage(name, storageAdapters) {
return new Storage(name, storageAdapters);
}
}

1 change: 1 addition & 0 deletions src/nativescript/index.ts
@@ -1,2 +1,3 @@
export * from './kinvey';
export { Push } from './push';
export { StorageAdapter } from './cache';