diff --git a/src/app/modules/data/data.module.ts b/src/app/modules/data/data.module.ts
index 25c46bb7..95d75741 100644
--- a/src/app/modules/data/data.module.ts
+++ b/src/app/modules/data/data.module.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 StApps
+ * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -17,6 +17,7 @@ import {HttpClientModule} from '@angular/common/http';
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {IonicModule} from '@ionic/angular';
+import {StorageModule} from '../storage/storage.module';
import {DataRoutingModule} from './data-routing.module';
import {DataProvider} from './data.provider';
import {DataDetailComponent} from './detail/data-detail.component';
@@ -48,6 +49,7 @@ import {EventListItemComponent} from './types/event/event-list-item.component';
FormsModule,
DataRoutingModule,
HttpClientModule,
+ StorageModule,
],
providers: [
DataProvider,
diff --git a/src/app/modules/data/data.provider.spec.ts b/src/app/modules/data/data.provider.spec.ts
new file mode 100644
index 00000000..3f0826a5
--- /dev/null
+++ b/src/app/modules/data/data.provider.spec.ts
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2018, 2019 StApps
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+import {TestBed} from '@angular/core/testing';
+import {Client} from '@openstapps/api/lib/client';
+import {SCMessage, SCSaveableThing, SCSearchQuery, SCSearchResponse,
+ SCSearchValueFilter, SCThing, SCThingOriginType, SCThings, SCThingType} from '@openstapps/core';
+import {StorageProvider} from '../storage/storage.provider';
+import {DataModule} from './data.module';
+import {DataProvider, DataScope} from './data.provider';
+import {StAppsWebHttpClient} from './stapps-web-http-client.provider';
+
+describe('DataProvider', () => {
+ let dataProvider: DataProvider;
+ let storageProvider: StorageProvider;
+ const sampleResponse: SCSearchResponse = {
+ data: [
+ {
+ categories: ['main dish'],
+ name: 'foo dish',
+ origin: {
+ indexed: '12345',
+ name: 'bar',
+ type: SCThingOriginType.Remote,
+ },
+ type: SCThingType.Dish,
+ uid: '123',
+ },
+ {
+ categories: ['dessert'],
+ name: 'foo dessert',
+ origin: {
+ indexed: '12345',
+ name: 'bar',
+ type: SCThingOriginType.Remote,
+ },
+ type: SCThingType.Dish,
+ uid: '123',
+ },
+ ],
+ facets: [
+ {
+ buckets: [],
+ field: 'foo',
+ },
+ ],
+ pagination: {
+ count: 0,
+ offset: 0,
+ total: 0,
+ },
+ stats: {
+ time: 123,
+ },
+ };
+ const sampleFilter: SCSearchValueFilter = {
+ arguments: {
+ field: 'type',
+ value: 'dish',
+ },
+ type: 'value',
+ };
+ const sampleQuery: SCSearchQuery = {
+ filter: sampleFilter,
+ };
+
+ const sampleThing: SCMessage = {
+ audiences: ['students'],
+ message: 'Foo Message',
+ name: 'foo',
+ origin: {
+ indexed: 'SOME-DATE',
+ name: 'some name',
+ type: SCThingOriginType.Remote,
+ },
+ type: SCThingType.Message,
+ uid: '123',
+ };
+ const sampleSavable: SCSaveableThing = {
+ data: sampleThing,
+ name: sampleThing.name,
+ origin: {
+ created: new Date().toISOString(),
+ type: SCThingOriginType.User,
+ },
+ type: SCThingType.Message,
+ uid: sampleThing.uid,
+ };
+ const otherSampleThing: SCMessage = {...sampleThing, uid: '456', name: 'bar'};
+
+ beforeEach(async () => {
+ TestBed.configureTestingModule({
+ imports: [DataModule],
+ providers: [
+ DataProvider,
+ StAppsWebHttpClient,
+ ],
+ });
+ storageProvider = TestBed.get(StorageProvider);
+ dataProvider = TestBed.get(DataProvider);
+ await storageProvider.deleteAll();
+ });
+
+ it('should generate data key', async () => {
+ dataProvider.storagePrefix = 'foo.data';
+ expect(dataProvider.getDataKey('123')).toBe('foo.data.123');
+ });
+
+ it('should provide backend data items using search query', async () => {
+ spyOn(Client.prototype, 'search').and.callFake(() => {
+ return {
+ then: (callback: any) => {
+ return callback(sampleResponse);
+ },
+ };
+ });
+ const response = await dataProvider.search(sampleQuery);
+ expect(response).toEqual(sampleResponse);
+ });
+
+ it('should put an data item into the local database (storage)', async () => {
+ let providedThing: SCSaveableThing;
+ spyOn(storageProvider, 'put').and.callFake((_id: any, thing: any) => {
+ providedThing = thing;
+ providedThing.origin.created = sampleSavable.origin.created;
+ });
+ expect(storageProvider.put).not.toHaveBeenCalled();
+ expect(providedThing!).not.toBeDefined();
+ await dataProvider.put(sampleThing);
+ expect(providedThing!).toBeDefined();
+ expect(providedThing!).toEqual(sampleSavable);
+ });
+
+ it('should correctly set and get single data item from the local database (storage)', async () => {
+ await dataProvider.put(sampleThing);
+ spyOn(storageProvider, 'get').and.callThrough();
+ expect(storageProvider.get).not.toHaveBeenCalled();
+ const providedThing = await dataProvider.get(sampleThing.uid, DataScope.Local);
+ providedThing.origin.created = sampleSavable.origin.created;
+ expect(storageProvider.get).toHaveBeenCalledWith(dataProvider.getDataKey(sampleThing.uid));
+ expect(providedThing).toEqual(sampleSavable);
+ });
+
+ it('should provide all data items from the local database (storage)', async () => {
+ await dataProvider.put(sampleThing);
+ await dataProvider.put(otherSampleThing);
+ const result = await dataProvider.getAll();
+ expect(Array.from(result.keys()).sort()).toEqual([
+ dataProvider.getDataKey(sampleThing.uid), dataProvider.getDataKey(otherSampleThing.uid),
+ ]);
+ expect(result.get(dataProvider.getDataKey(sampleThing.uid))!.data).toEqual(sampleThing);
+ expect(result.get(dataProvider.getDataKey(otherSampleThing.uid))!.data).toEqual(otherSampleThing);
+ });
+
+ it('should provide single data from the backend', async () => {
+ spyOn(Client.prototype, 'getThing').and.callFake(() => {
+ return {
+ then: (callback: any) => {
+ return callback(sampleThing);
+ },
+ };
+ });
+ expect(Client.prototype.getThing).not.toHaveBeenCalled();
+ const providedThing = await dataProvider.get(sampleThing.uid, DataScope.Remote);
+ expect(Client.prototype.getThing).toHaveBeenCalledWith(sampleThing.uid);
+ expect(providedThing).toBe(sampleThing);
+ });
+
+ it('should get an item from both local and remote database', async () => {
+ spyOn(Client.prototype, 'getThing').and.callFake(() => {
+ return {
+ then: (callback: any) => {
+ return callback(sampleThing);
+ },
+ };
+ });
+ spyOn(storageProvider, 'get').and.callFake(() => {
+ return {
+ then: (callback: any) => {
+ return callback(sampleSavable);
+ },
+ };
+ });
+ const result = await dataProvider.get(sampleThing.uid);
+ expect(result.get(DataScope.Local)).toEqual(sampleSavable);
+ expect(result.get(DataScope.Remote)).toEqual(sampleThing);
+ });
+
+ it('should properly delete a data item from the local database (storage)', async () => {
+ spyOn(storageProvider, 'delete').and.callThrough();
+ await dataProvider.put(sampleThing);
+ expect(await storageProvider.length()).toBe(1);
+ await dataProvider.delete(sampleThing.uid);
+ expect(storageProvider.delete).toHaveBeenCalledWith(dataProvider.getDataKey(sampleThing.uid));
+ expect(await storageProvider.length()).toBe(0);
+ });
+
+ it('should properly delete all the data items from the local database (storage)', async () => {
+ spyOn(storageProvider, 'delete').and.callThrough();
+ await dataProvider.put(sampleThing);
+ await dataProvider.put(otherSampleThing);
+ await storageProvider.put('some-uid', {some: 'thing'});
+ expect(await storageProvider.length()).toBe(3);
+ await dataProvider.deleteAll();
+ expect(storageProvider.delete).toHaveBeenCalledWith(
+ dataProvider.getDataKey(sampleThing.uid),
+ dataProvider.getDataKey(otherSampleThing.uid),
+ );
+ const result = await storageProvider.getAll();
+ expect(Array.from(result.keys())).toEqual(['some-uid']);
+ });
+});
diff --git a/src/app/modules/data/data.provider.ts b/src/app/modules/data/data.provider.ts
index e461a7d8..e8518a4f 100644
--- a/src/app/modules/data/data.provider.ts
+++ b/src/app/modules/data/data.provider.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 StApps
+ * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -13,6 +13,16 @@
* this program. If not, see .
*/
import {Injectable} from '@angular/core';
+import {Client} from '@openstapps/api/lib/client';
+import {SCSearchQuery, SCSearchResponse, SCThingOriginType, SCThings, SCThingType} from '@openstapps/core';
+import {SCSaveableThing} from '@openstapps/core';
+import {StorageProvider} from '../storage/storage.provider';
+import {StAppsWebHttpClient} from './stapps-web-http-client.provider';
+
+export enum DataScope {
+ Local = 'local',
+ Remote = 'remote',
+}
/*
Generated class for the DataProvider provider.
@@ -22,7 +32,119 @@ import {Injectable} from '@angular/core';
*/
@Injectable()
export class DataProvider {
- constructor() {
- // @TODO
+ private _storagePrefix: string = 'stapps.data';
+ // @TODO: get backendUrl and appVersion and storagePrefix from config (module)
+ appVersion: string = '1.0.6';
+ backendUrl: string = 'https://stappsbe01.innocampus.tu-berlin.de';
+ client: Client;
+ storageProvider: StorageProvider;
+
+ constructor(stAppsWebHttpClient: StAppsWebHttpClient, storageProvider: StorageProvider) {
+ this.client = new Client(stAppsWebHttpClient, this.backendUrl, this.appVersion);
+ this.storageProvider = storageProvider;
+ }
+
+ get storagePrefix(): string {
+ return this._storagePrefix;
+ }
+
+ set storagePrefix(storagePrefix) {
+ this._storagePrefix = storagePrefix;
+ }
+
+ /**
+ * Provides key for storing data into the local database
+ *
+ * @param uid Unique identifier of a resource
+ */
+ getDataKey(uid: string): string {
+ return `${this.storagePrefix}.${uid}`;
+ }
+
+ /**
+ * Provides a saveable thing from the local database using the provided UID
+ */
+ async get(uid: string, scope: DataScope.Local): Promise>;
+ /**
+ * Provides a thing from the backend
+ */
+ async get(uid: string, scope: DataScope.Remote): Promise>;
+ /**
+ * Provides a thing from both local database and backend
+ */
+ async get(uid: string): Promise