[CORD-1338] Inline navigation for related models
Change-Id: I58ff4a4675d1ce1140fe162f1f8360f2dc9a6527
diff --git a/src/app/datasources/helpers/model-discoverer.service.ts b/src/app/datasources/helpers/model-discoverer.service.ts
index 1ef2904..37ecc93 100644
--- a/src/app/datasources/helpers/model-discoverer.service.ts
+++ b/src/app/datasources/helpers/model-discoverer.service.ts
@@ -186,11 +186,15 @@
id: null
},
data: {
- model: model.name
+ model: model.name,
},
component: 'xosCrud',
};
+ if (angular.isDefined(model.relations)) {
+ state.data.relations = model.relations;
+ }
+
try {
this.XosRuntimeStates.addState(
this.stateNameFromModel(model),
diff --git a/src/app/datasources/helpers/model.discoverer.service.spec.ts b/src/app/datasources/helpers/model.discoverer.service.spec.ts
index d5a3232..b72b311 100644
--- a/src/app/datasources/helpers/model.discoverer.service.spec.ts
+++ b/src/app/datasources/helpers/model.discoverer.service.spec.ts
@@ -190,6 +190,30 @@
scope.$apply();
});
+ it('should add a state with relations in the system', (done) => {
+ MockXosRuntimeStates.addState.calls.reset();
+ service['addState']({name: 'Tenant', app: 'services.vsg', relations: [{model: 'Something', type: 'manytoone'}]})
+ .then((model) => {
+ expect(MockXosRuntimeStates.addState).toHaveBeenCalledWith('xos.vsg.tenant', {
+ parent: 'xos.vsg',
+ url: '/tenants/:id?',
+ params: {
+ id: null
+ },
+ data: {
+ model: 'Tenant',
+ relations: [
+ {model: 'Something', type: 'manytoone'}
+ ]
+ },
+ component: 'xosCrud',
+ });
+ expect(model.clientUrl).toBe('vsg/tenants/:id?');
+ done();
+ });
+ scope.$apply();
+ });
+
it('should add an item to navigation', () => {
service['addNavItem']({name: 'Tenant', app: 'services.vsg'});
expect(MockXosNavigationService.add).toHaveBeenCalledWith({
diff --git a/src/app/datasources/rest/modeldefs.rest.ts b/src/app/datasources/rest/modeldefs.rest.ts
index fdb4b99..0af00ff 100644
--- a/src/app/datasources/rest/modeldefs.rest.ts
+++ b/src/app/datasources/rest/modeldefs.rest.ts
@@ -16,6 +16,7 @@
export interface IXosModelDefsRelation {
model: string; // model name
type: string; // relation type
+ on_field: string; // the field that is containing the relation
}
export interface IXosModeldef {
diff --git a/src/app/datasources/stores/model.store.spec.ts b/src/app/datasources/stores/model.store.spec.ts
index b114de4..846ef84 100644
--- a/src/app/datasources/stores/model.store.spec.ts
+++ b/src/app/datasources/stores/model.store.spec.ts
@@ -31,7 +31,7 @@
const queryData = [
{id: 1, name: 'foo'},
- {id: 1, name: 'bar'}
+ {id: 2, name: 'bar'}
];
const MockAppCfg = {
@@ -72,23 +72,42 @@
.respond(queryData);
}));
- it('should return an Observable', () => {
- expect(typeof service.query('test').subscribe).toBe('function');
+ describe('the QUERY method', () => {
+ it('should return an Observable', () => {
+ expect(typeof service.query('test').subscribe).toBe('function');
+ });
+
+ it('the first event should be the resource response', (done) => {
+ let event = 0;
+ service.query('sample')
+ .subscribe(collection => {
+ event++;
+ if (event === 2) {
+ expect(collection[0].id).toEqual(queryData[0].id);
+ expect(collection[1].id).toEqual(queryData[1].id);
+ done();
+ }
+ });
+ $scope.$apply();
+ httpBackend.flush();
+ });
});
- it('the first event should be the resource response', (done) => {
- let event = 0;
- service.query('sample')
- .subscribe(collection => {
- event++;
- if (event === 2) {
- expect(collection[0].id).toEqual(queryData[0].id);
- expect(collection[1].id).toEqual(queryData[1].id);
- done();
- }
- });
- $scope.$apply();
- httpBackend.flush();
+ describe('the GET method', () => {
+ it('should return an observable containing a single model', (done) => {
+ let event = 0;
+ service.get('sample', queryData[1].id)
+ .subscribe((model) => {
+ event++;
+ if (event === 2) {
+ expect(model.id).toEqual(queryData[1].id);
+ expect(model.name).toEqual(queryData[1].name);
+ done();
+ }
+ });
+ httpBackend.flush();
+ $scope.$apply();
+ });
});
describe('when a web-socket event is received for that model', () => {
diff --git a/src/app/datasources/stores/model.store.ts b/src/app/datasources/stores/model.store.ts
index 950d441..10da16e 100644
--- a/src/app/datasources/stores/model.store.ts
+++ b/src/app/datasources/stores/model.store.ts
@@ -8,6 +8,7 @@
export interface IXosModelStoreService {
query(model: string, apiUrl?: string): Observable<any>;
+ get(model: string, id: string | number): Observable<any>;
search(modelName: string): any[];
}
@@ -26,7 +27,7 @@
this.efficientNext = this.XosDebouncer.debounce(this.next, 500, this, false);
}
- public query(modelName: string, apiUrl: string): Observable<any> {
+ public query(modelName: string, apiUrl?: string): Observable<any> {
// if there isn't already an observable for that item
// create a new one and .next() is called by this.loadInitialData once data are received
if (!this._collections[modelName]) {
@@ -80,8 +81,31 @@
}
}
- public get(model: string, id: number) {
- // TODO implement a get method
+ public get(modelName: string, modelId: string | number): Observable<any> {
+ const subject = new BehaviorSubject([]);
+
+ const _findModel = (subject) => {
+ this._collections[modelName]
+ .subscribe((res) => {
+ const model = _.find(res, {id: modelId});
+ if (model) {
+ subject.next(model);
+ }
+ });
+ };
+
+ if (!this._collections[modelName]) {
+ // cache the models in that collection
+ this.query(modelName)
+ .subscribe((res) => {
+ _findModel(subject);
+ });
+ }
+ else {
+ _findModel(subject);
+ }
+
+ return subject.asObservable();
}
private next(subject: BehaviorSubject<any>): void {
@@ -89,7 +113,7 @@
}
private loadInitialData(model: string, apiUrl?: string) {
- // TODO provide always the apiUrl togheter with the query() params
+ // TODO provide always the apiUrl together with the query() params
if (!angular.isDefined(apiUrl)) {
// NOTE check what is the correct pattern to pluralize this
apiUrl = this.storeHelpers.urlFromCoreModel(model);