diff --git a/angular.json b/angular.json index b81ba049fa..56e06bd86c 100644 --- a/angular.json +++ b/angular.json @@ -79,6 +79,10 @@ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.production.ts" + }, + { + "replace": "src/config/store/devtools.ts", + "with": "src/config/store/devtools.prod.ts" } ], "optimization": true, @@ -204,6 +208,10 @@ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.production.ts" + }, + { + "replace": "src/config/store/devtools.ts", + "with": "src/config/store/devtools.prod.ts" } ] } diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts b/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts index 65026c1504..2a5a544a40 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts @@ -237,7 +237,24 @@ describe('AdminSidebarComponent', () => { expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ parentID: 'access_control', visible: false, })); + }); + // We check that the menu section has not been called with visible set to true + // The reason why we don't check if it has been called with visible set to false + // Is because the function does not get called unless a user is authorised + it('should not show the import section', () => { + expect(menuService.addSection).not.toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ + id: 'import', visible: true, + })); + }); + + // We check that the menu section has not been called with visible set to true + // The reason why we don't check if it has been called with visible set to false + // Is because the function does not get called unless a user is authorised + it('should not show the export section', () => { + expect(menuService.addSection).not.toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ + id: 'export', visible: true, + })); }); }); @@ -268,6 +285,15 @@ describe('AdminSidebarComponent', () => { expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ id: 'workflow', visible: true, })); + expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ + id: 'workflow', visible: true, + })); + expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ + id: 'import', visible: true, + })); + expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ + id: 'export', visible: true, + })); }); }); diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.ts b/src/app/admin/admin-sidebar/admin-sidebar.component.ts index caf8aa2cfb..b0091da86d 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.ts @@ -1,9 +1,13 @@ import { Component, HostListener, Injector, OnInit } from '@angular/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { combineLatest, combineLatest as observableCombineLatest, Observable, BehaviorSubject } from 'rxjs'; -import { debounceTime, first, map, take, distinctUntilChanged, withLatestFrom } from 'rxjs/operators'; +import { debounceTime, first, map, take, filter, distinctUntilChanged, withLatestFrom } from 'rxjs/operators'; import { AuthService } from '../../core/auth/auth.service'; -import { ScriptDataService } from '../../core/data/processes/script-data.service'; +import { + ScriptDataService, + METADATA_IMPORT_SCRIPT_NAME, + METADATA_EXPORT_SCRIPT_NAME +} from '../../core/data/processes/script-data.service'; import { slideHorizontal, slideSidebar } from '../../shared/animations/slide'; import { CreateCollectionParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component'; import { CreateCommunityParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component'; @@ -322,19 +326,6 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { */ createExportMenuSections() { const menuList = [ - /* Export */ - { - id: 'export', - active: false, - visible: true, - model: { - type: MenuItemType.TEXT, - text: 'menu.section.export' - } as TextMenuItemModel, - icon: 'file-export', - index: 3, - shouldPersistOnRouteChange: true - }, // TODO: enable this menu item once the feature has been implemented // { // id: 'export_community', @@ -379,12 +370,26 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { observableCombineLatest( this.authorizationService.isAuthorized(FeatureID.AdministratorOf), - // this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_EXPORT_SCRIPT_NAME) + this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_EXPORT_SCRIPT_NAME) ).pipe( - // TODO uncomment when #635 (https://github.com/DSpace/dspace-angular/issues/635) is fixed; otherwise even in production mode, the metadata export button is only available after a refresh (and not in dev mode) - // filter(([authorized, metadataExportScriptExists]: boolean[]) => authorized && metadataExportScriptExists), + filter(([authorized, metadataExportScriptExists]: boolean[]) => authorized && metadataExportScriptExists), take(1) ).subscribe(() => { + // Hides the export menu for unauthorised people + // If in the future more sub-menus are added, + // it should be reviewed if they need to be in this subscribe + this.menuService.addSection(this.menuID, { + id: 'export', + active: false, + visible: true, + model: { + type: MenuItemType.TEXT, + text: 'menu.section.export' + } as TextMenuItemModel, + icon: 'file-export', + index: 3, + shouldPersistOnRouteChange: true + }); this.menuService.addSection(this.menuID, { id: 'export_metadata', parentID: 'export', @@ -408,18 +413,6 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { */ createImportMenuSections() { const menuList = [ - /* Import */ - { - id: 'import', - active: false, - visible: true, - model: { - type: MenuItemType.TEXT, - text: 'menu.section.import' - } as TextMenuItemModel, - icon: 'file-import', - index: 2 - }, // TODO: enable this menu item once the feature has been implemented // { // id: 'import_batch', @@ -439,12 +432,25 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { observableCombineLatest( this.authorizationService.isAuthorized(FeatureID.AdministratorOf), - // this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_IMPORT_SCRIPT_NAME) + this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_IMPORT_SCRIPT_NAME) ).pipe( - // TODO uncomment when #635 (https://github.com/DSpace/dspace-angular/issues/635) is fixed - // filter(([authorized, metadataImportScriptExists]: boolean[]) => authorized && metadataImportScriptExists), + filter(([authorized, metadataImportScriptExists]: boolean[]) => authorized && metadataImportScriptExists), take(1) ).subscribe(() => { + // Hides the import menu for unauthorised people + // If in the future more sub-menus are added, + // it should be reviewed if they need to be in this subscribe + this.menuService.addSection(this.menuID, { + id: 'import', + active: false, + visible: true, + model: { + type: MenuItemType.TEXT, + text: 'menu.section.import' + } as TextMenuItemModel, + icon: 'file-import', + index: 2 + }); this.menuService.addSection(this.menuID, { id: 'import_metadata', parentID: 'import', diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 2c3e0ea3ee..c133efdd5c 100755 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -8,7 +8,6 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { EffectsModule } from '@ngrx/effects'; import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store'; import { MetaReducer, Store, StoreModule, USER_PROVIDED_META_REDUCERS } from '@ngrx/store'; -import { StoreDevtoolsModule } from '@ngrx/store-devtools'; import { DYNAMIC_ERROR_MESSAGES_MATCHER, DYNAMIC_MATCHER_PROVIDERS, @@ -61,6 +60,8 @@ import { ThemedAdminSidebarComponent } from './admin/admin-sidebar/themed-admin- import { APP_CONFIG, AppConfig } from '../config/app-config.interface'; import { NgxMaskModule } from 'ngx-mask'; +import { StoreDevModules } from '../config/store/devtools'; + export function getConfig() { return environment; } @@ -96,15 +97,9 @@ const IMPORTS = [ StoreModule.forRoot(appReducers, storeModuleConfig), StoreRouterConnectingModule.forRoot(), ThemedEntryComponentModule.withEntryComponents(), + StoreDevModules, ]; -IMPORTS.push( - StoreDevtoolsModule.instrument({ - maxAge: 1000, - logOnly: environment.production, - }) -); - const PROVIDERS = [ { provide: APP_CONFIG, diff --git a/src/app/core/data/processes/script-data.service.ts b/src/app/core/data/processes/script-data.service.ts index bf51fadea1..75a66c822a 100644 --- a/src/app/core/data/processes/script-data.service.ts +++ b/src/app/core/data/processes/script-data.service.ts @@ -18,6 +18,8 @@ import { Observable } from 'rxjs'; import { dataService } from '../../cache/builders/build-decorators'; import { SCRIPT } from '../../../process-page/scripts/script.resource-type'; import { Process } from '../../../process-page/processes/process.model'; +import { hasValue } from '../../../shared/empty.util'; +import { getFirstCompletedRemoteData } from '../../shared/operators'; import { RestRequest } from '../rest-request.model'; import { CoreState } from '../../core-state.model'; @@ -63,4 +65,16 @@ export class ScriptDataService extends DataService