diff --git a/.browserslistrc b/.browserslistrc index f8a421c330..427441dc93 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -2,10 +2,16 @@ # For additional information regarding the format and rule options, please see: # https://github.com/browserslist/browserslist#queries +# For the full list of supported browsers by the Angular framework, please see: +# https://angular.io/guide/browser-support + # You can see what browsers were selected by your queries by running: # npx browserslist -> 0.5% -last 2 versions +last 1 Chrome version +last 1 Firefox version +last 2 Edge major versions +last 2 Safari major versions +last 2 iOS major versions Firefox ESR -not IE 9-11 # For IE 9-11 support, remove 'not'. +not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000000..6d5aa89db7 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,222 @@ +{ + "root": true, + "plugins": [ + "@typescript-eslint", + "@angular-eslint/eslint-plugin", + "eslint-plugin-import", + "eslint-plugin-jsdoc", + "eslint-plugin-deprecation", + "eslint-plugin-unused-imports" + ], + "overrides": [ + { + "files": [ + "*.ts" + ], + "parserOptions": { + "project": [ + "./tsconfig.json", + "./cypress/tsconfig.json" + ], + "createDefaultProgram": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking", + "plugin:@angular-eslint/recommended", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "max-classes-per-file": [ + "error", + 1 + ], + "comma-dangle": [ + "off", + "always-multiline" + ], + "eol-last": [ + "error", + "always" + ], + "no-console": [ + "error", + { + "allow": [ + "log", + "warn", + "dir", + "timeLog", + "assert", + "clear", + "count", + "countReset", + "group", + "groupEnd", + "table", + "debug", + "info", + "dirxml", + "error", + "groupCollapsed", + "Console", + "profile", + "profileEnd", + "timeStamp", + "context" + ] + } + ], + "curly": "error", + "brace-style": [ + "error", + "1tbs", + { + "allowSingleLine": true + } + ], + "eqeqeq": [ + "error", + "always", + { + "null": "ignore" + } + ], + "radix": "error", + "guard-for-in": "error", + "no-bitwise": "error", + "no-restricted-imports": "error", + "no-caller": "error", + "no-debugger": "error", + "no-redeclare": "error", + "no-eval": "error", + "no-fallthrough": "error", + "no-trailing-spaces": "error", + "space-infix-ops": "error", + "keyword-spacing": "error", + "no-var": "error", + "no-unused-expressions": [ + "error", + { + "allowTernary": true + } + ], + "prefer-const": "off", // todo: re-enable & fix errors (more strict than it used to be in TSLint) + "prefer-spread": "off", + "no-underscore-dangle": "off", + + // todo: disabled rules from eslint:recommended, consider re-enabling & fixing + "no-prototype-builtins": "off", + "no-useless-escape": "off", + "no-case-declarations": "off", + "no-extra-boolean-cast": "off", + + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "ds", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "ds", + "style": "kebab-case" + } + ], + "@angular-eslint/pipe-prefix": [ + "error", + { + "prefixes": [ + "ds" + ] + } + ], + "@angular-eslint/no-attribute-decorator": "error", + "@angular-eslint/no-forward-ref": "error", + "@angular-eslint/no-output-native": "warn", + "@angular-eslint/no-output-on-prefix": "warn", + "@angular-eslint/no-conflicting-lifecycle": "warn", + + "@typescript-eslint/no-inferrable-types":[ + "error", + { + "ignoreParameters": true + } + ], + "@typescript-eslint/quotes": [ + "error", + "single", + { + "avoidEscape": true, + "allowTemplateLiterals": true + } + ], + "@typescript-eslint/semi": "error", + "@typescript-eslint/no-shadow": "error", + "@typescript-eslint/dot-notation": "error", + "@typescript-eslint/consistent-type-definitions": "error", + "@typescript-eslint/prefer-function-type": "error", + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "property", + "format": null + } + ], + "@typescript-eslint/member-ordering": [ + "error", + { + "default": [ + "static-field", + "instance-field", + "static-method", + "instance-method" + ] + } + ], + "@typescript-eslint/type-annotation-spacing": "error", + "@typescript-eslint/unified-signatures": "error", + "@typescript-eslint/ban-types": "warn", // todo: deal with {} type issues & re-enable + "@typescript-eslint/no-floating-promises": "warn", + "@typescript-eslint/no-misused-promises": "warn", + "@typescript-eslint/restrict-plus-operands": "warn", + "@typescript-eslint/unbound-method": "off", + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/no-var-requires": "off", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-unnecessary-type-assertion": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/restrict-template-expressions": "off", + "@typescript-eslint/require-await": "off", + + "deprecation/deprecation": "warn", + + "import/order": "off", + "import/no-deprecated": "warn" + } + }, + { + "files": [ + "*.html" + ], + "extends": [ + "plugin:@angular-eslint/template/recommended" + ], + "rules": { + // todo: re-enable & fix errors + "@angular-eslint/template/no-negated-async": "off", + "@angular-eslint/template/eqeqeq": "off" + } + } + ] +} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 539fd740ee..eeddb37441 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -70,7 +70,10 @@ jobs: run: yarn install --frozen-lockfile - name: Run lint - run: yarn run lint + run: yarn run lint --quiet + + - name: Check for circular dependencies + run: yarn run check-circ-deps - name: Run build run: yarn run build:prod @@ -128,6 +131,14 @@ jobs: name: e2e-test-screenshots path: cypress/screenshots + - name: Stop app (in case it stays up after e2e tests) + run: | + app_pid=$(lsof -t -i:4000) + if [[ ! -z $app_pid ]]; then + echo "App was still up! (PID: $app_pid)" + kill -9 $app_pid + fi + # Start up the app with SSR enabled (run in background) - name: Start app in SSR (server-side rendering) mode run: | diff --git a/.gitignore b/.gitignore index 026110f222..b27e636620 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/.angular/cache /__build__ /__server_build__ /node_modules diff --git a/Dockerfile b/Dockerfile index 2d98971112..a7c1640d0b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,4 +9,9 @@ EXPOSE 4000 # We run yarn install with an increased network timeout (5min) to avoid "ESOCKETTIMEDOUT" errors from hub.docker.com # See, for example https://github.com/yarnpkg/yarn/issues/5540 RUN yarn install --network-timeout 300000 -CMD yarn run start:dev + +# On startup, run in DEVELOPMENT mode (this defaults to live reloading enabled, etc). +# Listen / accept connections from all IP addresses. +# NOTE: At this time it is only possible to run Docker container in Production mode +# if you have a public IP. See https://github.com/DSpace/dspace-angular/issues/1485 +CMD yarn serve --host 0.0.0.0 diff --git a/angular.json b/angular.json index a0a4cd8ea1..56e06bd86c 100644 --- a/angular.json +++ b/angular.json @@ -17,7 +17,6 @@ "build": { "builder": "@angular-builders/custom-webpack:browser", "options": { - "extractCss": true, "preserveSymlinks": true, "customWebpackConfig": { "path": "./webpack/webpack.browser.ts", @@ -67,16 +66,27 @@ "scripts": [] }, "configurations": { + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, "production": { "fileReplacements": [ { "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, "outputHashing": "all", - "extractCss": true, "namedChunks": false, "aot": true, "extractLicenses": true, @@ -104,6 +114,9 @@ "port": 4000 }, "configurations": { + "development": { + "browserTarget": "dspace-angular:build:development" + }, "production": { "browserTarget": "dspace-angular:build:production" } @@ -157,19 +170,6 @@ } } }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "tsconfig.app.json", - "tsconfig.spec.json", - "cypress/tsconfig.json" - ], - "exclude": [ - "**/node_modules/**" - ] - } - }, "e2e": { "builder": "@cypress/schematic:cypress", "options": { @@ -197,6 +197,10 @@ "tsConfig": "tsconfig.server.json" }, "configurations": { + "development": { + "sourceMap": true, + "optimization": false + }, "production": { "sourceMap": false, "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" } ] } @@ -253,12 +261,22 @@ "watch": true, "headless": false } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "src/**/*.ts", + "src/**/*.html" + ] + } } } } }, "defaultProject": "dspace-angular", "cli": { - "analytics": false + "analytics": false, + "defaultCollection": "@angular-eslint/schematics" } -} \ No newline at end of file +} diff --git a/cypress/integration/submission.spec.ts b/cypress/integration/submission.spec.ts index c877479f44..009c50115b 100644 --- a/cypress/integration/submission.spec.ts +++ b/cypress/integration/submission.spec.ts @@ -42,6 +42,7 @@ describe('New Submission page', () => { cy.get('button#deposit').click(); // A warning alert should display. + cy.get('ds-notification div.alert-success').should('not.exist'); cy.get('ds-notification div.alert-warning').should('be.visible'); // First section should have an exclamation error in the header diff --git a/karma.conf.js b/karma.conf.js index 24cd067fd1..8418312b1a 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -22,7 +22,7 @@ module.exports = function (config) { reports: ['html', 'lcovonly', 'text-summary'], fixWebpackSourcePaths: true }, - reporters: ['mocha', 'kjhtml'], + reporters: ['mocha', 'kjhtml', 'coverage-istanbul'], mochaReporter: { ignoreSkipped: true, output: 'autowatch' diff --git a/package.json b/package.json index 5e98af53dd..75e22b40f3 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,10 @@ "start:dev": "nodemon --exec \"cross-env NODE_ENV=development yarn run serve\"", "start:prod": "yarn run build:prod && cross-env NODE_ENV=production yarn run serve:ssr", "start:mirador:prod": "yarn run build:mirador && yarn run start:prod", - "serve": "ts-node --project ./tsconfig.ts-node.json scripts/serve.ts", + "serve": "ng serve -c development", "serve:ssr": "node dist/server/main", "analyze": "webpack-bundle-analyzer dist/browser/stats.json", - "build": "ng build", + "build": "ng build -c development", "build:stats": "ng build --stats-json", "build:prod": "yarn run build:ssr", "build:ssr": "ng build --configuration production && ng run dspace-angular:server:production", @@ -36,7 +36,8 @@ "merge-i18n": "ts-node --project ./tsconfig.ts-node.json scripts/merge-i18n-files.ts", "cypress:open": "cypress open", "cypress:run": "cypress run", - "env:yaml": "ts-node --project ./tsconfig.ts-node.json scripts/env-to-yaml.ts" + "env:yaml": "ts-node --project ./tsconfig.ts-node.json scripts/env-to-yaml.ts", + "check-circ-deps": "npx madge --exclude '(bitstream|bundle|collection|config-submission-form|eperson|item|version)\\.model\\.ts$' --circular --extensions ts ./" }, "browser": { "fs": false, @@ -47,32 +48,35 @@ "private": true, "resolutions": { "minimist": "^1.2.5", - "webdriver-manager": "^12.1.8" + "webdriver-manager": "^12.1.8", + "ts-node": "10.2.1" }, "dependencies": { - "@angular/animations": "~11.2.14", - "@angular/cdk": "^11.2.13", - "@angular/common": "~11.2.14", - "@angular/compiler": "~11.2.14", - "@angular/core": "~11.2.14", - "@angular/forms": "~11.2.14", - "@angular/localize": "11.2.14", - "@angular/platform-browser": "~11.2.14", - "@angular/platform-browser-dynamic": "~11.2.14", - "@angular/platform-server": "~11.2.14", - "@angular/router": "~11.2.14", - "@kolkov/ngx-gallery": "^1.2.3", - "@ng-bootstrap/ng-bootstrap": "9.1.3", - "@ng-dynamic-forms/core": "^13.0.0", - "@ng-dynamic-forms/ui-ng-bootstrap": "^13.0.0", - "@ngrx/effects": "^11.1.1", - "@ngrx/router-store": "^11.1.1", - "@ngrx/store": "^11.1.1", - "@nguniversal/express-engine": "11.2.1", + "@angular/animations": "~13.2.6", + "@angular/cdk": "^13.2.6", + "@angular/common": "~13.2.6", + "@angular/compiler": "~13.2.6", + "@angular/core": "~13.2.6", + "@angular/forms": "~13.2.6", + "@angular/localize": "13.2.6", + "@angular/platform-browser": "~13.2.6", + "@angular/platform-browser-dynamic": "~13.2.6", + "@angular/platform-server": "~13.2.6", + "@angular/router": "~13.2.6", + "@babel/runtime": "^7.17.2", + "@kolkov/ngx-gallery": "^2.0.1", + "@material-ui/core": "^4.11.0", + "@material-ui/icons": "^4.9.1", + "@ng-bootstrap/ng-bootstrap": "^11.0.0", + "@ng-dynamic-forms/core": "^14.0.1", + "@ng-dynamic-forms/ui-ng-bootstrap": "^14.0.1", + "@ngrx/effects": "^13.0.2", + "@ngrx/router-store": "^13.0.2", + "@ngrx/store": "^13.0.2", + "@nguniversal/express-engine": "^13.0.2", "@ngx-translate/core": "^13.0.0", "@nicky-lenaers/ngx-scroll-to": "^9.0.0", "angular-idle-preload": "3.0.0", - "angular2-text-mask": "9.0.0", "angulartics2": "^10.0.0", "bootstrap": "4.3.1", "caniuse-lite": "^1.0.30001165", @@ -102,7 +106,7 @@ "mirador-share-plugin": "^0.11.0", "moment": "^2.29.1", "morgan": "^1.10.0", - "ng-mocks": "11.11.2", + "ng-mocks": "^13.1.1", "ng2-file-upload": "1.4.0", "ng2-nouislider": "^1.8.3", "ngx-infinite-scroll": "^10.0.1", @@ -111,7 +115,9 @@ "ngx-sortablejs": "^11.1.0", "nouislider": "^14.6.3", "pem": "1.14.4", - "postcss-cli": "^8.3.0", + "postcss-cli": "^9.1.0", + "prop-types": "^15.7.2", + "react-copy-to-clipboard": "^5.0.1", "reflect-metadata": "^0.1.13", "rxjs": "^6.6.3", "sortablejs": "1.13.0", @@ -119,19 +125,24 @@ "url-parse": "^1.5.6", "uuid": "^8.3.2", "webfontloader": "1.6.28", - "zone.js": "^0.10.3" + "zone.js": "~0.11.5" }, "devDependencies": { - "@angular-builders/custom-webpack": "10.0.1", - "@angular-devkit/build-angular": "~0.1102.15", - "@angular/cli": "~11.2.15", - "@angular/compiler-cli": "~11.2.14", - "@angular/language-service": "~11.2.14", + "@angular-builders/custom-webpack": "~13.1.0", + "@angular-devkit/build-angular": "~13.2.6", + "@angular-eslint/builder": "13.1.0", + "@angular-eslint/eslint-plugin": "13.1.0", + "@angular-eslint/eslint-plugin-template": "13.1.0", + "@angular-eslint/schematics": "13.1.0", + "@angular-eslint/template-parser": "13.1.0", + "@angular/cli": "~13.2.6", + "@angular/compiler-cli": "~13.2.6", + "@angular/language-service": "~13.2.6", "@cypress/schematic": "^1.5.0", "@fortawesome/fontawesome-free": "^5.5.0", - "@ngrx/store-devtools": "^11.1.1", - "@ngtools/webpack": "10.2.3", - "@nguniversal/builders": "~11.2.1", + "@ngrx/store-devtools": "^13.0.2", + "@ngtools/webpack": "^13.2.6", + "@nguniversal/builders": "^13.0.2", "@types/deep-freeze": "0.1.2", "@types/express": "^4.17.9", "@types/file-saver": "^2.0.1", @@ -140,22 +151,28 @@ "@types/js-cookie": "2.2.6", "@types/lodash": "^4.14.165", "@types/node": "^14.14.9", + "@typescript-eslint/eslint-plugin": "5.11.0", + "@typescript-eslint/parser": "5.11.0", "axe-core": "^4.3.3", - "codelyzer": "^6.0.0", "compression-webpack-plugin": "^3.0.1", "copy-webpack-plugin": "^6.4.1", "cross-env": "^7.0.3", - "css-loader": "3.4.0", - "cssnano": "^4.1.10", + "css-loader": "^6.2.0", + "css-minimizer-webpack-plugin": "^3.4.1", + "cssnano": "^5.0.6", "cypress": "9.5.1", "cypress-axe": "^0.13.0", "debug-loader": "^0.0.1", "deep-freeze": "0.0.1", "dotenv": "^8.2.0", + "eslint": "^8.2.0", + "eslint-plugin-deprecation": "^1.3.2", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-jsdoc": "^38.0.6", + "eslint-plugin-unused-imports": "^2.0.0", "fork-ts-checker-webpack-plugin": "^6.0.3", "html-loader": "^1.3.2", - "html-webpack-plugin": "^4.5.0", - "jasmine-core": "~3.6.0", + "jasmine-core": "^3.8.0", "jasmine-marbles": "0.6.0", "jasmine-spec-reporter": "~5.0.0", "karma": "^6.3.14", @@ -164,12 +181,13 @@ "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", "karma-mocha-reporter": "2.2.5", + "ngx-mask": "^12.0.0", "nodemon": "^2.0.15", - "optimize-css-assets-webpack-plugin": "^5.0.4", - "postcss-apply": "0.11.0", - "postcss-import": "^12.0.1", - "postcss-loader": "^3.0.0", - "postcss-preset-env": "6.7.0", + "postcss": "^8.1", + "postcss-apply": "0.12.0", + "postcss-import": "^14.0.0", + "postcss-loader": "^4.0.3", + "postcss-preset-env": "^7.4.2", "postcss-responsive-type": "1.0.0", "protractor": "^7.0.0", "protractor-istanbul-plugin": "2.0.0", @@ -178,15 +196,15 @@ "react-dom": "^16.14.0", "rimraf": "^3.0.2", "rxjs-spy": "^7.5.3", + "sass": "~1.32.6", + "sass-loader": "^12.6.0", "sass-resources-loader": "^2.1.1", - "script-ext-html-webpack-plugin": "2.1.5", - "string-replace-loader": "^2.3.0", + "string-replace-loader": "^3.1.0", "terser-webpack-plugin": "^2.3.1", "ts-loader": "^5.2.0", "ts-node": "^8.10.2", - "tslint": "^6.1.3", - "typescript": "~4.0.5", - "webpack": "^4.44.2", + "typescript": "~4.5.5", + "webpack": "^5.69.1", "webpack-bundle-analyzer": "^4.4.0", "webpack-cli": "^4.2.0", "webpack-dev-server": "^4.5.0" diff --git a/scripts/test-rest.ts b/scripts/test-rest.ts index aa3b64f62b..b2a3ebd1af 100644 --- a/scripts/test-rest.ts +++ b/scripts/test-rest.ts @@ -3,7 +3,7 @@ import * as https from 'https'; import { AppConfig } from '../src/config/app-config.interface'; import { buildAppConfig } from '../src/config/config.server'; - + const appConfig: AppConfig = buildAppConfig(); /** diff --git a/server.ts b/server.ts index da3b877bc1..bc840fb73c 100644 --- a/server.ts +++ b/server.ts @@ -15,7 +15,7 @@ * import for `ngExpressEngine`. */ -import 'zone.js/dist/zone-node'; +import 'zone.js/node'; import 'reflect-metadata'; import 'rxjs'; diff --git a/src/app/access-control/epeople-registry/epeople-registry.actions.ts b/src/app/access-control/epeople-registry/epeople-registry.actions.ts index b8b1044362..a07ea37df2 100644 --- a/src/app/access-control/epeople-registry/epeople-registry.actions.ts +++ b/src/app/access-control/epeople-registry/epeople-registry.actions.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ import { Action } from '@ngrx/store'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { type } from '../../shared/ngrx/type'; @@ -16,7 +17,6 @@ export const EPeopleRegistryActionTypes = { CANCEL_EDIT_EPERSON: type('dspace/epeople-registry/CANCEL_EDIT_EPERSON'), }; -/* tslint:disable:max-classes-per-file */ /** * Used to edit an EPerson in the EPeople registry */ @@ -37,7 +37,6 @@ export class EPeopleRegistryCancelEPersonAction implements Action { type = EPeopleRegistryActionTypes.CANCEL_EDIT_EPERSON; } -/* tslint:enable:max-classes-per-file */ /** * Export a type alias of all actions in this action group diff --git a/src/app/access-control/epeople-registry/epeople-registry.component.spec.ts b/src/app/access-control/epeople-registry/epeople-registry.component.spec.ts index bcf7e8f1d9..c0d70fd0b2 100644 --- a/src/app/access-control/epeople-registry/epeople-registry.component.spec.ts +++ b/src/app/access-control/epeople-registry/epeople-registry.component.spec.ts @@ -9,7 +9,6 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model'; import { RemoteData } from '../../core/data/remote-data'; -import { FindListOptions } from '../../core/data/request.models'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { PageInfo } from '../../core/shared/page-info.model'; @@ -27,6 +26,7 @@ import { AuthorizationDataService } from '../../core/data/feature-authorization/ import { RequestService } from '../../core/data/request.service'; import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; +import { FindListOptions } from '../../core/data/find-list-options.model'; describe('EPeopleRegistryComponent', () => { let component: EPeopleRegistryComponent; diff --git a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts index 644b893265..4957958658 100644 --- a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts +++ b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts @@ -8,7 +8,6 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { buildPaginatedList, PaginatedList } from '../../../core/data/paginated-list.model'; import { RemoteData } from '../../../core/data/remote-data'; -import { FindListOptions } from '../../../core/data/request.models'; import { EPersonDataService } from '../../../core/eperson/eperson-data.service'; import { EPerson } from '../../../core/eperson/models/eperson.model'; import { PageInfo } from '../../../core/shared/page-info.model'; @@ -29,6 +28,7 @@ import { createPaginatedList } from '../../../shared/testing/utils.test'; import { RequestService } from '../../../core/data/request.service'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; +import { FindListOptions } from '../../../core/data/find-list-options.model'; import { ValidateEmailNotTaken } from './validators/email-taken.validator'; import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; diff --git a/src/app/access-control/group-registry/group-registry.actions.ts b/src/app/access-control/group-registry/group-registry.actions.ts index bc1c0b97a6..8144bd0599 100644 --- a/src/app/access-control/group-registry/group-registry.actions.ts +++ b/src/app/access-control/group-registry/group-registry.actions.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ import { Action } from '@ngrx/store'; import { Group } from '../../core/eperson/models/group.model'; import { type } from '../../shared/ngrx/type'; @@ -16,7 +17,6 @@ export const GroupRegistryActionTypes = { CANCEL_EDIT_GROUP: type('dspace/epeople-registry/CANCEL_EDIT_GROUP'), }; -/* tslint:disable:max-classes-per-file */ /** * Used to edit a Group in the Group registry */ @@ -37,7 +37,6 @@ export class GroupRegistryCancelGroupAction implements Action { type = GroupRegistryActionTypes.CANCEL_EDIT_GROUP; } -/* tslint:enable:max-classes-per-file */ /** * Export a type alias of all actions in this action group diff --git a/src/app/admin/admin-registries/bitstream-formats/bitstream-format.actions.ts b/src/app/admin/admin-registries/bitstream-formats/bitstream-format.actions.ts index 84917905d3..c5bebb292b 100644 --- a/src/app/admin/admin-registries/bitstream-formats/bitstream-format.actions.ts +++ b/src/app/admin/admin-registries/bitstream-formats/bitstream-format.actions.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ import { Action } from '@ngrx/store'; import { type } from '../../../shared/ngrx/type'; import { BitstreamFormat } from '../../../core/shared/bitstream-format.model'; @@ -17,7 +18,6 @@ export const BitstreamFormatsRegistryActionTypes = { DESELECT_ALL_FORMAT: type('dspace/bitstream-formats-registry/DESELECT_ALL_FORMAT') }; -/* tslint:disable:max-classes-per-file */ /** * Used to select a single bitstream format in the bitstream format registry */ @@ -51,7 +51,6 @@ export class BitstreamFormatsRegistryDeselectAllAction implements Action { type = BitstreamFormatsRegistryActionTypes.DESELECT_ALL_FORMAT; } -/* tslint:enable:max-classes-per-file */ /** * Export a type alias of all actions in this action group diff --git a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.spec.ts b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.spec.ts index 8cfba1d37b..7d3a726eec 100644 --- a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.spec.ts +++ b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.spec.ts @@ -25,9 +25,9 @@ import { import { createPaginatedList } from '../../../shared/testing/utils.test'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model'; -import { FindListOptions } from '../../../core/data/request.models'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; +import { FindListOptions } from '../../../core/data/find-list-options.model'; describe('BitstreamFormatsComponent', () => { let comp: BitstreamFormatsComponent; diff --git a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.ts b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.ts index cbbcbe07a4..89d8ac29f3 100644 --- a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.ts +++ b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.ts @@ -5,7 +5,6 @@ import { PaginatedList } from '../../../core/data/paginated-list.model'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { BitstreamFormat } from '../../../core/shared/bitstream-format.model'; import { BitstreamFormatDataService } from '../../../core/data/bitstream-format-data.service'; -import { FindListOptions } from '../../../core/data/request.models'; import { map, switchMap, take } from 'rxjs/operators'; import { hasValue } from '../../../shared/empty.util'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; @@ -13,6 +12,7 @@ import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { NoContent } from '../../../core/shared/NoContent.model'; import { PaginationService } from '../../../core/pagination/pagination.service'; +import { FindListOptions } from '../../../core/data/find-list-options.model'; /** * This component renders a list of bitstream formats diff --git a/src/app/admin/admin-registries/metadata-registry/metadata-registry.actions.ts b/src/app/admin/admin-registries/metadata-registry/metadata-registry.actions.ts index 9737928a13..3fc872ca43 100644 --- a/src/app/admin/admin-registries/metadata-registry/metadata-registry.actions.ts +++ b/src/app/admin/admin-registries/metadata-registry/metadata-registry.actions.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ import { Action } from '@ngrx/store'; import { type } from '../../../shared/ngrx/type'; import { MetadataSchema } from '../../../core/metadata/metadata-schema.model'; @@ -26,7 +27,6 @@ export const MetadataRegistryActionTypes = { DESELECT_ALL_FIELD: type('dspace/metadata-registry/DESELECT_ALL_FIELD') }; -/* tslint:disable:max-classes-per-file */ /** * Used to edit a metadata schema in the metadata registry */ @@ -133,7 +133,6 @@ export class MetadataRegistryDeselectAllFieldAction implements Action { type = MetadataRegistryActionTypes.DESELECT_ALL_FIELD; } -/* tslint:enable:max-classes-per-file */ /** * Export a type alias of all actions in this action group diff --git a/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.spec.ts b/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.spec.ts index 0253725cb9..74bfc5f0a4 100644 --- a/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.spec.ts +++ b/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.spec.ts @@ -21,8 +21,8 @@ import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.u import { PaginationService } from '../../../core/pagination/pagination.service'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model'; -import { FindListOptions } from '../../../core/data/request.models'; import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; +import { FindListOptions } from '../../../core/data/find-list-options.model'; describe('MetadataRegistryComponent', () => { let comp: MetadataRegistryComponent; @@ -52,7 +52,7 @@ describe('MetadataRegistryComponent', () => { } ]; const mockSchemas = createSuccessfulRemoteDataObject$(buildPaginatedList(null, mockSchemasList)); - /* tslint:disable:no-empty */ + /* eslint-disable no-empty,@typescript-eslint/no-empty-function */ const registryServiceStub = { getMetadataSchemas: () => mockSchemas, getActiveMetadataSchema: () => observableOf(undefined), @@ -66,7 +66,7 @@ describe('MetadataRegistryComponent', () => { }, clearMetadataSchemaRequests: () => observableOf(undefined) }; - /* tslint:enable:no-empty */ + /* eslint-enable no-empty, @typescript-eslint/no-empty-function */ paginationService = new PaginationServiceStub(); diff --git a/src/app/admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.spec.ts b/src/app/admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.spec.ts index f486c3c132..8d416c2df8 100644 --- a/src/app/admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.spec.ts +++ b/src/app/admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.spec.ts @@ -17,7 +17,7 @@ describe('MetadataSchemaFormComponent', () => { let fixture: ComponentFixture; let registryService: RegistryService; - /* tslint:disable:no-empty */ + /* eslint-disable no-empty,@typescript-eslint/no-empty-function */ const registryServiceStub = { getActiveMetadataSchema: () => observableOf(undefined), createOrUpdateMetadataSchema: (schema: MetadataSchema) => observableOf(schema), @@ -33,7 +33,7 @@ describe('MetadataSchemaFormComponent', () => { }; } }; - /* tslint:enable:no-empty */ + /* eslint-enable no-empty, @typescript-eslint/no-empty-function */ beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ diff --git a/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.spec.ts b/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.spec.ts index 1bd25be113..e13180d633 100644 --- a/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.spec.ts +++ b/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.spec.ts @@ -24,7 +24,7 @@ describe('MetadataFieldFormComponent', () => { prefix: 'fake' }); - /* tslint:disable:no-empty */ + /* eslint-disable no-empty,@typescript-eslint/no-empty-function */ const registryServiceStub = { getActiveMetadataField: () => observableOf(undefined), createMetadataField: (field: MetadataField) => observableOf(field), @@ -43,7 +43,7 @@ describe('MetadataFieldFormComponent', () => { }; } }; - /* tslint:enable:no-empty */ + /* eslint-enable no-empty, @typescript-eslint/no-empty-function */ beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ diff --git a/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts b/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts index 6eb3c5b1a4..c4116dc9e0 100644 --- a/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts +++ b/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts @@ -25,9 +25,9 @@ import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.u import { VarDirective } from '../../../shared/utils/var.directive'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model'; -import { FindListOptions } from '../../../core/data/request.models'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; +import { FindListOptions } from '../../../core/data/find-list-options.model'; describe('MetadataSchemaComponent', () => { let comp: MetadataSchemaComponent; @@ -106,7 +106,7 @@ describe('MetadataSchemaComponent', () => { } ]; const mockSchemas = createSuccessfulRemoteDataObject$(buildPaginatedList(null, mockSchemasList)); - /* tslint:disable:no-empty */ + /* eslint-disable no-empty,@typescript-eslint/no-empty-function */ const registryServiceStub = { getMetadataSchemas: () => mockSchemas, getMetadataFieldsBySchema: (schema: MetadataSchema) => createSuccessfulRemoteDataObject$(buildPaginatedList(null, mockFieldsList.filter((value) => value.id === 3 || value.id === 4))), @@ -122,7 +122,7 @@ describe('MetadataSchemaComponent', () => { }, clearMetadataFieldRequests: () => observableOf(undefined) }; - /* tslint:enable:no-empty */ + /* eslint-enable no-empty, @typescript-eslint/no-empty-function */ const schemaNameParam = 'mock'; const activatedRouteStub = Object.assign(new ActivatedRouteStub(), { params: observableOf({ diff --git a/src/app/admin/admin-search-page/admin-search-results/item-admin-search-result-actions.component.html b/src/app/admin/admin-search-page/admin-search-results/item-admin-search-result-actions.component.html index fbbbbb255c..ba4ab15363 100644 --- a/src/app/admin/admin-search-page/admin-search-results/item-admin-search-result-actions.component.html +++ b/src/app/admin/admin-search-page/admin-search-results/item-admin-search-result-actions.component.html @@ -1,28 +1,30 @@ - - {{"admin.search.item.move" | translate}} - +
+ + {{"admin.search.item.move" | translate}} + - + {{"admin.search.item.make-private" | translate}} - + - + {{"admin.search.item.make-public" | translate}} - + - - {{"admin.search.item.edit" | translate}} - + + {{"admin.search.item.edit" | translate}} + - - {{"admin.search.item.withdraw" | translate}} - + + {{"admin.search.item.withdraw" | translate}} + - - {{"admin.search.item.reinstate" | translate}} - + + {{"admin.search.item.reinstate" | translate}} + - + {{"admin.search.item.delete" | translate}} - + +
diff --git a/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts b/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts index 50f9f8a79e..47f693cb99 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts @@ -1,10 +1,10 @@ import { Component, Inject, Injector, OnInit } from '@angular/core'; import { MenuSectionComponent } from '../../../shared/menu/menu-section/menu-section.component'; -import { MenuID } from '../../../shared/menu/initial-menus-state'; import { MenuService } from '../../../shared/menu/menu.service'; import { rendersSectionForMenu } from '../../../shared/menu/menu-section.decorator'; import { LinkMenuItemModel } from '../../../shared/menu/menu-item/models/link.model'; -import { MenuSection } from '../../../shared/menu/menu.reducer'; +import { MenuSection } from '../../../shared/menu/menu-section.model'; +import { MenuID } from '../../../shared/menu/menu-id.model'; import { isNotEmpty } from '../../../shared/empty.util'; import { Router } from '@angular/router'; @@ -12,7 +12,7 @@ import { Router } from '@angular/router'; * Represents a non-expandable section in the admin sidebar */ @Component({ - /* tslint:disable:component-selector */ + /* eslint-disable @angular-eslint/component-selector */ selector: 'li[ds-admin-sidebar-section]', templateUrl: './admin-sidebar-section.component.html', styleUrls: ['./admin-sidebar-section.component.scss'], 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 dd5d7e507a..a8c5ef800c 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.ts @@ -1,18 +1,35 @@ 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 { BehaviorSubject, combineLatest as observableCombineLatest, combineLatest, Observable } from 'rxjs'; +import { debounceTime, distinctUntilChanged, filter, first, map, take, withLatestFrom } from 'rxjs/operators'; import { AuthService } from '../../core/auth/auth.service'; -import { ScriptDataService } from '../../core/data/processes/script-data.service'; +import { + METADATA_EXPORT_SCRIPT_NAME, + METADATA_IMPORT_SCRIPT_NAME, + ScriptDataService +} 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'; -import { CreateItemParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component'; -import { EditCollectionSelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component'; -import { EditCommunitySelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component'; -import { EditItemSelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component'; -import { ExportMetadataSelectorComponent } from '../../shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component'; -import { MenuID, MenuItemType } from '../../shared/menu/initial-menus-state'; +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'; +import { + CreateItemParentSelectorComponent +} from '../../shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component'; +import { + EditCollectionSelectorComponent +} from '../../shared/dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component'; +import { + EditCommunitySelectorComponent +} from '../../shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component'; +import { + EditItemSelectorComponent +} from '../../shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component'; +import { + ExportMetadataSelectorComponent +} from '../../shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component'; import { LinkMenuItemModel } from '../../shared/menu/menu-item/models/link.model'; import { OnClickMenuItemModel } from '../../shared/menu/menu-item/models/onclick.model'; import { TextMenuItemModel } from '../../shared/menu/menu-item/models/text.model'; @@ -23,6 +40,9 @@ import { AuthorizationDataService } from '../../core/data/feature-authorization/ import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { Router, ActivatedRoute } from '@angular/router'; import {NOTIFICATIONS_RECITER_SUGGESTION_PATH} from "../admin-notifications/admin-notifications-routing-paths"; +import { MenuID } from '../../shared/menu/menu-id.model'; +import { MenuItemType } from '../../shared/menu/menu-item-type.model'; +import { ActivatedRoute } from '@angular/router'; /** * Component representing the admin sidebar @@ -82,12 +102,12 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { * Set and calculate all initial values of the instance variables */ ngOnInit(): void { - this.createMenu(); super.ngOnInit(); this.sidebarWidth = this.variableService.getVariable('sidebarItemsWidth'); this.authService.isAuthenticated() .subscribe((loggedIn: boolean) => { if (loggedIn) { + this.createMenu(); this.menuService.showMenu(this.menuID); } }); @@ -322,19 +342,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', @@ -377,14 +384,28 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { ]; menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, menuSection)); - observableCombineLatest( + observableCombineLatest([ this.authorizationService.isAuthorized(FeatureID.AdministratorOf), - // 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), + this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_EXPORT_SCRIPT_NAME) + ]).pipe( + 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 +429,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', @@ -437,14 +446,27 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { shouldPersistOnRouteChange: true }))); - observableCombineLatest( + observableCombineLatest([ this.authorizationService.isAuthorized(FeatureID.AdministratorOf), - // 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), + this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_IMPORT_SCRIPT_NAME) + ]).pipe( + 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', @@ -576,10 +598,10 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { * Create menu sections dependent on whether or not the current user can manage access control groups */ createAccessControlMenuSections() { - observableCombineLatest( + observableCombineLatest([ this.authorizationService.isAuthorized(FeatureID.AdministratorOf), this.authorizationService.isAuthorized(FeatureID.CanManageGroups) - ).subscribe(([isSiteAdmin, canManageGroups]) => { + ]).subscribe(([isSiteAdmin, canManageGroups]) => { const menuList = [ /* Access Control */ { diff --git a/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts b/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts index aaa6a85c51..7cd20b15d2 100644 --- a/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts +++ b/src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts @@ -4,18 +4,18 @@ import { AdminSidebarSectionComponent } from '../admin-sidebar-section/admin-sid import { slide } from '../../../shared/animations/slide'; import { CSSVariableService } from '../../../shared/sass-helper/sass-helper.service'; import { bgColor } from '../../../shared/animations/bgColor'; -import { MenuID } from '../../../shared/menu/initial-menus-state'; import { MenuService } from '../../../shared/menu/menu.service'; import { combineLatest as combineLatestObservable, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { rendersSectionForMenu } from '../../../shared/menu/menu-section.decorator'; +import { MenuID } from '../../../shared/menu/menu-id.model'; import { Router } from '@angular/router'; /** * Represents a expandable section in the sidebar */ @Component({ - /* tslint:disable:component-selector */ + /* eslint-disable @angular-eslint/component-selector */ selector: 'li[ds-expandable-admin-sidebar-section]', templateUrl: './expandable-admin-sidebar-section.component.html', styleUrls: ['./expandable-admin-sidebar-section.component.scss'], diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/workflow-item-admin-workflow-actions.component.html b/src/app/admin/admin-workflow-page/admin-workflow-search-results/workflow-item-admin-workflow-actions.component.html index 4d7266514c..7f19ef3c2e 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/workflow-item-admin-workflow-actions.component.html +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/workflow-item-admin-workflow-actions.component.html @@ -1,7 +1,8 @@ - +
+ {{"admin.workflow.item.delete" | translate}} - - - + + {{"admin.workflow.item.send-back" | translate}} - + +
diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 669411d9aa..db217ce161 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -201,7 +201,6 @@ export class AppComponent implements OnInit, AfterViewInit { if (event instanceof NavigationStart) { resolveEndFound = false; this.isRouteLoading$.next(true); - this.isThemeLoading$.next(true); } else if (event instanceof ResolveEnd) { resolveEndFound = true; const activatedRouteSnapShot: ActivatedRouteSnapshot = event.state.root; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 0885eea83b..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, @@ -59,6 +58,9 @@ import { PageInternalServerErrorComponent } from './page-internal-server-error/p import { ThemedAdminSidebarComponent } from './admin/admin-sidebar/themed-admin-sidebar.component'; 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; @@ -90,19 +92,14 @@ const IMPORTS = [ ScrollToModule.forRoot(), NgbModule, TranslateModule.forRoot(), + NgxMaskModule.forRoot(), EffectsModule.forRoot(appEffects), 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/app.reducer.ts b/src/app/app.reducer.ts index 5bd4f745d9..1d6e86463d 100644 --- a/src/app/app.reducer.ts +++ b/src/app/app.reducer.ts @@ -22,7 +22,7 @@ import { nameVariantReducer } from './shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.reducer'; import { formReducer, FormState } from './shared/form/form.reducer'; -import { menusReducer, MenusState } from './shared/menu/menu.reducer'; +import { menusReducer} from './shared/menu/menu.reducer'; import { notificationsReducer, NotificationsState @@ -49,6 +49,7 @@ import { import { sidebarReducer, SidebarState } from './shared/sidebar/sidebar.reducer'; import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer'; import { ThemeState, themeReducer } from './shared/theme-support/theme.reducer'; +import { MenusState } from './shared/menu/menus-state.model'; import { correlationIdReducer } from './correlation-id/correlation-id.reducer'; export interface AppState { diff --git a/src/app/bitstream-page/legacy-bitstream-url.resolver.spec.ts b/src/app/bitstream-page/legacy-bitstream-url.resolver.spec.ts index 25e245c5b7..045582cb26 100644 --- a/src/app/bitstream-page/legacy-bitstream-url.resolver.spec.ts +++ b/src/app/bitstream-page/legacy-bitstream-url.resolver.spec.ts @@ -2,8 +2,8 @@ import { LegacyBitstreamUrlResolver } from './legacy-bitstream-url.resolver'; import { of as observableOf, EMPTY } from 'rxjs'; import { BitstreamDataService } from '../core/data/bitstream-data.service'; import { RemoteData } from '../core/data/remote-data'; -import { RequestEntryState } from '../core/data/request.reducer'; import { TestScheduler } from 'rxjs/testing'; +import { RequestEntryState } from '../core/data/request-entry-state.model'; describe(`LegacyBitstreamUrlResolver`, () => { let resolver: LegacyBitstreamUrlResolver; diff --git a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.spec.ts b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.spec.ts index 7b0ddcb18e..15ec9d78db 100644 --- a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.spec.ts +++ b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.spec.ts @@ -20,9 +20,9 @@ import { VarDirective } from '../../shared/utils/var.directive'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { FindListOptions } from '../../core/data/request.models'; import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; +import { FindListOptions } from '../../core/data/find-list-options.model'; describe('BrowseByDatePageComponent', () => { let comp: BrowseByDatePageComponent; diff --git a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html index 324a581ae0..cd5f4f03a2 100644 --- a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html +++ b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html @@ -31,7 +31,6 @@ [sortConfig]="(currentSort$ |async)" [type]="startsWithType" [startsWithOptions]="startsWithOptions" - [enableArrows]="true" (prev)="goPrev()" (next)="goNext()"> diff --git a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts index f789389697..8f9eaa16bc 100644 --- a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts +++ b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts @@ -127,10 +127,10 @@ export class BrowseByMetadataPageComponent implements OnInit { return [Object.assign({}, routeParams, queryParams),currentPage,currentSort]; }) ).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => { - this.browseId = params.id || this.defaultBrowseId; + this.browseId = params.id || this.defaultBrowseId; this.authority = params.authority; - this.value = +params.value || params.value || ''; - this.startsWith = +params.startsWith || params.startsWith; + this.value = +params.value || params.value || ''; + this.startsWith = +params.startsWith || params.startsWith; const searchOptions = browseParamsToOptions(params, currentPage, currentSort, this.browseId); if (isNotEmpty(this.value)) { this.updatePageWithItems(searchOptions, this.value, this.authority); diff --git a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.spec.ts b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.spec.ts index 584da1c45a..554b059ac5 100644 --- a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.spec.ts +++ b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.spec.ts @@ -20,9 +20,9 @@ import { VarDirective } from '../../shared/utils/var.directive'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { FindListOptions } from '../../core/data/request.models'; import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; +import { FindListOptions } from '../../core/data/find-list-options.model'; describe('BrowseByTitlePageComponent', () => { let comp: BrowseByTitlePageComponent; diff --git a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts index b2798b7fa8..79492a499b 100644 --- a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts +++ b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts @@ -45,7 +45,7 @@ export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent { return [Object.assign({}, routeParams, queryParams),currentPage,currentSort]; }) ).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => { - this.browseId = params.id || this.defaultBrowseId; + this.browseId = params.id || this.defaultBrowseId; this.updatePageWithItems(browseParamsToOptions(params, currentPage, currentSort, this.browseId), undefined, undefined); this.updateParent(params.scope); })); diff --git a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts index e8d8d3eb11..7f0e6815ed 100644 --- a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts +++ b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts @@ -16,7 +16,7 @@ import { Collection } from '../../core/shared/collection.model'; import { RemoteData } from '../../core/data/remote-data'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { EventEmitter } from '@angular/core'; +import { ChangeDetectionStrategy, EventEmitter } from '@angular/core'; import { HostWindowService } from '../../shared/host-window.service'; import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; import { By } from '@angular/platform-browser'; @@ -41,6 +41,8 @@ import { } from '../../shared/remote-data.utils'; import { createPaginatedList } from '../../shared/testing/utils.test'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { MyDSpacePageComponent, SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-page.component'; +import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service.stub'; describe('CollectionItemMapperComponent', () => { let comp: CollectionItemMapperComponent; @@ -110,15 +112,15 @@ describe('CollectionItemMapperComponent', () => { }; const searchServiceStub = Object.assign(new SearchServiceStub(), { search: () => observableOf(emptyList), - /* tslint:disable:no-empty */ + /* eslint-disable no-empty,@typescript-eslint/no-empty-function */ clearDiscoveryRequests: () => {} - /* tslint:enable:no-empty */ + /* eslint-enable no-empty,@typescript-eslint/no-empty-function */ }); const collectionDataServiceStub = { getMappedItems: () => observableOf(emptyList), - /* tslint:disable:no-empty */ + /* eslint-disable no-empty,@typescript-eslint/no-empty-function */ clearMappedItemsRequests: () => {} - /* tslint:enable:no-empty */ + /* eslint-enable no-empty, @typescript-eslint/no-empty-function */ }; const routeServiceStub = { getRouteParameterValue: () => { @@ -159,6 +161,14 @@ describe('CollectionItemMapperComponent', () => { { provide: RouteService, useValue: routeServiceStub }, { provide: AuthorizationDataService, useValue: authorizationDataService } ] + }).overrideComponent(CollectionItemMapperComponent, { + set: { + providers: [ + { + provide: SEARCH_CONFIG_SERVICE, + useClass: SearchConfigurationServiceStub + } + ] } }).compileComponents(); })); diff --git a/src/app/collection-page/collection-page-routing.module.ts b/src/app/collection-page/collection-page-routing.module.ts index 5879e523af..29e342f140 100644 --- a/src/app/collection-page/collection-page-routing.module.ts +++ b/src/app/collection-page/collection-page-routing.module.ts @@ -18,9 +18,9 @@ import { COLLECTION_CREATE_PATH } from './collection-page-routing-paths'; import { CollectionPageAdministratorGuard } from './collection-page-administrator.guard'; -import { MenuItemType } from '../shared/menu/initial-menus-state'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { ThemedCollectionPageComponent } from './themed-collection-page.component'; +import { MenuItemType } from '../shared/menu/menu-item-type.model'; @NgModule({ imports: [ diff --git a/src/app/collection-page/collection-page.component.html b/src/app/collection-page/collection-page.component.html index 6eceb696bd..6e4437e0e0 100644 --- a/src/app/collection-page/collection-page.component.html +++ b/src/app/collection-page/collection-page.component.html @@ -34,7 +34,7 @@ [title]="'collection.page.news'"> -
+
diff --git a/src/app/collection-page/collection-page.component.ts b/src/app/collection-page/collection-page.component.ts index be602f8458..09471d4c6d 100644 --- a/src/app/collection-page/collection-page.component.ts +++ b/src/app/collection-page/collection-page.component.ts @@ -16,7 +16,6 @@ import { Item } from '../core/shared/item.model'; import { getAllSucceededRemoteDataPayload, getFirstSucceededRemoteData, - redirectOn4xx, toDSpaceObjectListRD } from '../core/shared/operators'; @@ -28,6 +27,7 @@ import { PaginationService } from '../core/pagination/pagination.service'; import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../core/data/feature-authorization/feature-id'; import { getCollectionPageRoute } from './collection-page-routing-paths'; +import { redirectOn4xx } from '../core/shared/authorized.operators'; @Component({ selector: 'ds-collection-page', diff --git a/src/app/collection-page/delete-collection-page/delete-collection-page.component.html b/src/app/collection-page/delete-collection-page/delete-collection-page.component.html index 4abb149498..c33675a752 100644 --- a/src/app/collection-page/delete-collection-page/delete-collection-page.component.html +++ b/src/app/collection-page/delete-collection-page/delete-collection-page.component.html @@ -5,11 +5,11 @@

{{ 'collection.delete.text' | translate:{ dso: dso.name } }}

-
+
- diff --git a/src/app/collection-page/edit-collection-page/collection-metadata/collection-metadata.component.html b/src/app/collection-page/edit-collection-page/collection-metadata/collection-metadata.component.html index 9818813e32..ffd8f71343 100644 --- a/src/app/collection-page/edit-collection-page/collection-metadata/collection-metadata.component.html +++ b/src/app/collection-page/edit-collection-page/collection-metadata/collection-metadata.component.html @@ -1,6 +1,6 @@
-
+
- {{ 'communityList.showMore' | translate }} diff --git a/src/app/community-list-page/community-list/community-list.component.spec.ts b/src/app/community-list-page/community-list/community-list.component.spec.ts index 1f020b7744..575edf14e8 100644 --- a/src/app/community-list-page/community-list/community-list.component.spec.ts +++ b/src/app/community-list-page/community-list/community-list.component.spec.ts @@ -1,7 +1,7 @@ import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync } from '@angular/core/testing'; import { CommunityListComponent } from './community-list.component'; -import { CommunityListService, FlatNode, showMoreFlatNode, toFlatNode } from '../community-list-service'; +import { CommunityListService, showMoreFlatNode, toFlatNode } from '../community-list-service'; import { CdkTreeModule } from '@angular/cdk/tree'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; @@ -15,6 +15,7 @@ import { Collection } from '../../core/shared/collection.model'; import { of as observableOf } from 'rxjs'; import { By } from '@angular/platform-browser'; import { isEmpty, isNotEmpty } from '../../shared/empty.util'; +import { FlatNode } from '../flat-node.model'; describe('CommunityListComponent', () => { let component: CommunityListComponent; diff --git a/src/app/community-list-page/community-list/community-list.component.ts b/src/app/community-list-page/community-list/community-list.component.ts index 49065c5ec5..556387da25 100644 --- a/src/app/community-list-page/community-list/community-list.component.ts +++ b/src/app/community-list-page/community-list/community-list.component.ts @@ -1,11 +1,12 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { take } from 'rxjs/operators'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { FindListOptions } from '../../core/data/request.models'; -import { CommunityListService, FlatNode } from '../community-list-service'; +import { CommunityListService} from '../community-list-service'; import { CommunityListDatasource } from '../community-list-datasource'; import { FlatTreeControl } from '@angular/cdk/tree'; import { isEmpty } from '../../shared/empty.util'; +import { FlatNode } from '../flat-node.model'; +import { FindListOptions } from '../../core/data/find-list-options.model'; /** * A tree-structured list of nodes representing the communities, their subCommunities and collections. diff --git a/src/app/community-list-page/community-list/themed-community-list.component.ts b/src/app/community-list-page/community-list/themed-community-list.component.ts index adbfed85f3..4a986e737c 100644 --- a/src/app/community-list-page/community-list/themed-community-list.component.ts +++ b/src/app/community-list-page/community-list/themed-community-list.component.ts @@ -7,7 +7,8 @@ import { Component } from '@angular/core'; selector: 'ds-themed-community-list', styleUrls: [], templateUrl: '../../shared/theme-support/themed.component.html', -})export class ThemedCommunityListComponent extends ThemedComponent { +}) +export class ThemedCommunityListComponent extends ThemedComponent { protected getComponentName(): string { return 'CommunityListComponent'; } diff --git a/src/app/community-list-page/flat-node.model.ts b/src/app/community-list-page/flat-node.model.ts new file mode 100644 index 0000000000..0aabbeb489 --- /dev/null +++ b/src/app/community-list-page/flat-node.model.ts @@ -0,0 +1,22 @@ +import { Observable } from 'rxjs'; +import { Community } from '../core/shared/community.model'; +import { Collection } from '../core/shared/collection.model'; +import { ShowMoreFlatNode } from './show-more-flat-node.model'; + +/** + * Each node in the tree is represented by a flatNode which contains info about the node itself and its position and + * state in the tree. There are nodes representing communities, collections and show more links. + */ +export interface FlatNode { + isExpandable$: Observable; + name: string; + id: string; + level: number; + isExpanded?: boolean; + parent?: FlatNode; + payload: Community | Collection | ShowMoreFlatNode; + isShowMoreNode: boolean; + route?: string; + currentCommunityPage?: number; + currentCollectionPage?: number; +} diff --git a/src/app/community-list-page/show-more-flat-node.model.ts b/src/app/community-list-page/show-more-flat-node.model.ts new file mode 100644 index 0000000000..801c9e7388 --- /dev/null +++ b/src/app/community-list-page/show-more-flat-node.model.ts @@ -0,0 +1,6 @@ +/** + * The show more links in the community tree are also represented by a flatNode so we know where in + * the tree it should be rendered an who its parent is (needed for the action resulting in clicking this link) + */ +export class ShowMoreFlatNode { +} diff --git a/src/app/community-page/community-page-routing.module.ts b/src/app/community-page/community-page-routing.module.ts index ad1b1fd2f2..25326448a8 100644 --- a/src/app/community-page/community-page-routing.module.ts +++ b/src/app/community-page/community-page-routing.module.ts @@ -11,9 +11,9 @@ import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.servi import { LinkService } from '../core/cache/builders/link.service'; import { COMMUNITY_EDIT_PATH, COMMUNITY_CREATE_PATH } from './community-page-routing-paths'; import { CommunityPageAdministratorGuard } from './community-page-administrator.guard'; -import { MenuItemType } from '../shared/menu/initial-menus-state'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { ThemedCommunityPageComponent } from './themed-community-page.component'; +import { MenuItemType } from '../shared/menu/menu-item-type.model'; @NgModule({ imports: [ diff --git a/src/app/community-page/community-page.component.html b/src/app/community-page/community-page.component.html index 27420e95b3..fa2408d298 100644 --- a/src/app/community-page/community-page.component.html +++ b/src/app/community-page/community-page.component.html @@ -20,7 +20,7 @@ [title]="'community.page.news'"> -
+
diff --git a/src/app/community-page/community-page.component.ts b/src/app/community-page/community-page.component.ts index 70259a599b..b1a0cfc946 100644 --- a/src/app/community-page/community-page.component.ts +++ b/src/app/community-page/community-page.component.ts @@ -13,11 +13,12 @@ import { MetadataService } from '../core/metadata/metadata.service'; import { fadeInOut } from '../shared/animations/fade'; import { hasValue } from '../shared/empty.util'; -import { getAllSucceededRemoteDataPayload, redirectOn4xx } from '../core/shared/operators'; +import { getAllSucceededRemoteDataPayload} from '../core/shared/operators'; import { AuthService } from '../core/auth/auth.service'; import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../core/data/feature-authorization/feature-id'; import { getCommunityPageRoute } from './community-page-routing-paths'; +import { redirectOn4xx } from '../core/shared/authorized.operators'; @Component({ selector: 'ds-community-page', diff --git a/src/app/community-page/delete-community-page/delete-community-page.component.html b/src/app/community-page/delete-community-page/delete-community-page.component.html index 658f3da436..751d001c51 100644 --- a/src/app/community-page/delete-community-page/delete-community-page.component.html +++ b/src/app/community-page/delete-community-page/delete-community-page.component.html @@ -5,11 +5,11 @@

{{ 'community.delete.text' | translate:{ dso: dso.name } }}

-
+
- diff --git a/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts b/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts index 93a6c6fbb1..ec61fac613 100644 --- a/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts +++ b/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts @@ -11,7 +11,6 @@ import { CommunityPageSubCollectionListComponent } from './community-page-sub-co import { Community } from '../../core/shared/community.model'; import { SharedModule } from '../../shared/shared.module'; import { CollectionDataService } from '../../core/data/collection-data.service'; -import { FindListOptions } from '../../core/data/request.models'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { buildPaginatedList } from '../../core/data/paginated-list.model'; import { PageInfo } from '../../core/shared/page-info.model'; @@ -25,6 +24,7 @@ import { PaginationService } from '../../core/pagination/pagination.service'; import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../shared/theme-support/theme.service'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; +import { FindListOptions } from '../../core/data/find-list-options.model'; describe('CommunityPageSubCollectionList Component', () => { let comp: CommunityPageSubCollectionListComponent; diff --git a/src/app/community-page/sub-community-list/community-page-sub-community-list.component.spec.ts b/src/app/community-page/sub-community-list/community-page-sub-community-list.component.spec.ts index e573259b63..2bc829a3b0 100644 --- a/src/app/community-page/sub-community-list/community-page-sub-community-list.component.spec.ts +++ b/src/app/community-page/sub-community-list/community-page-sub-community-list.component.spec.ts @@ -13,7 +13,6 @@ import { buildPaginatedList } from '../../core/data/paginated-list.model'; import { PageInfo } from '../../core/shared/page-info.model'; import { SharedModule } from '../../shared/shared.module'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; -import { FindListOptions } from '../../core/data/request.models'; import { HostWindowService } from '../../shared/host-window.service'; import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; import { CommunityDataService } from '../../core/data/community-data.service'; @@ -25,6 +24,7 @@ import { PaginationService } from '../../core/pagination/pagination.service'; import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../shared/theme-support/theme.service'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; +import { FindListOptions } from '../../core/data/find-list-options.model'; describe('CommunityPageSubCommunityListComponent Component', () => { let comp: CommunityPageSubCommunityListComponent; diff --git a/src/app/core/auth/auth-request.service.ts b/src/app/core/auth/auth-request.service.ts index 00a94822d3..da38d730a5 100644 --- a/src/app/core/auth/auth-request.service.ts +++ b/src/app/core/auth/auth-request.service.ts @@ -3,7 +3,7 @@ import { distinctUntilChanged, filter, map, mergeMap, switchMap, tap } from 'rxj import { HALEndpointService } from '../shared/hal-endpoint.service'; import { RequestService } from '../data/request.service'; import { isNotEmpty } from '../../shared/empty.util'; -import { GetRequest, PostRequest, RestRequest, } from '../data/request.models'; +import { GetRequest, PostRequest, } from '../data/request.models'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { getFirstCompletedRemoteData } from '../shared/operators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; @@ -11,6 +11,7 @@ import { RemoteData } from '../data/remote-data'; import { AuthStatus } from './models/auth-status.model'; import { ShortLivedToken } from './models/short-lived-token.model'; import { URLCombiner } from '../url-combiner/url-combiner'; +import { RestRequest } from '../data/rest-request.model'; /** * Abstract service to send authentication requests diff --git a/src/app/core/auth/auth.actions.ts b/src/app/core/auth/auth.actions.ts index 15e42c8576..60440d371e 100644 --- a/src/app/core/auth/auth.actions.ts +++ b/src/app/core/auth/auth.actions.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ // import @ngrx import { Action } from '@ngrx/store'; // import type function @@ -39,7 +40,6 @@ export const AuthActionTypes = { UNSET_USER_AS_IDLE: type('dspace/auth/UNSET_USER_AS_IDLE') }; -/* tslint:disable:max-classes-per-file */ /** * Authenticate. @@ -411,7 +411,6 @@ export class SetUserAsIdleAction implements Action { export class UnsetUserAsIdleAction implements Action { public type: string = AuthActionTypes.UNSET_USER_AS_IDLE; } -/* tslint:enable:max-classes-per-file */ /** * Actions type. diff --git a/src/app/core/auth/auth.effects.spec.ts b/src/app/core/auth/auth.effects.spec.ts index ff3873beef..f09db04d99 100644 --- a/src/app/core/auth/auth.effects.spec.ts +++ b/src/app/core/auth/auth.effects.spec.ts @@ -1,4 +1,4 @@ -import { fakeAsync, flush, TestBed } from '@angular/core/testing'; +import { fakeAsync, TestBed, tick } from '@angular/core/testing'; import { provideMockActions } from '@ngrx/effects/testing'; import { Store, StoreModule } from '@ngrx/store'; @@ -396,44 +396,43 @@ describe('AuthEffects', () => { }); describe('when auth loaded is false', () => { - it('should not call removeToken method', (done) => { + it('should not call removeToken method', fakeAsync(() => { store.overrideSelector(isAuthenticatedLoaded, false); - actions = hot('--a-|', { a: { type: StoreActionTypes.REHYDRATE } }); + actions = observableOf({ type: StoreActionTypes.REHYDRATE }); spyOn(authServiceStub, 'removeToken'); authEffects.clearInvalidTokenOnRehydrate$.subscribe(() => { - expect(authServiceStub.removeToken).not.toHaveBeenCalled(); - + expect(false).toBeTrue(); // subscribe to trigger taps, fail if the effect emits (we don't expect it to) }); - - done(); - }); + tick(1000); + expect(authServiceStub.removeToken).not.toHaveBeenCalled(); + })); }); describe('when auth loaded is true', () => { - it('should call removeToken method', fakeAsync(() => { + it('should call removeToken method', (done) => { + spyOn(console, 'log').and.callThrough(); + store.overrideSelector(isAuthenticatedLoaded, true); - actions = hot('--a-|', { a: { type: StoreActionTypes.REHYDRATE } }); + actions = observableOf({ type: StoreActionTypes.REHYDRATE }); spyOn(authServiceStub, 'removeToken'); authEffects.clearInvalidTokenOnRehydrate$.subscribe(() => { expect(authServiceStub.removeToken).toHaveBeenCalled(); - flush(); + done(); }); - - })); + }); }); }); describe('invalidateAuthorizationsRequestCache$', () => { it('should call invalidateAuthorizationsRequestCache method in response to a REHYDRATE action', (done) => { - actions = hot('--a-|', { a: { type: StoreActionTypes.REHYDRATE } }); + actions = observableOf({ type: StoreActionTypes.REHYDRATE }); authEffects.invalidateAuthorizationsRequestCache$.subscribe(() => { expect((authEffects as any).authorizationsService.invalidateAuthorizationsRequestCache).toHaveBeenCalled(); + done(); }); - - done(); }); }); }); diff --git a/src/app/core/auth/auth.effects.ts b/src/app/core/auth/auth.effects.ts index 4f1494051f..22d1bf35e7 100644 --- a/src/app/core/auth/auth.effects.ts +++ b/src/app/core/auth/auth.effects.ts @@ -10,7 +10,7 @@ import { } from 'rxjs'; import { catchError, filter, map, observeOn, switchMap, take, tap } from 'rxjs/operators'; // import @ngrx -import { Actions, Effect, ofType } from '@ngrx/effects'; +import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Action, select, Store } from '@ngrx/store'; // import services @@ -67,8 +67,7 @@ export class AuthEffects { * Authenticate user. * @method authenticate */ - @Effect() - public authenticate$: Observable = this.actions$.pipe( + public authenticate$: Observable = createEffect(() => this.actions$.pipe( ofType(AuthActionTypes.AUTHENTICATE), switchMap((action: AuthenticateAction) => { return this.authService.authenticate(action.payload.email, action.payload.password).pipe( @@ -77,26 +76,23 @@ export class AuthEffects { catchError((error) => observableOf(new AuthenticationErrorAction(error))) ); }) - ); + )); - @Effect() - public authenticateSuccess$: Observable = this.actions$.pipe( + public authenticateSuccess$: Observable = createEffect(() => this.actions$.pipe( ofType(AuthActionTypes.AUTHENTICATE_SUCCESS), map((action: AuthenticationSuccessAction) => new AuthenticatedAction(action.payload)) - ); + )); - @Effect() - public authenticated$: Observable = this.actions$.pipe( + public authenticated$: Observable = createEffect(() => this.actions$.pipe( ofType(AuthActionTypes.AUTHENTICATED), switchMap((action: AuthenticatedAction) => { return this.authService.authenticatedUser(action.payload).pipe( map((userHref: string) => new AuthenticatedSuccessAction((userHref !== null), action.payload, userHref)), catchError((error) => observableOf(new AuthenticatedErrorAction(error))),); }) - ); + )); - @Effect() - public authenticatedSuccess$: Observable = this.actions$.pipe( + public authenticatedSuccess$: Observable = createEffect(() => this.actions$.pipe( ofType(AuthActionTypes.AUTHENTICATED_SUCCESS), tap((action: AuthenticatedSuccessAction) => this.authService.storeToken(action.payload.authToken)), switchMap((action: AuthenticatedSuccessAction) => this.authService.getRedirectUrl().pipe( @@ -110,26 +106,23 @@ export class AuthEffects { return new RetrieveAuthenticatedEpersonAction(action.payload.userHref); } }) - ); + )); - @Effect({ dispatch: false }) - public redirectAfterLoginSuccess$: Observable = this.actions$.pipe( + public redirectAfterLoginSuccess$: Observable = createEffect(() => this.actions$.pipe( ofType(AuthActionTypes.REDIRECT_AFTER_LOGIN_SUCCESS), tap((action: RedirectAfterLoginSuccessAction) => { this.authService.clearRedirectUrl(); this.authService.navigateToRedirectUrl(action.payload); }) - ); + ), { dispatch: false }); // It means "reacts to this action but don't send another" - @Effect({ dispatch: false }) - public authenticatedError$: Observable = this.actions$.pipe( + public authenticatedError$: Observable = createEffect(() => this.actions$.pipe( ofType(AuthActionTypes.AUTHENTICATED_ERROR), tap((action: LogOutSuccessAction) => this.authService.removeToken()) - ); + ), { dispatch: false }); - @Effect() - public retrieveAuthenticatedEperson$: Observable = this.actions$.pipe( + public retrieveAuthenticatedEperson$: Observable = createEffect(() => this.actions$.pipe( ofType(AuthActionTypes.RETRIEVE_AUTHENTICATED_EPERSON), switchMap((action: RetrieveAuthenticatedEpersonAction) => { const impersonatedUserID = this.authService.getImpersonateID(); @@ -143,20 +136,18 @@ export class AuthEffects { map((user: EPerson) => new RetrieveAuthenticatedEpersonSuccessAction(user.id)), catchError((error) => observableOf(new RetrieveAuthenticatedEpersonErrorAction(error)))); }) - ); + )); - @Effect() - public checkToken$: Observable = this.actions$.pipe(ofType(AuthActionTypes.CHECK_AUTHENTICATION_TOKEN), + public checkToken$: Observable = createEffect(() => this.actions$.pipe(ofType(AuthActionTypes.CHECK_AUTHENTICATION_TOKEN), switchMap(() => { return this.authService.hasValidAuthenticationToken().pipe( map((token: AuthTokenInfo) => new AuthenticatedAction(token)), catchError((error) => observableOf(new CheckAuthenticationTokenCookieAction())) ); }) - ); + )); - @Effect() - public checkTokenCookie$: Observable = this.actions$.pipe( + public checkTokenCookie$: Observable = createEffect(() => this.actions$.pipe( ofType(AuthActionTypes.CHECK_AUTHENTICATION_TOKEN_COOKIE), switchMap(() => { return this.authService.checkAuthenticationCookie().pipe( @@ -171,10 +162,9 @@ export class AuthEffects { catchError((error) => observableOf(new AuthenticatedErrorAction(error))) ); }) - ); + )); - @Effect() - public retrieveToken$: Observable = this.actions$.pipe( + public retrieveToken$: Observable = createEffect(() => this.actions$.pipe( ofType(AuthActionTypes.RETRIEVE_TOKEN), switchMap((action: AuthenticateAction) => { return this.authService.refreshAuthenticationToken(null).pipe( @@ -183,55 +173,51 @@ export class AuthEffects { catchError((error) => observableOf(new AuthenticationErrorAction(error))) ); }) - ); + )); - @Effect() - public refreshToken$: Observable = this.actions$.pipe(ofType(AuthActionTypes.REFRESH_TOKEN), + public refreshToken$: Observable = createEffect(() => this.actions$.pipe(ofType(AuthActionTypes.REFRESH_TOKEN), switchMap((action: RefreshTokenAction) => { return this.authService.refreshAuthenticationToken(action.payload).pipe( map((token: AuthTokenInfo) => new RefreshTokenSuccessAction(token)), catchError((error) => observableOf(new RefreshTokenErrorAction())) ); }) - ); + )); // It means "reacts to this action but don't send another" - @Effect({ dispatch: false }) - public refreshTokenSuccess$: Observable = this.actions$.pipe( + public refreshTokenSuccess$: Observable = createEffect(() => this.actions$.pipe( ofType(AuthActionTypes.REFRESH_TOKEN_SUCCESS), tap((action: RefreshTokenSuccessAction) => this.authService.replaceToken(action.payload)) - ); + ), { dispatch: false }); /** * When the store is rehydrated in the browser, * clear a possible invalid token or authentication errors */ - @Effect({ dispatch: false }) - public clearInvalidTokenOnRehydrate$: Observable = this.actions$.pipe( + public clearInvalidTokenOnRehydrate$: Observable = createEffect(() => this.actions$.pipe( ofType(StoreActionTypes.REHYDRATE), switchMap(() => { const isLoaded$ = this.store.pipe(select(isAuthenticatedLoaded)); const authenticated$ = this.store.pipe(select(isAuthenticated)); - return observableCombineLatest(isLoaded$, authenticated$).pipe( + return observableCombineLatest([isLoaded$, authenticated$]).pipe( take(1), filter(([loaded, authenticated]) => loaded && !authenticated), tap(() => this.authService.removeToken()), tap(() => this.authService.resetAuthenticationError()) ); - })); + })), { dispatch: false }); /** * When the store is rehydrated in the browser, invalidate all cache hits regarding the * authorizations endpoint, to be sure to have consistent responses after a login with external idp * */ - @Effect({ dispatch: false }) invalidateAuthorizationsRequestCache$ = this.actions$ + invalidateAuthorizationsRequestCache$ = createEffect(() => this.actions$ .pipe(ofType(StoreActionTypes.REHYDRATE), tap(() => this.authorizationsService.invalidateAuthorizationsRequestCache()) - ); + ), { dispatch: false }); - @Effect() - public logOut$: Observable = this.actions$ + public logOut$: Observable = createEffect(() => this.actions$ .pipe( ofType(AuthActionTypes.LOG_OUT), switchMap(() => { @@ -241,26 +227,23 @@ export class AuthEffects { catchError((error) => observableOf(new LogOutErrorAction(error))) ); }) - ); + )); - @Effect({ dispatch: false }) - public logOutSuccess$: Observable = this.actions$ + public logOutSuccess$: Observable = createEffect(() => this.actions$ .pipe(ofType(AuthActionTypes.LOG_OUT_SUCCESS), tap(() => this.authService.removeToken()), tap(() => this.authService.clearRedirectUrl()), tap(() => this.authService.refreshAfterLogout()) - ); + ), { dispatch: false }); - @Effect({ dispatch: false }) - public redirectToLoginTokenExpired$: Observable = this.actions$ + public redirectToLoginTokenExpired$: Observable = createEffect(() => this.actions$ .pipe( ofType(AuthActionTypes.REDIRECT_TOKEN_EXPIRED), tap(() => this.authService.removeToken()), tap(() => this.authService.redirectToLoginWhenTokenExpired()) - ); + ), { dispatch: false }); - @Effect() - public retrieveMethods$: Observable = this.actions$ + public retrieveMethods$: Observable = createEffect(() => this.actions$ .pipe( ofType(AuthActionTypes.RETRIEVE_AUTH_METHODS), switchMap((action: RetrieveAuthMethodsAction) => { @@ -270,7 +253,7 @@ export class AuthEffects { catchError((error) => observableOf(new RetrieveAuthMethodsErrorAction())) ); }) - ); + )); /** * For any action that is not in {@link IDLE_TIMER_IGNORE_TYPES} that comes in => Start the idleness timer @@ -278,8 +261,7 @@ export class AuthEffects { * => Return the action to set the user as idle ({@link SetUserAsIdleAction}) * @method trackIdleness */ - @Effect() - public trackIdleness$: Observable = this.actions$.pipe( + public trackIdleness$: Observable = createEffect(() => this.actions$.pipe( filter((action: Action) => !IDLE_TIMER_IGNORE_TYPES.includes(action.type)), // Using switchMap the effect will stop subscribing to the previous timer if a new action comes // in, and start a new timer @@ -290,7 +272,7 @@ export class AuthEffects { // Re-enter the zone to dispatch the action observeOn(new EnterZoneScheduler(this.zone, queueScheduler)), map(() => new SetUserAsIdleAction()), - ); + )); /** * @constructor diff --git a/src/app/core/auth/auth.interceptor.spec.ts b/src/app/core/auth/auth.interceptor.spec.ts index 029deb5326..04bbc4acaf 100644 --- a/src/app/core/auth/auth.interceptor.spec.ts +++ b/src/app/core/auth/auth.interceptor.spec.ts @@ -20,9 +20,9 @@ describe(`AuthInterceptor`, () => { const authServiceStub = new AuthServiceStub(); const store: Store = jasmine.createSpyObj('store', { - /* tslint:disable:no-empty */ + /* eslint-disable no-empty,@typescript-eslint/no-empty-function */ dispatch: {}, - /* tslint:enable:no-empty */ + /* eslint-enable no-empty, @typescript-eslint/no-empty-function */ select: observableOf(true) }); diff --git a/src/app/core/auth/auth.interceptor.ts b/src/app/core/auth/auth.interceptor.ts index a49030110b..e55d0c0ff9 100644 --- a/src/app/core/auth/auth.interceptor.ts +++ b/src/app/core/auth/auth.interceptor.ts @@ -144,7 +144,7 @@ export class AuthInterceptor implements HttpInterceptor { const regex = /(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g; const realms = completeWWWauthenticateHeader.match(regex); - // tslint:disable-next-line:forin + // eslint-disable-next-line guard-for-in for (const j in realms) { const splittedRealm = realms[j].split(', '); diff --git a/src/app/core/auth/auth.service.ts b/src/app/core/auth/auth.service.ts index 5738948ebd..f89fa21681 100644 --- a/src/app/core/auth/auth.service.ts +++ b/src/app/core/auth/auth.service.ts @@ -112,7 +112,7 @@ export class AuthService { if (hasValue(rd.payload) && rd.payload.authenticated) { return rd.payload; } else { - throw(new Error('Invalid email or password')); + throw (new Error('Invalid email or password')); } })); @@ -166,7 +166,7 @@ export class AuthService { if (hasValue(status) && status.authenticated) { return status._links.eperson.href; } else { - throw(new Error('Not authenticated')); + throw (new Error('Not authenticated')); } })); } @@ -249,7 +249,7 @@ export class AuthService { if (hasValue(status) && status.authenticated) { return status.token; } else { - throw(new Error('Not authenticated')); + throw (new Error('Not authenticated')); } })); } @@ -288,7 +288,7 @@ export class AuthService { if (hasValue(status) && !status.authenticated) { return true; } else { - throw(new Error('auth.errors.invalid-user')); + throw (new Error('auth.errors.invalid-user')); } })); } diff --git a/src/app/core/auth/authenticated.guard.ts b/src/app/core/auth/authenticated.guard.ts index 0b9eeec509..1ab1d2e0a5 100644 --- a/src/app/core/auth/authenticated.guard.ts +++ b/src/app/core/auth/authenticated.guard.ts @@ -11,9 +11,9 @@ import { Observable } from 'rxjs'; import { map, find, switchMap } from 'rxjs/operators'; import { select, Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { isAuthenticated, isAuthenticationLoading } from './selectors'; import { AuthService, LOGIN_ROUTE } from './auth.service'; +import { CoreState } from '../core-state.model'; /** * Prevent unauthorized activating and loading of routes diff --git a/src/app/core/auth/models/auth-status.model.ts b/src/app/core/auth/models/auth-status.model.ts index 197c025407..fbe6ed6476 100644 --- a/src/app/core/auth/models/auth-status.model.ts +++ b/src/app/core/auth/models/auth-status.model.ts @@ -2,7 +2,6 @@ import { autoserialize, deserialize, deserializeAs } from 'cerialize'; import { Observable } from 'rxjs'; import { link, typedObject } from '../../cache/builders/build-decorators'; import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer'; -import { CacheableObject } from '../../cache/object-cache.reducer'; import { RemoteData } from '../../data/remote-data'; import { EPerson } from '../../eperson/models/eperson.model'; import { EPERSON } from '../../eperson/models/eperson.resource-type'; @@ -13,6 +12,7 @@ import { AuthError } from './auth-error.model'; import { AUTH_STATUS } from './auth-status.resource-type'; import { AuthTokenInfo } from './auth-token-info.model'; import { AuthMethod } from './auth.method'; +import { CacheableObject } from '../../cache/cacheable-object.model'; /** * Object that represents the authenticated status of a user diff --git a/src/app/core/auth/models/short-lived-token.model.ts b/src/app/core/auth/models/short-lived-token.model.ts index 118c724328..3786bd8e6a 100644 --- a/src/app/core/auth/models/short-lived-token.model.ts +++ b/src/app/core/auth/models/short-lived-token.model.ts @@ -1,10 +1,10 @@ -import { CacheableObject } from '../../cache/object-cache.reducer'; import { typedObject } from '../../cache/builders/build-decorators'; import { excludeFromEquals } from '../../utilities/equals.decorators'; import { autoserialize, autoserializeAs, deserialize } from 'cerialize'; import { ResourceType } from '../../shared/resource-type'; import { SHORT_LIVED_TOKEN } from './short-lived-token.resource-type'; import { HALLink } from '../../shared/hal-link.model'; +import { CacheableObject } from '../../cache/cacheable-object.model'; /** * A short-lived token that can be used to authenticate a rest request diff --git a/src/app/core/auth/selectors.ts b/src/app/core/auth/selectors.ts index 9ee9f7eb2e..1d002b3908 100644 --- a/src/app/core/auth/selectors.ts +++ b/src/app/core/auth/selectors.ts @@ -8,6 +8,8 @@ import { createSelector } from '@ngrx/store'; */ import { AuthState } from './auth.reducer'; import { AppState } from '../../app.reducer'; +import { CoreState } from '../core-state.model'; +import { coreSelector } from '../core.selectors'; /** * Returns the user state. @@ -15,7 +17,7 @@ import { AppState } from '../../app.reducer'; * @param {AppState} state Top level state. * @return {AuthState} */ -export const getAuthState = (state: any) => state.core.auth; +export const getAuthState = createSelector(coreSelector, (state: CoreState) => state.auth); /** * Returns true if the user is authenticated. diff --git a/src/app/core/auth/server-auth.service.ts b/src/app/core/auth/server-auth.service.ts index ea5a3b41f2..fc8ab18bfb 100644 --- a/src/app/core/auth/server-auth.service.ts +++ b/src/app/core/auth/server-auth.service.ts @@ -36,7 +36,7 @@ export class ServerAuthService extends AuthService { if (hasValue(status) && status.authenticated) { return status._links.eperson.href; } else { - throw(new Error('Not authenticated')); + throw (new Error('Not authenticated')); } })); } diff --git a/src/app/core/auth/token-response-parsing.service.ts b/src/app/core/auth/token-response-parsing.service.ts index d39b3cc33d..1ba7a16b14 100644 --- a/src/app/core/auth/token-response-parsing.service.ts +++ b/src/app/core/auth/token-response-parsing.service.ts @@ -1,9 +1,9 @@ import { ResponseParsingService } from '../data/parsing.service'; -import { RestRequest } from '../data/request.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { RestResponse, TokenResponse } from '../cache/response.models'; import { isNotEmpty } from '../../shared/empty.util'; import { Injectable } from '@angular/core'; +import { RestRequest } from '../data/rest-request.model'; @Injectable() /** diff --git a/src/app/core/browse/browse-definition-data.service.spec.ts b/src/app/core/browse/browse-definition-data.service.spec.ts index d6770f80c0..5b57ee96c1 100644 --- a/src/app/core/browse/browse-definition-data.service.spec.ts +++ b/src/app/core/browse/browse-definition-data.service.spec.ts @@ -1,7 +1,7 @@ import { BrowseDefinitionDataService } from './browse-definition-data.service'; -import { FindListOptions } from '../data/request.models'; import { followLink } from '../../shared/utils/follow-link-config.model'; import { EMPTY } from 'rxjs'; +import { FindListOptions } from '../data/find-list-options.model'; describe(`BrowseDefinitionDataService`, () => { let service: BrowseDefinitionDataService; diff --git a/src/app/core/browse/browse-definition-data.service.ts b/src/app/core/browse/browse-definition-data.service.ts index dd66d8fa53..6a27bb3f7a 100644 --- a/src/app/core/browse/browse-definition-data.service.ts +++ b/src/app/core/browse/browse-definition-data.service.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ import { Injectable } from '@angular/core'; import { dataService } from '../cache/builders/build-decorators'; import { BROWSE_DEFINITION } from '../shared/browse-definition.resource-type'; @@ -6,7 +7,6 @@ import { BrowseDefinition } from '../shared/browse-definition.model'; import { RequestService } from '../data/request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; @@ -15,10 +15,10 @@ import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { Observable } from 'rxjs'; import { RemoteData } from '../data/remote-data'; -import { FindListOptions } from '../data/request.models'; import { PaginatedList } from '../data/paginated-list.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from '../data/find-list-options.model'; -/* tslint:disable:max-classes-per-file */ class DataServiceImpl extends DataService { protected linkPath = 'browses'; @@ -123,4 +123,3 @@ export class BrowseDefinitionDataService { } } -/* tslint:enable:max-classes-per-file */ diff --git a/src/app/core/browse/browse.service.spec.ts b/src/app/core/browse/browse.service.spec.ts index ac68fadb31..db802dcbdd 100644 --- a/src/app/core/browse/browse.service.spec.ts +++ b/src/app/core/browse/browse.service.spec.ts @@ -5,7 +5,6 @@ import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-bu import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; -import { RequestEntry } from '../data/request.reducer'; import { RequestService } from '../data/request.service'; import { BrowseDefinition } from '../shared/browse-definition.model'; import { BrowseEntrySearchOptions } from './browse-entry-search-options.model'; @@ -13,6 +12,7 @@ import { BrowseService } from './browse.service'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { createPaginatedList, getFirstUsedArgumentOfSpyMethod } from '../../shared/testing/utils.test'; import { getMockHrefOnlyDataService } from '../../shared/mocks/href-only-data.service.mock'; +import { RequestEntry } from '../data/request-entry.model'; describe('BrowseService', () => { let scheduler: TestScheduler; diff --git a/src/app/core/cache/builders/build-decorators.spec.ts b/src/app/core/cache/builders/build-decorators.spec.ts index 0c6074630b..064a2b3f83 100644 --- a/src/app/core/cache/builders/build-decorators.spec.ts +++ b/src/app/core/cache/builders/build-decorators.spec.ts @@ -1,9 +1,9 @@ +/* eslint-disable max-classes-per-file */ import { HALLink } from '../../shared/hal-link.model'; import { HALResource } from '../../shared/hal-resource.model'; import { ResourceType } from '../../shared/resource-type'; import { dataService, getDataServiceFor, getLinkDefinition, link, } from './build-decorators'; -/* tslint:disable:max-classes-per-file */ class TestService { } @@ -80,4 +80,3 @@ describe('build decorators', () => { }); }); }); -/* tslint:enable:max-classes-per-file */ diff --git a/src/app/core/cache/builders/build-decorators.ts b/src/app/core/cache/builders/build-decorators.ts index b561ababde..193eeb57e8 100644 --- a/src/app/core/cache/builders/build-decorators.ts +++ b/src/app/core/cache/builders/build-decorators.ts @@ -4,11 +4,11 @@ import { GenericConstructor } from '../../shared/generic-constructor'; import { HALResource } from '../../shared/hal-resource.model'; import { ResourceType } from '../../shared/resource-type'; import { - CacheableObject, - TypedObject, getResourceTypeValueFor } from '../object-cache.reducer'; import { InjectionToken } from '@angular/core'; +import { CacheableObject } from '../cacheable-object.model'; +import { TypedObject } from '../typed-object.model'; export const DATA_SERVICE_FACTORY = new InjectionToken<(resourceType: ResourceType) => GenericConstructor>('getDataServiceFor', { providedIn: 'root', diff --git a/src/app/core/cache/builders/link.service.spec.ts b/src/app/core/cache/builders/link.service.spec.ts index f567c39314..5e71a45053 100644 --- a/src/app/core/cache/builders/link.service.spec.ts +++ b/src/app/core/cache/builders/link.service.spec.ts @@ -1,18 +1,18 @@ +/* eslint-disable max-classes-per-file */ import { Injectable } from '@angular/core'; import { TestBed } from '@angular/core/testing'; import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; -import { FindListOptions } from '../../data/request.models'; import { HALLink } from '../../shared/hal-link.model'; import { HALResource } from '../../shared/hal-resource.model'; import { ResourceType } from '../../shared/resource-type'; import { LinkService } from './link.service'; import { DATA_SERVICE_FACTORY, LINK_DEFINITION_FACTORY, LINK_DEFINITION_MAP_FACTORY } from './build-decorators'; import { isEmpty } from 'rxjs/operators'; +import { FindListOptions } from '../../data/find-list-options.model'; const TEST_MODEL = new ResourceType('testmodel'); let result: any; -/* tslint:disable:max-classes-per-file */ class TestModel implements HALResource { static type = TEST_MODEL; @@ -251,4 +251,3 @@ describe('LinkService', () => { }); }); -/* tslint:enable:max-classes-per-file */ diff --git a/src/app/core/cache/builders/remote-data-build.service.spec.ts b/src/app/core/cache/builders/remote-data-build.service.spec.ts index 0cb45733a6..1d22da494f 100644 --- a/src/app/core/cache/builders/remote-data-build.service.spec.ts +++ b/src/app/core/cache/builders/remote-data-build.service.spec.ts @@ -13,10 +13,11 @@ import { RequestService } from '../../data/request.service'; import { UnCacheableObject } from '../../shared/uncacheable-object.model'; import { RemoteData } from '../../data/remote-data'; import { Observable, of as observableOf } from 'rxjs'; -import { RequestEntry, RequestEntryState } from '../../data/request.reducer'; import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; import { take } from 'rxjs/operators'; import { HALLink } from '../../shared/hal-link.model'; +import { RequestEntryState } from '../../data/request-entry-state.model'; +import { RequestEntry } from '../../data/request-entry.model'; describe('RemoteDataBuildService', () => { let service: RemoteDataBuildService; diff --git a/src/app/core/cache/builders/remote-data-build.service.ts b/src/app/core/cache/builders/remote-data-build.service.ts index 6b67549f2d..016f6b16f6 100644 --- a/src/app/core/cache/builders/remote-data-build.service.ts +++ b/src/app/core/cache/builders/remote-data-build.service.ts @@ -11,14 +11,7 @@ import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.u import { FollowLinkConfig, followLink } from '../../../shared/utils/follow-link-config.model'; import { PaginatedList } from '../../data/paginated-list.model'; import { RemoteData } from '../../data/remote-data'; -import { - RequestEntry, - ResponseState, - RequestEntryState, - hasSucceeded -} from '../../data/request.reducer'; import { RequestService } from '../../data/request.service'; -import { getRequestFromRequestHref, getRequestFromRequestUUID } from '../../shared/operators'; import { ObjectCacheService } from '../object-cache.service'; import { LinkService } from './link.service'; import { HALLink } from '../../shared/hal-link.model'; @@ -28,6 +21,10 @@ import { HALResource } from '../../shared/hal-resource.model'; import { PAGINATED_LIST } from '../../data/paginated-list.resource-type'; import { getUrlWithoutEmbedParams } from '../../index/index.selectors'; import { getResourceTypeValueFor } from '../object-cache.reducer'; +import { hasSucceeded, RequestEntryState } from '../../data/request-entry-state.model'; +import { getRequestFromRequestHref, getRequestFromRequestUUID } from '../../shared/request.operators'; +import { RequestEntry } from '../../data/request-entry.model'; +import { ResponseState } from '../../data/response-state.model'; @Injectable() export class RemoteDataBuildService { diff --git a/src/app/core/cache/cacheable-object.model.ts b/src/app/core/cache/cacheable-object.model.ts new file mode 100644 index 0000000000..b7d1609d58 --- /dev/null +++ b/src/app/core/cache/cacheable-object.model.ts @@ -0,0 +1,22 @@ +/* tslint:disable:max-classes-per-file */ +import { HALResource } from '../shared/hal-resource.model'; +import { HALLink } from '../shared/hal-link.model'; +import { TypedObject } from './typed-object.model'; + +/** + * An interface to represent objects that can be cached + * + * A cacheable object should have a self link + */ +export class CacheableObject extends TypedObject implements HALResource { + uuid?: string; + handle?: string; + _links: { + self: HALLink; + }; + // isNew: boolean; + // dirtyType: DirtyType; + // hasDirtyAttributes: boolean; + // changedAttributes: AttributeDiffh; + // save(): void; +} diff --git a/src/app/core/cache/object-cache.actions.ts b/src/app/core/cache/object-cache.actions.ts index ed509341a7..88b4730b3f 100644 --- a/src/app/core/cache/object-cache.actions.ts +++ b/src/app/core/cache/object-cache.actions.ts @@ -1,8 +1,9 @@ +/* eslint-disable max-classes-per-file */ import { Action } from '@ngrx/store'; import { type } from '../../shared/ngrx/type'; -import { CacheableObject } from './object-cache.reducer'; import { Operation } from 'fast-json-patch'; +import { CacheableObject } from './cacheable-object.model'; /** * The list of ObjectCacheAction type definitions @@ -15,7 +16,6 @@ export const ObjectCacheActionTypes = { APPLY_PATCH: type('dspace/core/cache/object/APPLY_PATCH') }; -/* tslint:disable:max-classes-per-file */ /** * An ngrx action to add an object to the cache */ @@ -126,7 +126,6 @@ export class ApplyPatchObjectCacheAction implements Action { } } -/* tslint:enable:max-classes-per-file */ /** * A type to encompass all ObjectCacheActions diff --git a/src/app/core/cache/object-cache.effects.ts b/src/app/core/cache/object-cache.effects.ts index 2bd8ad0e3c..fa2bf6f690 100644 --- a/src/app/core/cache/object-cache.effects.ts +++ b/src/app/core/cache/object-cache.effects.ts @@ -1,6 +1,6 @@ import { map } from 'rxjs/operators'; import { Injectable } from '@angular/core'; -import { Actions, Effect, ofType } from '@ngrx/effects'; +import { Actions, createEffect, ofType } from '@ngrx/effects'; import { StoreActionTypes } from '../../store.actions'; import { ResetObjectCacheTimestampsAction } from './object-cache.actions'; @@ -16,10 +16,10 @@ export class ObjectCacheEffects { * This assumes that the server cached everything a negligible * time ago, and will likely need to be revisited later */ - @Effect() fixTimestampsOnRehydrate = this.actions$ + fixTimestampsOnRehydrate = createEffect(() => this.actions$ .pipe(ofType(StoreActionTypes.REHYDRATE), map(() => new ResetObjectCacheTimestampsAction(new Date().getTime())) - ); + )); constructor(private actions$: Actions) { } diff --git a/src/app/core/cache/object-cache.reducer.spec.ts b/src/app/core/cache/object-cache.reducer.spec.ts index 077a1e67f8..61d587b6de 100644 --- a/src/app/core/cache/object-cache.reducer.spec.ts +++ b/src/app/core/cache/object-cache.reducer.spec.ts @@ -105,10 +105,10 @@ describe('objectCacheReducer', () => { const action = new AddToObjectCacheAction(objectToCache, timeCompleted, msToLive, requestUUID, altLink1); const newState = objectCacheReducer(testState, action); - /* tslint:disable:no-string-literal */ + /* eslint-disable @typescript-eslint/dot-notation */ expect(newState[selfLink1].data['foo']).toBe('baz'); expect(newState[selfLink1].data['somethingElse']).toBe(true); - /* tslint:enable:no-string-literal */ + /* eslint-enable @typescript-eslint/dot-notation */ }); it('should perform the ADD action without affecting the previous state', () => { diff --git a/src/app/core/cache/object-cache.reducer.ts b/src/app/core/cache/object-cache.reducer.ts index 8c1420704c..9001d334ce 100644 --- a/src/app/core/cache/object-cache.reducer.ts +++ b/src/app/core/cache/object-cache.reducer.ts @@ -1,18 +1,17 @@ -import { HALLink } from '../shared/hal-link.model'; -import { HALResource } from '../shared/hal-resource.model'; +/* eslint-disable max-classes-per-file */ import { + AddPatchObjectCacheAction, + AddToObjectCacheAction, + ApplyPatchObjectCacheAction, ObjectCacheAction, ObjectCacheActionTypes, - AddToObjectCacheAction, RemoveFromObjectCacheAction, - ResetObjectCacheTimestampsAction, - AddPatchObjectCacheAction, - ApplyPatchObjectCacheAction + ResetObjectCacheTimestampsAction } from './object-cache.actions'; import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { CacheEntry } from './cache-entry'; -import { ResourceType } from '../shared/resource-type'; import { applyPatch, Operation } from 'fast-json-patch'; +import { CacheableObject } from './cacheable-object.model'; /** * An interface to represent a JsonPatch @@ -29,11 +28,6 @@ export interface Patch { operations: Operation[]; } -export abstract class TypedObject { - static type: ResourceType; - type: ResourceType; -} - /** * Get the string value for an object that may be a string or a ResourceType * @@ -49,25 +43,6 @@ export const getResourceTypeValueFor = (type: any): string => { } }; -/* tslint:disable:max-classes-per-file */ -/** - * An interface to represent objects that can be cached - * - * A cacheable object should have a self link - */ -export class CacheableObject extends TypedObject implements HALResource { - uuid?: string; - handle?: string; - _links: { - self: HALLink; - }; - // isNew: boolean; - // dirtyType: DirtyType; - // hasDirtyAttributes: boolean; - // changedAttributes: AttributeDiffh; - // save(): void; -} - /** * An entry in the ObjectCache */ @@ -110,7 +85,6 @@ export class ObjectCacheEntry implements CacheEntry { alternativeLinks: string[]; } -/* tslint:enable:max-classes-per-file */ /** * The ObjectCache State diff --git a/src/app/core/cache/object-cache.service.spec.ts b/src/app/core/cache/object-cache.service.spec.ts index 6863361c34..bde6831967 100644 --- a/src/app/core/cache/object-cache.service.spec.ts +++ b/src/app/core/cache/object-cache.service.spec.ts @@ -7,7 +7,7 @@ import { Operation } from 'fast-json-patch'; import { empty, of as observableOf } from 'rxjs'; import { first } from 'rxjs/operators'; -import { coreReducers, CoreState } from '../core.reducers'; +import { coreReducers} from '../core.reducers'; import { RestRequestMethod } from '../data/rest-request-method'; import { Item } from '../shared/item.model'; import { @@ -20,10 +20,11 @@ import { Patch } from './object-cache.reducer'; import { ObjectCacheService } from './object-cache.service'; import { AddToSSBAction } from './server-sync-buffer.actions'; import { RemoveFromIndexBySubstringAction } from '../index/index.actions'; -import { IndexName } from '../index/index.reducer'; import { HALLink } from '../shared/hal-link.model'; import { storeModuleConfig } from '../../app.reducer'; import { TestColdObservable } from 'jasmine-marbles/src/test-observables'; +import { IndexName } from '../index/index-name.model'; +import { CoreState } from '../core-state.model'; describe('ObjectCacheService', () => { let service: ObjectCacheService; diff --git a/src/app/core/cache/object-cache.service.ts b/src/app/core/cache/object-cache.service.ts index 5fec462670..6d48242178 100644 --- a/src/app/core/cache/object-cache.service.ts +++ b/src/app/core/cache/object-cache.service.ts @@ -5,7 +5,7 @@ import { combineLatest as observableCombineLatest, Observable, of as observableO import { distinctUntilChanged, filter, map, mergeMap, switchMap, take } from 'rxjs/operators'; import { hasValue, isNotEmpty, isEmpty } from '../../shared/empty.util'; -import { CoreState } from '../core.reducers'; +import { CoreState } from '../core-state.model'; import { coreSelector } from '../core.selectors'; import { RestRequestMethod } from '../data/rest-request-method'; import { @@ -22,11 +22,12 @@ import { RemoveFromObjectCacheAction } from './object-cache.actions'; -import { CacheableObject, ObjectCacheEntry, ObjectCacheState } from './object-cache.reducer'; +import { ObjectCacheEntry, ObjectCacheState } from './object-cache.reducer'; import { AddToSSBAction } from './server-sync-buffer.actions'; import { RemoveFromIndexBySubstringAction } from '../index/index.actions'; -import { IndexName } from '../index/index.reducer'; import { HALLink } from '../shared/hal-link.model'; +import { CacheableObject } from './cacheable-object.model'; +import { IndexName } from '../index/index-name.model'; /** * The base selector function to select the object cache in the store diff --git a/src/app/core/cache/response.models.ts b/src/app/core/cache/response.models.ts index 3c7c272830..197bf130fb 100644 --- a/src/app/core/cache/response.models.ts +++ b/src/app/core/cache/response.models.ts @@ -1,11 +1,11 @@ -import { RequestError } from '../data/request.models'; +/* eslint-disable max-classes-per-file */ import { PageInfo } from '../shared/page-info.model'; import { ConfigObject } from '../config/models/config.model'; import { DSpaceObject } from '../shared/dspace-object.model'; import { HALLink } from '../shared/hal-link.model'; import { UnCacheableObject } from '../shared/uncacheable-object.model'; +import { RequestError } from '../data/request-error.model'; -/* tslint:disable:max-classes-per-file */ export class RestResponse { public toCache = true; public timeCompleted: number; @@ -140,4 +140,3 @@ export class FilteredDiscoveryQueryResponse extends RestResponse { super(true, statusCode, statusText); } } -/* tslint:enable:max-classes-per-file */ diff --git a/src/app/core/cache/server-sync-buffer.actions.ts b/src/app/core/cache/server-sync-buffer.actions.ts index 6095083a6c..c07c4e6adf 100644 --- a/src/app/core/cache/server-sync-buffer.actions.ts +++ b/src/app/core/cache/server-sync-buffer.actions.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ import { Action } from '@ngrx/store'; import { type } from '../../shared/ngrx/type'; @@ -12,7 +13,6 @@ export const ServerSyncBufferActionTypes = { EMPTY: type('dspace/core/cache/syncbuffer/EMPTY'), }; -/* tslint:disable:max-classes-per-file */ /** * An ngrx action to add a new cached object to the server sync buffer @@ -71,7 +71,6 @@ export class EmptySSBAction implements Action { } } -/* tslint:enable:max-classes-per-file */ /** * A type to encompass all ServerSyncBufferActions diff --git a/src/app/core/cache/server-sync-buffer.effects.spec.ts b/src/app/core/cache/server-sync-buffer.effects.spec.ts index a53c6af982..833c6b580f 100644 --- a/src/app/core/cache/server-sync-buffer.effects.spec.ts +++ b/src/app/core/cache/server-sync-buffer.effects.spec.ts @@ -83,7 +83,7 @@ describe('ServerSyncBufferEffects', () => { }); it('should return a COMMIT action in response to an ADD action', () => { - // tslint:disable-next-line:no-shadowed-variable + // eslint-disable-next-line @typescript-eslint/no-shadow testScheduler.run(({ hot, expectObservable }) => { actions = hot('a', { a: { diff --git a/src/app/core/cache/server-sync-buffer.effects.ts b/src/app/core/cache/server-sync-buffer.effects.ts index d8ed88e12c..9571d4af5b 100644 --- a/src/app/core/cache/server-sync-buffer.effects.ts +++ b/src/app/core/cache/server-sync-buffer.effects.ts @@ -1,6 +1,6 @@ import { delay, exhaustMap, map, switchMap, take } from 'rxjs/operators'; import { Injectable } from '@angular/core'; -import { Actions, Effect, ofType } from '@ngrx/effects'; +import { Actions, createEffect, ofType } from '@ngrx/effects'; import { coreSelector } from '../core.selectors'; import { AddToSSBAction, @@ -8,7 +8,6 @@ import { EmptySSBAction, ServerSyncBufferActionTypes } from './server-sync-buffer.actions'; -import { CoreState } from '../core.reducers'; import { Action, createSelector, MemoizedSelector, select, Store } from '@ngrx/store'; import { ServerSyncBufferEntry, ServerSyncBufferState } from './server-sync-buffer.reducer'; import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; @@ -22,6 +21,7 @@ import { environment } from '../../../environments/environment'; import { ObjectCacheEntry } from './object-cache.reducer'; import { Operation } from 'fast-json-patch'; import { NoOpAction } from '../../shared/ngrx/no-op.action'; +import { CoreState } from '../core-state.model'; @Injectable() export class ServerSyncBufferEffects { @@ -32,7 +32,7 @@ export class ServerSyncBufferEffects { * Then dispatch a CommitSSBAction * When the delay is running, no new AddToSSBActions are processed in this effect */ - @Effect() setTimeoutForServerSync = this.actions$ + setTimeoutForServerSync = createEffect(() => this.actions$ .pipe( ofType(ServerSyncBufferActionTypes.ADD), exhaustMap((action: AddToSSBAction) => { @@ -42,7 +42,7 @@ export class ServerSyncBufferEffects { delay(timeoutInSeconds * 1000), ); }) - ); + )); /** * When a CommitSSBAction is dispatched @@ -50,7 +50,7 @@ export class ServerSyncBufferEffects { * When the list of actions is not empty, also dispatch an EmptySSBAction * When the list is empty dispatch a NO_ACTION placeholder action */ - @Effect() commitServerSyncBuffer = this.actions$ + commitServerSyncBuffer = createEffect(() => this.actions$ .pipe( ofType(ServerSyncBufferActionTypes.COMMIT), switchMap((action: CommitSSBAction) => { @@ -86,7 +86,7 @@ export class ServerSyncBufferEffects { }) ); }) - ); + )); /** * private method to create an ApplyPatchObjectCacheAction based on a cache entry diff --git a/src/app/core/cache/typed-object.model.ts b/src/app/core/cache/typed-object.model.ts new file mode 100644 index 0000000000..02a530941a --- /dev/null +++ b/src/app/core/cache/typed-object.model.ts @@ -0,0 +1,6 @@ +import { ResourceType } from '../shared/resource-type'; + +export abstract class TypedObject { + static type: ResourceType; + type: ResourceType; +} diff --git a/src/app/core/config/config.service.spec.ts b/src/app/core/config/config.service.spec.ts index 1eca35d223..be354ddc6f 100644 --- a/src/app/core/config/config.service.spec.ts +++ b/src/app/core/config/config.service.spec.ts @@ -3,11 +3,12 @@ import { TestScheduler } from 'rxjs/testing'; import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { ConfigService } from './config.service'; import { RequestService } from '../data/request.service'; -import { FindListOptions, GetRequest } from '../data/request.models'; +import { GetRequest } from '../data/request.models'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock'; +import { FindListOptions } from '../data/find-list-options.model'; const LINK_NAME = 'test'; const BROWSE = 'search/findByCollection'; diff --git a/src/app/core/config/config.service.ts b/src/app/core/config/config.service.ts index ddf909b5b0..3bc87c8de0 100644 --- a/src/app/core/config/config.service.ts +++ b/src/app/core/config/config.service.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ import { Observable } from 'rxjs'; import { RequestService } from '../data/request.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -6,7 +7,6 @@ import { ConfigObject } from './models/config.model'; import { RemoteData } from '../data/remote-data'; import { DataService } from '../data/data.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; @@ -14,6 +14,7 @@ import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { getFirstCompletedRemoteData } from '../shared/operators'; import { map } from 'rxjs/operators'; +import { CoreState } from '../core-state.model'; class DataServiceImpl extends DataService { constructor( @@ -31,7 +32,6 @@ class DataServiceImpl extends DataService { } } -// tslint:disable-next-line:max-classes-per-file export abstract class ConfigService { /** * A private DataService instance to delegate specific methods to. diff --git a/src/app/core/config/models/config.model.ts b/src/app/core/config/models/config.model.ts index 53250ee045..170aa334ed 100644 --- a/src/app/core/config/models/config.model.ts +++ b/src/app/core/config/models/config.model.ts @@ -1,8 +1,8 @@ import { autoserialize, deserialize } from 'cerialize'; -import { CacheableObject } from '../../cache/object-cache.reducer'; import { HALLink } from '../../shared/hal-link.model'; import { ResourceType } from '../../shared/resource-type'; import { excludeFromEquals } from '../../utilities/equals.decorators'; +import { CacheableObject } from '../../cache/cacheable-object.model'; export abstract class ConfigObject implements CacheableObject { diff --git a/src/app/core/config/submission-accesses-config.service.ts b/src/app/core/config/submission-accesses-config.service.ts index de9afc66ea..7c2d2046d9 100644 --- a/src/app/core/config/submission-accesses-config.service.ts +++ b/src/app/core/config/submission-accesses-config.service.ts @@ -7,7 +7,6 @@ import { dataService } from '../cache/builders/build-decorators'; import { SUBMISSION_ACCESSES_TYPE } from './models/config-type'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; @@ -16,6 +15,7 @@ import { SubmissionAccessesModel } from './models/config-submission-accesses.mod import { RemoteData } from '../data/remote-data'; import { Observable } from 'rxjs'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { CoreState } from '../core-state.model'; /** * Provides methods to retrieve, from REST server, bitstream access conditions configurations applicable during the submission process. diff --git a/src/app/core/config/submission-forms-config.service.ts b/src/app/core/config/submission-forms-config.service.ts index a5c3f98060..1db5c2fa01 100644 --- a/src/app/core/config/submission-forms-config.service.ts +++ b/src/app/core/config/submission-forms-config.service.ts @@ -5,7 +5,6 @@ import { RequestService } from '../data/request.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; @@ -17,6 +16,7 @@ import { SubmissionFormsModel } from './models/config-submission-forms.model'; import { RemoteData } from '../data/remote-data'; import { Observable } from 'rxjs'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { CoreState } from '../core-state.model'; @Injectable() @dataService(SUBMISSION_FORMS_TYPE) diff --git a/src/app/core/config/submission-uploads-config.service.ts b/src/app/core/config/submission-uploads-config.service.ts index a9e35a3183..8ad17749bd 100644 --- a/src/app/core/config/submission-uploads-config.service.ts +++ b/src/app/core/config/submission-uploads-config.service.ts @@ -7,7 +7,6 @@ import { dataService } from '../cache/builders/build-decorators'; import { SUBMISSION_UPLOADS_TYPE } from './models/config-type'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service'; @@ -16,6 +15,7 @@ import { SubmissionUploadsModel } from './models/config-submission-uploads.model import { RemoteData } from '../data/remote-data'; import { Observable } from 'rxjs'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { CoreState } from '../core-state.model'; /** * Provides methods to retrieve, from REST server, bitstream access conditions configurations applicable during the submission process. diff --git a/src/app/core/core-state.model.ts b/src/app/core/core-state.model.ts new file mode 100644 index 0000000000..b8211fdb55 --- /dev/null +++ b/src/app/core/core-state.model.ts @@ -0,0 +1,30 @@ +import { + BitstreamFormatRegistryState +} from '../admin/admin-registries/bitstream-formats/bitstream-format.reducers'; +import { ObjectCacheState } from './cache/object-cache.reducer'; +import { ServerSyncBufferState } from './cache/server-sync-buffer.reducer'; +import { ObjectUpdatesState } from './data/object-updates/object-updates.reducer'; +import { HistoryState } from './history/history.reducer'; +import { MetaIndexState } from './index/index.reducer'; +import { AuthState } from './auth/auth.reducer'; +import { JsonPatchOperationsState } from './json-patch/json-patch-operations.reducer'; +import { MetaTagState } from './metadata/meta-tag.reducer'; +import { RouteState } from './services/route.reducer'; +import { RequestState } from './data/request-state.model'; + +/** + * The core sub-state in the NgRx store + */ +export interface CoreState { + 'bitstreamFormats': BitstreamFormatRegistryState; + 'cache/object': ObjectCacheState; + 'cache/syncbuffer': ServerSyncBufferState; + 'cache/object-updates': ObjectUpdatesState; + 'data/request': RequestState; + 'history': HistoryState; + 'index': MetaIndexState; + 'auth': AuthState; + 'json/patch': JsonPatchOperationsState; + 'metaTag': MetaTagState; + 'route': RouteState; +} diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 6293fbd109..590eebf395 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -38,7 +38,7 @@ import { SubmissionSectionModel } from './config/models/config-submission-sectio import { SubmissionUploadsModel } from './config/models/config-submission-uploads.model'; import { SubmissionFormsConfigService } from './config/submission-forms-config.service'; import { coreEffects } from './core.effects'; -import { coreReducers, CoreState } from './core.reducers'; +import { coreReducers} from './core.reducers'; import { BitstreamFormatDataService } from './data/bitstream-format-data.service'; import { CollectionDataService } from './data/collection-data.service'; import { CommunityDataService } from './data/community-data.service'; @@ -160,6 +160,7 @@ import { RootDataService } from './data/root-data.service'; import { Root } from './data/root.model'; import { SearchConfig } from './shared/search/search-filters/search-config.model'; import { SequenceService } from './shared/sequence.service'; +import { CoreState } from './core-state.model'; import { GroupDataService } from './eperson/group-data.service'; import { SubmissionAccessesModel } from './config/models/config-submission-accesses.model'; import { OpenaireSuggestionTarget } from './openaire/reciter-suggestions/models/openaire-suggestion-target.model'; diff --git a/src/app/core/core.reducers.ts b/src/app/core/core.reducers.ts index 8b3ec32b46..c0165c5384 100644 --- a/src/app/core/core.reducers.ts +++ b/src/app/core/core.reducers.ts @@ -1,33 +1,19 @@ import { ActionReducerMap, } from '@ngrx/store'; -import { objectCacheReducer, ObjectCacheState } from './cache/object-cache.reducer'; -import { indexReducer, MetaIndexState } from './index/index.reducer'; -import { requestReducer, RequestState } from './data/request.reducer'; -import { authReducer, AuthState } from './auth/auth.reducer'; -import { jsonPatchOperationsReducer, JsonPatchOperationsState } from './json-patch/json-patch-operations.reducer'; -import { serverSyncBufferReducer, ServerSyncBufferState } from './cache/server-sync-buffer.reducer'; -import { objectUpdatesReducer, ObjectUpdatesState } from './data/object-updates/object-updates.reducer'; -import { routeReducer, RouteState } from './services/route.reducer'; +import { objectCacheReducer } from './cache/object-cache.reducer'; +import { indexReducer } from './index/index.reducer'; +import { requestReducer } from './data/request.reducer'; +import { authReducer } from './auth/auth.reducer'; +import { jsonPatchOperationsReducer } from './json-patch/json-patch-operations.reducer'; +import { serverSyncBufferReducer } from './cache/server-sync-buffer.reducer'; +import { objectUpdatesReducer } from './data/object-updates/object-updates.reducer'; +import { routeReducer } from './services/route.reducer'; import { - bitstreamFormatReducer, - BitstreamFormatRegistryState + bitstreamFormatReducer } from '../admin/admin-registries/bitstream-formats/bitstream-format.reducers'; -import { historyReducer, HistoryState } from './history/history.reducer'; -import { metaTagReducer, MetaTagState } from './metadata/meta-tag.reducer'; - -export interface CoreState { - 'bitstreamFormats': BitstreamFormatRegistryState; - 'cache/object': ObjectCacheState; - 'cache/syncbuffer': ServerSyncBufferState; - 'cache/object-updates': ObjectUpdatesState; - 'data/request': RequestState; - 'history': HistoryState; - 'index': MetaIndexState; - 'auth': AuthState; - 'json/patch': JsonPatchOperationsState; - 'metaTag': MetaTagState; - 'route': RouteState; -} +import { historyReducer } from './history/history.reducer'; +import { metaTagReducer } from './metadata/meta-tag.reducer'; +import { CoreState } from './core-state.model'; export const coreReducers: ActionReducerMap = { 'bitstreamFormats': bitstreamFormatReducer, diff --git a/src/app/core/core.selectors.ts b/src/app/core/core.selectors.ts index 60365be7c2..77c7974de2 100644 --- a/src/app/core/core.selectors.ts +++ b/src/app/core/core.selectors.ts @@ -1,5 +1,5 @@ import { createFeatureSelector } from '@ngrx/store'; -import { CoreState } from './core.reducers'; +import { CoreState } from './core-state.model'; /** * Base selector to select the core state from the store diff --git a/src/app/core/data/base-response-parsing.service.spec.ts b/src/app/core/data/base-response-parsing.service.spec.ts index 94285d49d8..da9fa7a643 100644 --- a/src/app/core/data/base-response-parsing.service.spec.ts +++ b/src/app/core/data/base-response-parsing.service.spec.ts @@ -1,10 +1,11 @@ +/* eslint-disable max-classes-per-file */ import { BaseResponseParsingService } from './base-response-parsing.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CacheableObject } from '../cache/object-cache.reducer'; -import { GetRequest, RestRequest } from './request.models'; +import { GetRequest} from './request.models'; import { DSpaceObject } from '../shared/dspace-object.model'; +import { CacheableObject } from '../cache/cacheable-object.model'; +import { RestRequest } from './rest-request.model'; -/* tslint:disable:max-classes-per-file */ class TestService extends BaseResponseParsingService { toCache = true; @@ -101,4 +102,3 @@ describe('BaseResponseParsingService', () => { }); }); }); -/* tslint:enable:max-classes-per-file */ diff --git a/src/app/core/data/base-response-parsing.service.ts b/src/app/core/data/base-response-parsing.service.ts index b571b29f02..18e6623683 100644 --- a/src/app/core/data/base-response-parsing.service.ts +++ b/src/app/core/data/base-response-parsing.service.ts @@ -1,16 +1,16 @@ +/* eslint-disable max-classes-per-file */ import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { Serializer } from '../serializer'; import { PageInfo } from '../shared/page-info.model'; import { ObjectCacheService } from '../cache/object-cache.service'; import { GenericConstructor } from '../shared/generic-constructor'; import { PaginatedList, buildPaginatedList } from './paginated-list.model'; import { getClassForType } from '../cache/builders/build-decorators'; -import { RestRequest } from './request.models'; import { environment } from '../../../environments/environment'; +import { CacheableObject } from '../cache/cacheable-object.model'; +import { RestRequest } from './rest-request.model'; -/* tslint:disable:max-classes-per-file */ /** * Return true if halObj has a value for `_links.self` @@ -180,4 +180,3 @@ export abstract class BaseResponseParsingService { return statusCode >= 200 && statusCode < 300; } } -/* tslint:enable:max-classes-per-file */ diff --git a/src/app/core/data/bitstream-data.service.ts b/src/app/core/data/bitstream-data.service.ts index 23aec80ff2..16f2cc16c2 100644 --- a/src/app/core/data/bitstream-data.service.ts +++ b/src/app/core/data/bitstream-data.service.ts @@ -9,7 +9,6 @@ import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { Bitstream } from '../shared/bitstream.model'; import { BITSTREAM } from '../shared/bitstream.resource-type'; import { Bundle } from '../shared/bundle.model'; @@ -20,15 +19,17 @@ import { DataService } from './data.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; import { buildPaginatedList, PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; -import { FindListOptions, PutRequest } from './request.models'; +import { PutRequest } from './request.models'; import { RequestService } from './request.service'; import { BitstreamFormatDataService } from './bitstream-format-data.service'; import { BitstreamFormat } from '../shared/bitstream-format.model'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; -import { sendRequest } from '../shared/operators'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { PageInfo } from '../shared/page-info.model'; import { RequestParam } from '../cache/models/request-param.model'; +import { sendRequest } from '../shared/request.operators'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; /** * A service to retrieve {@link Bitstream}s from the REST API diff --git a/src/app/core/data/bitstream-format-data.service.spec.ts b/src/app/core/data/bitstream-format-data.service.spec.ts index c072803c83..c1ebf90a47 100644 --- a/src/app/core/data/bitstream-format-data.service.spec.ts +++ b/src/app/core/data/bitstream-format-data.service.spec.ts @@ -1,5 +1,4 @@ import { BitstreamFormatDataService } from './bitstream-format-data.service'; -import { RequestEntry } from './request.reducer'; import { RestResponse } from '../cache/response.models'; import { Observable, of as observableOf } from 'rxjs'; import { Action, Store } from '@ngrx/store'; @@ -17,8 +16,9 @@ import { BitstreamFormatsRegistrySelectAction } from '../../admin/admin-registries/bitstream-formats/bitstream-format.actions'; import { TestScheduler } from 'rxjs/testing'; -import { CoreState } from '../core.reducers'; import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; +import { CoreState } from '../core-state.model'; +import { RequestEntry } from './request-entry.model'; describe('BitstreamFormatDataService', () => { let service: BitstreamFormatDataService; diff --git a/src/app/core/data/bitstream-format-data.service.ts b/src/app/core/data/bitstream-format-data.service.ts index 0d0dc5eb63..1af3db8103 100644 --- a/src/app/core/data/bitstream-format-data.service.ts +++ b/src/app/core/data/bitstream-format-data.service.ts @@ -13,18 +13,18 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { coreSelector } from '../core.selectors'; import { BitstreamFormat } from '../shared/bitstream-format.model'; import { BITSTREAM_FORMAT } from '../shared/bitstream-format.resource-type'; import { Bitstream } from '../shared/bitstream.model'; import { HALEndpointService } from '../shared/hal-endpoint.service'; -import { sendRequest } from '../shared/operators'; import { DataService } from './data.service'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; import { RemoteData } from './remote-data'; import { PostRequest, PutRequest } from './request.models'; import { RequestService } from './request.service'; +import { sendRequest } from '../shared/request.operators'; +import { CoreState } from '../core-state.model'; const bitstreamFormatsStateSelector = createSelector( coreSelector, diff --git a/src/app/core/data/bundle-data.service.spec.ts b/src/app/core/data/bundle-data.service.spec.ts index ed149a624f..12eec9e33d 100644 --- a/src/app/core/data/bundle-data.service.spec.ts +++ b/src/app/core/data/bundle-data.service.spec.ts @@ -3,7 +3,6 @@ import { Store } from '@ngrx/store'; import { compare, Operation } from 'fast-json-patch'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; -import { CoreState } from '../core.reducers'; import { Item } from '../shared/item.model'; import { ChangeAnalyzer } from './change-analyzer'; import { getMockRequestService } from '../../shared/mocks/request.service.mock'; @@ -13,6 +12,7 @@ import { HALLink } from '../shared/hal-link.model'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { createPaginatedList } from '../../shared/testing/utils.test'; import { Bundle } from '../shared/bundle.model'; +import { CoreState } from '../core-state.model'; class DummyChangeAnalyzer implements ChangeAnalyzer { diff(object1: Item, object2: Item): Operation[] { diff --git a/src/app/core/data/bundle-data.service.ts b/src/app/core/data/bundle-data.service.ts index 3c885c0afd..fa5ee51b45 100644 --- a/src/app/core/data/bundle-data.service.ts +++ b/src/app/core/data/bundle-data.service.ts @@ -9,7 +9,6 @@ import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { Bundle } from '../shared/bundle.model'; import { BUNDLE } from '../shared/bundle.resource-type'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -18,11 +17,13 @@ import { DataService } from './data.service'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; -import { FindListOptions, GetRequest } from './request.models'; +import { GetRequest } from './request.models'; import { RequestService } from './request.service'; import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; import { Bitstream } from '../shared/bitstream.model'; -import { RequestEntryState } from './request.reducer'; +import { RequestEntryState } from './request-entry-state.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; /** * A service to retrieve {@link Bundle}s from the REST API diff --git a/src/app/core/data/change-analyzer.ts b/src/app/core/data/change-analyzer.ts index 8efe26314e..45fd9b7e84 100644 --- a/src/app/core/data/change-analyzer.ts +++ b/src/app/core/data/change-analyzer.ts @@ -1,6 +1,6 @@ import { Operation } from 'fast-json-patch'; -import { TypedObject } from '../cache/object-cache.reducer'; +import { TypedObject } from '../cache/typed-object.model'; /** * An interface to determine what differs between two diff --git a/src/app/core/data/collection-data.service.ts b/src/app/core/data/collection-data.service.ts index 470c036df2..c243b49d3f 100644 --- a/src/app/core/data/collection-data.service.ts +++ b/src/app/core/data/collection-data.service.ts @@ -13,7 +13,6 @@ import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RequestParam } from '../cache/models/request-param.model'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; import { Collection } from '../shared/collection.model'; @@ -27,9 +26,15 @@ import { CommunityDataService } from './community-data.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; -import { ContentSourceRequest, FindListOptions, RestRequest, UpdateContentSourceRequest } from './request.models'; +import { + ContentSourceRequest, + UpdateContentSourceRequest +} from './request.models'; import { RequestService } from './request.service'; import { BitstreamDataService } from './bitstream-data.service'; +import { RestRequest } from './rest-request.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; @Injectable() @dataService(COLLECTION) @@ -282,4 +287,12 @@ export class CollectionDataService extends ComColDataService { return this.findAllByHref(item._links.mappedCollections.href, findListOptions); } + + protected getScopeCommunityHref(options: FindListOptions) { + return this.cds.getEndpoint().pipe( + map((endpoint: string) => this.cds.getIDHref(endpoint, options.scopeID)), + filter((href: string) => isNotEmpty(href)), + take(1) + ); + } } diff --git a/src/app/core/data/comcol-data.service.spec.ts b/src/app/core/data/comcol-data.service.spec.ts index 864c583dc2..81d683b37a 100644 --- a/src/app/core/data/comcol-data.service.spec.ts +++ b/src/app/core/data/comcol-data.service.spec.ts @@ -7,13 +7,11 @@ import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { Community } from '../shared/community.model'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { ComColDataService } from './comcol-data.service'; import { CommunityDataService } from './community-data.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; -import { FindListOptions } from './request.models'; import { RequestService } from './request.service'; import { createFailedRemoteDataObject$, @@ -22,9 +20,17 @@ import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; import { BitstreamDataService } from './bitstream-data.service'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; const LINK_NAME = 'test'; +const scopeID = 'd9d30c0c-69b7-4369-8397-ca67c888974d'; + +const communitiesEndpoint = 'https://rest.api/core/communities'; + +const communityEndpoint = `${communitiesEndpoint}/${scopeID}`; + class TestService extends ComColDataService { constructor( @@ -47,9 +53,14 @@ class TestService extends ComColDataService { // implementation in subclasses for communities/collections return undefined; } + + protected getScopeCommunityHref(options: FindListOptions): Observable { + // implementation in subclasses for communities/collections + return observableOf(communityEndpoint); + } } -// tslint:disable:no-shadowed-variable +/* eslint-disable @typescript-eslint/no-shadow */ describe('ComColDataService', () => { let service: TestService; let requestService: RequestService; @@ -66,12 +77,9 @@ describe('ComColDataService', () => { const http = {} as HttpClient; const comparator = {} as any; - const scopeID = 'd9d30c0c-69b7-4369-8397-ca67c888974d'; const options = Object.assign(new FindListOptions(), { scopeID: scopeID }); - const communitiesEndpoint = 'https://rest.api/core/communities'; - const communityEndpoint = `${communitiesEndpoint}/${scopeID}`; const scopedEndpoint = `${communityEndpoint}/${LINK_NAME}`; const mockHalService = { diff --git a/src/app/core/data/comcol-data.service.ts b/src/app/core/data/comcol-data.service.ts index 12aedf8009..01cd18df0c 100644 --- a/src/app/core/data/comcol-data.service.ts +++ b/src/app/core/data/comcol-data.service.ts @@ -4,10 +4,7 @@ import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util'; import { ObjectCacheService } from '../cache/object-cache.service'; import { Community } from '../shared/community.model'; import { HALLink } from '../shared/hal-link.model'; -import { CommunityDataService } from './community-data.service'; - import { DataService } from './data.service'; -import { FindListOptions } from './request.models'; import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -19,9 +16,9 @@ import { NoContent } from '../shared/NoContent.model'; import { createFailedRemoteDataObject$ } from '../../shared/remote-data.utils'; import { URLCombiner } from '../url-combiner/url-combiner'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { FindListOptions } from './find-list-options.model'; export abstract class ComColDataService extends DataService { - protected abstract cds: CommunityDataService; protected abstract objectCache: ObjectCacheService; protected abstract halService: HALEndpointService; protected abstract bitstreamDataService: BitstreamDataService; @@ -40,11 +37,7 @@ export abstract class ComColDataService extend if (isEmpty(options.scopeID)) { return this.halService.getEndpoint(linkPath); } else { - const scopeCommunityHrefObs = this.cds.getEndpoint().pipe( - map((endpoint: string) => this.cds.getIDHref(endpoint, options.scopeID)), - filter((href: string) => isNotEmpty(href)), - take(1) - ); + const scopeCommunityHrefObs = this.getScopeCommunityHref(options); this.createAndSendGetRequest(scopeCommunityHrefObs, true); @@ -65,6 +58,8 @@ export abstract class ComColDataService extend } } + protected abstract getScopeCommunityHref(options: FindListOptions): Observable; + protected abstract getFindByParentHref(parentUUID: string): Observable; public findByParent(parentUUID: string, options: FindListOptions = {}, ...linksToFollow: FollowLinkConfig[]): Observable>> { diff --git a/src/app/core/data/community-data.service.ts b/src/app/core/data/community-data.service.ts index 8dee72e391..903d9bc79c 100644 --- a/src/app/core/data/community-data.service.ts +++ b/src/app/core/data/community-data.service.ts @@ -3,12 +3,11 @@ import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; -import { switchMap } from 'rxjs/operators'; +import { filter, map, switchMap, take } from 'rxjs/operators'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { Community } from '../shared/community.model'; import { COMMUNITY } from '../shared/community.resource-type'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -16,17 +15,18 @@ import { ComColDataService } from './comcol-data.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; -import { FindListOptions } from './request.models'; import { RequestService } from './request.service'; import { BitstreamDataService } from './bitstream-data.service'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { isNotEmpty } from '../../shared/empty.util'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; @Injectable() @dataService(COMMUNITY) export class CommunityDataService extends ComColDataService { protected linkPath = 'communities'; protected topLinkPath = 'search/top'; - protected cds = this; constructor( protected requestService: RequestService, @@ -58,4 +58,11 @@ export class CommunityDataService extends ComColDataService { ); } + protected getScopeCommunityHref(options: FindListOptions) { + return this.getEndpoint().pipe( + map((endpoint: string) => this.getIDHref(endpoint, options.scopeID)), + filter((href: string) => isNotEmpty(href)), + take(1) + ); + } } diff --git a/src/app/core/data/configuration-data.service.ts b/src/app/core/data/configuration-data.service.ts index 91d5af6ecc..c8241aa9c7 100644 --- a/src/app/core/data/configuration-data.service.ts +++ b/src/app/core/data/configuration-data.service.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; @@ -6,7 +7,6 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { DataService } from './data.service'; import { RemoteData } from './remote-data'; @@ -14,8 +14,8 @@ import { RequestService } from './request.service'; import { ConfigurationProperty } from '../shared/configuration-property.model'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; import { CONFIG_PROPERTY } from '../shared/config-property.resource-type'; +import { CoreState } from '../core-state.model'; -/* tslint:disable:max-classes-per-file */ class DataServiceImpl extends DataService { protected linkPath = 'properties'; @@ -60,4 +60,3 @@ export class ConfigurationDataService { return this.dataService.findById(name); } } -/* tslint:enable:max-classes-per-file */ diff --git a/src/app/core/data/content-source-response-parsing.service.ts b/src/app/core/data/content-source-response-parsing.service.ts index 42b8f85c42..066ccf28c9 100644 --- a/src/app/core/data/content-source-response-parsing.service.ts +++ b/src/app/core/data/content-source-response-parsing.service.ts @@ -4,8 +4,8 @@ import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; import { ContentSource } from '../shared/content-source.model'; import { MetadataConfig } from '../shared/metadata-config.model'; -import { RestRequest } from './request.models'; import { DspaceRestResponseParsingService } from './dspace-rest-response-parsing.service'; +import { RestRequest } from './rest-request.model'; @Injectable() /** diff --git a/src/app/core/data/data.service.spec.ts b/src/app/core/data/data.service.spec.ts index 5bc7423824..f680fed6a4 100644 --- a/src/app/core/data/data.service.spec.ts +++ b/src/app/core/data/data.service.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ import { HttpClient } from '@angular/common/http'; import { Store } from '@ngrx/store'; import { compare, Operation } from 'fast-json-patch'; @@ -7,14 +8,13 @@ import { followLink } from '../../shared/utils/follow-link-config.model'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { SortDirection, SortOptions } from '../cache/models/sort-options.model'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { DSpaceObject } from '../shared/dspace-object.model'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { Item } from '../shared/item.model'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { ChangeAnalyzer } from './change-analyzer'; import { DataService } from './data.service'; -import { FindListOptions, PatchRequest } from './request.models'; +import { PatchRequest } from './request.models'; import { RequestService } from './request.service'; import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; @@ -22,11 +22,12 @@ import { RequestParam } from '../cache/models/request-param.model'; import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock'; import { TestScheduler } from 'rxjs/testing'; import { RemoteData } from './remote-data'; -import { RequestEntryState } from './request.reducer'; +import { RequestEntryState } from './request-entry-state.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; const endpoint = 'https://rest.api/core'; -/* tslint:disable:max-classes-per-file */ class TestService extends DataService { constructor( @@ -833,4 +834,3 @@ describe('DataService', () => { }); }); }); -/* tslint:enable:max-classes-per-file */ diff --git a/src/app/core/data/data.service.ts b/src/app/core/data/data.service.ts index 6bad02e776..310ad704ec 100644 --- a/src/app/core/data/data.service.ts +++ b/src/app/core/data/data.service.ts @@ -21,9 +21,7 @@ import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { getClassForType } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RequestParam } from '../cache/models/request-param.model'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; import { DSpaceObject } from '../shared/dspace-object.model'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -35,7 +33,6 @@ import { RemoteData } from './remote-data'; import { CreateRequest, GetRequest, - FindListOptions, PatchRequest, PutRequest, DeleteRequest @@ -45,6 +42,9 @@ import { RestRequestMethod } from './rest-request-method'; import { UpdateDataService } from './update-data.service'; import { GenericConstructor } from '../shared/generic-constructor'; import { NoContent } from '../shared/NoContent.model'; +import { CacheableObject } from '../cache/cacheable-object.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; export abstract class DataService implements UpdateDataService { protected abstract requestService: RequestService; diff --git a/src/app/core/data/debug-response-parsing.service.ts b/src/app/core/data/debug-response-parsing.service.ts index fbc07cbb39..992a29e4b8 100644 --- a/src/app/core/data/debug-response-parsing.service.ts +++ b/src/app/core/data/debug-response-parsing.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { RestResponse } from '../cache/response.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { ResponseParsingService } from './parsing.service'; -import { RestRequest } from './request.models'; +import { RestRequest } from './rest-request.model'; @Injectable() export class DebugResponseParsingService implements ResponseParsingService { diff --git a/src/app/core/data/default-change-analyzer.service.ts b/src/app/core/data/default-change-analyzer.service.ts index 83f93b034c..70c45bbc2d 100644 --- a/src/app/core/data/default-change-analyzer.service.ts +++ b/src/app/core/data/default-change-analyzer.service.ts @@ -2,9 +2,9 @@ import { Injectable } from '@angular/core'; import { compare } from 'fast-json-patch'; import { Operation } from 'fast-json-patch'; import { getClassForType } from '../cache/builders/build-decorators'; -import { TypedObject } from '../cache/object-cache.reducer'; import { DSpaceNotNullSerializer } from '../dspace-rest/dspace-not-null.serializer'; import { ChangeAnalyzer } from './change-analyzer'; +import { TypedObject } from '../cache/typed-object.model'; /** * A class to determine what differs between two diff --git a/src/app/core/data/dso-redirect-data.service.spec.ts b/src/app/core/data/dso-redirect-data.service.spec.ts index bcd25487c2..3f3a799e45 100644 --- a/src/app/core/data/dso-redirect-data.service.spec.ts +++ b/src/app/core/data/dso-redirect-data.service.spec.ts @@ -6,13 +6,13 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { followLink } from '../../shared/utils/follow-link-config.model'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { DsoRedirectDataService } from './dso-redirect-data.service'; import { GetRequest, IdentifierType } from './request.models'; import { RequestService } from './request.service'; import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; import { Item } from '../shared/item.model'; +import { CoreState } from '../core-state.model'; describe('DsoRedirectDataService', () => { let scheduler: TestScheduler; diff --git a/src/app/core/data/dso-redirect-data.service.ts b/src/app/core/data/dso-redirect-data.service.ts index 83395d4719..6270689f03 100644 --- a/src/app/core/data/dso-redirect-data.service.ts +++ b/src/app/core/data/dso-redirect-data.service.ts @@ -9,7 +9,6 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { DataService } from './data.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; @@ -20,6 +19,7 @@ import { getFirstCompletedRemoteData } from '../shared/operators'; import { DSpaceObject } from '../shared/dspace-object.model'; import { Item } from '../shared/item.model'; import { getItemPageRoute } from '../../item-page/item-page-routing-paths'; +import { CoreState } from '../core-state.model'; @Injectable() export class DsoRedirectDataService extends DataService { diff --git a/src/app/core/data/dso-response-parsing.service.ts b/src/app/core/data/dso-response-parsing.service.ts index 7dde1f53a1..fd5a22fae9 100644 --- a/src/app/core/data/dso-response-parsing.service.ts +++ b/src/app/core/data/dso-response-parsing.service.ts @@ -3,12 +3,12 @@ import { Injectable } from '@angular/core'; import { ObjectCacheService } from '../cache/object-cache.service'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { RestResponse, DSOSuccessResponse } from '../cache/response.models'; -import { RestRequest } from './request.models'; import { ResponseParsingService } from './parsing.service'; import { BaseResponseParsingService } from './base-response-parsing.service'; import { hasNoValue, hasValue } from '../../shared/empty.util'; import { DSpaceObject } from '../shared/dspace-object.model'; +import { RestRequest } from './rest-request.model'; @Injectable() export class DSOResponseParsingService extends BaseResponseParsingService implements ResponseParsingService { diff --git a/src/app/core/data/dspace-object-data.service.ts b/src/app/core/data/dspace-object-data.service.ts index eb230e2f54..ae0d525281 100644 --- a/src/app/core/data/dspace-object-data.service.ts +++ b/src/app/core/data/dspace-object-data.service.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; @@ -7,7 +8,6 @@ import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { DSpaceObject } from '../shared/dspace-object.model'; import { DSPACE_OBJECT } from '../shared/dspace-object.resource-type'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -15,10 +15,10 @@ import { DataService } from './data.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; import { RemoteData } from './remote-data'; import { RequestService } from './request.service'; -import { FindListOptions } from './request.models'; import { PaginatedList } from './paginated-list.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; -/* tslint:disable:max-classes-per-file */ class DataServiceImpl extends DataService { protected linkPath = 'dso'; @@ -104,4 +104,3 @@ export class DSpaceObjectDataService { } } -/* tslint:enable:max-classes-per-file */ diff --git a/src/app/core/data/dspace-rest-response-parsing.service.ts b/src/app/core/data/dspace-rest-response-parsing.service.ts index 2fda0bf40a..500afc4aff 100644 --- a/src/app/core/data/dspace-rest-response-parsing.service.ts +++ b/src/app/core/data/dspace-rest-response-parsing.service.ts @@ -1,13 +1,12 @@ +/* eslint-disable max-classes-per-file */ import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { Serializer } from '../serializer'; import { PageInfo } from '../shared/page-info.model'; import { ObjectCacheService } from '../cache/object-cache.service'; import { GenericConstructor } from '../shared/generic-constructor'; import { PaginatedList, buildPaginatedList } from './paginated-list.model'; import { getClassForType } from '../cache/builders/build-decorators'; -import { RestRequest } from './request.models'; import { environment } from '../../../environments/environment'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { DSpaceObject } from '../shared/dspace-object.model'; @@ -17,8 +16,9 @@ import { ParsedResponse } from '../cache/response.models'; import { RestRequestMethod } from './rest-request-method'; import { getUrlWithoutEmbedParams, getEmbedSizeParams } from '../index/index.selectors'; import { URLCombiner } from '../url-combiner/url-combiner'; +import { CacheableObject } from '../cache/cacheable-object.model'; +import { RestRequest } from './rest-request.model'; -/* tslint:disable:max-classes-per-file */ /** * Return true if obj has a value for `_links.self` @@ -271,4 +271,3 @@ export class DspaceRestResponseParsingService implements ResponseParsingService return statusCode >= 200 && statusCode < 300; } } -/* tslint:enable:max-classes-per-file */ diff --git a/src/app/core/data/endpoint-map-response-parsing.service.ts b/src/app/core/data/endpoint-map-response-parsing.service.ts index 1a81deaea0..728714876c 100644 --- a/src/app/core/data/endpoint-map-response-parsing.service.ts +++ b/src/app/core/data/endpoint-map-response-parsing.service.ts @@ -7,12 +7,12 @@ import { import { hasValue } from '../../shared/empty.util'; import { getClassForType } from '../cache/builders/build-decorators'; import { GenericConstructor } from '../shared/generic-constructor'; -import { RestRequest } from './request.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { ParsedResponse } from '../cache/response.models'; import { DSpaceObject } from '../shared/dspace-object.model'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { environment } from '../../../environments/environment'; +import { CacheableObject } from '../cache/cacheable-object.model'; +import { RestRequest } from './rest-request.model'; /** * ResponseParsingService able to deal with HAL Endpoints that are only needed as steps diff --git a/src/app/core/data/entity-type.service.ts b/src/app/core/data/entity-type.service.ts index 40b9373107..d08e6d28e7 100644 --- a/src/app/core/data/entity-type.service.ts +++ b/src/app/core/data/entity-type.service.ts @@ -3,14 +3,12 @@ import { DataService } from './data.service'; import { RequestService } from './request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { ObjectCacheService } from '../cache/object-cache.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; import { Injectable } from '@angular/core'; -import { FindListOptions } from './request.models'; import { Observable } from 'rxjs'; import { filter, map, switchMap, take } from 'rxjs/operators'; import { RemoteData } from './remote-data'; @@ -19,6 +17,8 @@ import { PaginatedList } from './paginated-list.model'; import { ItemType } from '../shared/item-relationships/item-type.model'; import { getFirstSucceededRemoteData, getRemoteDataPayload } from '../shared/operators'; import { RelationshipTypeService } from './relationship-type.service'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; /** * Service handling all ItemType requests diff --git a/src/app/core/data/eperson-registration.service.spec.ts b/src/app/core/data/eperson-registration.service.spec.ts index c2c2353ded..dc13fff3a0 100644 --- a/src/app/core/data/eperson-registration.service.spec.ts +++ b/src/app/core/data/eperson-registration.service.spec.ts @@ -1,7 +1,6 @@ import { RequestService } from './request.service'; import { EpersonRegistrationService } from './eperson-registration.service'; import { RestResponse } from '../cache/response.models'; -import { RequestEntry } from './request.reducer'; import { cold } from 'jasmine-marbles'; import { PostRequest } from './request.models'; import { Registration } from '../shared/registration.model'; @@ -9,6 +8,7 @@ import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-servic import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; import { of as observableOf } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; +import { RequestEntry } from './request-entry.model'; describe('EpersonRegistrationService', () => { let testScheduler; @@ -100,7 +100,7 @@ describe('EpersonRegistrationService', () => { })); }); - // tslint:disable:no-shadowed-variable + /* eslint-disable @typescript-eslint/no-shadow */ it('should use cached responses and /registrations/search/findByToken?', () => { testScheduler.run(({ cold, expectObservable }) => { rdbService.buildSingle.and.returnValue(cold('a', { a: rd })); diff --git a/src/app/core/data/external-source.service.ts b/src/app/core/data/external-source.service.ts index d2fc9e6d96..6ea7e07d28 100644 --- a/src/app/core/data/external-source.service.ts +++ b/src/app/core/data/external-source.service.ts @@ -4,12 +4,10 @@ import { ExternalSource } from '../shared/external-source.model'; import { RequestService } from './request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; -import { FindListOptions } from './request.models'; import { Observable } from 'rxjs'; import { distinctUntilChanged, map, switchMap, take } from 'rxjs/operators'; import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; @@ -19,6 +17,8 @@ import { PaginatedList } from './paginated-list.model'; import { ExternalSourceEntry } from '../shared/external-source-entry.model'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; /** * A service handling all external source requests diff --git a/src/app/core/data/facet-config-response-parsing.service.ts b/src/app/core/data/facet-config-response-parsing.service.ts index 8c24bd61d9..3e4493c32b 100644 --- a/src/app/core/data/facet-config-response-parsing.service.ts +++ b/src/app/core/data/facet-config-response-parsing.service.ts @@ -3,9 +3,9 @@ import { SearchFilterConfig } from '../../shared/search/models/search-filter-con import { ParsedResponse } from '../cache/response.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; -import { RestRequest } from './request.models'; import { DspaceRestResponseParsingService } from './dspace-rest-response-parsing.service'; import { FacetConfigResponse } from '../../shared/search/models/facet-config-response.model'; +import { RestRequest } from './rest-request.model'; @Injectable() export class FacetConfigResponseParsingService extends DspaceRestResponseParsingService { diff --git a/src/app/core/data/facet-value-response-parsing.service.ts b/src/app/core/data/facet-value-response-parsing.service.ts index 12a2d4ba8c..0911ed5073 100644 --- a/src/app/core/data/facet-value-response-parsing.service.ts +++ b/src/app/core/data/facet-value-response-parsing.service.ts @@ -3,9 +3,9 @@ import { FacetValue } from '../../shared/search/models/facet-value.model'; import { ParsedResponse } from '../cache/response.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; -import { RestRequest } from './request.models'; import { FacetValues } from '../../shared/search/models/facet-values.model'; import { DspaceRestResponseParsingService } from './dspace-rest-response-parsing.service'; +import { RestRequest } from './rest-request.model'; @Injectable() export class FacetValueResponseParsingService extends DspaceRestResponseParsingService { diff --git a/src/app/core/data/feature-authorization/authorization-data.service.spec.ts b/src/app/core/data/feature-authorization/authorization-data.service.spec.ts index 01bd23d7c7..df46d3f0a1 100644 --- a/src/app/core/data/feature-authorization/authorization-data.service.spec.ts +++ b/src/app/core/data/feature-authorization/authorization-data.service.spec.ts @@ -4,7 +4,6 @@ import { AuthService } from '../../auth/auth.service'; import { Site } from '../../shared/site.model'; import { EPerson } from '../../eperson/models/eperson.model'; import { of as observableOf } from 'rxjs'; -import { FindListOptions } from '../request.models'; import { FeatureID } from './feature-id'; import { hasValue } from '../../../shared/empty.util'; import { RequestParam } from '../../cache/models/request-param.model'; @@ -12,6 +11,7 @@ import { Authorization } from '../../shared/authorization.model'; import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { createPaginatedList } from '../../../shared/testing/utils.test'; import { Feature } from '../../shared/feature.model'; +import { FindListOptions } from '../find-list-options.model'; describe('AuthorizationDataService', () => { let service: AuthorizationDataService; diff --git a/src/app/core/data/feature-authorization/authorization-data.service.ts b/src/app/core/data/feature-authorization/authorization-data.service.ts index b9812cdbb3..1f8c8b2284 100644 --- a/src/app/core/data/feature-authorization/authorization-data.service.ts +++ b/src/app/core/data/feature-authorization/authorization-data.service.ts @@ -7,7 +7,6 @@ import { Authorization } from '../../shared/authorization.model'; import { RequestService } from '../request.service'; import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../../core.reducers'; import { ObjectCacheService } from '../../cache/object-cache.service'; import { HALEndpointService } from '../../shared/hal-endpoint.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; @@ -15,7 +14,6 @@ import { HttpClient } from '@angular/common/http'; import { DSOChangeAnalyzer } from '../dso-change-analyzer.service'; import { AuthService } from '../../auth/auth.service'; import { SiteDataService } from '../site-data.service'; -import { FindListOptions } from '../request.models'; import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; import { RemoteData } from '../remote-data'; import { PaginatedList } from '../paginated-list.model'; @@ -26,6 +24,8 @@ import { AuthorizationSearchParams } from './authorization-search-params'; import { addSiteObjectUrlIfEmpty, oneAuthorizationMatchesFeature } from './authorization-utils'; import { FeatureID } from './feature-id'; import { getFirstCompletedRemoteData } from '../../shared/operators'; +import { CoreState } from '../../core-state.model'; +import { FindListOptions } from '../find-list-options.model'; /** * A service to retrieve {@link Authorization}s from the REST API diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts index 3a6cf745c9..18c7f8fb43 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts @@ -2,9 +2,9 @@ import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTr import { AuthorizationDataService } from '../authorization-data.service'; import { FeatureID } from '../feature-id'; import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; -import { returnForbiddenUrlTreeOrLoginOnAllFalse } from '../../../shared/operators'; import { switchMap } from 'rxjs/operators'; import { AuthService } from '../../../auth/auth.service'; +import { returnForbiddenUrlTreeOrLoginOnAllFalse } from '../../../shared/authorized.operators'; /** * Abstract Guard for preventing unauthorized activating and loading of routes when a user diff --git a/src/app/core/data/feature-authorization/feature-data.service.ts b/src/app/core/data/feature-authorization/feature-data.service.ts index 12be6f8452..cbe8356660 100644 --- a/src/app/core/data/feature-authorization/feature-data.service.ts +++ b/src/app/core/data/feature-authorization/feature-data.service.ts @@ -6,12 +6,12 @@ import { Feature } from '../../shared/feature.model'; import { RequestService } from '../request.service'; import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../../core.reducers'; import { ObjectCacheService } from '../../cache/object-cache.service'; import { HALEndpointService } from '../../shared/hal-endpoint.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; import { DSOChangeAnalyzer } from '../dso-change-analyzer.service'; +import { CoreState } from '../../core-state.model'; /** * A service to retrieve {@link Feature}s from the REST API diff --git a/src/app/core/data/filtered-discovery-page-response-parsing.service.ts b/src/app/core/data/filtered-discovery-page-response-parsing.service.ts index 7a2ff7962d..da7a21c488 100644 --- a/src/app/core/data/filtered-discovery-page-response-parsing.service.ts +++ b/src/app/core/data/filtered-discovery-page-response-parsing.service.ts @@ -1,10 +1,10 @@ import { Injectable } from '@angular/core'; import { ResponseParsingService } from './parsing.service'; -import { RestRequest } from './request.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { BaseResponseParsingService } from './base-response-parsing.service'; import { ObjectCacheService } from '../cache/object-cache.service'; import { FilteredDiscoveryQueryResponse, RestResponse } from '../cache/response.models'; +import { RestRequest } from './rest-request.model'; /** * A ResponseParsingService used to parse RawRestResponse coming from the REST API to a discovery query (string) @@ -16,7 +16,8 @@ export class FilteredDiscoveryPageResponseParsingService extends BaseResponsePar toCache = false; constructor( protected objectCache: ObjectCacheService, - ) { super(); + ) { + super(); } /** diff --git a/src/app/core/data/find-list-options.model.ts b/src/app/core/data/find-list-options.model.ts new file mode 100644 index 0000000000..52a527d9e0 --- /dev/null +++ b/src/app/core/data/find-list-options.model.ts @@ -0,0 +1,14 @@ +import { SortOptions } from '../cache/models/sort-options.model'; +import { RequestParam } from '../cache/models/request-param.model'; + +/** + * The options for a find list request + */ +export class FindListOptions { + scopeID?: string; + elementsPerPage?: number; + currentPage?: number; + sort?: SortOptions; + searchParams?: RequestParam[]; + startsWith?: string; +} diff --git a/src/app/core/data/href-only-data.service.spec.ts b/src/app/core/data/href-only-data.service.spec.ts index dd4be83203..64c451837d 100644 --- a/src/app/core/data/href-only-data.service.spec.ts +++ b/src/app/core/data/href-only-data.service.spec.ts @@ -1,8 +1,8 @@ import { HrefOnlyDataService } from './href-only-data.service'; import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; -import { FindListOptions } from './request.models'; import { DataService } from './data.service'; +import { FindListOptions } from './find-list-options.model'; describe(`HrefOnlyDataService`, () => { let service: HrefOnlyDataService; diff --git a/src/app/core/data/href-only-data.service.ts b/src/app/core/data/href-only-data.service.ts index b1bc14ec6f..60c225cb34 100644 --- a/src/app/core/data/href-only-data.service.ts +++ b/src/app/core/data/href-only-data.service.ts @@ -1,8 +1,8 @@ +/* eslint-disable max-classes-per-file */ import { DataService } from './data.service'; import { RequestService } from './request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; @@ -10,7 +10,6 @@ import { HttpClient } from '@angular/common/http'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; import { Injectable } from '@angular/core'; import { VOCABULARY_ENTRY } from '../submission/vocabularies/models/vocabularies.resource-type'; -import { FindListOptions } from './request.models'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { dataService } from '../cache/builders/build-decorators'; import { RemoteData } from './remote-data'; @@ -18,9 +17,10 @@ import { Observable } from 'rxjs'; import { PaginatedList } from './paginated-list.model'; import { ITEM_TYPE } from '../shared/item-relationships/item-type.resource-type'; import { LICENSE } from '../shared/license.resource-type'; -import { CacheableObject } from '../cache/object-cache.reducer'; +import { CacheableObject } from '../cache/cacheable-object.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; -/* tslint:disable:max-classes-per-file */ class DataServiceImpl extends DataService { // linkPath isn't used if we're only searching by href. protected linkPath = undefined; diff --git a/src/app/core/data/item-data.service.spec.ts b/src/app/core/data/item-data.service.spec.ts index 26a6b52cc3..cc1e3b6e20 100644 --- a/src/app/core/data/item-data.service.spec.ts +++ b/src/app/core/data/item-data.service.spec.ts @@ -8,13 +8,14 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { BrowseService } from '../browse/browse.service'; import { ObjectCacheService } from '../cache/object-cache.service'; import { RestResponse } from '../cache/response.models'; -import { CoreState } from '../core.reducers'; import { ExternalSourceEntry } from '../shared/external-source-entry.model'; import { ItemDataService } from './item-data.service'; -import { DeleteRequest, FindListOptions, PostRequest } from './request.models'; -import { RequestEntry } from './request.reducer'; +import { DeleteRequest, PostRequest } from './request.models'; import { RequestService } from './request.service'; import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock'; +import { CoreState } from '../core-state.model'; +import { RequestEntry } from './request-entry.model'; +import { FindListOptions } from './find-list-options.model'; describe('ItemDataService', () => { let scheduler: TestScheduler; diff --git a/src/app/core/data/item-data.service.ts b/src/app/core/data/item-data.service.ts index a8d380124e..cb5d7a3d57 100644 --- a/src/app/core/data/item-data.service.ts +++ b/src/app/core/data/item-data.service.ts @@ -9,21 +9,19 @@ import { BrowseService } from '../browse/browse.service'; import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; import { Collection } from '../shared/collection.model'; import { ExternalSourceEntry } from '../shared/external-source-entry.model'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { Item } from '../shared/item.model'; import { ITEM } from '../shared/item.resource-type'; -import { sendRequest } from '../shared/operators'; import { URLCombiner } from '../url-combiner/url-combiner'; import { DataService } from './data.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; -import { DeleteRequest, FindListOptions, GetRequest, PostRequest, PutRequest, RestRequest } from './request.models'; +import { DeleteRequest, GetRequest, PostRequest, PutRequest} from './request.models'; import { RequestService } from './request.service'; import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model'; import { Bundle } from '../shared/bundle.model'; @@ -34,6 +32,10 @@ import { NoContent } from '../shared/NoContent.model'; import { GenericConstructor } from '../shared/generic-constructor'; import { ResponseParsingService } from './parsing.service'; import { StatusCodeOnlyResponseParsingService } from './status-code-only-response-parsing.service'; +import { sendRequest } from '../shared/request.operators'; +import { RestRequest } from './rest-request.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; @Injectable() @dataService(ITEM) diff --git a/src/app/core/data/item-request-data.service.ts b/src/app/core/data/item-request-data.service.ts index 41ad19211a..2bab0b304f 100644 --- a/src/app/core/data/item-request-data.service.ts +++ b/src/app/core/data/item-request-data.service.ts @@ -3,7 +3,7 @@ import { Observable } from 'rxjs'; import { distinctUntilChanged, filter, find, map } from 'rxjs/operators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; -import { getFirstCompletedRemoteData, sendRequest } from '../shared/operators'; +import { getFirstCompletedRemoteData } from '../shared/operators'; import { RemoteData } from './remote-data'; import { PostRequest, PutRequest } from './request.models'; import { RequestService } from './request.service'; @@ -11,13 +11,14 @@ import { ItemRequest } from '../shared/item-request.model'; import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { DataService } from './data.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; import { RequestCopyEmail } from '../../request-copy/email-request-copy/request-copy-email.model'; import { HttpOptions } from '../dspace-rest/dspace-rest.service'; +import { CoreState } from '../core-state.model'; +import { sendRequest } from '../shared/request.operators'; /** * A service responsible for fetching/sending data from/to the REST API on the itemrequests endpoint diff --git a/src/app/core/data/item-template-data.service.spec.ts b/src/app/core/data/item-template-data.service.spec.ts index 1458527506..4b8aa362ba 100644 --- a/src/app/core/data/item-template-data.service.spec.ts +++ b/src/app/core/data/item-template-data.service.spec.ts @@ -1,12 +1,9 @@ import { ItemTemplateDataService } from './item-template-data.service'; -import { RestRequest } from './request.models'; -import { RequestEntry } from './request.reducer'; import { RestResponse } from '../cache/response.models'; import { RequestService } from './request.service'; import { Observable, of as observableOf } from 'rxjs'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { BrowseService } from '../browse/browse.service'; import { cold } from 'jasmine-marbles'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -15,6 +12,9 @@ import { HttpClient } from '@angular/common/http'; import { CollectionDataService } from './collection-data.service'; import { RestRequestMethod } from './rest-request-method'; import { Item } from '../shared/item.model'; +import { RestRequest } from './rest-request.model'; +import { CoreState } from '../core-state.model'; +import { RequestEntry } from './request-entry.model'; describe('ItemTemplateDataService', () => { let service: ItemTemplateDataService; diff --git a/src/app/core/data/item-template-data.service.ts b/src/app/core/data/item-template-data.service.ts index 19e6941385..fd9f7de031 100644 --- a/src/app/core/data/item-template-data.service.ts +++ b/src/app/core/data/item-template-data.service.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file */ import { Injectable } from '@angular/core'; import { ItemDataService } from './item-data.service'; import { UpdateDataService } from './update-data.service'; @@ -9,7 +10,6 @@ import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; import { RequestService } from './request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; @@ -23,8 +23,8 @@ import { NoContent } from '../shared/NoContent.model'; import { hasValue } from '../../shared/empty.util'; import { Operation } from 'fast-json-patch'; import { getFirstCompletedRemoteData } from '../shared/operators'; +import { CoreState } from '../core-state.model'; -/* tslint:disable:max-classes-per-file */ /** * A custom implementation of the ItemDataService, but for collection item templates * Makes sure to change the endpoint before sending out CRUD requests for the item template @@ -228,4 +228,3 @@ export class ItemTemplateDataService implements UpdateDataService { return this.dataService.getCollectionEndpoint(collectionID); } } -/* tslint:enable:max-classes-per-file */ diff --git a/src/app/core/data/metadata-field-data.service.spec.ts b/src/app/core/data/metadata-field-data.service.spec.ts index bb621f74b3..54a174e365 100644 --- a/src/app/core/data/metadata-field-data.service.spec.ts +++ b/src/app/core/data/metadata-field-data.service.spec.ts @@ -4,12 +4,12 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { of as observableOf } from 'rxjs'; import { RestResponse } from '../cache/response.models'; import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub'; -import { FindListOptions } from './request.models'; import { MetadataFieldDataService } from './metadata-field-data.service'; import { MetadataSchema } from '../metadata/metadata-schema.model'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { RequestParam } from '../cache/models/request-param.model'; +import { FindListOptions } from './find-list-options.model'; describe('MetadataFieldDataService', () => { let metadataFieldService: MetadataFieldDataService; diff --git a/src/app/core/data/metadata-field-data.service.ts b/src/app/core/data/metadata-field-data.service.ts index 3b11859361..5a78213c84 100644 --- a/src/app/core/data/metadata-field-data.service.ts +++ b/src/app/core/data/metadata-field-data.service.ts @@ -7,7 +7,6 @@ import { RemoteData } from './remote-data'; import { RequestService } from './request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../core.reducers'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { ObjectCacheService } from '../cache/object-cache.service'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; @@ -16,11 +15,12 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { METADATA_FIELD } from '../metadata/metadata-field.resource-type'; import { MetadataField } from '../metadata/metadata-field.model'; import { MetadataSchema } from '../metadata/metadata-schema.model'; -import { FindListOptions } from './request.models'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { Observable } from 'rxjs'; import { take } from 'rxjs/operators'; import { RequestParam } from '../cache/models/request-param.model'; +import { CoreState } from '../core-state.model'; +import { FindListOptions } from './find-list-options.model'; /** * A service responsible for fetching/sending data from/to the REST API on the metadatafields endpoint diff --git a/src/app/core/data/metadata-schema-data.service.ts b/src/app/core/data/metadata-schema-data.service.ts index ff1796313e..f277f6cab6 100644 --- a/src/app/core/data/metadata-schema-data.service.ts +++ b/src/app/core/data/metadata-schema-data.service.ts @@ -5,7 +5,6 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { CoreState } from '../core.reducers'; import { MetadataSchema } from '../metadata/metadata-schema.model'; import { METADATA_SCHEMA } from '../metadata/metadata-schema.resource-type'; import { HALEndpointService } from '../shared/hal-endpoint.service'; @@ -16,6 +15,7 @@ import { Observable } from 'rxjs'; import { hasValue } from '../../shared/empty.util'; import { tap } from 'rxjs/operators'; import { RemoteData } from './remote-data'; +import { CoreState } from '../core-state.model'; /** * A service responsible for fetching/sending data from/to the REST API on the metadataschemas endpoint diff --git a/src/app/core/data/mydspace-response-parsing.service.ts b/src/app/core/data/mydspace-response-parsing.service.ts index e111aca9dd..e46e319149 100644 --- a/src/app/core/data/mydspace-response-parsing.service.ts +++ b/src/app/core/data/mydspace-response-parsing.service.ts @@ -1,12 +1,12 @@ import { Injectable } from '@angular/core'; import { ParsedResponse } from '../cache/response.models'; import { DSpaceSerializer } from '../dspace-rest/dspace.serializer'; -import { RestRequest } from './request.models'; import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; import { hasValue } from '../../shared/empty.util'; import { SearchObjects } from '../../shared/search/models/search-objects.model'; import { MetadataMap, MetadataValue } from '../shared/metadata.models'; import { DspaceRestResponseParsingService } from './dspace-rest-response-parsing.service'; +import { RestRequest } from './rest-request.model'; @Injectable() export class MyDSpaceResponseParsingService extends DspaceRestResponseParsingService { diff --git a/src/app/core/data/object-updates/field-change-type.model.ts b/src/app/core/data/object-updates/field-change-type.model.ts new file mode 100644 index 0000000000..7d8e308945 --- /dev/null +++ b/src/app/core/data/object-updates/field-change-type.model.ts @@ -0,0 +1,8 @@ +/** + * Enum that represents the different types of updates that can be performed on a field in the ObjectUpdates store + */ +export enum FieldChangeType { + UPDATE = 0, + ADD = 1, + REMOVE = 2 +} diff --git a/src/app/core/data/object-updates/field-update.model.ts b/src/app/core/data/object-updates/field-update.model.ts new file mode 100644 index 0000000000..47b6782471 --- /dev/null +++ b/src/app/core/data/object-updates/field-update.model.ts @@ -0,0 +1,10 @@ +import { Identifiable } from './identifiable.model'; +import { FieldChangeType } from './field-change-type.model'; + +/** + * The state of a single field update + */ +export interface FieldUpdate { + field: Identifiable; + changeType: FieldChangeType; +} diff --git a/src/app/core/data/object-updates/field-updates.model.ts b/src/app/core/data/object-updates/field-updates.model.ts new file mode 100644 index 0000000000..eff804bd02 --- /dev/null +++ b/src/app/core/data/object-updates/field-updates.model.ts @@ -0,0 +1,8 @@ +import { FieldUpdate } from './field-update.model'; + +/** + * The states of all field updates available for a single page, mapped by uuid + */ +export interface FieldUpdates { + [uuid: string]: FieldUpdate; +} diff --git a/src/app/core/data/object-updates/identifiable.model.ts b/src/app/core/data/object-updates/identifiable.model.ts new file mode 100644 index 0000000000..7d32859338 --- /dev/null +++ b/src/app/core/data/object-updates/identifiable.model.ts @@ -0,0 +1,6 @@ +/** + * Represents every object that has a UUID + */ +export interface Identifiable { + uuid: string; +} diff --git a/src/app/core/data/object-updates/object-updates.actions.ts b/src/app/core/data/object-updates/object-updates.actions.ts index 13bbabb286..615dedbaf9 100644 --- a/src/app/core/data/object-updates/object-updates.actions.ts +++ b/src/app/core/data/object-updates/object-updates.actions.ts @@ -1,9 +1,11 @@ -import {type} from '../../../shared/ngrx/type'; -import {Action} from '@ngrx/store'; -import {Identifiable} from './object-updates.reducer'; -import {INotification} from '../../../shared/notifications/models/notification.model'; +/* eslint-disable max-classes-per-file */ +import { type } from '../../../shared/ngrx/type'; +import { Action } from '@ngrx/store'; +import { INotification } from '../../../shared/notifications/models/notification.model'; import { PatchOperationService } from './patch-operation-service/patch-operation.service'; import { GenericConstructor } from '../../shared/generic-constructor'; +import { Identifiable } from './identifiable.model'; +import { FieldChangeType } from './field-change-type.model'; /** * The list of ObjectUpdatesAction type definitions @@ -21,16 +23,6 @@ export const ObjectUpdatesActionTypes = { REMOVE_FIELD: type('dspace/core/cache/object-updates/REMOVE_FIELD') }; -/* tslint:disable:max-classes-per-file */ - -/** - * Enum that represents the different types of updates that can be performed on a field in the ObjectUpdates store - */ -export enum FieldChangeType { - UPDATE = 0, - ADD = 1, - REMOVE = 2 -} /** * An ngrx action to initialize a new page's fields in the ObjectUpdates state @@ -283,7 +275,6 @@ export class RemoveFieldUpdateAction implements Action { } } -/* tslint:enable:max-classes-per-file */ /** * A type to encompass all ObjectUpdatesActions diff --git a/src/app/core/data/object-updates/object-updates.effects.ts b/src/app/core/data/object-updates/object-updates.effects.ts index c9c3237ef5..1dfdc95f23 100644 --- a/src/app/core/data/object-updates/object-updates.effects.ts +++ b/src/app/core/data/object-updates/object-updates.effects.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { Actions, Effect, ofType } from '@ngrx/effects'; +import { Actions, createEffect, ofType } from '@ngrx/effects'; import { DiscardObjectUpdatesAction, ObjectUpdatesAction, @@ -52,7 +52,7 @@ export class ObjectUpdatesEffects { /** * Effect that makes sure all last fired ObjectUpdatesActions are stored in the map of this service, with the url as their key */ - @Effect({ dispatch: false }) mapLastActions$ = this.actions$ + mapLastActions$ = createEffect(() => this.actions$ .pipe( ofType(...Object.values(ObjectUpdatesActionTypes)), map((action: ObjectUpdatesAction) => { @@ -64,12 +64,12 @@ export class ObjectUpdatesEffects { this.actionMap$[url].next(action); } }) - ); + ), { dispatch: false }); /** * Effect that makes sure all last fired NotificationActions are stored in the notification map of this service, with the id as their key */ - @Effect({ dispatch: false }) mapLastNotificationActions$ = this.actions$ + mapLastNotificationActions$ = createEffect(() => this.actions$ .pipe( ofType(...Object.values(NotificationsActionTypes)), map((action: RemoveNotificationAction) => { @@ -80,7 +80,7 @@ export class ObjectUpdatesEffects { this.notificationActionMap$[id].next(action); } ) - ); + ), { dispatch: false }); /** * Effect that checks whether the removeAction's notification timeout ends before a user triggers another ObjectUpdatesAction @@ -88,7 +88,7 @@ export class ObjectUpdatesEffects { * When a REINSTATE action is fired during the timeout, a NO_ACTION action will be returned * When any other ObjectUpdatesAction is fired during the timeout, a RemoteObjectUpdatesAction will be returned */ - @Effect() removeAfterDiscardOrReinstateOnUndo$ = this.actions$ + removeAfterDiscardOrReinstateOnUndo$ = createEffect(() => this.actions$ .pipe( ofType(ObjectUpdatesActionTypes.DISCARD), switchMap((action: DiscardObjectUpdatesAction) => { @@ -134,7 +134,7 @@ export class ObjectUpdatesEffects { ); } ) - ); + )); constructor(private actions$: Actions, private notificationsService: NotificationsService) { diff --git a/src/app/core/data/object-updates/object-updates.reducer.spec.ts b/src/app/core/data/object-updates/object-updates.reducer.spec.ts index 4d7fce24a7..a51a1431bb 100644 --- a/src/app/core/data/object-updates/object-updates.reducer.spec.ts +++ b/src/app/core/data/object-updates/object-updates.reducer.spec.ts @@ -2,7 +2,6 @@ import * as deepFreeze from 'deep-freeze'; import { AddFieldUpdateAction, DiscardObjectUpdatesAction, - FieldChangeType, InitializeFieldsAction, ReinstateObjectUpdatesAction, RemoveAllObjectUpdatesAction, @@ -14,6 +13,7 @@ import { } from './object-updates.actions'; import { OBJECT_UPDATES_TRASH_PATH, objectUpdatesReducer } from './object-updates.reducer'; import { Relationship } from '../../shared/item-relationships/relationship.model'; +import { FieldChangeType } from './field-change-type.model'; class NullAction extends RemoveFieldUpdateAction { type = null; diff --git a/src/app/core/data/object-updates/object-updates.reducer.ts b/src/app/core/data/object-updates/object-updates.reducer.ts index 6dfb4ab584..14bacc52db 100644 --- a/src/app/core/data/object-updates/object-updates.reducer.ts +++ b/src/app/core/data/object-updates/object-updates.reducer.ts @@ -1,16 +1,15 @@ import { AddFieldUpdateAction, DiscardObjectUpdatesAction, - FieldChangeType, InitializeFieldsAction, ObjectUpdatesAction, ObjectUpdatesActionTypes, ReinstateObjectUpdatesAction, RemoveFieldUpdateAction, RemoveObjectUpdatesAction, + SelectVirtualMetadataAction, SetEditableFieldUpdateAction, SetValidFieldUpdateAction, - SelectVirtualMetadataAction, } from './object-updates.actions'; import { hasNoValue, hasValue } from '../../../shared/empty.util'; import { Relationship } from '../../shared/item-relationships/relationship.model'; @@ -18,6 +17,9 @@ import { PatchOperationService } from './patch-operation-service/patch-operation import { Item } from '../../shared/item.model'; import { RelationshipType } from '../../shared/item-relationships/relationship-type.model'; import { GenericConstructor } from '../../shared/generic-constructor'; +import { Identifiable } from './identifiable.model'; +import { FieldUpdates } from './field-updates.model'; +import { FieldChangeType } from './field-change-type.model'; /** * Path where discarded objects are saved @@ -40,28 +42,6 @@ export interface FieldStates { [uuid: string]: FieldState; } -/** - * Represents every object that has a UUID - */ -export interface Identifiable { - uuid: string; -} - -/** - * The state of a single field update - */ -export interface FieldUpdate { - field: Identifiable; - changeType: FieldChangeType; -} - -/** - * The states of all field updates available for a single page, mapped by uuid - */ -export interface FieldUpdates { - [uuid: string]: FieldUpdate; -} - /** * The states of all virtual metadata selections available for a single page, mapped by the relationship uuid */ diff --git a/src/app/core/data/object-updates/object-updates.service.spec.ts b/src/app/core/data/object-updates/object-updates.service.spec.ts index 6c0b0f99c4..9cf856f03a 100644 --- a/src/app/core/data/object-updates/object-updates.service.spec.ts +++ b/src/app/core/data/object-updates/object-updates.service.spec.ts @@ -1,9 +1,7 @@ import { Store } from '@ngrx/store'; -import { CoreState } from '../../core.reducers'; import { ObjectUpdatesService } from './object-updates.service'; import { DiscardObjectUpdatesAction, - FieldChangeType, InitializeFieldsAction, ReinstateObjectUpdatesAction, RemoveFieldUpdateAction, @@ -16,6 +14,8 @@ import { NotificationType } from '../../../shared/notifications/models/notificat import { OBJECT_UPDATES_TRASH_PATH } from './object-updates.reducer'; import { Relationship } from '../../shared/item-relationships/relationship.model'; import { Injector } from '@angular/core'; +import { FieldChangeType } from './field-change-type.model'; +import { CoreState } from '../../core-state.model'; describe('ObjectUpdatesService', () => { let service: ObjectUpdatesService; diff --git a/src/app/core/data/object-updates/object-updates.service.ts b/src/app/core/data/object-updates/object-updates.service.ts index 88c7c0e453..2fb6d47d31 100644 --- a/src/app/core/data/object-updates/object-updates.service.ts +++ b/src/app/core/data/object-updates/object-updates.service.ts @@ -1,11 +1,8 @@ import { Injectable, Injector } from '@angular/core'; import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store'; -import { CoreState } from '../../core.reducers'; import { coreSelector } from '../../core.selectors'; import { FieldState, - FieldUpdates, - Identifiable, OBJECT_UPDATES_TRASH_PATH, ObjectUpdatesEntry, ObjectUpdatesState, @@ -15,7 +12,6 @@ import { Observable } from 'rxjs'; import { AddFieldUpdateAction, DiscardObjectUpdatesAction, - FieldChangeType, InitializeFieldsAction, ReinstateObjectUpdatesAction, RemoveFieldUpdateAction, @@ -35,6 +31,10 @@ import { INotification } from '../../../shared/notifications/models/notification import { Operation } from 'fast-json-patch'; import { PatchOperationService } from './patch-operation-service/patch-operation.service'; import { GenericConstructor } from '../../shared/generic-constructor'; +import { Identifiable } from './identifiable.model'; +import { FieldUpdates } from './field-updates.model'; +import { FieldChangeType } from './field-change-type.model'; +import { CoreState } from '../../core-state.model'; function objectUpdatesStateSelector(): MemoizedSelector { return createSelector(coreSelector, (state: CoreState) => state['cache/object-updates']); diff --git a/src/app/core/data/object-updates/patch-operation-service/metadata-patch-operation.service.spec.ts b/src/app/core/data/object-updates/patch-operation-service/metadata-patch-operation.service.spec.ts index 9708266cd7..db46426b79 100644 --- a/src/app/core/data/object-updates/patch-operation-service/metadata-patch-operation.service.spec.ts +++ b/src/app/core/data/object-updates/patch-operation-service/metadata-patch-operation.service.spec.ts @@ -1,8 +1,8 @@ import { MetadataPatchOperationService } from './metadata-patch-operation.service'; -import { FieldUpdates } from '../object-updates.reducer'; import { Operation } from 'fast-json-patch'; -import { FieldChangeType } from '../object-updates.actions'; import { MetadatumViewModel } from '../../../shared/metadata.models'; +import { FieldUpdates } from '../field-updates.model'; +import { FieldChangeType } from '../field-change-type.model'; describe('MetadataPatchOperationService', () => { let service: MetadataPatchOperationService; diff --git a/src/app/core/data/object-updates/patch-operation-service/metadata-patch-operation.service.ts b/src/app/core/data/object-updates/patch-operation-service/metadata-patch-operation.service.ts index 59c981872a..33e9129a9d 100644 --- a/src/app/core/data/object-updates/patch-operation-service/metadata-patch-operation.service.ts +++ b/src/app/core/data/object-updates/patch-operation-service/metadata-patch-operation.service.ts @@ -1,14 +1,14 @@ import { PatchOperationService } from './patch-operation.service'; import { MetadatumViewModel } from '../../../shared/metadata.models'; -import { FieldUpdates } from '../object-updates.reducer'; import { Operation } from 'fast-json-patch'; -import { FieldChangeType } from '../object-updates.actions'; import { Injectable } from '@angular/core'; import { MetadataPatchOperation } from './operations/metadata/metadata-patch-operation.model'; import { hasValue } from '../../../../shared/empty.util'; import { MetadataPatchAddOperation } from './operations/metadata/metadata-patch-add-operation.model'; import { MetadataPatchRemoveOperation } from './operations/metadata/metadata-patch-remove-operation.model'; import { MetadataPatchReplaceOperation } from './operations/metadata/metadata-patch-replace-operation.model'; +import { FieldUpdates } from '../field-updates.model'; +import { FieldChangeType } from '../field-change-type.model'; /** * Service transforming {@link FieldUpdates} into {@link Operation}s for metadata values diff --git a/src/app/core/data/object-updates/patch-operation-service/patch-operation.service.ts b/src/app/core/data/object-updates/patch-operation-service/patch-operation.service.ts index 7c67f9a2e5..171c1d2a54 100644 --- a/src/app/core/data/object-updates/patch-operation-service/patch-operation.service.ts +++ b/src/app/core/data/object-updates/patch-operation-service/patch-operation.service.ts @@ -1,5 +1,5 @@ -import { FieldUpdates } from '../object-updates.reducer'; import { Operation } from 'fast-json-patch'; +import { FieldUpdates } from '../field-updates.model'; /** * Interface for a service dealing with the transformations of patch operations from the object-updates store diff --git a/src/app/core/data/paginated-list.model.ts b/src/app/core/data/paginated-list.model.ts index e85a91f791..415bfe234e 100644 --- a/src/app/core/data/paginated-list.model.ts +++ b/src/app/core/data/paginated-list.model.ts @@ -3,11 +3,11 @@ import { hasValue, isEmpty, hasNoValue, isUndefined } from '../../shared/empty.u import { HALResource } from '../shared/hal-resource.model'; import { HALLink } from '../shared/hal-link.model'; import { typedObject } from '../cache/builders/build-decorators'; -import { CacheableObject } from '../cache/object-cache.reducer'; import { PAGINATED_LIST } from './paginated-list.resource-type'; import { ResourceType } from '../shared/resource-type'; import { excludeFromEquals } from '../utilities/equals.decorators'; import { autoserialize, deserialize } from 'cerialize'; +import { CacheableObject } from '../cache/cacheable-object.model'; /** * Factory function for a paginated list diff --git a/src/app/core/data/parsing.service.ts b/src/app/core/data/parsing.service.ts index bebbd63fd7..fbebe75b2b 100644 --- a/src/app/core/data/parsing.service.ts +++ b/src/app/core/data/parsing.service.ts @@ -1,6 +1,6 @@ import { RawRestResponse } from '../dspace-rest/raw-rest-response.model'; -import { RestRequest } from './request.models'; import { ParsedResponse } from '../cache/response.models'; +import { RestRequest } from './rest-request.model'; export interface ResponseParsingService { parse(request: RestRequest, data: RawRestResponse): ParsedResponse; diff --git a/src/app/core/data/processes/process-data.service.ts b/src/app/core/data/processes/process-data.service.ts index cadcdb3bfe..81b4cbd503 100644 --- a/src/app/core/data/processes/process-data.service.ts +++ b/src/app/core/data/processes/process-data.service.ts @@ -3,7 +3,6 @@ import { DataService } from '../data.service'; import { RequestService } from '../request.service'; import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../../core.reducers'; import { ObjectCacheService } from '../../cache/object-cache.service'; import { HALEndpointService } from '../../shared/hal-endpoint.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; @@ -18,6 +17,7 @@ import { PaginatedList } from '../paginated-list.model'; import { Bitstream } from '../../shared/bitstream.model'; import { RemoteData } from '../remote-data'; import { BitstreamDataService } from '../bitstream-data.service'; +import { CoreState } from '../../core-state.model'; @Injectable() @dataService(PROCESS) diff --git a/src/app/core/data/processes/script-data.service.ts b/src/app/core/data/processes/script-data.service.ts index 69b4270173..75a66c822a 100644 --- a/src/app/core/data/processes/script-data.service.ts +++ b/src/app/core/data/processes/script-data.service.ts @@ -2,7 +2,6 @@ import { Injectable } from '@angular/core'; import { DataService } from '../data.service'; import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; -import { CoreState } from '../../core.reducers'; import { ObjectCacheService } from '../../cache/object-cache.service'; import { HALEndpointService } from '../../shared/hal-endpoint.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; @@ -13,12 +12,16 @@ import { ProcessParameter } from '../../../process-page/processes/process-parame import { map, take } from 'rxjs/operators'; import { URLCombiner } from '../../url-combiner/url-combiner'; import { RemoteData } from '../remote-data'; -import { MultipartPostRequest, RestRequest } from '../request.models'; +import { MultipartPostRequest} from '../request.models'; import { RequestService } from '../request.service'; 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'; export const METADATA_IMPORT_SCRIPT_NAME = 'metadata-import'; export const METADATA_EXPORT_SCRIPT_NAME = 'metadata-export'; @@ -62,4 +65,16 @@ export class ScriptDataService extends DataService