diff --git a/ui/src/app/contention/reducer/contention.reducer.spec.ts b/ui/src/app/contention/reducer/contention.reducer.spec.ts index e69de29bb..a96438428 100644 --- a/ui/src/app/contention/reducer/contention.reducer.spec.ts +++ b/ui/src/app/contention/reducer/contention.reducer.spec.ts @@ -0,0 +1,59 @@ +import { reducer, initialState as snapshot, getContention } from './contention.reducer'; +import { ContentionActionTypes, ShowContentionAction, ResolveContentionAction, CancelContentionAction } from '../action/contention.action'; + +describe('Contention Reducer', () => { + + const contention = { + base: {}, + ours: {}, + theirs: {}, + + rejectionObject: {}, + resolutionObject: {}, + + ourChanges: {}, + theirChanges: {}, + + handlers: { + resolve: (value: {}) => ({}), + reject: (value: {}) => ({}) + } + }; + + const populated = { ...snapshot, contention: { ...contention } }; + + const resolution = { value: contention.ours, handlers: contention.handlers }; + + describe('undefined action', () => { + it('should return the default state', () => { + const result = reducer(snapshot, {} as any); + + expect(result).toEqual(snapshot); + }); + }); + + describe(`${ContentionActionTypes.SHOW_CONTENTION}`, () => { + it('should reset to initial state', () => { + expect(reducer(snapshot, new ShowContentionAction(contention))).toEqual(populated); + }); + }); + + describe(`${ContentionActionTypes.RESOLVE_CONTENTION}`, () => { + it('should reset to initial state', () => { + expect(reducer(snapshot, new ResolveContentionAction(resolution))).toEqual(snapshot); + }); + }); + + describe(`${ContentionActionTypes.CANCEL_CONTENTION}`, () => { + it('should reset to initial state', () => { + expect(reducer(snapshot, new CancelContentionAction(resolution))).toEqual(snapshot); + }); + }); + + describe(`getContention method`, () => { + it('should return the contention object from the state', () => { + expect(getContention(snapshot)).toBe(snapshot.contention); + expect(getContention(populated)).toEqual(contention); + }); + }); +}); diff --git a/ui/src/app/metadata/domain/domain.util.spec.ts b/ui/src/app/metadata/domain/domain.util.spec.ts index 548379455..f5e775495 100644 --- a/ui/src/app/metadata/domain/domain.util.spec.ts +++ b/ui/src/app/metadata/domain/domain.util.spec.ts @@ -1,4 +1,5 @@ import * as util from './domain.util'; +import { MetadataProvider } from './model'; describe('Domain Utility methods', () => { @@ -33,4 +34,23 @@ describe('Domain Utility methods', () => { expect(util.getEntityIdsFn(entities)).toEqual(['foo', 'bar']); }); }); + + describe('mergeProviderOrderFn', () => { + const providers = [ + { resourceId: 'foo', name: 'foo', '@type': 'foo', enabled: true, xmlId: 'id', sortKey: 1, metadataFilters: [] }, + { resourceId: 'bar', name: 'bar', '@type': 'bar', enabled: false, xmlId: 'id2', sortKey: 2, metadataFilters: [] }, + { resourceId: 'baz', name: 'baz', '@type': 'baz', enabled: false, xmlId: 'id3', sortKey: 3, metadataFilters: [] } + ]; + it('1 should sort the list accordingly', () => { + let order = ['bar', 'foo', 'baz'], + ordered = util.mergeOrderFn([...providers], order); + expect(ordered.indexOf(providers[0])).toBe(1); + }); + + it('2 should sort the list accordingly', () => { + let order = ['foo', 'bar', 'baz'], + ordered = util.mergeOrderFn(providers, order); + expect(ordered.indexOf(providers[0])).toBe(0); + }); + }); }); diff --git a/ui/src/app/metadata/domain/domain.util.ts b/ui/src/app/metadata/domain/domain.util.ts index 599ce04b7..2840e9cbc 100644 --- a/ui/src/app/metadata/domain/domain.util.ts +++ b/ui/src/app/metadata/domain/domain.util.ts @@ -1,3 +1,5 @@ +import { Metadata } from './domain.type'; + /* * Utility functions */ @@ -8,3 +10,13 @@ export const getInCollectionFn = (entities, selectedId) => { return selectedId && entities[selectedId]; }; export const getEntityIdsFn = list => list.map(entity => entity.entityId); + +export const mergeOrderFn = (entities: Metadata[], order: string[]): Metadata[] => { + return [...entities.sort( + (a: Metadata, b: Metadata) => { + const aIndex = order.indexOf(a.resourceId); + const bIndex = order.indexOf(b.resourceId); + return aIndex > bIndex ? 1 : bIndex > aIndex ? -1 : 0; + } + )]; +}; diff --git a/ui/src/app/metadata/domain/model/metadata-order.ts b/ui/src/app/metadata/domain/model/metadata-order.ts deleted file mode 100644 index b365af3b0..000000000 --- a/ui/src/app/metadata/domain/model/metadata-order.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface ProviderOrder { - resourceIds: string[]; -} diff --git a/ui/src/app/metadata/domain/model/metadata-resolver.ts b/ui/src/app/metadata/domain/model/metadata-resolver.ts index 431d9d448..699ab615f 100644 --- a/ui/src/app/metadata/domain/model/metadata-resolver.ts +++ b/ui/src/app/metadata/domain/model/metadata-resolver.ts @@ -11,6 +11,7 @@ import { } from '../model'; export interface MetadataResolver extends MetadataBase { + resourceId?: string; entityId: string; serviceProviderName: string; organization?: Organization; diff --git a/ui/src/app/metadata/domain/service/filter.service.ts b/ui/src/app/metadata/domain/service/filter.service.ts index 92dabd797..bb610bf01 100644 --- a/ui/src/app/metadata/domain/service/filter.service.ts +++ b/ui/src/app/metadata/domain/service/filter.service.ts @@ -8,28 +8,38 @@ import { MetadataFilter } from '../../domain/model'; export class MetadataFilterService { readonly endpoint = '/MetadataResolvers'; + readonly order = 'FiltersPositionOrder'; readonly base = '/api'; + readonly path = 'Filters'; constructor( private http: HttpClient ) { } query(providerId: string): Observable { - return this.http.get(`${this.base}${this.endpoint}/${providerId}/Filters`); + return this.http.get(`${this.base}${this.endpoint}/${providerId}/${this.path}`); } find(providerId: string, filterId: string): Observable { - return this.http.get(`${this.base}${this.endpoint}/${providerId}/Filters/${ filterId }`); + return this.http.get(`${this.base}${this.endpoint}/${providerId}/${this.path}/${filterId}`); } update(providerId: string, filter: MetadataFilter): Observable { - return this.http.put(`${this.base}${this.endpoint}/${providerId}/Filters/${ filter.resourceId }`, filter); + return this.http.put(`${this.base}${this.endpoint}/${providerId}/${this.path}/${ filter.resourceId }`, filter); } save(providerId: string, filter: MetadataFilter): Observable { - return this.http.post(`${this.base}${this.endpoint}/${providerId}/Filters`, filter); + return this.http.post(`${this.base}${this.endpoint}/${providerId}/${this.path}`, filter); + } + + getOrder(providerId: string): Observable { + return this.http.get(`${this.base}${this.endpoint}/${providerId}/${this.order}`); + } + + setOrder(providerId: string, order: string[]): Observable { + return this.http.post(`${this.base}${this.endpoint}/${providerId}/${this.order}`, order); } remove(providerId: string, filterId: string): Observable { - return this.http.delete(`${this.base}${this.endpoint}/${providerId}/Filters/${ filterId }`); + return this.http.delete(`${this.base}${this.endpoint}/${providerId}/${this.path}/${filterId}`); } } diff --git a/ui/src/app/metadata/domain/service/provider.service.ts b/ui/src/app/metadata/domain/service/provider.service.ts index ede4bb172..dca9bb1c3 100644 --- a/ui/src/app/metadata/domain/service/provider.service.ts +++ b/ui/src/app/metadata/domain/service/provider.service.ts @@ -4,8 +4,6 @@ import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { MetadataProvider } from '../../domain/model'; -import { FileBackedHttpMetadataProvider } from '../model/providers'; -import { ProviderOrder } from '../model/metadata-order'; @Injectable() @@ -36,11 +34,15 @@ export class MetadataProviderService { return this.http.post(`${this.base}${this.endpoint}`, provider); } - getOrder(): Observable { - return this.http.get(`${this.base}${this.order}`); + getOrder(): Observable { + return this.http.get<{ [resourceIds: string]: string[] }>(`${this.base}${this.order}`).pipe( + map( + (order: {[resourceIds: string]: string[]}) => order.resourceIds + ) + ); } - setOrder(order: ProviderOrder): Observable { - return this.http.post(`${this.base}${this.order}`, order); + setOrder(order: string[]): Observable { + return this.http.post(`${this.base}${this.order}`, { resourceIds: order }); } } diff --git a/ui/src/app/metadata/filter/action/collection.action.ts b/ui/src/app/metadata/filter/action/collection.action.ts index e2b38793d..afc514ea0 100644 --- a/ui/src/app/metadata/filter/action/collection.action.ts +++ b/ui/src/app/metadata/filter/action/collection.action.ts @@ -1,7 +1,8 @@ import { Action } from '@ngrx/store'; -import { MetadataFilter } from '../../domain/model/metadata-filter'; import { Update } from '@ngrx/entity'; +import { MetadataFilter } from '../../domain/model/metadata-filter'; + export enum FilterCollectionActionTypes { SELECT_FILTER_REQUEST = '[Metadata Filter Collection] Select Filter Request', SELECT_FILTER_SUCCESS = '[Metadata Filter Collection] Select Filter Success', @@ -21,7 +22,18 @@ export enum FilterCollectionActionTypes { REMOVE_FILTER_REQUEST = '[Metadata Filter Collection] Remove Filter Request', REMOVE_FILTER_SUCCESS = '[Metadata Filter Collection] Remove Filter Success', - REMOVE_FILTER_FAIL = '[Metadata Filter Collection] Remove Filter Fail' + REMOVE_FILTER_FAIL = '[Metadata Filter Collection] Remove Filter Fail', + + SET_ORDER_FILTER_REQUEST = '[Metadata Filter Collection] Set Order Filter Request', + SET_ORDER_FILTER_SUCCESS = '[Metadata Filter Collection] Set Order Filter Success', + SET_ORDER_FILTER_FAIL = '[Metadata Filter Collection] Set Order Filter Fail', + + GET_ORDER_FILTER_REQUEST = '[Metadata Filter Collection] Get Order Filter Request', + GET_ORDER_FILTER_SUCCESS = '[Metadata Filter Collection] Get Order Filter Success', + GET_ORDER_FILTER_FAIL = '[Metadata Filter Collection] Get Order Filter Fail', + + CHANGE_FILTER_ORDER_UP = '[Metadata Filter Collection] Change Order Up', + CHANGE_FILTER_ORDER_DOWN = '[Metadata Filter Collection] Change Order Down', } export class SelectFilter implements Action { @@ -114,6 +126,54 @@ export class RemoveFilterFail implements Action { constructor(public error: Error) { } } +export class SetOrderFilterRequest implements Action { + readonly type = FilterCollectionActionTypes.SET_ORDER_FILTER_REQUEST; + + constructor(public payload: string[]) { } +} + +export class SetOrderFilterSuccess implements Action { + readonly type = FilterCollectionActionTypes.SET_ORDER_FILTER_SUCCESS; + + constructor() { } +} + +export class SetOrderFilterFail implements Action { + readonly type = FilterCollectionActionTypes.SET_ORDER_FILTER_FAIL; + + constructor(public payload: Error) { } +} + +export class GetOrderFilterRequest implements Action { + readonly type = FilterCollectionActionTypes.GET_ORDER_FILTER_REQUEST; + + constructor() { } +} + +export class GetOrderFilterSuccess implements Action { + readonly type = FilterCollectionActionTypes.GET_ORDER_FILTER_SUCCESS; + + constructor(public payload: string[]) { } +} + +export class GetOrderFilterFail implements Action { + readonly type = FilterCollectionActionTypes.GET_ORDER_FILTER_FAIL; + + constructor(public payload: Error) { } +} + +export class ChangeFilterOrderUp implements Action { + readonly type = FilterCollectionActionTypes.CHANGE_FILTER_ORDER_UP; + + constructor(public payload: string) { } +} + +export class ChangeFilterOrderDown implements Action { + readonly type = FilterCollectionActionTypes.CHANGE_FILTER_ORDER_DOWN; + + constructor(public payload: string) { } +} + export type FilterCollectionActionsUnion = | LoadFilterRequest | LoadFilterSuccess @@ -129,4 +189,12 @@ export type FilterCollectionActionsUnion = | SelectFilterFail | UpdateFilterRequest | UpdateFilterSuccess - | UpdateFilterFail; + | UpdateFilterFail + | ChangeFilterOrderDown + | ChangeFilterOrderUp + | GetOrderFilterRequest + | GetOrderFilterSuccess + | GetOrderFilterFail + | SetOrderFilterRequest + | SetOrderFilterSuccess + | SetOrderFilterFail; diff --git a/ui/src/app/metadata/filter/effect/collection.effect.ts b/ui/src/app/metadata/filter/effect/collection.effect.ts index 779bb088f..e9673464d 100644 --- a/ui/src/app/metadata/filter/effect/collection.effect.ts +++ b/ui/src/app/metadata/filter/effect/collection.effect.ts @@ -6,12 +6,36 @@ import { Router } from '@angular/router'; import { of } from 'rxjs'; import { switchMap, map, catchError, tap, combineLatest, skipWhile, debounceTime, withLatestFrom } from 'rxjs/operators'; -import * as actions from '../action/collection.action'; +import { + LoadFilterRequest, + LoadFilterSuccess, + LoadFilterError, + UpdateFilterRequest, + UpdateFilterSuccess, + UpdateFilterFail, + SelectFilter, + SelectFilterSuccess, + SelectFilterFail, + AddFilterRequest, + AddFilterSuccess, + AddFilterFail, + GetOrderFilterRequest, + GetOrderFilterSuccess, + GetOrderFilterFail, + SetOrderFilterRequest, + SetOrderFilterSuccess, + SetOrderFilterFail, + ChangeFilterOrderUp, + ChangeFilterOrderDown, + RemoveFilterRequest, + RemoveFilterSuccess, + RemoveFilterFail +} from '../action/collection.action'; import { FilterCollectionActionTypes } from '../action/collection.action'; import * as fromFilter from '../reducer'; import * as fromProvider from '../../provider/reducer'; import { MetadataFilter } from '../../domain/model'; -import { removeNulls } from '../../../shared/util'; +import { removeNulls, array_move } from '../../../shared/util'; import { EntityAttributesFilterEntity } from '../../domain/entity/filter/entity-attributes-filter'; import { MetadataFilterService } from '../../domain/service/filter.service'; import { SelectProviderRequest } from '../../provider/action/collection.action'; @@ -22,29 +46,29 @@ export class FilterCollectionEffects { @Effect() loadFilters$ = this.actions$.pipe( - ofType(FilterCollectionActionTypes.LOAD_FILTER_REQUEST), + ofType(FilterCollectionActionTypes.LOAD_FILTER_REQUEST), map(action => action.payload), skipWhile(providerId => !providerId), switchMap(providerId => this.filterService .query(providerId) .pipe( - map(filters => new actions.LoadFilterSuccess(filters)), - catchError(error => of(new actions.LoadFilterError(error))) + map(filters => new LoadFilterSuccess(filters)), + catchError(error => of(new LoadFilterError(error))) ) ) ); @Effect() selectFilterRequest$ = this.actions$.pipe( - ofType(FilterCollectionActionTypes.SELECT_FILTER_REQUEST), + ofType(FilterCollectionActionTypes.SELECT_FILTER_REQUEST), map(action => action.payload), withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), switchMap(([filterId, providerId]) => { return this.filterService .find(providerId, filterId) .pipe( - map(p => new actions.SelectFilterSuccess(p)), - catchError(error => of(new actions.SelectFilterFail(error))) + map(p => new SelectFilterSuccess(p)), + catchError(error => of(new SelectFilterFail(error))) ); } ) @@ -52,7 +76,7 @@ export class FilterCollectionEffects { @Effect() addFilter$ = this.actions$.pipe( - ofType(FilterCollectionActionTypes.ADD_FILTER_REQUEST), + ofType(FilterCollectionActionTypes.ADD_FILTER_REQUEST), map(action => action.payload), map(filter => { return { @@ -65,14 +89,14 @@ export class FilterCollectionEffects { return this.filterService .save(providerId, unsaved as MetadataFilter) .pipe( - map(saved => new actions.AddFilterSuccess(saved)), - catchError(error => of(new actions.AddFilterFail(error))) + map(saved => new AddFilterSuccess(saved)), + catchError(error => of(new AddFilterFail(error))) ); }) ); @Effect({ dispatch: false }) addFilterSuccessRedirect$ = this.actions$.pipe( - ofType(FilterCollectionActionTypes.ADD_FILTER_SUCCESS), + ofType(FilterCollectionActionTypes.ADD_FILTER_SUCCESS), map(action => action.payload), withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), tap(([filter, provider]) => this.router.navigate(['/', 'metadata', 'provider', provider, 'filters'])) @@ -80,7 +104,7 @@ export class FilterCollectionEffects { @Effect() updateFilter$ = this.actions$.pipe( - ofType(FilterCollectionActionTypes.UPDATE_FILTER_REQUEST), + ofType(FilterCollectionActionTypes.UPDATE_FILTER_REQUEST), map(action => action.payload), withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), switchMap(([filter, providerId]) => { @@ -89,49 +113,127 @@ export class FilterCollectionEffects { return this.filterService .update(providerId, filter) .pipe( - map(p => new actions.UpdateFilterSuccess({ + map(p => new UpdateFilterSuccess({ id: p.resourceId, changes: p })), - catchError(err => of(new actions.UpdateFilterFail(filter))) + catchError(err => of(new UpdateFilterFail(filter))) ); }) ); @Effect() updateFilterSuccessReloadProvider$ = this.actions$.pipe( - ofType(FilterCollectionActionTypes.UPDATE_FILTER_SUCCESS), - map(action => action.payload), + ofType( + FilterCollectionActionTypes.UPDATE_FILTER_SUCCESS, + FilterCollectionActionTypes.SET_ORDER_FILTER_SUCCESS, + FilterCollectionActionTypes.REMOVE_FILTER_SUCCESS + ), withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), - map(([filter, providerId]) => new SelectProviderRequest(providerId)) + map(([action, providerId]) => new SelectProviderRequest(providerId)) ); @Effect({ dispatch: false }) updateFilterSuccessRedirect$ = this.actions$.pipe( - ofType(FilterCollectionActionTypes.UPDATE_FILTER_SUCCESS), + ofType(FilterCollectionActionTypes.UPDATE_FILTER_SUCCESS), map(action => action.payload), withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), tap(([filter, provider]) => this.router.navigate(['/', 'metadata', 'provider', provider, 'filters'])) ); + @Effect() + getOrderWithLoad$ = this.actions$.pipe( + ofType(FilterCollectionActionTypes.LOAD_FILTER_SUCCESS), + map(() => new GetOrderFilterRequest()) + ); + + @Effect() + getOrder$ = this.actions$.pipe( + ofType(FilterCollectionActionTypes.GET_ORDER_FILTER_REQUEST), + withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), + switchMap(([action, providerId]) => + this.filterService.getOrder(providerId).pipe( + map(order => new GetOrderFilterSuccess(order)), + catchError(err => of(new GetOrderFilterFail(err))) + ) + ) + ); + + /* + @Effect() + reloadOrderAfterChange$ = this.actions$.pipe( + ofType(FilterCollectionActionTypes.SET_ORDER_FILTER_SUCCESS), + map(() => new GetOrderFilterRequest()) + ); + */ + + @Effect() + setOrder$ = this.actions$.pipe( + ofType(FilterCollectionActionTypes.SET_ORDER_FILTER_REQUEST), + map(action => action.payload), + withLatestFrom( + this.store.select(fromProvider.getSelectedProviderId), + this.store.select(fromFilter.getPluginFilterOrder) + ), + switchMap(([order, providerId, pluginOrder]) => + this.filterService.setOrder(providerId, [...pluginOrder, ...order]).pipe( + map(() => new SetOrderFilterSuccess()), + catchError(err => of(new SetOrderFilterFail(err))) + ) + ) + ); + + @Effect() + changeOrderUp$ = this.actions$.pipe( + ofType(FilterCollectionActionTypes.CHANGE_FILTER_ORDER_UP), + map(action => action.payload), + withLatestFrom(this.store.select(fromFilter.getAdditionalFilterOrder)), + map(([id, order]) => { + const index = order.indexOf(id); + console.log(id, order); + if (index > 0) { + const newOrder = array_move(order, index, index - 1); + return new SetOrderFilterRequest(newOrder); + } else { + return new SetOrderFilterFail(new Error(`could not change order: ${id}`)); + } + }) + ); + + @Effect() + changeOrderDown$ = this.actions$.pipe( + ofType(FilterCollectionActionTypes.CHANGE_FILTER_ORDER_DOWN), + map(action => action.payload), + withLatestFrom(this.store.select(fromFilter.getAdditionalFilterOrder)), + map(([id, order]) => { + const index = order.indexOf(id); + if (index < order.length - 1) { + const newOrder = array_move(order, index, index + 1); + return new SetOrderFilterRequest(newOrder); + } else { + return new SetOrderFilterFail(new Error(`could not change order: ${id}`)); + } + }) + ); + @Effect() removeFilterRequest$ = this.actions$.pipe( - ofType(FilterCollectionActionTypes.REMOVE_FILTER_REQUEST), + ofType(FilterCollectionActionTypes.REMOVE_FILTER_REQUEST), map(action => action.payload), withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), switchMap(([filterId, providerId]) => this.filterService.remove(providerId, filterId).pipe( - map(removed => new actions.RemoveFilterSuccess(removed)), - catchError(err => of(new actions.RemoveFilterFail(err))) + map(removed => new RemoveFilterSuccess(removed)), + catchError(err => of(new RemoveFilterFail(err))) ) ) ); @Effect() removeFilterSuccess$ = this.actions$.pipe( - ofType(FilterCollectionActionTypes.REMOVE_FILTER_SUCCESS), + ofType(FilterCollectionActionTypes.REMOVE_FILTER_SUCCESS), map(action => action.payload), withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), - map(([filter, providerId]) => new actions.LoadFilterRequest(providerId)) + map(([filter, providerId]) => new LoadFilterRequest(providerId)) ); constructor( diff --git a/ui/src/app/metadata/filter/reducer/collection.reducer.spec.ts b/ui/src/app/metadata/filter/reducer/collection.reducer.spec.ts index 8ff807401..d044e66df 100644 --- a/ui/src/app/metadata/filter/reducer/collection.reducer.spec.ts +++ b/ui/src/app/metadata/filter/reducer/collection.reducer.spec.ts @@ -11,7 +11,9 @@ import { AddFilterSuccess, AddFilterFail, UpdateFilterFail, - RemoveFilterFail + RemoveFilterFail, + RemoveFilterRequest, + RemoveFilterSuccess } from '../action/collection.action'; import { EntityAttributesFilterEntity } from '../../domain/entity/filter/entity-attributes-filter'; @@ -101,6 +103,20 @@ describe('Filter Reducer', () => { }); }); + describe(`${FilterCollectionActionTypes.REMOVE_FILTER_REQUEST}`, () => { + it('should set saving to false', () => { + const action = new RemoveFilterRequest('foo'); + expect(reducer(snapshot, action).saving).toBe(true); + }); + }); + + describe(`${FilterCollectionActionTypes.REMOVE_FILTER_SUCCESS}`, () => { + it('should set saving to false', () => { + const action = new RemoveFilterSuccess('foo'); + expect(reducer(snapshot, action).saving).toBe(false); + }); + }); + describe(`${FilterCollectionActionTypes.UPDATE_FILTER_SUCCESS}`, () => { it('should update the filter in the collection', () => { spyOn(fromFilter.adapter, 'updateOne').and.callThrough(); @@ -113,4 +129,30 @@ describe('Filter Reducer', () => { expect(fromFilter.adapter.updateOne).toHaveBeenCalled(); }); }); + + describe('selector methods', () => { + describe('getSelectedFilterId', () => { + it('should return the state selectedFilterId', () => { + expect(fromFilter.getSelectedFilterId(snapshot)).toBe(snapshot.selectedFilterId); + }); + }); + + describe('getIsLoaded', () => { + it('should return the state loaded', () => { + expect(fromFilter.getIsLoaded(snapshot)).toBe(snapshot.loaded); + }); + }); + + describe('getError', () => { + it('should return the state saving', () => { + expect(fromFilter.getIsSaving(snapshot)).toBe(snapshot.saving); + }); + }); + + describe('getOrder', () => { + it('should return the state order', () => { + expect(fromFilter.getOrder(snapshot)).toBe(snapshot.order); + }); + }); + }); }); diff --git a/ui/src/app/metadata/filter/reducer/collection.reducer.ts b/ui/src/app/metadata/filter/reducer/collection.reducer.ts index 1f3788c54..4dad4f99f 100644 --- a/ui/src/app/metadata/filter/reducer/collection.reducer.ts +++ b/ui/src/app/metadata/filter/reducer/collection.reducer.ts @@ -6,6 +6,7 @@ export interface CollectionState extends EntityState { selectedFilterId: string | null; loaded: boolean; saving: boolean; + order: string[]; } export function sortByDate(a: MetadataFilter, b: MetadataFilter): number { @@ -20,7 +21,8 @@ export const adapter: EntityAdapter = createEntityAdapter state.selectedFilterId; export const getIsLoaded = (state: CollectionState) => state.loaded; export const getIsSaving = (state: CollectionState) => state.saving; +export const getOrder = (state: CollectionState) => state.order; export const { selectIds: selectFilterIds, selectEntities: selectFilterEntities, diff --git a/ui/src/app/metadata/filter/reducer/filter.reducer.spec.ts b/ui/src/app/metadata/filter/reducer/filter.reducer.spec.ts index ff832e198..bd7e71514 100644 --- a/ui/src/app/metadata/filter/reducer/filter.reducer.spec.ts +++ b/ui/src/app/metadata/filter/reducer/filter.reducer.spec.ts @@ -87,4 +87,24 @@ describe('Filter Reducer', () => { expect(result).toEqual(fromFilter.initialState); }); }); + + describe('selector methods', () => { + describe('getSelected', () => { + it('should return the state selected', () => { + expect(fromFilter.getSelected(snapshot)).toBe(snapshot.selected); + }); + }); + + describe('getChanges', () => { + it('should return the state changes', () => { + expect(fromFilter.getFilterChanges(snapshot)).toBe(snapshot.changes); + }); + }); + + describe('getPreview', () => { + it('should return the state preview', () => { + expect(fromFilter.getPreview(snapshot)).toBe(snapshot.preview); + }); + }); + }); }); diff --git a/ui/src/app/metadata/filter/reducer/index.spec.ts b/ui/src/app/metadata/filter/reducer/index.spec.ts index 3d30cf891..0a136de68 100644 --- a/ui/src/app/metadata/filter/reducer/index.spec.ts +++ b/ui/src/app/metadata/filter/reducer/index.spec.ts @@ -7,6 +7,40 @@ describe('filter selectors', () => { }); }); + describe('isAdditionalFilter', () => { + it('should return filtered objects', () => { + const filters = [{ '@type': 'EntityAttributes' }]; + expect(selectors.filterTypeFn(filters)).toEqual(filters); + }); + + it('should return filtered objects', () => { + const filters = [{ '@type': 'EntityAttributes' }, { '@type': 'EntityRoleWhiteList' }]; + expect(selectors.filterTypeFn(filters).length).toBe(1); + }); + }); + + describe('isFilterPlugin', () => { + it('should return false for entity attributes type', () => { + expect(selectors.isFilterPlugin('EntityAttributes')).toBe(false); + }); + selectors.filterPluginTypes.forEach(type => { + it(`should return false for ${ type } type`, () => { + expect(selectors.isFilterPlugin(type)).toBe(true); + }); + }); + }); + + describe('isAdditionalFilter', () => { + it('should return false for entity attributes type', () => { + expect(selectors.isAdditionalFilter('EntityAttributes')).toBe(true); + }); + selectors.filterPluginTypes.forEach(type => { + it(`should return false for ${ type } type`, () => { + expect(selectors.isAdditionalFilter(type)).toBe(false); + }); + }); + }); + describe('filterTypeFn', () => { it('should return filtered objects', () => { const filters = [{ '@type': 'EntityAttributes' }]; @@ -18,4 +52,56 @@ describe('filter selectors', () => { expect(selectors.filterTypeFn(filters).length).toBe(1); }); }); + + describe('filterOrderFn', () => { + it('should return the ordered & filtered entities when they are not filter plugins', () => { + const filters = {'foo': { '@type': 'EntityAttributes' }}; + const order = ['foo']; + expect(selectors.filterOrderFn(filters, order)).toEqual(order); + }); + + it('should return entities that exist in the collection of additional filters', () => { + const filters = { 'foo': { '@type': 'EntityAttributes' } }; + const order = ['bar']; + expect(selectors.filterOrderFn(filters, order)).toEqual([]); + }); + + it('should return only additional filters', () => { + const filters = { 'foo': { '@type': 'EntityRoleWhiteList' } }; + const order = ['foo']; + expect(selectors.filterOrderFn(filters, order)).toEqual([]); + }); + + it('should return filtered objects', () => { + const filters = { 'foo': { '@type': 'EntityAttributes' }, 'bar': { '@type': 'EntityRoleWhiteList' }}; + const order = ['foo', 'bar']; + expect(selectors.filterOrderFn(filters, order).length).toBe(1); + }); + }); + + describe('pluginOrderFn', () => { + it('should return the ordered & filtered entities when they are filter plugins', () => { + const filters = { 'foo': { '@type': 'EntityRoleWhiteList' } }; + const order = ['foo']; + expect(selectors.pluginOrderFn(filters, order)).toEqual(order); + }); + + it('should return entities that exist in the collection of filter plugins', () => { + const filters = { 'foo': { '@type': 'EntityAttributes' } }; + const order = ['bar']; + expect(selectors.pluginOrderFn(filters, order)).toEqual([]); + }); + + it('should return only filter plugins', () => { + const filters = { 'foo': { '@type': 'EntityAttributes' } }; + const order = ['foo']; + expect(selectors.pluginOrderFn(filters, order)).toEqual([]); + }); + + it('should return filtered objects', () => { + const filters = { 'foo': { '@type': 'EntityAttributes' }, 'bar': { '@type': 'EntityRoleWhiteList' } }; + const order = ['foo', 'bar']; + expect(selectors.pluginOrderFn(filters, order).length).toBe(1); + }); + }); }); diff --git a/ui/src/app/metadata/filter/reducer/index.ts b/ui/src/app/metadata/filter/reducer/index.ts index 60cbe4b34..caacf0254 100644 --- a/ui/src/app/metadata/filter/reducer/index.ts +++ b/ui/src/app/metadata/filter/reducer/index.ts @@ -50,11 +50,7 @@ export const getViewingMore = createSelector(getSearchFromState, fromSearch.getV export const getCollectionState = createSelector(getFilterState, getCollectionFromStateFn); export const getAllFilters = createSelector(getCollectionState, fromCollection.selectAllFilters); export const getCollectionSaving = createSelector(getCollectionState, fromCollection.getIsSaving); - -export const notAddtlFilters = ['RequiredValidUntil', 'SignatureValidation', 'EntityRoleWhiteList']; -export const filterTypeFn = filters => [...filters.filter(f => notAddtlFilters.indexOf(f['@type']) === -1)]; - -export const getAdditionalFilters = createSelector(getAllFilters, filterTypeFn); +export const getCollectionOrder = createSelector(getCollectionState, fromCollection.getOrder); export const getFilterEntities = createSelector(getCollectionState, fromCollection.selectFilterEntities); export const getSelectedFilterId = createSelector(getCollectionState, fromCollection.getSelectedFilterId); @@ -62,6 +58,19 @@ export const getSelectedFilter = createSelector(getFilterEntities, getSelectedFi export const getFilterIds = createSelector(getCollectionState, fromCollection.selectFilterIds); export const getFilterCollectionIsLoaded = createSelector(getCollectionState, fromCollection.getIsLoaded); +export const filterPluginTypes = ['RequiredValidUntil', 'SignatureValidation', 'EntityRoleWhiteList']; +export const isAdditionalFilter = (type) => filterPluginTypes.indexOf(type) === -1; +export const isFilterPlugin = (type) => filterPluginTypes.indexOf(type) >= 0; + +export const filterTypeFn = filters => [...filters.filter(f => isAdditionalFilter(f['@type']))]; +export const filterOrderFn = (filters, order) => order.filter(id => filters.hasOwnProperty(id) && isAdditionalFilter(filters[id]['@type'])); +export const pluginOrderFn = (filters, order) => order.filter(id => filters.hasOwnProperty(id) && isFilterPlugin(filters[id]['@type'])); + +export const getFilterList = createSelector(getAllFilters, filterTypeFn); +export const getAdditionalFilterOrder = createSelector(getFilterEntities, getCollectionOrder, filterOrderFn); +export const getAdditionalFilters = createSelector(getFilterList, getAdditionalFilterOrder, utils.mergeOrderFn); +export const getPluginFilterOrder = createSelector(getFilterEntities, getCollectionOrder, pluginOrderFn); + /* * Combine pieces of State */ diff --git a/ui/src/app/metadata/filter/reducer/search.reducer.spec.ts b/ui/src/app/metadata/filter/reducer/search.reducer.spec.ts index 7c77afc30..f778451da 100644 --- a/ui/src/app/metadata/filter/reducer/search.reducer.spec.ts +++ b/ui/src/app/metadata/filter/reducer/search.reducer.spec.ts @@ -1,4 +1,4 @@ -import { reducer } from './search.reducer'; +import { reducer, initialState as snapshot } from './search.reducer'; import * as fromFilter from './search.reducer'; import { SearchActionTypes, @@ -6,16 +6,11 @@ import { CancelViewMore, QueryEntityIds, LoadEntityIdsError, - LoadEntityIdsSuccess + LoadEntityIdsSuccess, + ClearSearch } from '../action/search.action'; - -const snapshot: fromFilter.SearchState = { - entityIds: [], - viewMore: false, - loading: false, - error: null, - term: '', -}; +import { FilterCollectionActionTypes, UpdateFilterSuccess, AddFilterSuccess } from '../action/collection.action'; +import { EntityAttributesFilterEntity } from '../../domain/entity'; describe('Filter Reducer', () => { describe('undefined action', () => { @@ -69,4 +64,61 @@ describe('Filter Reducer', () => { expect(result.error).toBe(err); }); }); + + describe(`${FilterCollectionActionTypes.UPDATE_FILTER_SUCCESS} action`, () => { + it('should reset the state', () => { + const update = { + id: 'foo', + changes: new EntityAttributesFilterEntity({ resourceId: 'foo', name: 'bar', createdDate: new Date().toLocaleDateString() }), + }; + const action = new UpdateFilterSuccess(update); + const result = reducer(snapshot, action); + + expect(result).toEqual(snapshot); + }); + }); + + describe(`${FilterCollectionActionTypes.ADD_FILTER_SUCCESS} action`, () => { + it('should reset the state', () => { + const filter = new EntityAttributesFilterEntity( + { resourceId: 'foo', name: 'bar', createdDate: new Date().toLocaleDateString() } + ); + const action = new AddFilterSuccess(filter); + const result = reducer(snapshot, action); + + expect(result).toEqual(snapshot); + }); + }); + + describe('selector methods', () => { + describe('getViewMore', () => { + it('should return the state viewMore', () => { + expect(fromFilter.getViewMore(snapshot)).toBe(snapshot.viewMore); + }); + }); + + describe('getEntityIds', () => { + it('should return the state entityIds', () => { + expect(fromFilter.getEntityIds(snapshot)).toBe(snapshot.entityIds); + }); + }); + + describe('getError', () => { + it('should return the state error', () => { + expect(fromFilter.getError(snapshot)).toBe(snapshot.error); + }); + }); + + describe('getLoading', () => { + it('should return the state loading', () => { + expect(fromFilter.getLoading(snapshot)).toBe(snapshot.loading); + }); + }); + + describe('getTerm', () => { + it('should return the state term', () => { + expect(fromFilter.getTerm(snapshot)).toBe(snapshot.term); + }); + }); + }); }); diff --git a/ui/src/app/metadata/manager/container/dashboard-providers-list.component.ts b/ui/src/app/metadata/manager/container/dashboard-providers-list.component.ts index 88ec0b19e..35ece7de0 100644 --- a/ui/src/app/metadata/manager/container/dashboard-providers-list.component.ts +++ b/ui/src/app/metadata/manager/container/dashboard-providers-list.component.ts @@ -8,7 +8,7 @@ import { ProviderState, getOrderedProviders } from '../../provider/reducer'; import { getOpenProviders } from '../reducer'; import { ToggleEntityDisplay } from '../action/manager.action'; import { map } from 'rxjs/operators'; -import { ChangeOrderUp, ChangeOrderDown } from '../../provider/action/collection.action'; +import { ChangeProviderOrderUp, ChangeProviderOrderDown } from '../../provider/action/collection.action'; @Component({ selector: 'dashboard-providers-list', @@ -26,7 +26,7 @@ export class DashboardProvidersListComponent implements OnInit { ) { } ngOnInit(): void { - this.providers$ = this.store.select(getOrderedProviders); + this.providers$ = this.store.select(getOrderedProviders) as Observable; this.providersOpen$ = this.store.select(getOpenProviders); } @@ -47,10 +47,10 @@ export class DashboardProvidersListComponent implements OnInit { } updateOrderUp(provider: MetadataProvider): void { - this.store.dispatch(new ChangeOrderUp(provider.resourceId)); + this.store.dispatch(new ChangeProviderOrderUp(provider.resourceId)); } updateOrderDown(provider: MetadataProvider): void { - this.store.dispatch(new ChangeOrderDown(provider.resourceId)); + this.store.dispatch(new ChangeProviderOrderDown(provider.resourceId)); } } diff --git a/ui/src/app/metadata/provider/action/collection.action.ts b/ui/src/app/metadata/provider/action/collection.action.ts index 637c58115..3eb1f1889 100644 --- a/ui/src/app/metadata/provider/action/collection.action.ts +++ b/ui/src/app/metadata/provider/action/collection.action.ts @@ -1,7 +1,6 @@ import { Action } from '@ngrx/store'; import { MetadataProvider } from '../../domain/model/metadata-provider'; import { Update } from '@ngrx/entity'; -import { ProviderOrder } from '../../domain/model/metadata-order'; export enum ProviderCollectionActionTypes { UPDATE_PROVIDER_REQUEST = '[Metadata Provider] Update Request', @@ -25,12 +24,12 @@ export enum ProviderCollectionActionTypes { REMOVE_PROVIDER_FAIL = '[Metadata Provider Collection] Remove Provider Fail', SET_ORDER_PROVIDER_REQUEST = '[Metadata Provider Collection] Set Order Provider Request', - SET_ORDER_PROVIDER_SUCCESS = '[Metadata Provider Collection] Set Order Remove Provider Success', - SET_ORDER_PROVIDER_FAIL = '[Metadata Provider Collection] Set Order Remove Provider Fail', + SET_ORDER_PROVIDER_SUCCESS = '[Metadata Provider Collection] Set Order Provider Success', + SET_ORDER_PROVIDER_FAIL = '[Metadata Provider Collection] Set Order Provider Fail', - GET_ORDER_PROVIDER_REQUEST = '[Metadata Provider Collection] Get Order Remove Provider Request', - GET_ORDER_PROVIDER_SUCCESS = '[Metadata Provider Collection] Get Order Remove Provider Success', - GET_ORDER_PROVIDER_FAIL = '[Metadata Provider Collection] Get Order Remove Provider Fail', + GET_ORDER_PROVIDER_REQUEST = '[Metadata Provider Collection] Get Order Provider Request', + GET_ORDER_PROVIDER_SUCCESS = '[Metadata Provider Collection] Get Order Provider Success', + GET_ORDER_PROVIDER_FAIL = '[Metadata Provider Collection] Get Order Provider Fail', CHANGE_PROVIDER_ORDER_UP = '[Metadata Provider Collection] Change Order Up', CHANGE_PROVIDER_ORDER_DOWN = '[Metadata Provider Collection] Change Order Down', @@ -129,7 +128,7 @@ export class RemoveProviderFail implements Action { export class SetOrderProviderRequest implements Action { readonly type = ProviderCollectionActionTypes.SET_ORDER_PROVIDER_REQUEST; - constructor(public payload: ProviderOrder) { } + constructor(public payload: string[]) { } } export class SetOrderProviderSuccess implements Action { @@ -153,7 +152,7 @@ export class GetOrderProviderRequest implements Action { export class GetOrderProviderSuccess implements Action { readonly type = ProviderCollectionActionTypes.GET_ORDER_PROVIDER_SUCCESS; - constructor(public payload: ProviderOrder) { } + constructor(public payload: string[]) { } } export class GetOrderProviderFail implements Action { @@ -162,13 +161,13 @@ export class GetOrderProviderFail implements Action { constructor(public payload: Error) { } } -export class ChangeOrderUp implements Action { +export class ChangeProviderOrderUp implements Action { readonly type = ProviderCollectionActionTypes.CHANGE_PROVIDER_ORDER_UP; constructor(public payload: string) { } } -export class ChangeOrderDown implements Action { +export class ChangeProviderOrderDown implements Action { readonly type = ProviderCollectionActionTypes.CHANGE_PROVIDER_ORDER_DOWN; constructor(public payload: string) { } @@ -196,5 +195,5 @@ export type ProviderCollectionActionsUnion = | GetOrderProviderRequest | GetOrderProviderSuccess | GetOrderProviderFail - | ChangeOrderUp - | ChangeOrderDown; + | ChangeProviderOrderUp + | ChangeProviderOrderDown; diff --git a/ui/src/app/metadata/provider/container/provider-filter-list.component.html b/ui/src/app/metadata/provider/container/provider-filter-list.component.html index 3f29393c0..2e13ae19f 100644 --- a/ui/src/app/metadata/provider/container/provider-filter-list.component.html +++ b/ui/src/app/metadata/provider/container/provider-filter-list.component.html @@ -34,7 +34,7 @@ - + @@ -44,18 +44,17 @@ - - diff --git a/ui/src/app/metadata/provider/container/provider-filter-list.component.ts b/ui/src/app/metadata/provider/container/provider-filter-list.component.ts index 0b1fee37b..62caec495 100644 --- a/ui/src/app/metadata/provider/container/provider-filter-list.component.ts +++ b/ui/src/app/metadata/provider/container/provider-filter-list.component.ts @@ -1,13 +1,20 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component, OnDestroy } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable, Subject } from 'rxjs'; -import { skipWhile, distinctUntilChanged, takeUntil, map } from 'rxjs/operators'; +import { skipWhile, takeUntil, withLatestFrom } from 'rxjs/operators'; import * as fromProvider from '../reducer'; import * as fromFilter from '../../filter/reducer'; import { MetadataFilter, MetadataProvider } from '../../domain/model'; import { NAV_FORMATS } from '../component/provider-editor-nav.component'; import { SetIndex } from '../../../wizard/action/wizard.action'; -import { UpdateFilterRequest, LoadFilterRequest, RemoveFilterRequest } from '../../filter/action/collection.action'; +import { + UpdateFilterRequest, + LoadFilterRequest, + ChangeFilterOrderUp, + ChangeFilterOrderDown, + RemoveFilterRequest +} from '../../filter/action/collection.action'; + @Component({ selector: 'provider-filter-list', @@ -27,7 +34,7 @@ export class ProviderFilterListComponent implements OnDestroy { constructor( private store: Store ) { - this.filters$ = this.store.select(fromFilter.getAdditionalFilters); + this.filters$ = this.store.select(fromFilter.getAdditionalFilters) as Observable; this.provider$ = this.store.select(fromProvider.getSelectedProvider).pipe(skipWhile(p => !p)); this.provider$ .pipe( @@ -46,6 +53,14 @@ export class ProviderFilterListComponent implements OnDestroy { this.store.dispatch(new UpdateFilterRequest({ ...filter, filterEnabled: !filter.filterEnabled })); } + updateOrderUp(filter: MetadataFilter): void { + this.store.dispatch(new ChangeFilterOrderUp(filter.resourceId)); + } + + updateOrderDown(filter: MetadataFilter): void { + this.store.dispatch(new ChangeFilterOrderDown(filter.resourceId)); + } + remove(id: string): void { this.store.dispatch(new RemoveFilterRequest(id)); } diff --git a/ui/src/app/metadata/provider/effect/collection.effect.ts b/ui/src/app/metadata/provider/effect/collection.effect.ts index 73ea1937b..eb6b0af4b 100644 --- a/ui/src/app/metadata/provider/effect/collection.effect.ts +++ b/ui/src/app/metadata/provider/effect/collection.effect.ts @@ -25,12 +25,13 @@ import { SetOrderProviderRequest, SetOrderProviderSuccess, SetOrderProviderFail, - ChangeOrderUp, - ChangeOrderDown + ChangeProviderOrderUp, + ChangeProviderOrderDown } from '../action/collection.action'; import { MetadataProviderService } from '../../domain/service/provider.service'; import * as fromProvider from '../reducer'; import * as fromRoot from '../../../app.reducer'; +import { array_move } from '../../../shared/util'; import { ClearProvider, ResetChanges } from '../action/entity.action'; import { ShowContentionAction } from '../../../contention/action/contention.action'; import { ContentionService } from '../../../contention/service/contention.service'; @@ -183,15 +184,14 @@ export class CollectionEffects { @Effect() changeOrderUp$ = this.actions$.pipe( - ofType(ProviderCollectionActionTypes.CHANGE_PROVIDER_ORDER_UP), + ofType(ProviderCollectionActionTypes.CHANGE_PROVIDER_ORDER_UP), map(action => action.payload), withLatestFrom(this.store.select(fromProvider.getProviderOrder)), - map(([id, orderSet]) => { - const order = orderSet.resourceIds; + map(([id, order]) => { const index = order.indexOf(id); if (index > 0) { - const newOrder = this.array_move(order, index, index - 1); - return new SetOrderProviderRequest({ resourceIds: newOrder }); + const newOrder = array_move(order, index, index - 1); + return new SetOrderProviderRequest(newOrder); } else { return new SetOrderProviderFail(new Error(`could not change order: ${ id }`)); } @@ -200,32 +200,20 @@ export class CollectionEffects { @Effect() changeOrderDown$ = this.actions$.pipe( - ofType(ProviderCollectionActionTypes.CHANGE_PROVIDER_ORDER_DOWN), + ofType(ProviderCollectionActionTypes.CHANGE_PROVIDER_ORDER_DOWN), map(action => action.payload), withLatestFrom(this.store.select(fromProvider.getProviderOrder)), - map(([id, orderSet]) => { - const order = orderSet.resourceIds; + map(([id, order]) => { const index = order.indexOf(id); if (index < order.length - 1) { - const newOrder = this.array_move(order, index, index + 1); - return new SetOrderProviderRequest({ resourceIds: newOrder }); + const newOrder = array_move(order, index, index + 1); + return new SetOrderProviderRequest(newOrder); } else { return new SetOrderProviderFail(new Error(`could not change order: ${id}`)); } }) ); - array_move(arr, old_index, new_index): any[] { - if (new_index >= arr.length) { - let k = new_index - arr.length + 1; - while (k--) { - arr.push(undefined); - } - } - arr.splice(new_index, 0, arr.splice(old_index, 1)[0]); - return arr; - } - constructor( private actions$: Actions, private router: Router, diff --git a/ui/src/app/metadata/provider/reducer/collection.reducer.ts b/ui/src/app/metadata/provider/reducer/collection.reducer.ts index 04dfb0297..8a2959851 100644 --- a/ui/src/app/metadata/provider/reducer/collection.reducer.ts +++ b/ui/src/app/metadata/provider/reducer/collection.reducer.ts @@ -1,12 +1,11 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import { ProviderCollectionActionTypes, ProviderCollectionActionsUnion } from '../action/collection.action'; import { MetadataProvider } from '../../domain/model'; -import { ProviderOrder } from '../../domain/model/metadata-order'; export interface CollectionState extends EntityState { selectedProviderId: string | null; loaded: boolean; - order: ProviderOrder; + order: string[]; } export const adapter: EntityAdapter = createEntityAdapter({ @@ -16,7 +15,7 @@ export const adapter: EntityAdapter = createEntityAdapter { @@ -74,23 +75,4 @@ describe(`provider reducer/selector functions`, () => { expect(fromProvider.getSchemaLockedFn({ locked: true }, false)).toEqual(false); }); }); - - describe('mergeProviderOrderFn', () => { - const providers = [ - { resourceId: 'foo', name: 'foo', '@type': 'foo', enabled: true, xmlId: 'id', sortKey: 1, metadataFilters: [] }, - { resourceId: 'bar', name: 'bar', '@type': 'bar', enabled: false, xmlId: 'id2', sortKey: 2, metadataFilters: [] }, - { resourceId: 'baz', name: 'baz', '@type': 'baz', enabled: false, xmlId: 'id3', sortKey: 3, metadataFilters: [] } - ]; - it('1 should sort the list accordingly', () => { - let order = {resourceIds: ['bar', 'foo', 'baz']}, - ordered = fromProvider.mergeProviderOrderFn([...providers], order); - expect(ordered.indexOf(providers[0])).toBe(1); - }); - - it('2 should sort the list accordingly', () => { - let order = { resourceIds: ['foo', 'bar', 'baz'] }, - ordered = fromProvider.mergeProviderOrderFn(providers, order); - expect(ordered.indexOf(providers[0])).toBe(0); - }); - }); }); diff --git a/ui/src/app/metadata/provider/reducer/index.ts b/ui/src/app/metadata/provider/reducer/index.ts index 1fefd6406..f9def08d4 100644 --- a/ui/src/app/metadata/provider/reducer/index.ts +++ b/ui/src/app/metadata/provider/reducer/index.ts @@ -8,8 +8,6 @@ import * as utils from '../../domain/domain.util'; import * as fromWizard from '../../../wizard/reducer'; import { MetadataProvider } from '../../domain/model'; -import { WizardStep } from '../../../wizard/model'; -import { ProviderOrder } from '../../domain/model/metadata-order'; export interface ProviderState { editor: fromEditor.EditorState; @@ -101,14 +99,4 @@ export const getProviderNames = createSelector(getAllProviders, (providers: Meta export const getProviderFilters = createSelector(getSelectedProvider, provider => provider.metadataFilters); export const getProviderXmlIds = createSelector(getAllProviders, (providers: MetadataProvider[]) => providers.map(p => p.xmlId)); - -export const mergeProviderOrderFn = (providers: MetadataProvider[], order: ProviderOrder): MetadataProvider[] => { - return [...providers.sort( - (a: MetadataProvider, b: MetadataProvider) => { - const aIndex = order.resourceIds.indexOf(a.resourceId); - const bIndex = order.resourceIds.indexOf(b.resourceId); - return aIndex > bIndex ? 1 : bIndex > aIndex ? -1 : 0; - } - )]; -}; -export const getOrderedProviders = createSelector(getAllProviders, getProviderOrder, mergeProviderOrderFn); +export const getOrderedProviders = createSelector(getAllProviders, getProviderOrder, utils.mergeOrderFn); diff --git a/ui/src/app/shared/util.ts b/ui/src/app/shared/util.ts index 2e506bffa..4224f5b8a 100644 --- a/ui/src/app/shared/util.ts +++ b/ui/src/app/shared/util.ts @@ -50,3 +50,14 @@ export function pick(approvedProperties: string[]): Function { return newObj; }, {}); } + +export function array_move(arr, old_index, new_index): any[] { + if (new_index >= arr.length) { + let k = new_index - arr.length + 1; + while (k--) { + arr.push(undefined); + } + } + arr.splice(new_index, 0, arr.splice(old_index, 1)[0]); + return arr; +}
Filter Name Filter Type
{{ i + 1 }} {{ filter.name }} {{ filter['@type'] }}