mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
Merge remote-tracking branch 'origin/main' into CST-5738
This commit is contained in:
@@ -2,10 +2,16 @@
|
|||||||
# For additional information regarding the format and rule options, please see:
|
# For additional information regarding the format and rule options, please see:
|
||||||
# https://github.com/browserslist/browserslist#queries
|
# 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:
|
# You can see what browsers were selected by your queries by running:
|
||||||
# npx browserslist
|
# npx browserslist
|
||||||
|
|
||||||
> 0.5%
|
last 1 Chrome version
|
||||||
last 2 versions
|
last 1 Firefox version
|
||||||
|
last 2 Edge major versions
|
||||||
|
last 2 Safari major versions
|
||||||
|
last 2 iOS major versions
|
||||||
Firefox ESR
|
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.
|
||||||
|
222
.eslintrc.json
Normal file
222
.eslintrc.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
13
.github/workflows/build.yml
vendored
13
.github/workflows/build.yml
vendored
@@ -70,7 +70,10 @@ jobs:
|
|||||||
run: yarn install --frozen-lockfile
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
- name: Run lint
|
- 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
|
- name: Run build
|
||||||
run: yarn run build:prod
|
run: yarn run build:prod
|
||||||
@@ -128,6 +131,14 @@ jobs:
|
|||||||
name: e2e-test-screenshots
|
name: e2e-test-screenshots
|
||||||
path: cypress/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)
|
# Start up the app with SSR enabled (run in background)
|
||||||
- name: Start app in SSR (server-side rendering) mode
|
- name: Start app in SSR (server-side rendering) mode
|
||||||
run: |
|
run: |
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
/.angular/cache
|
||||||
/__build__
|
/__build__
|
||||||
/__server_build__
|
/__server_build__
|
||||||
/node_modules
|
/node_modules
|
||||||
|
@@ -9,4 +9,9 @@ EXPOSE 4000
|
|||||||
# We run yarn install with an increased network timeout (5min) to avoid "ESOCKETTIMEDOUT" errors from hub.docker.com
|
# 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
|
# See, for example https://github.com/yarnpkg/yarn/issues/5540
|
||||||
RUN yarn install --network-timeout 300000
|
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
|
||||||
|
50
angular.json
50
angular.json
@@ -17,7 +17,6 @@
|
|||||||
"build": {
|
"build": {
|
||||||
"builder": "@angular-builders/custom-webpack:browser",
|
"builder": "@angular-builders/custom-webpack:browser",
|
||||||
"options": {
|
"options": {
|
||||||
"extractCss": true,
|
|
||||||
"preserveSymlinks": true,
|
"preserveSymlinks": true,
|
||||||
"customWebpackConfig": {
|
"customWebpackConfig": {
|
||||||
"path": "./webpack/webpack.browser.ts",
|
"path": "./webpack/webpack.browser.ts",
|
||||||
@@ -67,16 +66,27 @@
|
|||||||
"scripts": []
|
"scripts": []
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
|
"development": {
|
||||||
|
"buildOptimizer": false,
|
||||||
|
"optimization": false,
|
||||||
|
"vendorChunk": true,
|
||||||
|
"extractLicenses": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"namedChunks": true
|
||||||
|
},
|
||||||
"production": {
|
"production": {
|
||||||
"fileReplacements": [
|
"fileReplacements": [
|
||||||
{
|
{
|
||||||
"replace": "src/environments/environment.ts",
|
"replace": "src/environments/environment.ts",
|
||||||
"with": "src/environments/environment.production.ts"
|
"with": "src/environments/environment.production.ts"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"replace": "src/config/store/devtools.ts",
|
||||||
|
"with": "src/config/store/devtools.prod.ts"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"optimization": true,
|
"optimization": true,
|
||||||
"outputHashing": "all",
|
"outputHashing": "all",
|
||||||
"extractCss": true,
|
|
||||||
"namedChunks": false,
|
"namedChunks": false,
|
||||||
"aot": true,
|
"aot": true,
|
||||||
"extractLicenses": true,
|
"extractLicenses": true,
|
||||||
@@ -104,6 +114,9 @@
|
|||||||
"port": 4000
|
"port": 4000
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
|
"development": {
|
||||||
|
"browserTarget": "dspace-angular:build:development"
|
||||||
|
},
|
||||||
"production": {
|
"production": {
|
||||||
"browserTarget": "dspace-angular:build: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": {
|
"e2e": {
|
||||||
"builder": "@cypress/schematic:cypress",
|
"builder": "@cypress/schematic:cypress",
|
||||||
"options": {
|
"options": {
|
||||||
@@ -197,6 +197,10 @@
|
|||||||
"tsConfig": "tsconfig.server.json"
|
"tsConfig": "tsconfig.server.json"
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
|
"development": {
|
||||||
|
"sourceMap": true,
|
||||||
|
"optimization": false
|
||||||
|
},
|
||||||
"production": {
|
"production": {
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"optimization": true,
|
"optimization": true,
|
||||||
@@ -204,6 +208,10 @@
|
|||||||
{
|
{
|
||||||
"replace": "src/environments/environment.ts",
|
"replace": "src/environments/environment.ts",
|
||||||
"with": "src/environments/environment.production.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,
|
"watch": true,
|
||||||
"headless": false
|
"headless": false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"builder": "@angular-eslint/builder:lint",
|
||||||
|
"options": {
|
||||||
|
"lintFilePatterns": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.html"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultProject": "dspace-angular",
|
"defaultProject": "dspace-angular",
|
||||||
"cli": {
|
"cli": {
|
||||||
"analytics": false
|
"analytics": false,
|
||||||
|
"defaultCollection": "@angular-eslint/schematics"
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -148,6 +148,9 @@ languages:
|
|||||||
- code: fi
|
- code: fi
|
||||||
label: Suomi
|
label: Suomi
|
||||||
active: true
|
active: true
|
||||||
|
- code: tr
|
||||||
|
label: Türkçe
|
||||||
|
active: true
|
||||||
- code: bn
|
- code: bn
|
||||||
label: বাংলা
|
label: বাংলা
|
||||||
active: true
|
active: true
|
||||||
@@ -231,6 +234,10 @@ themes:
|
|||||||
rel: manifest
|
rel: manifest
|
||||||
href: assets/dspace/images/favicons/manifest.webmanifest
|
href: assets/dspace/images/favicons/manifest.webmanifest
|
||||||
|
|
||||||
|
# The default bundles that should always be displayed as suggestions when you upload a new bundle
|
||||||
|
bundle:
|
||||||
|
- standardBundles: [ ORIGINAL, THUMBNAIL, LICENSE ]
|
||||||
|
|
||||||
# Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with 'image' or 'video').
|
# Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with 'image' or 'video').
|
||||||
# For images, this enables a gallery viewer where you can zoom or page through images.
|
# For images, this enables a gallery viewer where you can zoom or page through images.
|
||||||
# For videos, this enables embedded video streaming
|
# For videos, this enables embedded video streaming
|
||||||
|
@@ -42,6 +42,7 @@ describe('New Submission page', () => {
|
|||||||
cy.get('button#deposit').click();
|
cy.get('button#deposit').click();
|
||||||
|
|
||||||
// A warning alert should display.
|
// 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');
|
cy.get('ds-notification div.alert-warning').should('be.visible');
|
||||||
|
|
||||||
// First section should have an exclamation error in the header
|
// First section should have an exclamation error in the header
|
||||||
|
@@ -22,7 +22,7 @@ module.exports = function (config) {
|
|||||||
reports: ['html', 'lcovonly', 'text-summary'],
|
reports: ['html', 'lcovonly', 'text-summary'],
|
||||||
fixWebpackSourcePaths: true
|
fixWebpackSourcePaths: true
|
||||||
},
|
},
|
||||||
reporters: ['mocha', 'kjhtml'],
|
reporters: ['mocha', 'kjhtml', 'coverage-istanbul'],
|
||||||
mochaReporter: {
|
mochaReporter: {
|
||||||
ignoreSkipped: true,
|
ignoreSkipped: true,
|
||||||
output: 'autowatch'
|
output: 'autowatch'
|
||||||
|
118
package.json
118
package.json
@@ -9,10 +9,10 @@
|
|||||||
"start:dev": "nodemon --exec \"cross-env NODE_ENV=development yarn run serve\"",
|
"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: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",
|
"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",
|
"serve:ssr": "node dist/server/main",
|
||||||
"analyze": "webpack-bundle-analyzer dist/browser/stats.json",
|
"analyze": "webpack-bundle-analyzer dist/browser/stats.json",
|
||||||
"build": "ng build",
|
"build": "ng build -c development",
|
||||||
"build:stats": "ng build --stats-json",
|
"build:stats": "ng build --stats-json",
|
||||||
"build:prod": "yarn run build:ssr",
|
"build:prod": "yarn run build:ssr",
|
||||||
"build:ssr": "ng build --configuration production && ng run dspace-angular:server:production",
|
"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",
|
"merge-i18n": "ts-node --project ./tsconfig.ts-node.json scripts/merge-i18n-files.ts",
|
||||||
"cypress:open": "cypress open",
|
"cypress:open": "cypress open",
|
||||||
"cypress:run": "cypress run",
|
"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": {
|
"browser": {
|
||||||
"fs": false,
|
"fs": false,
|
||||||
@@ -47,32 +48,35 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"minimist": "^1.2.5",
|
"minimist": "^1.2.5",
|
||||||
"webdriver-manager": "^12.1.8"
|
"webdriver-manager": "^12.1.8",
|
||||||
|
"ts-node": "10.2.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "~11.2.14",
|
"@angular/animations": "~13.2.6",
|
||||||
"@angular/cdk": "^11.2.13",
|
"@angular/cdk": "^13.2.6",
|
||||||
"@angular/common": "~11.2.14",
|
"@angular/common": "~13.2.6",
|
||||||
"@angular/compiler": "~11.2.14",
|
"@angular/compiler": "~13.2.6",
|
||||||
"@angular/core": "~11.2.14",
|
"@angular/core": "~13.2.6",
|
||||||
"@angular/forms": "~11.2.14",
|
"@angular/forms": "~13.2.6",
|
||||||
"@angular/localize": "11.2.14",
|
"@angular/localize": "13.2.6",
|
||||||
"@angular/platform-browser": "~11.2.14",
|
"@angular/platform-browser": "~13.2.6",
|
||||||
"@angular/platform-browser-dynamic": "~11.2.14",
|
"@angular/platform-browser-dynamic": "~13.2.6",
|
||||||
"@angular/platform-server": "~11.2.14",
|
"@angular/platform-server": "~13.2.6",
|
||||||
"@angular/router": "~11.2.14",
|
"@angular/router": "~13.2.6",
|
||||||
"@kolkov/ngx-gallery": "^1.2.3",
|
"@babel/runtime": "^7.17.2",
|
||||||
"@ng-bootstrap/ng-bootstrap": "9.1.3",
|
"@kolkov/ngx-gallery": "^2.0.1",
|
||||||
"@ng-dynamic-forms/core": "^13.0.0",
|
"@material-ui/core": "^4.11.0",
|
||||||
"@ng-dynamic-forms/ui-ng-bootstrap": "^13.0.0",
|
"@material-ui/icons": "^4.9.1",
|
||||||
"@ngrx/effects": "^11.1.1",
|
"@ng-bootstrap/ng-bootstrap": "^11.0.0",
|
||||||
"@ngrx/router-store": "^11.1.1",
|
"@ng-dynamic-forms/core": "^14.0.1",
|
||||||
"@ngrx/store": "^11.1.1",
|
"@ng-dynamic-forms/ui-ng-bootstrap": "^14.0.1",
|
||||||
"@nguniversal/express-engine": "11.2.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",
|
"@ngx-translate/core": "^13.0.0",
|
||||||
"@nicky-lenaers/ngx-scroll-to": "^9.0.0",
|
"@nicky-lenaers/ngx-scroll-to": "^9.0.0",
|
||||||
"angular-idle-preload": "3.0.0",
|
"angular-idle-preload": "3.0.0",
|
||||||
"angular2-text-mask": "9.0.0",
|
|
||||||
"angulartics2": "^10.0.0",
|
"angulartics2": "^10.0.0",
|
||||||
"bootstrap": "4.3.1",
|
"bootstrap": "4.3.1",
|
||||||
"caniuse-lite": "^1.0.30001165",
|
"caniuse-lite": "^1.0.30001165",
|
||||||
@@ -102,7 +106,7 @@
|
|||||||
"mirador-share-plugin": "^0.11.0",
|
"mirador-share-plugin": "^0.11.0",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"ng-mocks": "11.11.2",
|
"ng-mocks": "^13.1.1",
|
||||||
"ng2-file-upload": "1.4.0",
|
"ng2-file-upload": "1.4.0",
|
||||||
"ng2-nouislider": "^1.8.3",
|
"ng2-nouislider": "^1.8.3",
|
||||||
"ngx-infinite-scroll": "^10.0.1",
|
"ngx-infinite-scroll": "^10.0.1",
|
||||||
@@ -111,7 +115,9 @@
|
|||||||
"ngx-sortablejs": "^11.1.0",
|
"ngx-sortablejs": "^11.1.0",
|
||||||
"nouislider": "^14.6.3",
|
"nouislider": "^14.6.3",
|
||||||
"pem": "1.14.4",
|
"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",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rxjs": "^6.6.3",
|
"rxjs": "^6.6.3",
|
||||||
"sortablejs": "1.13.0",
|
"sortablejs": "1.13.0",
|
||||||
@@ -119,19 +125,24 @@
|
|||||||
"url-parse": "^1.5.6",
|
"url-parse": "^1.5.6",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"webfontloader": "1.6.28",
|
"webfontloader": "1.6.28",
|
||||||
"zone.js": "^0.10.3"
|
"zone.js": "~0.11.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-builders/custom-webpack": "10.0.1",
|
"@angular-builders/custom-webpack": "~13.1.0",
|
||||||
"@angular-devkit/build-angular": "~0.1102.15",
|
"@angular-devkit/build-angular": "~13.2.6",
|
||||||
"@angular/cli": "~11.2.15",
|
"@angular-eslint/builder": "13.1.0",
|
||||||
"@angular/compiler-cli": "~11.2.14",
|
"@angular-eslint/eslint-plugin": "13.1.0",
|
||||||
"@angular/language-service": "~11.2.14",
|
"@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",
|
"@cypress/schematic": "^1.5.0",
|
||||||
"@fortawesome/fontawesome-free": "^5.5.0",
|
"@fortawesome/fontawesome-free": "^5.5.0",
|
||||||
"@ngrx/store-devtools": "^11.1.1",
|
"@ngrx/store-devtools": "^13.0.2",
|
||||||
"@ngtools/webpack": "10.2.3",
|
"@ngtools/webpack": "^13.2.6",
|
||||||
"@nguniversal/builders": "~11.2.1",
|
"@nguniversal/builders": "^13.0.2",
|
||||||
"@types/deep-freeze": "0.1.2",
|
"@types/deep-freeze": "0.1.2",
|
||||||
"@types/express": "^4.17.9",
|
"@types/express": "^4.17.9",
|
||||||
"@types/file-saver": "^2.0.1",
|
"@types/file-saver": "^2.0.1",
|
||||||
@@ -140,22 +151,28 @@
|
|||||||
"@types/js-cookie": "2.2.6",
|
"@types/js-cookie": "2.2.6",
|
||||||
"@types/lodash": "^4.14.165",
|
"@types/lodash": "^4.14.165",
|
||||||
"@types/node": "^14.14.9",
|
"@types/node": "^14.14.9",
|
||||||
|
"@typescript-eslint/eslint-plugin": "5.11.0",
|
||||||
|
"@typescript-eslint/parser": "5.11.0",
|
||||||
"axe-core": "^4.3.3",
|
"axe-core": "^4.3.3",
|
||||||
"codelyzer": "^6.0.0",
|
|
||||||
"compression-webpack-plugin": "^3.0.1",
|
"compression-webpack-plugin": "^3.0.1",
|
||||||
"copy-webpack-plugin": "^6.4.1",
|
"copy-webpack-plugin": "^6.4.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"css-loader": "3.4.0",
|
"css-loader": "^6.2.0",
|
||||||
"cssnano": "^4.1.10",
|
"css-minimizer-webpack-plugin": "^3.4.1",
|
||||||
|
"cssnano": "^5.0.6",
|
||||||
"cypress": "9.5.1",
|
"cypress": "9.5.1",
|
||||||
"cypress-axe": "^0.13.0",
|
"cypress-axe": "^0.13.0",
|
||||||
"debug-loader": "^0.0.1",
|
"debug-loader": "^0.0.1",
|
||||||
"deep-freeze": "0.0.1",
|
"deep-freeze": "0.0.1",
|
||||||
"dotenv": "^8.2.0",
|
"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",
|
"fork-ts-checker-webpack-plugin": "^6.0.3",
|
||||||
"html-loader": "^1.3.2",
|
"html-loader": "^1.3.2",
|
||||||
"html-webpack-plugin": "^4.5.0",
|
"jasmine-core": "^3.8.0",
|
||||||
"jasmine-core": "~3.6.0",
|
|
||||||
"jasmine-marbles": "0.6.0",
|
"jasmine-marbles": "0.6.0",
|
||||||
"jasmine-spec-reporter": "~5.0.0",
|
"jasmine-spec-reporter": "~5.0.0",
|
||||||
"karma": "^6.3.14",
|
"karma": "^6.3.14",
|
||||||
@@ -164,12 +181,13 @@
|
|||||||
"karma-jasmine": "~4.0.0",
|
"karma-jasmine": "~4.0.0",
|
||||||
"karma-jasmine-html-reporter": "^1.5.0",
|
"karma-jasmine-html-reporter": "^1.5.0",
|
||||||
"karma-mocha-reporter": "2.2.5",
|
"karma-mocha-reporter": "2.2.5",
|
||||||
|
"ngx-mask": "^12.0.0",
|
||||||
"nodemon": "^2.0.15",
|
"nodemon": "^2.0.15",
|
||||||
"optimize-css-assets-webpack-plugin": "^5.0.4",
|
"postcss": "^8.1",
|
||||||
"postcss-apply": "0.11.0",
|
"postcss-apply": "0.12.0",
|
||||||
"postcss-import": "^12.0.1",
|
"postcss-import": "^14.0.0",
|
||||||
"postcss-loader": "^3.0.0",
|
"postcss-loader": "^4.0.3",
|
||||||
"postcss-preset-env": "6.7.0",
|
"postcss-preset-env": "^7.4.2",
|
||||||
"postcss-responsive-type": "1.0.0",
|
"postcss-responsive-type": "1.0.0",
|
||||||
"protractor": "^7.0.0",
|
"protractor": "^7.0.0",
|
||||||
"protractor-istanbul-plugin": "2.0.0",
|
"protractor-istanbul-plugin": "2.0.0",
|
||||||
@@ -178,15 +196,15 @@
|
|||||||
"react-dom": "^16.14.0",
|
"react-dom": "^16.14.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rxjs-spy": "^7.5.3",
|
"rxjs-spy": "^7.5.3",
|
||||||
|
"sass": "~1.32.6",
|
||||||
|
"sass-loader": "^12.6.0",
|
||||||
"sass-resources-loader": "^2.1.1",
|
"sass-resources-loader": "^2.1.1",
|
||||||
"script-ext-html-webpack-plugin": "2.1.5",
|
"string-replace-loader": "^3.1.0",
|
||||||
"string-replace-loader": "^2.3.0",
|
|
||||||
"terser-webpack-plugin": "^2.3.1",
|
"terser-webpack-plugin": "^2.3.1",
|
||||||
"ts-loader": "^5.2.0",
|
"ts-loader": "^5.2.0",
|
||||||
"ts-node": "^8.10.2",
|
"ts-node": "^8.10.2",
|
||||||
"tslint": "^6.1.3",
|
"typescript": "~4.5.5",
|
||||||
"typescript": "~4.0.5",
|
"webpack": "^5.69.1",
|
||||||
"webpack": "^4.44.2",
|
|
||||||
"webpack-bundle-analyzer": "^4.4.0",
|
"webpack-bundle-analyzer": "^4.4.0",
|
||||||
"webpack-cli": "^4.2.0",
|
"webpack-cli": "^4.2.0",
|
||||||
"webpack-dev-server": "^4.5.0"
|
"webpack-dev-server": "^4.5.0"
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
* import for `ngExpressEngine`.
|
* import for `ngExpressEngine`.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'zone.js/dist/zone-node';
|
import 'zone.js/node';
|
||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
import 'rxjs';
|
import 'rxjs';
|
||||||
|
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable max-classes-per-file */
|
||||||
import { Action } from '@ngrx/store';
|
import { Action } from '@ngrx/store';
|
||||||
import { EPerson } from '../../core/eperson/models/eperson.model';
|
import { EPerson } from '../../core/eperson/models/eperson.model';
|
||||||
import { type } from '../../shared/ngrx/type';
|
import { type } from '../../shared/ngrx/type';
|
||||||
@@ -16,7 +17,6 @@ export const EPeopleRegistryActionTypes = {
|
|||||||
CANCEL_EDIT_EPERSON: type('dspace/epeople-registry/CANCEL_EDIT_EPERSON'),
|
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
|
* Used to edit an EPerson in the EPeople registry
|
||||||
*/
|
*/
|
||||||
@@ -37,7 +37,6 @@ export class EPeopleRegistryCancelEPersonAction implements Action {
|
|||||||
type = EPeopleRegistryActionTypes.CANCEL_EDIT_EPERSON;
|
type = EPeopleRegistryActionTypes.CANCEL_EDIT_EPERSON;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tslint:enable:max-classes-per-file */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export a type alias of all actions in this action group
|
* Export a type alias of all actions in this action group
|
||||||
|
@@ -9,7 +9,6 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
|||||||
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
|
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||||
import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model';
|
import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { FindListOptions } from '../../core/data/request.models';
|
|
||||||
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
|
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
|
||||||
import { EPerson } from '../../core/eperson/models/eperson.model';
|
import { EPerson } from '../../core/eperson/models/eperson.model';
|
||||||
import { PageInfo } from '../../core/shared/page-info.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 { RequestService } from '../../core/data/request.service';
|
||||||
import { PaginationService } from '../../core/pagination/pagination.service';
|
import { PaginationService } from '../../core/pagination/pagination.service';
|
||||||
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
||||||
|
import { FindListOptions } from '../../core/data/find-list-options.model';
|
||||||
|
|
||||||
describe('EPeopleRegistryComponent', () => {
|
describe('EPeopleRegistryComponent', () => {
|
||||||
let component: EPeopleRegistryComponent;
|
let component: EPeopleRegistryComponent;
|
||||||
|
@@ -8,7 +8,6 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
|||||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
import { buildPaginatedList, PaginatedList } from '../../../core/data/paginated-list.model';
|
import { buildPaginatedList, PaginatedList } from '../../../core/data/paginated-list.model';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { FindListOptions } from '../../../core/data/request.models';
|
|
||||||
import { EPersonDataService } from '../../../core/eperson/eperson-data.service';
|
import { EPersonDataService } from '../../../core/eperson/eperson-data.service';
|
||||||
import { EPerson } from '../../../core/eperson/models/eperson.model';
|
import { EPerson } from '../../../core/eperson/models/eperson.model';
|
||||||
import { PageInfo } from '../../../core/shared/page-info.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 { RequestService } from '../../../core/data/request.service';
|
||||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||||
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
|
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 { ValidateEmailNotTaken } from './validators/email-taken.validator';
|
||||||
import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service';
|
import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service';
|
||||||
|
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable max-classes-per-file */
|
||||||
import { Action } from '@ngrx/store';
|
import { Action } from '@ngrx/store';
|
||||||
import { Group } from '../../core/eperson/models/group.model';
|
import { Group } from '../../core/eperson/models/group.model';
|
||||||
import { type } from '../../shared/ngrx/type';
|
import { type } from '../../shared/ngrx/type';
|
||||||
@@ -16,7 +17,6 @@ export const GroupRegistryActionTypes = {
|
|||||||
CANCEL_EDIT_GROUP: type('dspace/epeople-registry/CANCEL_EDIT_GROUP'),
|
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
|
* Used to edit a Group in the Group registry
|
||||||
*/
|
*/
|
||||||
@@ -37,7 +37,6 @@ export class GroupRegistryCancelGroupAction implements Action {
|
|||||||
type = GroupRegistryActionTypes.CANCEL_EDIT_GROUP;
|
type = GroupRegistryActionTypes.CANCEL_EDIT_GROUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tslint:enable:max-classes-per-file */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export a type alias of all actions in this action group
|
* Export a type alias of all actions in this action group
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable max-classes-per-file */
|
||||||
import { Action } from '@ngrx/store';
|
import { Action } from '@ngrx/store';
|
||||||
import { type } from '../../../shared/ngrx/type';
|
import { type } from '../../../shared/ngrx/type';
|
||||||
import { BitstreamFormat } from '../../../core/shared/bitstream-format.model';
|
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')
|
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
|
* 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;
|
type = BitstreamFormatsRegistryActionTypes.DESELECT_ALL_FORMAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tslint:enable:max-classes-per-file */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export a type alias of all actions in this action group
|
* Export a type alias of all actions in this action group
|
||||||
|
@@ -25,9 +25,9 @@ import {
|
|||||||
import { createPaginatedList } from '../../../shared/testing/utils.test';
|
import { createPaginatedList } from '../../../shared/testing/utils.test';
|
||||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||||
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-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 { PaginationService } from '../../../core/pagination/pagination.service';
|
||||||
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
|
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
|
||||||
|
import { FindListOptions } from '../../../core/data/find-list-options.model';
|
||||||
|
|
||||||
describe('BitstreamFormatsComponent', () => {
|
describe('BitstreamFormatsComponent', () => {
|
||||||
let comp: BitstreamFormatsComponent;
|
let comp: BitstreamFormatsComponent;
|
||||||
|
@@ -5,7 +5,6 @@ import { PaginatedList } from '../../../core/data/paginated-list.model';
|
|||||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||||
import { BitstreamFormat } from '../../../core/shared/bitstream-format.model';
|
import { BitstreamFormat } from '../../../core/shared/bitstream-format.model';
|
||||||
import { BitstreamFormatDataService } from '../../../core/data/bitstream-format-data.service';
|
import { BitstreamFormatDataService } from '../../../core/data/bitstream-format-data.service';
|
||||||
import { FindListOptions } from '../../../core/data/request.models';
|
|
||||||
import { map, switchMap, take } from 'rxjs/operators';
|
import { map, switchMap, take } from 'rxjs/operators';
|
||||||
import { hasValue } from '../../../shared/empty.util';
|
import { hasValue } from '../../../shared/empty.util';
|
||||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
@@ -13,6 +12,7 @@ import { Router } from '@angular/router';
|
|||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { NoContent } from '../../../core/shared/NoContent.model';
|
import { NoContent } from '../../../core/shared/NoContent.model';
|
||||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||||
|
import { FindListOptions } from '../../../core/data/find-list-options.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders a list of bitstream formats
|
* This component renders a list of bitstream formats
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable max-classes-per-file */
|
||||||
import { Action } from '@ngrx/store';
|
import { Action } from '@ngrx/store';
|
||||||
import { type } from '../../../shared/ngrx/type';
|
import { type } from '../../../shared/ngrx/type';
|
||||||
import { MetadataSchema } from '../../../core/metadata/metadata-schema.model';
|
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')
|
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
|
* Used to edit a metadata schema in the metadata registry
|
||||||
*/
|
*/
|
||||||
@@ -133,7 +133,6 @@ export class MetadataRegistryDeselectAllFieldAction implements Action {
|
|||||||
type = MetadataRegistryActionTypes.DESELECT_ALL_FIELD;
|
type = MetadataRegistryActionTypes.DESELECT_ALL_FIELD;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tslint:enable:max-classes-per-file */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export a type alias of all actions in this action group
|
* Export a type alias of all actions in this action group
|
||||||
|
@@ -21,8 +21,8 @@ import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.u
|
|||||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||||
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-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 { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
|
||||||
|
import { FindListOptions } from '../../../core/data/find-list-options.model';
|
||||||
|
|
||||||
describe('MetadataRegistryComponent', () => {
|
describe('MetadataRegistryComponent', () => {
|
||||||
let comp: MetadataRegistryComponent;
|
let comp: MetadataRegistryComponent;
|
||||||
@@ -52,7 +52,7 @@ describe('MetadataRegistryComponent', () => {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
const mockSchemas = createSuccessfulRemoteDataObject$(buildPaginatedList(null, mockSchemasList));
|
const mockSchemas = createSuccessfulRemoteDataObject$(buildPaginatedList(null, mockSchemasList));
|
||||||
/* tslint:disable:no-empty */
|
/* eslint-disable no-empty,@typescript-eslint/no-empty-function */
|
||||||
const registryServiceStub = {
|
const registryServiceStub = {
|
||||||
getMetadataSchemas: () => mockSchemas,
|
getMetadataSchemas: () => mockSchemas,
|
||||||
getActiveMetadataSchema: () => observableOf(undefined),
|
getActiveMetadataSchema: () => observableOf(undefined),
|
||||||
@@ -66,7 +66,7 @@ describe('MetadataRegistryComponent', () => {
|
|||||||
},
|
},
|
||||||
clearMetadataSchemaRequests: () => observableOf(undefined)
|
clearMetadataSchemaRequests: () => observableOf(undefined)
|
||||||
};
|
};
|
||||||
/* tslint:enable:no-empty */
|
/* eslint-enable no-empty, @typescript-eslint/no-empty-function */
|
||||||
|
|
||||||
paginationService = new PaginationServiceStub();
|
paginationService = new PaginationServiceStub();
|
||||||
|
|
||||||
|
@@ -17,7 +17,7 @@ describe('MetadataSchemaFormComponent', () => {
|
|||||||
let fixture: ComponentFixture<MetadataSchemaFormComponent>;
|
let fixture: ComponentFixture<MetadataSchemaFormComponent>;
|
||||||
let registryService: RegistryService;
|
let registryService: RegistryService;
|
||||||
|
|
||||||
/* tslint:disable:no-empty */
|
/* eslint-disable no-empty,@typescript-eslint/no-empty-function */
|
||||||
const registryServiceStub = {
|
const registryServiceStub = {
|
||||||
getActiveMetadataSchema: () => observableOf(undefined),
|
getActiveMetadataSchema: () => observableOf(undefined),
|
||||||
createOrUpdateMetadataSchema: (schema: MetadataSchema) => observableOf(schema),
|
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(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
|
@@ -24,7 +24,7 @@ describe('MetadataFieldFormComponent', () => {
|
|||||||
prefix: 'fake'
|
prefix: 'fake'
|
||||||
});
|
});
|
||||||
|
|
||||||
/* tslint:disable:no-empty */
|
/* eslint-disable no-empty,@typescript-eslint/no-empty-function */
|
||||||
const registryServiceStub = {
|
const registryServiceStub = {
|
||||||
getActiveMetadataField: () => observableOf(undefined),
|
getActiveMetadataField: () => observableOf(undefined),
|
||||||
createMetadataField: (field: MetadataField) => observableOf(field),
|
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(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
|
@@ -25,9 +25,9 @@ import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.u
|
|||||||
import { VarDirective } from '../../../shared/utils/var.directive';
|
import { VarDirective } from '../../../shared/utils/var.directive';
|
||||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||||
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-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 { PaginationService } from '../../../core/pagination/pagination.service';
|
||||||
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
|
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
|
||||||
|
import { FindListOptions } from '../../../core/data/find-list-options.model';
|
||||||
|
|
||||||
describe('MetadataSchemaComponent', () => {
|
describe('MetadataSchemaComponent', () => {
|
||||||
let comp: MetadataSchemaComponent;
|
let comp: MetadataSchemaComponent;
|
||||||
@@ -106,7 +106,7 @@ describe('MetadataSchemaComponent', () => {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
const mockSchemas = createSuccessfulRemoteDataObject$(buildPaginatedList(null, mockSchemasList));
|
const mockSchemas = createSuccessfulRemoteDataObject$(buildPaginatedList(null, mockSchemasList));
|
||||||
/* tslint:disable:no-empty */
|
/* eslint-disable no-empty,@typescript-eslint/no-empty-function */
|
||||||
const registryServiceStub = {
|
const registryServiceStub = {
|
||||||
getMetadataSchemas: () => mockSchemas,
|
getMetadataSchemas: () => mockSchemas,
|
||||||
getMetadataFieldsBySchema: (schema: MetadataSchema) => createSuccessfulRemoteDataObject$(buildPaginatedList(null, mockFieldsList.filter((value) => value.id === 3 || value.id === 4))),
|
getMetadataFieldsBySchema: (schema: MetadataSchema) => createSuccessfulRemoteDataObject$(buildPaginatedList(null, mockFieldsList.filter((value) => value.id === 3 || value.id === 4))),
|
||||||
@@ -122,7 +122,7 @@ describe('MetadataSchemaComponent', () => {
|
|||||||
},
|
},
|
||||||
clearMetadataFieldRequests: () => observableOf(undefined)
|
clearMetadataFieldRequests: () => observableOf(undefined)
|
||||||
};
|
};
|
||||||
/* tslint:enable:no-empty */
|
/* eslint-enable no-empty, @typescript-eslint/no-empty-function */
|
||||||
const schemaNameParam = 'mock';
|
const schemaNameParam = 'mock';
|
||||||
const activatedRouteStub = Object.assign(new ActivatedRouteStub(), {
|
const activatedRouteStub = Object.assign(new ActivatedRouteStub(), {
|
||||||
params: observableOf({
|
params: observableOf({
|
||||||
|
@@ -1,28 +1,30 @@
|
|||||||
<a [ngClass]="{'btn-sm': small}" class="btn btn-secondary my-1 move-link" [routerLink]="[getMoveRoute()]" [title]="'admin.search.item.move' | translate">
|
<div class="space-children-mr my-1">
|
||||||
<i class="fa fa-arrow-circle-right"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.move" | translate}}</span>
|
<a [ngClass]="{'btn-sm': small}" class="btn btn-secondary move-link" [routerLink]="[getMoveRoute()]" [title]="'admin.search.item.move' | translate">
|
||||||
</a>
|
<i class="fa fa-arrow-circle-right"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.move" | translate}}</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
<a [ngClass]="{'btn-sm': small}" *ngIf="item && item.isDiscoverable" class="btn btn-secondary my-1 private-link" [routerLink]="[getPrivateRoute()]" [title]="'admin.search.item.make-private' | translate">
|
<a [ngClass]="{'btn-sm': small}" *ngIf="item && item.isDiscoverable" class="btn btn-secondary private-link" [routerLink]="[getPrivateRoute()]" [title]="'admin.search.item.make-private' | translate">
|
||||||
<i class="fa fa-eye-slash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.make-private" | translate}}</span>
|
<i class="fa fa-eye-slash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.make-private" | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a [ngClass]="{'btn-sm': small}" *ngIf="item && !item.isDiscoverable" class="btn btn-secondary my-1 public-link" [routerLink]="[getPublicRoute()]" [title]="'admin.search.item.make-public' | translate">
|
<a [ngClass]="{'btn-sm': small}" *ngIf="item && !item.isDiscoverable" class="btn btn-secondary public-link" [routerLink]="[getPublicRoute()]" [title]="'admin.search.item.make-public' | translate">
|
||||||
<i class="fa fa-eye"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.make-public" | translate}}</span>
|
<i class="fa fa-eye"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.make-public" | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a [ngClass]="{'btn-sm': small}" class="btn btn-secondary my-1 edit-link" [routerLink]="[getEditRoute()]" [title]="'admin.search.item.edit' | translate">
|
<a [ngClass]="{'btn-sm': small}" class="btn btn-secondary edit-link" [routerLink]="[getEditRoute()]" [title]="'admin.search.item.edit' | translate">
|
||||||
<i class="fa fa-edit"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.edit" | translate}}</span>
|
<i class="fa fa-edit"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.edit" | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a [ngClass]="{'btn-sm': small}" *ngIf="item && !item.isWithdrawn" class="btn btn-warning t my-1 withdraw-link" [routerLink]="[getWithdrawRoute()]" [title]="'admin.search.item.withdraw' | translate">
|
<a [ngClass]="{'btn-sm': small}" *ngIf="item && !item.isWithdrawn" class="btn btn-warning t withdraw-link" [routerLink]="[getWithdrawRoute()]" [title]="'admin.search.item.withdraw' | translate">
|
||||||
<i class="fa fa-ban"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.withdraw" | translate}}</span>
|
<i class="fa fa-ban"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.withdraw" | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a [ngClass]="{'btn-sm': small}" *ngIf="item && item.isWithdrawn" class="btn btn-warning my-1 reinstate-link" [routerLink]="[getReinstateRoute()]" [title]="'admin.search.item.reinstate' | translate">
|
<a [ngClass]="{'btn-sm': small}" *ngIf="item && item.isWithdrawn" class="btn btn-warning reinstate-link" [routerLink]="[getReinstateRoute()]" [title]="'admin.search.item.reinstate' | translate">
|
||||||
<i class="fa fa-undo"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.reinstate" | translate}}</span>
|
<i class="fa fa-undo"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.reinstate" | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a [ngClass]="{'btn-sm': small}" class="btn btn-danger my-1 delete-link" [routerLink]="[getDeleteRoute()]" [title]="'admin.search.item.delete' | translate">
|
<a [ngClass]="{'btn-sm': small}" class="btn btn-danger delete-link" [routerLink]="[getDeleteRoute()]" [title]="'admin.search.item.delete' | translate">
|
||||||
<i class="fa fa-trash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.delete" | translate}}</span>
|
<i class="fa fa-trash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.delete" | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
import { Component, Inject, Injector, OnInit } from '@angular/core';
|
import { Component, Inject, Injector, OnInit } from '@angular/core';
|
||||||
import { MenuSectionComponent } from '../../../shared/menu/menu-section/menu-section.component';
|
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 { MenuService } from '../../../shared/menu/menu.service';
|
||||||
import { rendersSectionForMenu } from '../../../shared/menu/menu-section.decorator';
|
import { rendersSectionForMenu } from '../../../shared/menu/menu-section.decorator';
|
||||||
import { LinkMenuItemModel } from '../../../shared/menu/menu-item/models/link.model';
|
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 { isNotEmpty } from '../../../shared/empty.util';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ import { Router } from '@angular/router';
|
|||||||
* Represents a non-expandable section in the admin sidebar
|
* Represents a non-expandable section in the admin sidebar
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
/* tslint:disable:component-selector */
|
/* eslint-disable @angular-eslint/component-selector */
|
||||||
selector: 'li[ds-admin-sidebar-section]',
|
selector: 'li[ds-admin-sidebar-section]',
|
||||||
templateUrl: './admin-sidebar-section.component.html',
|
templateUrl: './admin-sidebar-section.component.html',
|
||||||
styleUrls: ['./admin-sidebar-section.component.scss'],
|
styleUrls: ['./admin-sidebar-section.component.scss'],
|
||||||
|
@@ -237,7 +237,24 @@ describe('AdminSidebarComponent', () => {
|
|||||||
expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({
|
expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({
|
||||||
parentID: 'access_control', visible: false,
|
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({
|
expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({
|
||||||
id: 'workflow', visible: true,
|
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,
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,18 +1,35 @@
|
|||||||
import { Component, HostListener, Injector, OnInit } from '@angular/core';
|
import { Component, HostListener, Injector, OnInit } from '@angular/core';
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { combineLatest, combineLatest as observableCombineLatest, Observable, BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject, combineLatest as observableCombineLatest, combineLatest, Observable } from 'rxjs';
|
||||||
import { debounceTime, first, map, take, distinctUntilChanged, withLatestFrom } from 'rxjs/operators';
|
import { debounceTime, distinctUntilChanged, filter, first, map, take, withLatestFrom } from 'rxjs/operators';
|
||||||
import { AuthService } from '../../core/auth/auth.service';
|
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 { slideHorizontal, slideSidebar } from '../../shared/animations/slide';
|
||||||
import { CreateCollectionParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component';
|
import {
|
||||||
import { CreateCommunityParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component';
|
CreateCollectionParentSelectorComponent
|
||||||
import { CreateItemParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component';
|
} from '../../shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component';
|
||||||
import { EditCollectionSelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component';
|
import {
|
||||||
import { EditCommunitySelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component';
|
CreateCommunityParentSelectorComponent
|
||||||
import { EditItemSelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component';
|
} from '../../shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component';
|
||||||
import { ExportMetadataSelectorComponent } from '../../shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component';
|
import {
|
||||||
import { MenuID, MenuItemType } from '../../shared/menu/initial-menus-state';
|
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 { LinkMenuItemModel } from '../../shared/menu/menu-item/models/link.model';
|
||||||
import { OnClickMenuItemModel } from '../../shared/menu/menu-item/models/onclick.model';
|
import { OnClickMenuItemModel } from '../../shared/menu/menu-item/models/onclick.model';
|
||||||
import { TextMenuItemModel } from '../../shared/menu/menu-item/models/text.model';
|
import { TextMenuItemModel } from '../../shared/menu/menu-item/models/text.model';
|
||||||
@@ -21,7 +38,9 @@ import { MenuService } from '../../shared/menu/menu.service';
|
|||||||
import { CSSVariableService } from '../../shared/sass-helper/sass-helper.service';
|
import { CSSVariableService } from '../../shared/sass-helper/sass-helper.service';
|
||||||
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
||||||
import { Router, ActivatedRoute } from '@angular/router';
|
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
|
* Component representing the admin sidebar
|
||||||
@@ -81,12 +100,12 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
|||||||
* Set and calculate all initial values of the instance variables
|
* Set and calculate all initial values of the instance variables
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.createMenu();
|
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
this.sidebarWidth = this.variableService.getVariable('sidebarItemsWidth');
|
this.sidebarWidth = this.variableService.getVariable('sidebarItemsWidth');
|
||||||
this.authService.isAuthenticated()
|
this.authService.isAuthenticated()
|
||||||
.subscribe((loggedIn: boolean) => {
|
.subscribe((loggedIn: boolean) => {
|
||||||
if (loggedIn) {
|
if (loggedIn) {
|
||||||
|
this.createMenu();
|
||||||
this.menuService.showMenu(this.menuID);
|
this.menuService.showMenu(this.menuID);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -321,19 +340,6 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
createExportMenuSections() {
|
createExportMenuSections() {
|
||||||
const menuList = [
|
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
|
// TODO: enable this menu item once the feature has been implemented
|
||||||
// {
|
// {
|
||||||
// id: 'export_community',
|
// id: 'export_community',
|
||||||
@@ -376,14 +382,28 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
|||||||
];
|
];
|
||||||
menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, menuSection));
|
menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, menuSection));
|
||||||
|
|
||||||
observableCombineLatest(
|
observableCombineLatest([
|
||||||
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
|
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
|
||||||
// this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_EXPORT_SCRIPT_NAME)
|
this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_EXPORT_SCRIPT_NAME)
|
||||||
).pipe(
|
]).pipe(
|
||||||
// TODO uncomment when #635 (https://github.com/DSpace/dspace-angular/issues/635) is fixed; otherwise even in production mode, the metadata export button is only available after a refresh (and not in dev mode)
|
filter(([authorized, metadataExportScriptExists]: boolean[]) => authorized && metadataExportScriptExists),
|
||||||
// filter(([authorized, metadataExportScriptExists]: boolean[]) => authorized && metadataExportScriptExists),
|
|
||||||
take(1)
|
take(1)
|
||||||
).subscribe(() => {
|
).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, {
|
this.menuService.addSection(this.menuID, {
|
||||||
id: 'export_metadata',
|
id: 'export_metadata',
|
||||||
parentID: 'export',
|
parentID: 'export',
|
||||||
@@ -407,18 +427,6 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
createImportMenuSections() {
|
createImportMenuSections() {
|
||||||
const menuList = [
|
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
|
// TODO: enable this menu item once the feature has been implemented
|
||||||
// {
|
// {
|
||||||
// id: 'import_batch',
|
// id: 'import_batch',
|
||||||
@@ -436,14 +444,27 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
|||||||
shouldPersistOnRouteChange: true
|
shouldPersistOnRouteChange: true
|
||||||
})));
|
})));
|
||||||
|
|
||||||
observableCombineLatest(
|
observableCombineLatest([
|
||||||
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
|
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
|
||||||
// this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_IMPORT_SCRIPT_NAME)
|
this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_IMPORT_SCRIPT_NAME)
|
||||||
).pipe(
|
]).pipe(
|
||||||
// TODO uncomment when #635 (https://github.com/DSpace/dspace-angular/issues/635) is fixed
|
filter(([authorized, metadataImportScriptExists]: boolean[]) => authorized && metadataImportScriptExists),
|
||||||
// filter(([authorized, metadataImportScriptExists]: boolean[]) => authorized && metadataImportScriptExists),
|
|
||||||
take(1)
|
take(1)
|
||||||
).subscribe(() => {
|
).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, {
|
this.menuService.addSection(this.menuID, {
|
||||||
id: 'import_metadata',
|
id: 'import_metadata',
|
||||||
parentID: 'import',
|
parentID: 'import',
|
||||||
@@ -552,10 +573,10 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
|||||||
* Create menu sections dependent on whether or not the current user can manage access control groups
|
* Create menu sections dependent on whether or not the current user can manage access control groups
|
||||||
*/
|
*/
|
||||||
createAccessControlMenuSections() {
|
createAccessControlMenuSections() {
|
||||||
observableCombineLatest(
|
observableCombineLatest([
|
||||||
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
|
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
|
||||||
this.authorizationService.isAuthorized(FeatureID.CanManageGroups)
|
this.authorizationService.isAuthorized(FeatureID.CanManageGroups)
|
||||||
).subscribe(([isSiteAdmin, canManageGroups]) => {
|
]).subscribe(([isSiteAdmin, canManageGroups]) => {
|
||||||
const menuList = [
|
const menuList = [
|
||||||
/* Access Control */
|
/* Access Control */
|
||||||
{
|
{
|
||||||
|
@@ -4,18 +4,18 @@ import { AdminSidebarSectionComponent } from '../admin-sidebar-section/admin-sid
|
|||||||
import { slide } from '../../../shared/animations/slide';
|
import { slide } from '../../../shared/animations/slide';
|
||||||
import { CSSVariableService } from '../../../shared/sass-helper/sass-helper.service';
|
import { CSSVariableService } from '../../../shared/sass-helper/sass-helper.service';
|
||||||
import { bgColor } from '../../../shared/animations/bgColor';
|
import { bgColor } from '../../../shared/animations/bgColor';
|
||||||
import { MenuID } from '../../../shared/menu/initial-menus-state';
|
|
||||||
import { MenuService } from '../../../shared/menu/menu.service';
|
import { MenuService } from '../../../shared/menu/menu.service';
|
||||||
import { combineLatest as combineLatestObservable, Observable } from 'rxjs';
|
import { combineLatest as combineLatestObservable, Observable } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { rendersSectionForMenu } from '../../../shared/menu/menu-section.decorator';
|
import { rendersSectionForMenu } from '../../../shared/menu/menu-section.decorator';
|
||||||
|
import { MenuID } from '../../../shared/menu/menu-id.model';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a expandable section in the sidebar
|
* Represents a expandable section in the sidebar
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
/* tslint:disable:component-selector */
|
/* eslint-disable @angular-eslint/component-selector */
|
||||||
selector: 'li[ds-expandable-admin-sidebar-section]',
|
selector: 'li[ds-expandable-admin-sidebar-section]',
|
||||||
templateUrl: './expandable-admin-sidebar-section.component.html',
|
templateUrl: './expandable-admin-sidebar-section.component.html',
|
||||||
styleUrls: ['./expandable-admin-sidebar-section.component.scss'],
|
styleUrls: ['./expandable-admin-sidebar-section.component.scss'],
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 delete-link" [routerLink]="[getDeleteRoute()]" [title]="'admin.workflow.item.delete' | translate">
|
<div class="space-children-mr">
|
||||||
|
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 delete-link" [routerLink]="[getDeleteRoute()]" [title]="'admin.workflow.item.delete' | translate">
|
||||||
<i class="fa fa-trash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.workflow.item.delete" | translate}}</span>
|
<i class="fa fa-trash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.workflow.item.delete" | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 send-back-link" [routerLink]="[getSendBackRoute()]" [title]="'admin.workflow.item.send-back' | translate">
|
||||||
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 send-back-link" [routerLink]="[getSendBackRoute()]" [title]="'admin.workflow.item.send-back' | translate">
|
|
||||||
<i class="fa fa-hand-point-left"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.workflow.item.send-back" | translate}}</span>
|
<i class="fa fa-hand-point-left"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.workflow.item.send-back" | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
</div>
|
||||||
|
@@ -201,7 +201,6 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
if (event instanceof NavigationStart) {
|
if (event instanceof NavigationStart) {
|
||||||
resolveEndFound = false;
|
resolveEndFound = false;
|
||||||
this.isRouteLoading$.next(true);
|
this.isRouteLoading$.next(true);
|
||||||
this.isThemeLoading$.next(true);
|
|
||||||
} else if (event instanceof ResolveEnd) {
|
} else if (event instanceof ResolveEnd) {
|
||||||
resolveEndFound = true;
|
resolveEndFound = true;
|
||||||
const activatedRouteSnapShot: ActivatedRouteSnapshot = event.state.root;
|
const activatedRouteSnapShot: ActivatedRouteSnapshot = event.state.root;
|
||||||
|
@@ -8,7 +8,6 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
|||||||
import { EffectsModule } from '@ngrx/effects';
|
import { EffectsModule } from '@ngrx/effects';
|
||||||
import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store';
|
import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store';
|
||||||
import { MetaReducer, Store, StoreModule, USER_PROVIDED_META_REDUCERS } from '@ngrx/store';
|
import { MetaReducer, Store, StoreModule, USER_PROVIDED_META_REDUCERS } from '@ngrx/store';
|
||||||
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
|
|
||||||
import {
|
import {
|
||||||
DYNAMIC_ERROR_MESSAGES_MATCHER,
|
DYNAMIC_ERROR_MESSAGES_MATCHER,
|
||||||
DYNAMIC_MATCHER_PROVIDERS,
|
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 { ThemedAdminSidebarComponent } from './admin/admin-sidebar/themed-admin-sidebar.component';
|
||||||
|
|
||||||
import { APP_CONFIG, AppConfig } from '../config/app-config.interface';
|
import { APP_CONFIG, AppConfig } from '../config/app-config.interface';
|
||||||
|
import { NgxMaskModule } from 'ngx-mask';
|
||||||
|
|
||||||
|
import { StoreDevModules } from '../config/store/devtools';
|
||||||
|
|
||||||
export function getConfig() {
|
export function getConfig() {
|
||||||
return environment;
|
return environment;
|
||||||
@@ -90,19 +92,14 @@ const IMPORTS = [
|
|||||||
ScrollToModule.forRoot(),
|
ScrollToModule.forRoot(),
|
||||||
NgbModule,
|
NgbModule,
|
||||||
TranslateModule.forRoot(),
|
TranslateModule.forRoot(),
|
||||||
|
NgxMaskModule.forRoot(),
|
||||||
EffectsModule.forRoot(appEffects),
|
EffectsModule.forRoot(appEffects),
|
||||||
StoreModule.forRoot(appReducers, storeModuleConfig),
|
StoreModule.forRoot(appReducers, storeModuleConfig),
|
||||||
StoreRouterConnectingModule.forRoot(),
|
StoreRouterConnectingModule.forRoot(),
|
||||||
ThemedEntryComponentModule.withEntryComponents(),
|
ThemedEntryComponentModule.withEntryComponents(),
|
||||||
|
StoreDevModules,
|
||||||
];
|
];
|
||||||
|
|
||||||
IMPORTS.push(
|
|
||||||
StoreDevtoolsModule.instrument({
|
|
||||||
maxAge: 1000,
|
|
||||||
logOnly: environment.production,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const PROVIDERS = [
|
const PROVIDERS = [
|
||||||
{
|
{
|
||||||
provide: APP_CONFIG,
|
provide: APP_CONFIG,
|
||||||
|
@@ -22,7 +22,7 @@ import {
|
|||||||
nameVariantReducer
|
nameVariantReducer
|
||||||
} from './shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.reducer';
|
} from './shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.reducer';
|
||||||
import { formReducer, FormState } from './shared/form/form.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 {
|
import {
|
||||||
notificationsReducer,
|
notificationsReducer,
|
||||||
NotificationsState
|
NotificationsState
|
||||||
@@ -49,6 +49,7 @@ import {
|
|||||||
import { sidebarReducer, SidebarState } from './shared/sidebar/sidebar.reducer';
|
import { sidebarReducer, SidebarState } from './shared/sidebar/sidebar.reducer';
|
||||||
import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer';
|
import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer';
|
||||||
import { ThemeState, themeReducer } from './shared/theme-support/theme.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';
|
import { correlationIdReducer } from './correlation-id/correlation-id.reducer';
|
||||||
|
|
||||||
export interface AppState {
|
export interface AppState {
|
||||||
|
@@ -2,8 +2,8 @@ import { LegacyBitstreamUrlResolver } from './legacy-bitstream-url.resolver';
|
|||||||
import { of as observableOf, EMPTY } from 'rxjs';
|
import { of as observableOf, EMPTY } from 'rxjs';
|
||||||
import { BitstreamDataService } from '../core/data/bitstream-data.service';
|
import { BitstreamDataService } from '../core/data/bitstream-data.service';
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
import { RequestEntryState } from '../core/data/request.reducer';
|
|
||||||
import { TestScheduler } from 'rxjs/testing';
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
|
import { RequestEntryState } from '../core/data/request-entry-state.model';
|
||||||
|
|
||||||
describe(`LegacyBitstreamUrlResolver`, () => {
|
describe(`LegacyBitstreamUrlResolver`, () => {
|
||||||
let resolver: LegacyBitstreamUrlResolver;
|
let resolver: LegacyBitstreamUrlResolver;
|
||||||
|
@@ -20,9 +20,9 @@ import { VarDirective } from '../../shared/utils/var.directive';
|
|||||||
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-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 { PaginationService } from '../../core/pagination/pagination.service';
|
||||||
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
||||||
|
import { FindListOptions } from '../../core/data/find-list-options.model';
|
||||||
|
|
||||||
describe('BrowseByDatePageComponent', () => {
|
describe('BrowseByDatePageComponent', () => {
|
||||||
let comp: BrowseByDatePageComponent;
|
let comp: BrowseByDatePageComponent;
|
||||||
|
@@ -24,7 +24,13 @@
|
|||||||
<section class="comcol-page-browse-section">
|
<section class="comcol-page-browse-section">
|
||||||
<div class="browse-by-metadata w-100">
|
<div class="browse-by-metadata w-100">
|
||||||
<ds-browse-by *ngIf="startsWithOptions" class="col-xs-12 w-100"
|
<ds-browse-by *ngIf="startsWithOptions" class="col-xs-12 w-100"
|
||||||
title="{{'browse.title' | translate:{collection: (parent$ | async)?.payload?.name || '', field: 'browse.metadata.' + browseId | translate, value: (value)? '"' + value + '"': ''} }}"
|
title="{{'browse.title' | translate:
|
||||||
|
{
|
||||||
|
collection: (parent$ | async)?.payload?.name || '',
|
||||||
|
field: 'browse.metadata.' + browseId | translate,
|
||||||
|
startsWith: (startsWith)? ('browse.startsWith' | translate: { startsWith: '"' + startsWith + '"' }) : '',
|
||||||
|
value: (value)? '"' + value + '"': ''
|
||||||
|
} }}"
|
||||||
parentname="{{(parent$ | async)?.payload?.name || ''}}"
|
parentname="{{(parent$ | async)?.payload?.name || ''}}"
|
||||||
[objects$]="(items$ !== undefined)? items$ : browseEntries$"
|
[objects$]="(items$ !== undefined)? items$ : browseEntries$"
|
||||||
[paginationConfig]="(currentPagination$ |async)"
|
[paginationConfig]="(currentPagination$ |async)"
|
||||||
|
@@ -127,10 +127,10 @@ export class BrowseByMetadataPageComponent implements OnInit {
|
|||||||
return [Object.assign({}, routeParams, queryParams),currentPage,currentSort];
|
return [Object.assign({}, routeParams, queryParams),currentPage,currentSort];
|
||||||
})
|
})
|
||||||
).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => {
|
).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => {
|
||||||
this.browseId = params.id || this.defaultBrowseId;
|
this.browseId = params.id || this.defaultBrowseId;
|
||||||
this.authority = params.authority;
|
this.authority = params.authority;
|
||||||
this.value = +params.value || params.value || '';
|
this.value = +params.value || params.value || '';
|
||||||
this.startsWith = +params.startsWith || params.startsWith;
|
this.startsWith = +params.startsWith || params.startsWith;
|
||||||
const searchOptions = browseParamsToOptions(params, currentPage, currentSort, this.browseId);
|
const searchOptions = browseParamsToOptions(params, currentPage, currentSort, this.browseId);
|
||||||
if (isNotEmpty(this.value)) {
|
if (isNotEmpty(this.value)) {
|
||||||
this.updatePageWithItems(searchOptions, this.value, this.authority);
|
this.updatePageWithItems(searchOptions, this.value, this.authority);
|
||||||
|
@@ -17,7 +17,7 @@ import { ThemedBrowseBySwitcherComponent } from './browse-by-switcher/themed-bro
|
|||||||
component: ThemedBrowseBySwitcherComponent,
|
component: ThemedBrowseBySwitcherComponent,
|
||||||
canActivate: [BrowseByGuard],
|
canActivate: [BrowseByGuard],
|
||||||
resolve: { breadcrumb: BrowseByI18nBreadcrumbResolver },
|
resolve: { breadcrumb: BrowseByI18nBreadcrumbResolver },
|
||||||
data: { title: 'browse.title', breadcrumbKey: 'browse.metadata' }
|
data: { title: 'browse.title.page', breadcrumbKey: 'browse.metadata' }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}])
|
}])
|
||||||
|
@@ -20,9 +20,9 @@ import { VarDirective } from '../../shared/utils/var.directive';
|
|||||||
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-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 { PaginationService } from '../../core/pagination/pagination.service';
|
||||||
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
||||||
|
import { FindListOptions } from '../../core/data/find-list-options.model';
|
||||||
|
|
||||||
describe('BrowseByTitlePageComponent', () => {
|
describe('BrowseByTitlePageComponent', () => {
|
||||||
let comp: BrowseByTitlePageComponent;
|
let comp: BrowseByTitlePageComponent;
|
||||||
|
@@ -45,7 +45,8 @@ export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent {
|
|||||||
return [Object.assign({}, routeParams, queryParams),currentPage,currentSort];
|
return [Object.assign({}, routeParams, queryParams),currentPage,currentSort];
|
||||||
})
|
})
|
||||||
).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => {
|
).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => {
|
||||||
this.browseId = params.id || this.defaultBrowseId;
|
this.startsWith = +params.startsWith || params.startsWith;
|
||||||
|
this.browseId = params.id || this.defaultBrowseId;
|
||||||
this.updatePageWithItems(browseParamsToOptions(params, currentPage, currentSort, this.browseId), undefined, undefined);
|
this.updatePageWithItems(browseParamsToOptions(params, currentPage, currentSort, this.browseId), undefined, undefined);
|
||||||
this.updateParent(params.scope);
|
this.updateParent(params.scope);
|
||||||
}));
|
}));
|
||||||
|
@@ -16,7 +16,7 @@ import { Collection } from '../../core/shared/collection.model';
|
|||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-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 { HostWindowService } from '../../shared/host-window.service';
|
||||||
import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub';
|
import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
@@ -41,6 +41,8 @@ import {
|
|||||||
} from '../../shared/remote-data.utils';
|
} from '../../shared/remote-data.utils';
|
||||||
import { createPaginatedList } from '../../shared/testing/utils.test';
|
import { createPaginatedList } from '../../shared/testing/utils.test';
|
||||||
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
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', () => {
|
describe('CollectionItemMapperComponent', () => {
|
||||||
let comp: CollectionItemMapperComponent;
|
let comp: CollectionItemMapperComponent;
|
||||||
@@ -110,15 +112,15 @@ describe('CollectionItemMapperComponent', () => {
|
|||||||
};
|
};
|
||||||
const searchServiceStub = Object.assign(new SearchServiceStub(), {
|
const searchServiceStub = Object.assign(new SearchServiceStub(), {
|
||||||
search: () => observableOf(emptyList),
|
search: () => observableOf(emptyList),
|
||||||
/* tslint:disable:no-empty */
|
/* eslint-disable no-empty,@typescript-eslint/no-empty-function */
|
||||||
clearDiscoveryRequests: () => {}
|
clearDiscoveryRequests: () => {}
|
||||||
/* tslint:enable:no-empty */
|
/* eslint-enable no-empty,@typescript-eslint/no-empty-function */
|
||||||
});
|
});
|
||||||
const collectionDataServiceStub = {
|
const collectionDataServiceStub = {
|
||||||
getMappedItems: () => observableOf(emptyList),
|
getMappedItems: () => observableOf(emptyList),
|
||||||
/* tslint:disable:no-empty */
|
/* eslint-disable no-empty,@typescript-eslint/no-empty-function */
|
||||||
clearMappedItemsRequests: () => {}
|
clearMappedItemsRequests: () => {}
|
||||||
/* tslint:enable:no-empty */
|
/* eslint-enable no-empty, @typescript-eslint/no-empty-function */
|
||||||
};
|
};
|
||||||
const routeServiceStub = {
|
const routeServiceStub = {
|
||||||
getRouteParameterValue: () => {
|
getRouteParameterValue: () => {
|
||||||
@@ -159,6 +161,14 @@ describe('CollectionItemMapperComponent', () => {
|
|||||||
{ provide: RouteService, useValue: routeServiceStub },
|
{ provide: RouteService, useValue: routeServiceStub },
|
||||||
{ provide: AuthorizationDataService, useValue: authorizationDataService }
|
{ provide: AuthorizationDataService, useValue: authorizationDataService }
|
||||||
]
|
]
|
||||||
|
}).overrideComponent(CollectionItemMapperComponent, {
|
||||||
|
set: {
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: SEARCH_CONFIG_SERVICE,
|
||||||
|
useClass: SearchConfigurationServiceStub
|
||||||
|
}
|
||||||
|
] }
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@@ -18,9 +18,9 @@ import {
|
|||||||
COLLECTION_CREATE_PATH
|
COLLECTION_CREATE_PATH
|
||||||
} from './collection-page-routing-paths';
|
} from './collection-page-routing-paths';
|
||||||
import { CollectionPageAdministratorGuard } from './collection-page-administrator.guard';
|
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 { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model';
|
||||||
import { ThemedCollectionPageComponent } from './themed-collection-page.component';
|
import { ThemedCollectionPageComponent } from './themed-collection-page.component';
|
||||||
|
import { MenuItemType } from '../shared/menu/menu-item-type.model';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@@ -34,7 +34,7 @@
|
|||||||
[title]="'collection.page.news'">
|
[title]="'collection.page.news'">
|
||||||
</ds-comcol-page-content>
|
</ds-comcol-page-content>
|
||||||
</header>
|
</header>
|
||||||
<div class="pl-2">
|
<div class="pl-2 space-children-mr">
|
||||||
<ds-dso-page-edit-button *ngIf="isCollectionAdmin$ | async" [pageRoute]="collectionPageRoute$ | async" [dso]="collection" [tooltipMsg]="'collection.page.edit'"></ds-dso-page-edit-button>
|
<ds-dso-page-edit-button *ngIf="isCollectionAdmin$ | async" [pageRoute]="collectionPageRoute$ | async" [dso]="collection" [tooltipMsg]="'collection.page.edit'"></ds-dso-page-edit-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -16,7 +16,6 @@ import { Item } from '../core/shared/item.model';
|
|||||||
import {
|
import {
|
||||||
getAllSucceededRemoteDataPayload,
|
getAllSucceededRemoteDataPayload,
|
||||||
getFirstSucceededRemoteData,
|
getFirstSucceededRemoteData,
|
||||||
redirectOn4xx,
|
|
||||||
toDSpaceObjectListRD
|
toDSpaceObjectListRD
|
||||||
} from '../core/shared/operators';
|
} 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 { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
||||||
import { FeatureID } from '../core/data/feature-authorization/feature-id';
|
import { FeatureID } from '../core/data/feature-authorization/feature-id';
|
||||||
import { getCollectionPageRoute } from './collection-page-routing-paths';
|
import { getCollectionPageRoute } from './collection-page-routing-paths';
|
||||||
|
import { redirectOn4xx } from '../core/shared/authorized.operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-collection-page',
|
selector: 'ds-collection-page',
|
||||||
|
@@ -5,11 +5,11 @@
|
|||||||
<h2 id="header" class="border-bottom pb-2">{{ 'collection.delete.head' | translate}}</h2>
|
<h2 id="header" class="border-bottom pb-2">{{ 'collection.delete.head' | translate}}</h2>
|
||||||
<p class="pb-2">{{ 'collection.delete.text' | translate:{ dso: dso.name } }}</p>
|
<p class="pb-2">{{ 'collection.delete.text' | translate:{ dso: dso.name } }}</p>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col text-right">
|
<div class="col text-right space-children-mr">
|
||||||
<button class="btn btn-outline-secondary" (click)="onCancel(dso)" [disabled]="(processing$ | async)">
|
<button class="btn btn-outline-secondary" (click)="onCancel(dso)" [disabled]="(processing$ | async)">
|
||||||
<i class="fas fa-times"></i> {{'collection.delete.cancel' | translate}}
|
<i class="fas fa-times"></i> {{'collection.delete.cancel' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger mr-2" (click)="onConfirm(dso)" [disabled]="(processing$ | async)">
|
<button class="btn btn-danger" (click)="onConfirm(dso)" [disabled]="(processing$ | async)">
|
||||||
<span *ngIf="processing$ | async"><i class='fas fa-circle-notch fa-spin'></i> {{'collection.delete.processing' | translate}}</span>
|
<span *ngIf="processing$ | async"><i class='fas fa-circle-notch fa-spin'></i> {{'collection.delete.processing' | translate}}</span>
|
||||||
<span *ngIf="!(processing$ | async)"><i class="fas fa-trash"></i> {{'collection.delete.confirm' | translate}}</span>
|
<span *ngIf="!(processing$ | async)"><i class="fas fa-trash"></i> {{'collection.delete.confirm' | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<div class="container-fluid mb-2" *ngVar="(itemTemplateRD$ | async) as itemTemplateRD">
|
<div class="container-fluid mb-2" *ngVar="(itemTemplateRD$ | async) as itemTemplateRD">
|
||||||
<label>{{ 'collection.edit.template.label' | translate}}</label>
|
<label>{{ 'collection.edit.template.label' | translate}}</label>
|
||||||
<div class="button-row">
|
<div class="button-row space-children-mr">
|
||||||
<button *ngIf="!itemTemplateRD?.payload" class="btn btn-success" (click)="addItemTemplate()">
|
<button *ngIf="!itemTemplateRD?.payload" class="btn btn-success" (click)="addItemTemplate()">
|
||||||
<i class="fas fa-plus"></i>
|
<i class="fas fa-plus"></i>
|
||||||
<span class="d-none d-sm-inline"> {{"collection.edit.template.add-button" | translate}}</span>
|
<span class="d-none d-sm-inline"> {{"collection.edit.template.add-button" | translate}}</span>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<div *ngVar="(contentSource$ |async) as contentSource">
|
<div *ngVar="(contentSource$ |async) as contentSource">
|
||||||
<div class="container-fluid" *ngIf="shouldShow">
|
<div class="container-fluid space-children-mr" *ngIf="shouldShow">
|
||||||
<h4>{{ 'collection.source.controls.head' | translate }}</h4>
|
<h4>{{ 'collection.source.controls.head' | translate }}</h4>
|
||||||
<div>
|
<div>
|
||||||
<span class="font-weight-bold">{{'collection.source.controls.harvest.status' | translate}}</span>
|
<span class="font-weight-bold">{{'collection.source.controls.harvest.status' | translate}}</span>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="d-inline-block float-right">
|
<div class="d-inline-block float-right space-children-mr">
|
||||||
<button class=" btn btn-danger" *ngIf="!(isReinstatable() | async)"
|
<button class=" btn btn-danger" *ngIf="!(isReinstatable() | async)"
|
||||||
[disabled]="!(hasChanges() | async)"
|
[disabled]="!(hasChanges() | async)"
|
||||||
(click)="discard()"><i
|
(click)="discard()"><i
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
<div class="container mt-2" *ngIf="(contentSource?.harvestType !== harvestTypeNone)">
|
<div class="container mt-2" *ngIf="(contentSource?.harvestType !== harvestTypeNone)">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="d-inline-block float-right ml-1">
|
<div class="d-inline-block float-right ml-1 space-children-mr">
|
||||||
<button class=" btn btn-danger" *ngIf="!(isReinstatable() | async)"
|
<button class=" btn btn-danger" *ngIf="!(isReinstatable() | async)"
|
||||||
[disabled]="!(hasChanges() | async)"
|
[disabled]="!(hasChanges() | async)"
|
||||||
(click)="discard()"><i
|
(click)="discard()"><i
|
||||||
|
@@ -9,7 +9,6 @@ import { ContentSource, ContentSourceHarvestType } from '../../../core/shared/co
|
|||||||
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
|
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
|
||||||
import { INotification, Notification } from '../../../shared/notifications/models/notification.model';
|
import { INotification, Notification } from '../../../shared/notifications/models/notification.model';
|
||||||
import { NotificationType } from '../../../shared/notifications/models/notification-type';
|
import { NotificationType } from '../../../shared/notifications/models/notification-type';
|
||||||
import { FieldUpdate } from '../../../core/data/object-updates/object-updates.reducer';
|
|
||||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
import { DynamicFormControlModel, DynamicFormService } from '@ng-dynamic-forms/core';
|
import { DynamicFormControlModel, DynamicFormService } from '@ng-dynamic-forms/core';
|
||||||
import { hasValue } from '../../../shared/empty.util';
|
import { hasValue } from '../../../shared/empty.util';
|
||||||
@@ -20,6 +19,7 @@ import { Collection } from '../../../core/shared/collection.model';
|
|||||||
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||||
import { RequestService } from '../../../core/data/request.service';
|
import { RequestService } from '../../../core/data/request.service';
|
||||||
import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
|
||||||
|
import { FieldUpdate } from '../../../core/data/object-updates/field-update.model';
|
||||||
|
|
||||||
const infoNotification: INotification = new Notification('id', NotificationType.Info, 'info');
|
const infoNotification: INotification = new Notification('id', NotificationType.Info, 'info');
|
||||||
const warningNotification: INotification = new Notification('id', NotificationType.Warning, 'warning');
|
const warningNotification: INotification = new Notification('id', NotificationType.Warning, 'warning');
|
||||||
|
@@ -23,7 +23,6 @@ import { RemoteData } from '../../../core/data/remote-data';
|
|||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
import { first, map, switchMap, take } from 'rxjs/operators';
|
import { first, map, switchMap, take } from 'rxjs/operators';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { FieldUpdate, FieldUpdates } from '../../../core/data/object-updates/object-updates.reducer';
|
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||||
import { getFirstSucceededRemoteData, getFirstCompletedRemoteData } from '../../../core/shared/operators';
|
import { getFirstSucceededRemoteData, getFirstCompletedRemoteData } from '../../../core/shared/operators';
|
||||||
@@ -31,6 +30,8 @@ import { MetadataConfig } from '../../../core/shared/metadata-config.model';
|
|||||||
import { INotification } from '../../../shared/notifications/models/notification.model';
|
import { INotification } from '../../../shared/notifications/models/notification.model';
|
||||||
import { RequestService } from '../../../core/data/request.service';
|
import { RequestService } from '../../../core/data/request.service';
|
||||||
import { environment } from '../../../../environments/environment';
|
import { environment } from '../../../../environments/environment';
|
||||||
|
import { FieldUpdate } from '../../../core/data/object-updates/field-update.model';
|
||||||
|
import { FieldUpdates } from '../../../core/data/object-updates/field-updates.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for managing the content source of the collection
|
* Component for managing the content source of the collection
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
import { FindListOptions } from '../core/data/request.models';
|
|
||||||
import { hasValue } from '../shared/empty.util';
|
import { hasValue } from '../shared/empty.util';
|
||||||
import { CommunityListService, FlatNode } from './community-list-service';
|
import { CommunityListService} from './community-list-service';
|
||||||
import { CollectionViewer, DataSource } from '@angular/cdk/collections';
|
import { CollectionViewer, DataSource } from '@angular/cdk/collections';
|
||||||
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||||
import { finalize } from 'rxjs/operators';
|
import { finalize } from 'rxjs/operators';
|
||||||
|
import { FlatNode } from './flat-node.model';
|
||||||
|
import { FindListOptions } from '../core/data/find-list-options.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DataSource object needed by a CDK Tree to render its nodes.
|
* DataSource object needed by a CDK Tree to render its nodes.
|
||||||
|
@@ -7,13 +7,14 @@ import { SortDirection, SortOptions } from '../core/cache/models/sort-options.mo
|
|||||||
import { buildPaginatedList } from '../core/data/paginated-list.model';
|
import { buildPaginatedList } from '../core/data/paginated-list.model';
|
||||||
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
|
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
|
||||||
import { StoreMock } from '../shared/testing/store.mock';
|
import { StoreMock } from '../shared/testing/store.mock';
|
||||||
import { CommunityListService, FlatNode, toFlatNode } from './community-list-service';
|
import { CommunityListService, toFlatNode } from './community-list-service';
|
||||||
import { CollectionDataService } from '../core/data/collection-data.service';
|
import { CollectionDataService } from '../core/data/collection-data.service';
|
||||||
import { CommunityDataService } from '../core/data/community-data.service';
|
import { CommunityDataService } from '../core/data/community-data.service';
|
||||||
import { Community } from '../core/shared/community.model';
|
import { Community } from '../core/shared/community.model';
|
||||||
import { Collection } from '../core/shared/collection.model';
|
import { Collection } from '../core/shared/collection.model';
|
||||||
import { FindListOptions } from '../core/data/request.models';
|
|
||||||
import { PageInfo } from '../core/shared/page-info.model';
|
import { PageInfo } from '../core/shared/page-info.model';
|
||||||
|
import { FlatNode } from './flat-node.model';
|
||||||
|
import { FindListOptions } from '../core/data/find-list-options.model';
|
||||||
|
|
||||||
describe('CommunityListService', () => {
|
describe('CommunityListService', () => {
|
||||||
let store: StoreMock<AppState>;
|
let store: StoreMock<AppState>;
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable max-classes-per-file */
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { createSelector, Store } from '@ngrx/store';
|
import { createSelector, Store } from '@ngrx/store';
|
||||||
|
|
||||||
@@ -6,45 +7,22 @@ import { filter, map, switchMap } from 'rxjs/operators';
|
|||||||
|
|
||||||
import { AppState } from '../app.reducer';
|
import { AppState } from '../app.reducer';
|
||||||
import { CommunityDataService } from '../core/data/community-data.service';
|
import { CommunityDataService } from '../core/data/community-data.service';
|
||||||
import { FindListOptions } from '../core/data/request.models';
|
|
||||||
import { Community } from '../core/shared/community.model';
|
import { Community } from '../core/shared/community.model';
|
||||||
import { Collection } from '../core/shared/collection.model';
|
import { Collection } from '../core/shared/collection.model';
|
||||||
import { PageInfo } from '../core/shared/page-info.model';
|
import { PageInfo } from '../core/shared/page-info.model';
|
||||||
import { hasValue, isNotEmpty } from '../shared/empty.util';
|
import { hasValue, isNotEmpty } from '../shared/empty.util';
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
import { PaginatedList, buildPaginatedList } from '../core/data/paginated-list.model';
|
import { buildPaginatedList, PaginatedList } from '../core/data/paginated-list.model';
|
||||||
import { CollectionDataService } from '../core/data/collection-data.service';
|
import { CollectionDataService } from '../core/data/collection-data.service';
|
||||||
import { CommunityListSaveAction } from './community-list.actions';
|
import { CommunityListSaveAction } from './community-list.actions';
|
||||||
import { CommunityListState } from './community-list.reducer';
|
import { CommunityListState } from './community-list.reducer';
|
||||||
import { getCommunityPageRoute } from '../community-page/community-page-routing-paths';
|
import { getCommunityPageRoute } from '../community-page/community-page-routing-paths';
|
||||||
import { getCollectionPageRoute } from '../collection-page/collection-page-routing-paths';
|
import { getCollectionPageRoute } from '../collection-page/collection-page-routing-paths';
|
||||||
import { getFirstSucceededRemoteData, getFirstCompletedRemoteData } from '../core/shared/operators';
|
import { getFirstCompletedRemoteData, getFirstSucceededRemoteData } from '../core/shared/operators';
|
||||||
import { followLink } from '../shared/utils/follow-link-config.model';
|
import { followLink } from '../shared/utils/follow-link-config.model';
|
||||||
|
import { FlatNode } from './flat-node.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
|
import { FindListOptions } from '../core/data/find-list-options.model';
|
||||||
* state in the tree. There are nodes representing communities, collections and show more links.
|
|
||||||
*/
|
|
||||||
export interface FlatNode {
|
|
||||||
isExpandable$: Observable<boolean>;
|
|
||||||
name: string;
|
|
||||||
id: string;
|
|
||||||
level: number;
|
|
||||||
isExpanded?: boolean;
|
|
||||||
parent?: FlatNode;
|
|
||||||
payload: Community | Collection | ShowMoreFlatNode;
|
|
||||||
isShowMoreNode: boolean;
|
|
||||||
route?: string;
|
|
||||||
currentCommunityPage?: number;
|
|
||||||
currentCollectionPage?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper method to combine an flatten an array of observables of flatNode arrays
|
// Helper method to combine an flatten an array of observables of flatNode arrays
|
||||||
export const combineAndFlatten = (obsList: Observable<FlatNode[]>[]): Observable<FlatNode[]> =>
|
export const combineAndFlatten = (obsList: Observable<FlatNode[]>[]): Observable<FlatNode[]> =>
|
||||||
@@ -108,7 +86,6 @@ export const MAX_COMCOLS_PER_PAGE = 20;
|
|||||||
* Service class for the community list, responsible for the creating of the flat list used by communityList dataSource
|
* Service class for the community list, responsible for the creating of the flat list used by communityList dataSource
|
||||||
* and connection to the store to retrieve and save the state of the community list
|
* and connection to the store to retrieve and save the state of the community list
|
||||||
*/
|
*/
|
||||||
// tslint:disable-next-line:max-classes-per-file
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CommunityListService {
|
export class CommunityListService {
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Action } from '@ngrx/store';
|
import { Action } from '@ngrx/store';
|
||||||
import { type } from '../shared/ngrx/type';
|
import { type } from '../shared/ngrx/type';
|
||||||
import { FlatNode } from './community-list-service';
|
import { FlatNode } from './flat-node.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All the action types of the community-list
|
* All the action types of the community-list
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { FlatNode } from './community-list-service';
|
|
||||||
import { CommunityListActions, CommunityListActionTypes, CommunityListSaveAction } from './community-list.actions';
|
import { CommunityListActions, CommunityListActionTypes, CommunityListSaveAction } from './community-list.actions';
|
||||||
|
import { FlatNode } from './flat-node.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* States we wish to put in store concerning the community list
|
* States we wish to put in store concerning the community list
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
<span class="fa fa-chevron-right invisible" aria-hidden="true"></span>
|
<span class="fa fa-chevron-right invisible" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="align-middle pt-2">
|
<div class="align-middle pt-2">
|
||||||
<a *ngIf="node!==loadingNode" [routerLink]="" (click)="getNextPage(node)"
|
<a *ngIf="node!==loadingNode" [routerLink]="[]" (click)="getNextPage(node)"
|
||||||
class="btn btn-outline-primary btn-sm" role="button">
|
class="btn btn-outline-primary btn-sm" role="button">
|
||||||
<i class="fas fa-angle-down"></i> {{ 'communityList.showMore' | translate }}
|
<i class="fas fa-angle-down"></i> {{ 'communityList.showMore' | translate }}
|
||||||
</a>
|
</a>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync } from '@angular/core/testing';
|
||||||
|
|
||||||
import { CommunityListComponent } from './community-list.component';
|
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 { CdkTreeModule } from '@angular/cdk/tree';
|
||||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock';
|
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 { of as observableOf } from 'rxjs';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { isEmpty, isNotEmpty } from '../../shared/empty.util';
|
import { isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||||
|
import { FlatNode } from '../flat-node.model';
|
||||||
|
|
||||||
describe('CommunityListComponent', () => {
|
describe('CommunityListComponent', () => {
|
||||||
let component: CommunityListComponent;
|
let component: CommunityListComponent;
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { take } from 'rxjs/operators';
|
import { take } from 'rxjs/operators';
|
||||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||||
import { FindListOptions } from '../../core/data/request.models';
|
import { CommunityListService} from '../community-list-service';
|
||||||
import { CommunityListService, FlatNode } from '../community-list-service';
|
|
||||||
import { CommunityListDatasource } from '../community-list-datasource';
|
import { CommunityListDatasource } from '../community-list-datasource';
|
||||||
import { FlatTreeControl } from '@angular/cdk/tree';
|
import { FlatTreeControl } from '@angular/cdk/tree';
|
||||||
import { isEmpty } from '../../shared/empty.util';
|
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.
|
* A tree-structured list of nodes representing the communities, their subCommunities and collections.
|
||||||
|
@@ -7,7 +7,8 @@ import { Component } from '@angular/core';
|
|||||||
selector: 'ds-themed-community-list',
|
selector: 'ds-themed-community-list',
|
||||||
styleUrls: [],
|
styleUrls: [],
|
||||||
templateUrl: '../../shared/theme-support/themed.component.html',
|
templateUrl: '../../shared/theme-support/themed.component.html',
|
||||||
})export class ThemedCommunityListComponent extends ThemedComponent<CommunityListComponent> {
|
})
|
||||||
|
export class ThemedCommunityListComponent extends ThemedComponent<CommunityListComponent> {
|
||||||
protected getComponentName(): string {
|
protected getComponentName(): string {
|
||||||
return 'CommunityListComponent';
|
return 'CommunityListComponent';
|
||||||
}
|
}
|
||||||
|
22
src/app/community-list-page/flat-node.model.ts
Normal file
22
src/app/community-list-page/flat-node.model.ts
Normal file
@@ -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<boolean>;
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
level: number;
|
||||||
|
isExpanded?: boolean;
|
||||||
|
parent?: FlatNode;
|
||||||
|
payload: Community | Collection | ShowMoreFlatNode;
|
||||||
|
isShowMoreNode: boolean;
|
||||||
|
route?: string;
|
||||||
|
currentCommunityPage?: number;
|
||||||
|
currentCollectionPage?: number;
|
||||||
|
}
|
6
src/app/community-list-page/show-more-flat-node.model.ts
Normal file
6
src/app/community-list-page/show-more-flat-node.model.ts
Normal file
@@ -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 {
|
||||||
|
}
|
@@ -11,9 +11,9 @@ import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.servi
|
|||||||
import { LinkService } from '../core/cache/builders/link.service';
|
import { LinkService } from '../core/cache/builders/link.service';
|
||||||
import { COMMUNITY_EDIT_PATH, COMMUNITY_CREATE_PATH } from './community-page-routing-paths';
|
import { COMMUNITY_EDIT_PATH, COMMUNITY_CREATE_PATH } from './community-page-routing-paths';
|
||||||
import { CommunityPageAdministratorGuard } from './community-page-administrator.guard';
|
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 { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model';
|
||||||
import { ThemedCommunityPageComponent } from './themed-community-page.component';
|
import { ThemedCommunityPageComponent } from './themed-community-page.component';
|
||||||
|
import { MenuItemType } from '../shared/menu/menu-item-type.model';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
[title]="'community.page.news'">
|
[title]="'community.page.news'">
|
||||||
</ds-comcol-page-content>
|
</ds-comcol-page-content>
|
||||||
</header>
|
</header>
|
||||||
<div class="pl-2">
|
<div class="pl-2 space-children-mr">
|
||||||
<ds-dso-page-edit-button *ngIf="isCommunityAdmin$ | async" [pageRoute]="communityPageRoute$ | async" [dso]="communityPayload" [tooltipMsg]="'community.page.edit'"></ds-dso-page-edit-button>
|
<ds-dso-page-edit-button *ngIf="isCommunityAdmin$ | async" [pageRoute]="communityPageRoute$ | async" [dso]="communityPayload" [tooltipMsg]="'community.page.edit'"></ds-dso-page-edit-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -13,11 +13,12 @@ import { MetadataService } from '../core/metadata/metadata.service';
|
|||||||
|
|
||||||
import { fadeInOut } from '../shared/animations/fade';
|
import { fadeInOut } from '../shared/animations/fade';
|
||||||
import { hasValue } from '../shared/empty.util';
|
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 { AuthService } from '../core/auth/auth.service';
|
||||||
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
||||||
import { FeatureID } from '../core/data/feature-authorization/feature-id';
|
import { FeatureID } from '../core/data/feature-authorization/feature-id';
|
||||||
import { getCommunityPageRoute } from './community-page-routing-paths';
|
import { getCommunityPageRoute } from './community-page-routing-paths';
|
||||||
|
import { redirectOn4xx } from '../core/shared/authorized.operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-community-page',
|
selector: 'ds-community-page',
|
||||||
|
@@ -5,11 +5,11 @@
|
|||||||
<h2 id="header" class="border-bottom pb-2">{{ 'community.delete.head' | translate}}</h2>
|
<h2 id="header" class="border-bottom pb-2">{{ 'community.delete.head' | translate}}</h2>
|
||||||
<p class="pb-2">{{ 'community.delete.text' | translate:{ dso: dso.name } }}</p>
|
<p class="pb-2">{{ 'community.delete.text' | translate:{ dso: dso.name } }}</p>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col text-right">
|
<div class="col text-right space-children-mr">
|
||||||
<button class="btn btn-outline-secondary" (click)="onCancel(dso)" [disabled]="(processing$ | async)">
|
<button class="btn btn-outline-secondary" (click)="onCancel(dso)" [disabled]="(processing$ | async)">
|
||||||
<i class="fas fa-times"></i> {{'community.delete.cancel' | translate}}
|
<i class="fas fa-times"></i> {{'community.delete.cancel' | translate}}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger mr-2" (click)="onConfirm(dso)" [disabled]="(processing$ | async)">
|
<button class="btn btn-danger" (click)="onConfirm(dso)" [disabled]="(processing$ | async)">
|
||||||
<span *ngIf="processing$ | async"><i class='fas fa-circle-notch fa-spin'></i> {{'community.delete.processing' | translate}}</span>
|
<span *ngIf="processing$ | async"><i class='fas fa-circle-notch fa-spin'></i> {{'community.delete.processing' | translate}}</span>
|
||||||
<span *ngIf="!(processing$ | async)"><i class="fas fa-trash"></i> {{'community.delete.confirm' | translate}}</span>
|
<span *ngIf="!(processing$ | async)"><i class="fas fa-trash"></i> {{'community.delete.confirm' | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
|
@@ -11,7 +11,6 @@ import { CommunityPageSubCollectionListComponent } from './community-page-sub-co
|
|||||||
import { Community } from '../../core/shared/community.model';
|
import { Community } from '../../core/shared/community.model';
|
||||||
import { SharedModule } from '../../shared/shared.module';
|
import { SharedModule } from '../../shared/shared.module';
|
||||||
import { CollectionDataService } from '../../core/data/collection-data.service';
|
import { CollectionDataService } from '../../core/data/collection-data.service';
|
||||||
import { FindListOptions } from '../../core/data/request.models';
|
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||||
import { buildPaginatedList } from '../../core/data/paginated-list.model';
|
import { buildPaginatedList } from '../../core/data/paginated-list.model';
|
||||||
import { PageInfo } from '../../core/shared/page-info.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 { getMockThemeService } from '../../shared/mocks/theme-service.mock';
|
||||||
import { ThemeService } from '../../shared/theme-support/theme.service';
|
import { ThemeService } from '../../shared/theme-support/theme.service';
|
||||||
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
||||||
|
import { FindListOptions } from '../../core/data/find-list-options.model';
|
||||||
|
|
||||||
describe('CommunityPageSubCollectionList Component', () => {
|
describe('CommunityPageSubCollectionList Component', () => {
|
||||||
let comp: CommunityPageSubCollectionListComponent;
|
let comp: CommunityPageSubCollectionListComponent;
|
||||||
|
@@ -13,7 +13,6 @@ import { buildPaginatedList } from '../../core/data/paginated-list.model';
|
|||||||
import { PageInfo } from '../../core/shared/page-info.model';
|
import { PageInfo } from '../../core/shared/page-info.model';
|
||||||
import { SharedModule } from '../../shared/shared.module';
|
import { SharedModule } from '../../shared/shared.module';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||||
import { FindListOptions } from '../../core/data/request.models';
|
|
||||||
import { HostWindowService } from '../../shared/host-window.service';
|
import { HostWindowService } from '../../shared/host-window.service';
|
||||||
import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub';
|
import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub';
|
||||||
import { CommunityDataService } from '../../core/data/community-data.service';
|
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 { getMockThemeService } from '../../shared/mocks/theme-service.mock';
|
||||||
import { ThemeService } from '../../shared/theme-support/theme.service';
|
import { ThemeService } from '../../shared/theme-support/theme.service';
|
||||||
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
||||||
|
import { FindListOptions } from '../../core/data/find-list-options.model';
|
||||||
|
|
||||||
describe('CommunityPageSubCommunityListComponent Component', () => {
|
describe('CommunityPageSubCommunityListComponent Component', () => {
|
||||||
let comp: CommunityPageSubCommunityListComponent;
|
let comp: CommunityPageSubCommunityListComponent;
|
||||||
|
@@ -3,7 +3,7 @@ import { distinctUntilChanged, filter, map, mergeMap, switchMap, tap } from 'rxj
|
|||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
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 { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
||||||
import { getFirstCompletedRemoteData } from '../shared/operators';
|
import { getFirstCompletedRemoteData } from '../shared/operators';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
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 { AuthStatus } from './models/auth-status.model';
|
||||||
import { ShortLivedToken } from './models/short-lived-token.model';
|
import { ShortLivedToken } from './models/short-lived-token.model';
|
||||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
import { URLCombiner } from '../url-combiner/url-combiner';
|
||||||
|
import { RestRequest } from '../data/rest-request.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract service to send authentication requests
|
* Abstract service to send authentication requests
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable max-classes-per-file */
|
||||||
// import @ngrx
|
// import @ngrx
|
||||||
import { Action } from '@ngrx/store';
|
import { Action } from '@ngrx/store';
|
||||||
// import type function
|
// import type function
|
||||||
@@ -39,7 +40,6 @@ export const AuthActionTypes = {
|
|||||||
UNSET_USER_AS_IDLE: type('dspace/auth/UNSET_USER_AS_IDLE')
|
UNSET_USER_AS_IDLE: type('dspace/auth/UNSET_USER_AS_IDLE')
|
||||||
};
|
};
|
||||||
|
|
||||||
/* tslint:disable:max-classes-per-file */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authenticate.
|
* Authenticate.
|
||||||
@@ -411,7 +411,6 @@ export class SetUserAsIdleAction implements Action {
|
|||||||
export class UnsetUserAsIdleAction implements Action {
|
export class UnsetUserAsIdleAction implements Action {
|
||||||
public type: string = AuthActionTypes.UNSET_USER_AS_IDLE;
|
public type: string = AuthActionTypes.UNSET_USER_AS_IDLE;
|
||||||
}
|
}
|
||||||
/* tslint:enable:max-classes-per-file */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Actions type.
|
* Actions type.
|
||||||
|
@@ -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 { provideMockActions } from '@ngrx/effects/testing';
|
||||||
import { Store, StoreModule } from '@ngrx/store';
|
import { Store, StoreModule } from '@ngrx/store';
|
||||||
@@ -396,44 +396,43 @@ describe('AuthEffects', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('when auth loaded is false', () => {
|
describe('when auth loaded is false', () => {
|
||||||
it('should not call removeToken method', (done) => {
|
it('should not call removeToken method', fakeAsync(() => {
|
||||||
store.overrideSelector(isAuthenticatedLoaded, false);
|
store.overrideSelector(isAuthenticatedLoaded, false);
|
||||||
actions = hot('--a-|', { a: { type: StoreActionTypes.REHYDRATE } });
|
actions = observableOf({ type: StoreActionTypes.REHYDRATE });
|
||||||
spyOn(authServiceStub, 'removeToken');
|
spyOn(authServiceStub, 'removeToken');
|
||||||
|
|
||||||
authEffects.clearInvalidTokenOnRehydrate$.subscribe(() => {
|
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)
|
||||||
|
|
||||||
});
|
});
|
||||||
|
tick(1000);
|
||||||
done();
|
expect(authServiceStub.removeToken).not.toHaveBeenCalled();
|
||||||
});
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when auth loaded is true', () => {
|
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);
|
store.overrideSelector(isAuthenticatedLoaded, true);
|
||||||
actions = hot('--a-|', { a: { type: StoreActionTypes.REHYDRATE } });
|
actions = observableOf({ type: StoreActionTypes.REHYDRATE });
|
||||||
spyOn(authServiceStub, 'removeToken');
|
spyOn(authServiceStub, 'removeToken');
|
||||||
|
|
||||||
authEffects.clearInvalidTokenOnRehydrate$.subscribe(() => {
|
authEffects.clearInvalidTokenOnRehydrate$.subscribe(() => {
|
||||||
expect(authServiceStub.removeToken).toHaveBeenCalled();
|
expect(authServiceStub.removeToken).toHaveBeenCalled();
|
||||||
flush();
|
done();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('invalidateAuthorizationsRequestCache$', () => {
|
describe('invalidateAuthorizationsRequestCache$', () => {
|
||||||
it('should call invalidateAuthorizationsRequestCache method in response to a REHYDRATE action', (done) => {
|
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(() => {
|
authEffects.invalidateAuthorizationsRequestCache$.subscribe(() => {
|
||||||
expect((authEffects as any).authorizationsService.invalidateAuthorizationsRequestCache).toHaveBeenCalled();
|
expect((authEffects as any).authorizationsService.invalidateAuthorizationsRequestCache).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -10,7 +10,7 @@ import {
|
|||||||
} from 'rxjs';
|
} from 'rxjs';
|
||||||
import { catchError, filter, map, observeOn, switchMap, take, tap } from 'rxjs/operators';
|
import { catchError, filter, map, observeOn, switchMap, take, tap } from 'rxjs/operators';
|
||||||
// import @ngrx
|
// import @ngrx
|
||||||
import { Actions, Effect, ofType } from '@ngrx/effects';
|
import { Actions, createEffect, ofType } from '@ngrx/effects';
|
||||||
import { Action, select, Store } from '@ngrx/store';
|
import { Action, select, Store } from '@ngrx/store';
|
||||||
|
|
||||||
// import services
|
// import services
|
||||||
@@ -67,8 +67,7 @@ export class AuthEffects {
|
|||||||
* Authenticate user.
|
* Authenticate user.
|
||||||
* @method authenticate
|
* @method authenticate
|
||||||
*/
|
*/
|
||||||
@Effect()
|
public authenticate$: Observable<Action> = createEffect(() => this.actions$.pipe(
|
||||||
public authenticate$: Observable<Action> = this.actions$.pipe(
|
|
||||||
ofType(AuthActionTypes.AUTHENTICATE),
|
ofType(AuthActionTypes.AUTHENTICATE),
|
||||||
switchMap((action: AuthenticateAction) => {
|
switchMap((action: AuthenticateAction) => {
|
||||||
return this.authService.authenticate(action.payload.email, action.payload.password).pipe(
|
return this.authService.authenticate(action.payload.email, action.payload.password).pipe(
|
||||||
@@ -77,26 +76,23 @@ export class AuthEffects {
|
|||||||
catchError((error) => observableOf(new AuthenticationErrorAction(error)))
|
catchError((error) => observableOf(new AuthenticationErrorAction(error)))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
));
|
||||||
|
|
||||||
@Effect()
|
public authenticateSuccess$: Observable<Action> = createEffect(() => this.actions$.pipe(
|
||||||
public authenticateSuccess$: Observable<Action> = this.actions$.pipe(
|
|
||||||
ofType(AuthActionTypes.AUTHENTICATE_SUCCESS),
|
ofType(AuthActionTypes.AUTHENTICATE_SUCCESS),
|
||||||
map((action: AuthenticationSuccessAction) => new AuthenticatedAction(action.payload))
|
map((action: AuthenticationSuccessAction) => new AuthenticatedAction(action.payload))
|
||||||
);
|
));
|
||||||
|
|
||||||
@Effect()
|
public authenticated$: Observable<Action> = createEffect(() => this.actions$.pipe(
|
||||||
public authenticated$: Observable<Action> = this.actions$.pipe(
|
|
||||||
ofType(AuthActionTypes.AUTHENTICATED),
|
ofType(AuthActionTypes.AUTHENTICATED),
|
||||||
switchMap((action: AuthenticatedAction) => {
|
switchMap((action: AuthenticatedAction) => {
|
||||||
return this.authService.authenticatedUser(action.payload).pipe(
|
return this.authService.authenticatedUser(action.payload).pipe(
|
||||||
map((userHref: string) => new AuthenticatedSuccessAction((userHref !== null), action.payload, userHref)),
|
map((userHref: string) => new AuthenticatedSuccessAction((userHref !== null), action.payload, userHref)),
|
||||||
catchError((error) => observableOf(new AuthenticatedErrorAction(error))),);
|
catchError((error) => observableOf(new AuthenticatedErrorAction(error))),);
|
||||||
})
|
})
|
||||||
);
|
));
|
||||||
|
|
||||||
@Effect()
|
public authenticatedSuccess$: Observable<Action> = createEffect(() => this.actions$.pipe(
|
||||||
public authenticatedSuccess$: Observable<Action> = this.actions$.pipe(
|
|
||||||
ofType(AuthActionTypes.AUTHENTICATED_SUCCESS),
|
ofType(AuthActionTypes.AUTHENTICATED_SUCCESS),
|
||||||
tap((action: AuthenticatedSuccessAction) => this.authService.storeToken(action.payload.authToken)),
|
tap((action: AuthenticatedSuccessAction) => this.authService.storeToken(action.payload.authToken)),
|
||||||
switchMap((action: AuthenticatedSuccessAction) => this.authService.getRedirectUrl().pipe(
|
switchMap((action: AuthenticatedSuccessAction) => this.authService.getRedirectUrl().pipe(
|
||||||
@@ -110,26 +106,23 @@ export class AuthEffects {
|
|||||||
return new RetrieveAuthenticatedEpersonAction(action.payload.userHref);
|
return new RetrieveAuthenticatedEpersonAction(action.payload.userHref);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
));
|
||||||
|
|
||||||
@Effect({ dispatch: false })
|
public redirectAfterLoginSuccess$: Observable<Action> = createEffect(() => this.actions$.pipe(
|
||||||
public redirectAfterLoginSuccess$: Observable<Action> = this.actions$.pipe(
|
|
||||||
ofType(AuthActionTypes.REDIRECT_AFTER_LOGIN_SUCCESS),
|
ofType(AuthActionTypes.REDIRECT_AFTER_LOGIN_SUCCESS),
|
||||||
tap((action: RedirectAfterLoginSuccessAction) => {
|
tap((action: RedirectAfterLoginSuccessAction) => {
|
||||||
this.authService.clearRedirectUrl();
|
this.authService.clearRedirectUrl();
|
||||||
this.authService.navigateToRedirectUrl(action.payload);
|
this.authService.navigateToRedirectUrl(action.payload);
|
||||||
})
|
})
|
||||||
);
|
), { dispatch: false });
|
||||||
|
|
||||||
// It means "reacts to this action but don't send another"
|
// It means "reacts to this action but don't send another"
|
||||||
@Effect({ dispatch: false })
|
public authenticatedError$: Observable<Action> = createEffect(() => this.actions$.pipe(
|
||||||
public authenticatedError$: Observable<Action> = this.actions$.pipe(
|
|
||||||
ofType(AuthActionTypes.AUTHENTICATED_ERROR),
|
ofType(AuthActionTypes.AUTHENTICATED_ERROR),
|
||||||
tap((action: LogOutSuccessAction) => this.authService.removeToken())
|
tap((action: LogOutSuccessAction) => this.authService.removeToken())
|
||||||
);
|
), { dispatch: false });
|
||||||
|
|
||||||
@Effect()
|
public retrieveAuthenticatedEperson$: Observable<Action> = createEffect(() => this.actions$.pipe(
|
||||||
public retrieveAuthenticatedEperson$: Observable<Action> = this.actions$.pipe(
|
|
||||||
ofType(AuthActionTypes.RETRIEVE_AUTHENTICATED_EPERSON),
|
ofType(AuthActionTypes.RETRIEVE_AUTHENTICATED_EPERSON),
|
||||||
switchMap((action: RetrieveAuthenticatedEpersonAction) => {
|
switchMap((action: RetrieveAuthenticatedEpersonAction) => {
|
||||||
const impersonatedUserID = this.authService.getImpersonateID();
|
const impersonatedUserID = this.authService.getImpersonateID();
|
||||||
@@ -143,20 +136,18 @@ export class AuthEffects {
|
|||||||
map((user: EPerson) => new RetrieveAuthenticatedEpersonSuccessAction(user.id)),
|
map((user: EPerson) => new RetrieveAuthenticatedEpersonSuccessAction(user.id)),
|
||||||
catchError((error) => observableOf(new RetrieveAuthenticatedEpersonErrorAction(error))));
|
catchError((error) => observableOf(new RetrieveAuthenticatedEpersonErrorAction(error))));
|
||||||
})
|
})
|
||||||
);
|
));
|
||||||
|
|
||||||
@Effect()
|
public checkToken$: Observable<Action> = createEffect(() => this.actions$.pipe(ofType(AuthActionTypes.CHECK_AUTHENTICATION_TOKEN),
|
||||||
public checkToken$: Observable<Action> = this.actions$.pipe(ofType(AuthActionTypes.CHECK_AUTHENTICATION_TOKEN),
|
|
||||||
switchMap(() => {
|
switchMap(() => {
|
||||||
return this.authService.hasValidAuthenticationToken().pipe(
|
return this.authService.hasValidAuthenticationToken().pipe(
|
||||||
map((token: AuthTokenInfo) => new AuthenticatedAction(token)),
|
map((token: AuthTokenInfo) => new AuthenticatedAction(token)),
|
||||||
catchError((error) => observableOf(new CheckAuthenticationTokenCookieAction()))
|
catchError((error) => observableOf(new CheckAuthenticationTokenCookieAction()))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
));
|
||||||
|
|
||||||
@Effect()
|
public checkTokenCookie$: Observable<Action> = createEffect(() => this.actions$.pipe(
|
||||||
public checkTokenCookie$: Observable<Action> = this.actions$.pipe(
|
|
||||||
ofType(AuthActionTypes.CHECK_AUTHENTICATION_TOKEN_COOKIE),
|
ofType(AuthActionTypes.CHECK_AUTHENTICATION_TOKEN_COOKIE),
|
||||||
switchMap(() => {
|
switchMap(() => {
|
||||||
return this.authService.checkAuthenticationCookie().pipe(
|
return this.authService.checkAuthenticationCookie().pipe(
|
||||||
@@ -171,10 +162,9 @@ export class AuthEffects {
|
|||||||
catchError((error) => observableOf(new AuthenticatedErrorAction(error)))
|
catchError((error) => observableOf(new AuthenticatedErrorAction(error)))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
));
|
||||||
|
|
||||||
@Effect()
|
public retrieveToken$: Observable<Action> = createEffect(() => this.actions$.pipe(
|
||||||
public retrieveToken$: Observable<Action> = this.actions$.pipe(
|
|
||||||
ofType(AuthActionTypes.RETRIEVE_TOKEN),
|
ofType(AuthActionTypes.RETRIEVE_TOKEN),
|
||||||
switchMap((action: AuthenticateAction) => {
|
switchMap((action: AuthenticateAction) => {
|
||||||
return this.authService.refreshAuthenticationToken(null).pipe(
|
return this.authService.refreshAuthenticationToken(null).pipe(
|
||||||
@@ -183,55 +173,51 @@ export class AuthEffects {
|
|||||||
catchError((error) => observableOf(new AuthenticationErrorAction(error)))
|
catchError((error) => observableOf(new AuthenticationErrorAction(error)))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
));
|
||||||
|
|
||||||
@Effect()
|
public refreshToken$: Observable<Action> = createEffect(() => this.actions$.pipe(ofType(AuthActionTypes.REFRESH_TOKEN),
|
||||||
public refreshToken$: Observable<Action> = this.actions$.pipe(ofType(AuthActionTypes.REFRESH_TOKEN),
|
|
||||||
switchMap((action: RefreshTokenAction) => {
|
switchMap((action: RefreshTokenAction) => {
|
||||||
return this.authService.refreshAuthenticationToken(action.payload).pipe(
|
return this.authService.refreshAuthenticationToken(action.payload).pipe(
|
||||||
map((token: AuthTokenInfo) => new RefreshTokenSuccessAction(token)),
|
map((token: AuthTokenInfo) => new RefreshTokenSuccessAction(token)),
|
||||||
catchError((error) => observableOf(new RefreshTokenErrorAction()))
|
catchError((error) => observableOf(new RefreshTokenErrorAction()))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
));
|
||||||
|
|
||||||
// It means "reacts to this action but don't send another"
|
// It means "reacts to this action but don't send another"
|
||||||
@Effect({ dispatch: false })
|
public refreshTokenSuccess$: Observable<Action> = createEffect(() => this.actions$.pipe(
|
||||||
public refreshTokenSuccess$: Observable<Action> = this.actions$.pipe(
|
|
||||||
ofType(AuthActionTypes.REFRESH_TOKEN_SUCCESS),
|
ofType(AuthActionTypes.REFRESH_TOKEN_SUCCESS),
|
||||||
tap((action: RefreshTokenSuccessAction) => this.authService.replaceToken(action.payload))
|
tap((action: RefreshTokenSuccessAction) => this.authService.replaceToken(action.payload))
|
||||||
);
|
), { dispatch: false });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the store is rehydrated in the browser,
|
* When the store is rehydrated in the browser,
|
||||||
* clear a possible invalid token or authentication errors
|
* clear a possible invalid token or authentication errors
|
||||||
*/
|
*/
|
||||||
@Effect({ dispatch: false })
|
public clearInvalidTokenOnRehydrate$: Observable<any> = createEffect(() => this.actions$.pipe(
|
||||||
public clearInvalidTokenOnRehydrate$: Observable<any> = this.actions$.pipe(
|
|
||||||
ofType(StoreActionTypes.REHYDRATE),
|
ofType(StoreActionTypes.REHYDRATE),
|
||||||
switchMap(() => {
|
switchMap(() => {
|
||||||
const isLoaded$ = this.store.pipe(select(isAuthenticatedLoaded));
|
const isLoaded$ = this.store.pipe(select(isAuthenticatedLoaded));
|
||||||
const authenticated$ = this.store.pipe(select(isAuthenticated));
|
const authenticated$ = this.store.pipe(select(isAuthenticated));
|
||||||
return observableCombineLatest(isLoaded$, authenticated$).pipe(
|
return observableCombineLatest([isLoaded$, authenticated$]).pipe(
|
||||||
take(1),
|
take(1),
|
||||||
filter(([loaded, authenticated]) => loaded && !authenticated),
|
filter(([loaded, authenticated]) => loaded && !authenticated),
|
||||||
tap(() => this.authService.removeToken()),
|
tap(() => this.authService.removeToken()),
|
||||||
tap(() => this.authService.resetAuthenticationError())
|
tap(() => this.authService.resetAuthenticationError())
|
||||||
);
|
);
|
||||||
}));
|
})), { dispatch: false });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the store is rehydrated in the browser, invalidate all cache hits regarding the
|
* 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
|
* 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),
|
.pipe(ofType(StoreActionTypes.REHYDRATE),
|
||||||
tap(() => this.authorizationsService.invalidateAuthorizationsRequestCache())
|
tap(() => this.authorizationsService.invalidateAuthorizationsRequestCache())
|
||||||
);
|
), { dispatch: false });
|
||||||
|
|
||||||
@Effect()
|
public logOut$: Observable<Action> = createEffect(() => this.actions$
|
||||||
public logOut$: Observable<Action> = this.actions$
|
|
||||||
.pipe(
|
.pipe(
|
||||||
ofType(AuthActionTypes.LOG_OUT),
|
ofType(AuthActionTypes.LOG_OUT),
|
||||||
switchMap(() => {
|
switchMap(() => {
|
||||||
@@ -241,26 +227,23 @@ export class AuthEffects {
|
|||||||
catchError((error) => observableOf(new LogOutErrorAction(error)))
|
catchError((error) => observableOf(new LogOutErrorAction(error)))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
));
|
||||||
|
|
||||||
@Effect({ dispatch: false })
|
public logOutSuccess$: Observable<Action> = createEffect(() => this.actions$
|
||||||
public logOutSuccess$: Observable<Action> = this.actions$
|
|
||||||
.pipe(ofType(AuthActionTypes.LOG_OUT_SUCCESS),
|
.pipe(ofType(AuthActionTypes.LOG_OUT_SUCCESS),
|
||||||
tap(() => this.authService.removeToken()),
|
tap(() => this.authService.removeToken()),
|
||||||
tap(() => this.authService.clearRedirectUrl()),
|
tap(() => this.authService.clearRedirectUrl()),
|
||||||
tap(() => this.authService.refreshAfterLogout())
|
tap(() => this.authService.refreshAfterLogout())
|
||||||
);
|
), { dispatch: false });
|
||||||
|
|
||||||
@Effect({ dispatch: false })
|
public redirectToLoginTokenExpired$: Observable<Action> = createEffect(() => this.actions$
|
||||||
public redirectToLoginTokenExpired$: Observable<Action> = this.actions$
|
|
||||||
.pipe(
|
.pipe(
|
||||||
ofType(AuthActionTypes.REDIRECT_TOKEN_EXPIRED),
|
ofType(AuthActionTypes.REDIRECT_TOKEN_EXPIRED),
|
||||||
tap(() => this.authService.removeToken()),
|
tap(() => this.authService.removeToken()),
|
||||||
tap(() => this.authService.redirectToLoginWhenTokenExpired())
|
tap(() => this.authService.redirectToLoginWhenTokenExpired())
|
||||||
);
|
), { dispatch: false });
|
||||||
|
|
||||||
@Effect()
|
public retrieveMethods$: Observable<Action> = createEffect(() => this.actions$
|
||||||
public retrieveMethods$: Observable<Action> = this.actions$
|
|
||||||
.pipe(
|
.pipe(
|
||||||
ofType(AuthActionTypes.RETRIEVE_AUTH_METHODS),
|
ofType(AuthActionTypes.RETRIEVE_AUTH_METHODS),
|
||||||
switchMap((action: RetrieveAuthMethodsAction) => {
|
switchMap((action: RetrieveAuthMethodsAction) => {
|
||||||
@@ -270,7 +253,7 @@ export class AuthEffects {
|
|||||||
catchError((error) => observableOf(new RetrieveAuthMethodsErrorAction()))
|
catchError((error) => observableOf(new RetrieveAuthMethodsErrorAction()))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For any action that is not in {@link IDLE_TIMER_IGNORE_TYPES} that comes in => Start the idleness timer
|
* 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})
|
* => Return the action to set the user as idle ({@link SetUserAsIdleAction})
|
||||||
* @method trackIdleness
|
* @method trackIdleness
|
||||||
*/
|
*/
|
||||||
@Effect()
|
public trackIdleness$: Observable<Action> = createEffect(() => this.actions$.pipe(
|
||||||
public trackIdleness$: Observable<Action> = this.actions$.pipe(
|
|
||||||
filter((action: Action) => !IDLE_TIMER_IGNORE_TYPES.includes(action.type)),
|
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
|
// Using switchMap the effect will stop subscribing to the previous timer if a new action comes
|
||||||
// in, and start a new timer
|
// in, and start a new timer
|
||||||
@@ -290,7 +272,7 @@ export class AuthEffects {
|
|||||||
// Re-enter the zone to dispatch the action
|
// Re-enter the zone to dispatch the action
|
||||||
observeOn(new EnterZoneScheduler(this.zone, queueScheduler)),
|
observeOn(new EnterZoneScheduler(this.zone, queueScheduler)),
|
||||||
map(() => new SetUserAsIdleAction()),
|
map(() => new SetUserAsIdleAction()),
|
||||||
);
|
));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @constructor
|
* @constructor
|
||||||
|
@@ -20,9 +20,9 @@ describe(`AuthInterceptor`, () => {
|
|||||||
|
|
||||||
const authServiceStub = new AuthServiceStub();
|
const authServiceStub = new AuthServiceStub();
|
||||||
const store: Store<TruncatablesState> = jasmine.createSpyObj('store', {
|
const store: Store<TruncatablesState> = jasmine.createSpyObj('store', {
|
||||||
/* tslint:disable:no-empty */
|
/* eslint-disable no-empty,@typescript-eslint/no-empty-function */
|
||||||
dispatch: {},
|
dispatch: {},
|
||||||
/* tslint:enable:no-empty */
|
/* eslint-enable no-empty, @typescript-eslint/no-empty-function */
|
||||||
select: observableOf(true)
|
select: observableOf(true)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -144,7 +144,7 @@ export class AuthInterceptor implements HttpInterceptor {
|
|||||||
const regex = /(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g;
|
const regex = /(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g;
|
||||||
const realms = completeWWWauthenticateHeader.match(regex);
|
const realms = completeWWWauthenticateHeader.match(regex);
|
||||||
|
|
||||||
// tslint:disable-next-line:forin
|
// eslint-disable-next-line guard-for-in
|
||||||
for (const j in realms) {
|
for (const j in realms) {
|
||||||
|
|
||||||
const splittedRealm = realms[j].split(', ');
|
const splittedRealm = realms[j].split(', ');
|
||||||
|
@@ -112,7 +112,7 @@ export class AuthService {
|
|||||||
if (hasValue(rd.payload) && rd.payload.authenticated) {
|
if (hasValue(rd.payload) && rd.payload.authenticated) {
|
||||||
return rd.payload;
|
return rd.payload;
|
||||||
} else {
|
} 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) {
|
if (hasValue(status) && status.authenticated) {
|
||||||
return status._links.eperson.href;
|
return status._links.eperson.href;
|
||||||
} else {
|
} else {
|
||||||
throw(new Error('Not authenticated'));
|
throw (new Error('Not authenticated'));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -249,7 +249,7 @@ export class AuthService {
|
|||||||
if (hasValue(status) && status.authenticated) {
|
if (hasValue(status) && status.authenticated) {
|
||||||
return status.token;
|
return status.token;
|
||||||
} else {
|
} else {
|
||||||
throw(new Error('Not authenticated'));
|
throw (new Error('Not authenticated'));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -288,7 +288,7 @@ export class AuthService {
|
|||||||
if (hasValue(status) && !status.authenticated) {
|
if (hasValue(status) && !status.authenticated) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
throw(new Error('auth.errors.invalid-user'));
|
throw (new Error('auth.errors.invalid-user'));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@@ -11,9 +11,9 @@ import { Observable } from 'rxjs';
|
|||||||
import { map, find, switchMap } from 'rxjs/operators';
|
import { map, find, switchMap } from 'rxjs/operators';
|
||||||
import { select, Store } from '@ngrx/store';
|
import { select, Store } from '@ngrx/store';
|
||||||
|
|
||||||
import { CoreState } from '../core.reducers';
|
|
||||||
import { isAuthenticated, isAuthenticationLoading } from './selectors';
|
import { isAuthenticated, isAuthenticationLoading } from './selectors';
|
||||||
import { AuthService, LOGIN_ROUTE } from './auth.service';
|
import { AuthService, LOGIN_ROUTE } from './auth.service';
|
||||||
|
import { CoreState } from '../core-state.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevent unauthorized activating and loading of routes
|
* Prevent unauthorized activating and loading of routes
|
||||||
|
@@ -2,7 +2,6 @@ import { autoserialize, deserialize, deserializeAs } from 'cerialize';
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { link, typedObject } from '../../cache/builders/build-decorators';
|
import { link, typedObject } from '../../cache/builders/build-decorators';
|
||||||
import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer';
|
import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer';
|
||||||
import { CacheableObject } from '../../cache/object-cache.reducer';
|
|
||||||
import { RemoteData } from '../../data/remote-data';
|
import { RemoteData } from '../../data/remote-data';
|
||||||
import { EPerson } from '../../eperson/models/eperson.model';
|
import { EPerson } from '../../eperson/models/eperson.model';
|
||||||
import { EPERSON } from '../../eperson/models/eperson.resource-type';
|
import { EPERSON } from '../../eperson/models/eperson.resource-type';
|
||||||
@@ -15,6 +14,7 @@ import { AuthError } from './auth-error.model';
|
|||||||
import { AUTH_STATUS } from './auth-status.resource-type';
|
import { AUTH_STATUS } from './auth-status.resource-type';
|
||||||
import { AuthTokenInfo } from './auth-token-info.model';
|
import { AuthTokenInfo } from './auth-token-info.model';
|
||||||
import { AuthMethod } from './auth.method';
|
import { AuthMethod } from './auth.method';
|
||||||
|
import { CacheableObject } from '../../cache/cacheable-object.model';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
import { CacheableObject } from '../../cache/object-cache.reducer';
|
|
||||||
import { typedObject } from '../../cache/builders/build-decorators';
|
import { typedObject } from '../../cache/builders/build-decorators';
|
||||||
import { excludeFromEquals } from '../../utilities/equals.decorators';
|
import { excludeFromEquals } from '../../utilities/equals.decorators';
|
||||||
import { autoserialize, autoserializeAs, deserialize } from 'cerialize';
|
import { autoserialize, autoserializeAs, deserialize } from 'cerialize';
|
||||||
import { ResourceType } from '../../shared/resource-type';
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
import { SHORT_LIVED_TOKEN } from './short-lived-token.resource-type';
|
import { SHORT_LIVED_TOKEN } from './short-lived-token.resource-type';
|
||||||
import { HALLink } from '../../shared/hal-link.model';
|
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
|
* A short-lived token that can be used to authenticate a rest request
|
||||||
|
@@ -8,6 +8,8 @@ import { createSelector } from '@ngrx/store';
|
|||||||
*/
|
*/
|
||||||
import { AuthState } from './auth.reducer';
|
import { AuthState } from './auth.reducer';
|
||||||
import { AppState } from '../../app.reducer';
|
import { AppState } from '../../app.reducer';
|
||||||
|
import { CoreState } from '../core-state.model';
|
||||||
|
import { coreSelector } from '../core.selectors';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the user state.
|
* Returns the user state.
|
||||||
@@ -15,7 +17,7 @@ import { AppState } from '../../app.reducer';
|
|||||||
* @param {AppState} state Top level state.
|
* @param {AppState} state Top level state.
|
||||||
* @return {AuthState}
|
* @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.
|
* Returns true if the user is authenticated.
|
||||||
|
@@ -36,7 +36,7 @@ export class ServerAuthService extends AuthService {
|
|||||||
if (hasValue(status) && status.authenticated) {
|
if (hasValue(status) && status.authenticated) {
|
||||||
return status._links.eperson.href;
|
return status._links.eperson.href;
|
||||||
} else {
|
} else {
|
||||||
throw(new Error('Not authenticated'));
|
throw (new Error('Not authenticated'));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
import { ResponseParsingService } from '../data/parsing.service';
|
import { ResponseParsingService } from '../data/parsing.service';
|
||||||
import { RestRequest } from '../data/request.models';
|
|
||||||
import { RawRestResponse } from '../dspace-rest/raw-rest-response.model';
|
import { RawRestResponse } from '../dspace-rest/raw-rest-response.model';
|
||||||
import { RestResponse, TokenResponse } from '../cache/response.models';
|
import { RestResponse, TokenResponse } from '../cache/response.models';
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
import { isNotEmpty } from '../../shared/empty.util';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { RestRequest } from '../data/rest-request.model';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
/**
|
/**
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { BrowseDefinitionDataService } from './browse-definition-data.service';
|
import { BrowseDefinitionDataService } from './browse-definition-data.service';
|
||||||
import { FindListOptions } from '../data/request.models';
|
|
||||||
import { followLink } from '../../shared/utils/follow-link-config.model';
|
import { followLink } from '../../shared/utils/follow-link-config.model';
|
||||||
import { EMPTY } from 'rxjs';
|
import { EMPTY } from 'rxjs';
|
||||||
|
import { FindListOptions } from '../data/find-list-options.model';
|
||||||
|
|
||||||
describe(`BrowseDefinitionDataService`, () => {
|
describe(`BrowseDefinitionDataService`, () => {
|
||||||
let service: BrowseDefinitionDataService;
|
let service: BrowseDefinitionDataService;
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable max-classes-per-file */
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { dataService } from '../cache/builders/build-decorators';
|
import { dataService } from '../cache/builders/build-decorators';
|
||||||
import { BROWSE_DEFINITION } from '../shared/browse-definition.resource-type';
|
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 { RequestService } from '../data/request.service';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { CoreState } from '../core.reducers';
|
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { NotificationsService } from '../../shared/notifications/notifications.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 { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { RemoteData } from '../data/remote-data';
|
import { RemoteData } from '../data/remote-data';
|
||||||
import { FindListOptions } from '../data/request.models';
|
|
||||||
import { PaginatedList } from '../data/paginated-list.model';
|
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<BrowseDefinition> {
|
class DataServiceImpl extends DataService<BrowseDefinition> {
|
||||||
protected linkPath = 'browses';
|
protected linkPath = 'browses';
|
||||||
@@ -123,4 +123,3 @@ export class BrowseDefinitionDataService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tslint:enable:max-classes-per-file */
|
|
||||||
|
@@ -5,7 +5,6 @@ import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-bu
|
|||||||
import { getMockRequestService } from '../../shared/mocks/request.service.mock';
|
import { getMockRequestService } from '../../shared/mocks/request.service.mock';
|
||||||
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub';
|
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { RequestEntry } from '../data/request.reducer';
|
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { BrowseDefinition } from '../shared/browse-definition.model';
|
import { BrowseDefinition } from '../shared/browse-definition.model';
|
||||||
import { BrowseEntrySearchOptions } from './browse-entry-search-options.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 { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||||
import { createPaginatedList, getFirstUsedArgumentOfSpyMethod } from '../../shared/testing/utils.test';
|
import { createPaginatedList, getFirstUsedArgumentOfSpyMethod } from '../../shared/testing/utils.test';
|
||||||
import { getMockHrefOnlyDataService } from '../../shared/mocks/href-only-data.service.mock';
|
import { getMockHrefOnlyDataService } from '../../shared/mocks/href-only-data.service.mock';
|
||||||
|
import { RequestEntry } from '../data/request-entry.model';
|
||||||
|
|
||||||
describe('BrowseService', () => {
|
describe('BrowseService', () => {
|
||||||
let scheduler: TestScheduler;
|
let scheduler: TestScheduler;
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
|
/* eslint-disable max-classes-per-file */
|
||||||
import { HALLink } from '../../shared/hal-link.model';
|
import { HALLink } from '../../shared/hal-link.model';
|
||||||
import { HALResource } from '../../shared/hal-resource.model';
|
import { HALResource } from '../../shared/hal-resource.model';
|
||||||
import { ResourceType } from '../../shared/resource-type';
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
import { dataService, getDataServiceFor, getLinkDefinition, link, } from './build-decorators';
|
import { dataService, getDataServiceFor, getLinkDefinition, link, } from './build-decorators';
|
||||||
|
|
||||||
/* tslint:disable:max-classes-per-file */
|
|
||||||
class TestService {
|
class TestService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,4 +80,3 @@ describe('build decorators', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
/* tslint:enable:max-classes-per-file */
|
|
||||||
|
@@ -4,11 +4,11 @@ import { GenericConstructor } from '../../shared/generic-constructor';
|
|||||||
import { HALResource } from '../../shared/hal-resource.model';
|
import { HALResource } from '../../shared/hal-resource.model';
|
||||||
import { ResourceType } from '../../shared/resource-type';
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
import {
|
import {
|
||||||
CacheableObject,
|
|
||||||
TypedObject,
|
|
||||||
getResourceTypeValueFor
|
getResourceTypeValueFor
|
||||||
} from '../object-cache.reducer';
|
} from '../object-cache.reducer';
|
||||||
import { InjectionToken } from '@angular/core';
|
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<any>>('getDataServiceFor', {
|
export const DATA_SERVICE_FACTORY = new InjectionToken<(resourceType: ResourceType) => GenericConstructor<any>>('getDataServiceFor', {
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
|
@@ -1,18 +1,18 @@
|
|||||||
|
/* eslint-disable max-classes-per-file */
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
|
import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
|
||||||
import { FindListOptions } from '../../data/request.models';
|
|
||||||
import { HALLink } from '../../shared/hal-link.model';
|
import { HALLink } from '../../shared/hal-link.model';
|
||||||
import { HALResource } from '../../shared/hal-resource.model';
|
import { HALResource } from '../../shared/hal-resource.model';
|
||||||
import { ResourceType } from '../../shared/resource-type';
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
import { LinkService } from './link.service';
|
import { LinkService } from './link.service';
|
||||||
import { DATA_SERVICE_FACTORY, LINK_DEFINITION_FACTORY, LINK_DEFINITION_MAP_FACTORY } from './build-decorators';
|
import { DATA_SERVICE_FACTORY, LINK_DEFINITION_FACTORY, LINK_DEFINITION_MAP_FACTORY } from './build-decorators';
|
||||||
import { isEmpty } from 'rxjs/operators';
|
import { isEmpty } from 'rxjs/operators';
|
||||||
|
import { FindListOptions } from '../../data/find-list-options.model';
|
||||||
|
|
||||||
const TEST_MODEL = new ResourceType('testmodel');
|
const TEST_MODEL = new ResourceType('testmodel');
|
||||||
let result: any;
|
let result: any;
|
||||||
|
|
||||||
/* tslint:disable:max-classes-per-file */
|
|
||||||
class TestModel implements HALResource {
|
class TestModel implements HALResource {
|
||||||
static type = TEST_MODEL;
|
static type = TEST_MODEL;
|
||||||
|
|
||||||
@@ -251,4 +251,3 @@ describe('LinkService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
/* tslint:enable:max-classes-per-file */
|
|
||||||
|
@@ -13,10 +13,11 @@ import { RequestService } from '../../data/request.service';
|
|||||||
import { UnCacheableObject } from '../../shared/uncacheable-object.model';
|
import { UnCacheableObject } from '../../shared/uncacheable-object.model';
|
||||||
import { RemoteData } from '../../data/remote-data';
|
import { RemoteData } from '../../data/remote-data';
|
||||||
import { Observable, of as observableOf } from 'rxjs';
|
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 { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
|
||||||
import { take } from 'rxjs/operators';
|
import { take } from 'rxjs/operators';
|
||||||
import { HALLink } from '../../shared/hal-link.model';
|
import { HALLink } from '../../shared/hal-link.model';
|
||||||
|
import { RequestEntryState } from '../../data/request-entry-state.model';
|
||||||
|
import { RequestEntry } from '../../data/request-entry.model';
|
||||||
|
|
||||||
describe('RemoteDataBuildService', () => {
|
describe('RemoteDataBuildService', () => {
|
||||||
let service: RemoteDataBuildService;
|
let service: RemoteDataBuildService;
|
||||||
|
@@ -11,14 +11,7 @@ import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.u
|
|||||||
import { FollowLinkConfig, followLink } from '../../../shared/utils/follow-link-config.model';
|
import { FollowLinkConfig, followLink } from '../../../shared/utils/follow-link-config.model';
|
||||||
import { PaginatedList } from '../../data/paginated-list.model';
|
import { PaginatedList } from '../../data/paginated-list.model';
|
||||||
import { RemoteData } from '../../data/remote-data';
|
import { RemoteData } from '../../data/remote-data';
|
||||||
import {
|
|
||||||
RequestEntry,
|
|
||||||
ResponseState,
|
|
||||||
RequestEntryState,
|
|
||||||
hasSucceeded
|
|
||||||
} from '../../data/request.reducer';
|
|
||||||
import { RequestService } from '../../data/request.service';
|
import { RequestService } from '../../data/request.service';
|
||||||
import { getRequestFromRequestHref, getRequestFromRequestUUID } from '../../shared/operators';
|
|
||||||
import { ObjectCacheService } from '../object-cache.service';
|
import { ObjectCacheService } from '../object-cache.service';
|
||||||
import { LinkService } from './link.service';
|
import { LinkService } from './link.service';
|
||||||
import { HALLink } from '../../shared/hal-link.model';
|
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 { PAGINATED_LIST } from '../../data/paginated-list.resource-type';
|
||||||
import { getUrlWithoutEmbedParams } from '../../index/index.selectors';
|
import { getUrlWithoutEmbedParams } from '../../index/index.selectors';
|
||||||
import { getResourceTypeValueFor } from '../object-cache.reducer';
|
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()
|
@Injectable()
|
||||||
export class RemoteDataBuildService {
|
export class RemoteDataBuildService {
|
||||||
|
22
src/app/core/cache/cacheable-object.model.ts
vendored
Normal file
22
src/app/core/cache/cacheable-object.model.ts
vendored
Normal file
@@ -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;
|
||||||
|
}
|
5
src/app/core/cache/object-cache.actions.ts
vendored
5
src/app/core/cache/object-cache.actions.ts
vendored
@@ -1,8 +1,9 @@
|
|||||||
|
/* eslint-disable max-classes-per-file */
|
||||||
import { Action } from '@ngrx/store';
|
import { Action } from '@ngrx/store';
|
||||||
|
|
||||||
import { type } from '../../shared/ngrx/type';
|
import { type } from '../../shared/ngrx/type';
|
||||||
import { CacheableObject } from './object-cache.reducer';
|
|
||||||
import { Operation } from 'fast-json-patch';
|
import { Operation } from 'fast-json-patch';
|
||||||
|
import { CacheableObject } from './cacheable-object.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of ObjectCacheAction type definitions
|
* The list of ObjectCacheAction type definitions
|
||||||
@@ -15,7 +16,6 @@ export const ObjectCacheActionTypes = {
|
|||||||
APPLY_PATCH: type('dspace/core/cache/object/APPLY_PATCH')
|
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
|
* 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
|
* A type to encompass all ObjectCacheActions
|
||||||
|
6
src/app/core/cache/object-cache.effects.ts
vendored
6
src/app/core/cache/object-cache.effects.ts
vendored
@@ -1,6 +1,6 @@
|
|||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { Injectable } from '@angular/core';
|
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 { StoreActionTypes } from '../../store.actions';
|
||||||
import { ResetObjectCacheTimestampsAction } from './object-cache.actions';
|
import { ResetObjectCacheTimestampsAction } from './object-cache.actions';
|
||||||
@@ -16,10 +16,10 @@ export class ObjectCacheEffects {
|
|||||||
* This assumes that the server cached everything a negligible
|
* This assumes that the server cached everything a negligible
|
||||||
* time ago, and will likely need to be revisited later
|
* time ago, and will likely need to be revisited later
|
||||||
*/
|
*/
|
||||||
@Effect() fixTimestampsOnRehydrate = this.actions$
|
fixTimestampsOnRehydrate = createEffect(() => this.actions$
|
||||||
.pipe(ofType(StoreActionTypes.REHYDRATE),
|
.pipe(ofType(StoreActionTypes.REHYDRATE),
|
||||||
map(() => new ResetObjectCacheTimestampsAction(new Date().getTime()))
|
map(() => new ResetObjectCacheTimestampsAction(new Date().getTime()))
|
||||||
);
|
));
|
||||||
|
|
||||||
constructor(private actions$: Actions) {
|
constructor(private actions$: Actions) {
|
||||||
}
|
}
|
||||||
|
@@ -105,10 +105,10 @@ describe('objectCacheReducer', () => {
|
|||||||
const action = new AddToObjectCacheAction(objectToCache, timeCompleted, msToLive, requestUUID, altLink1);
|
const action = new AddToObjectCacheAction(objectToCache, timeCompleted, msToLive, requestUUID, altLink1);
|
||||||
const newState = objectCacheReducer(testState, action);
|
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['foo']).toBe('baz');
|
||||||
expect(newState[selfLink1].data['somethingElse']).toBe(true);
|
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', () => {
|
it('should perform the ADD action without affecting the previous state', () => {
|
||||||
|
38
src/app/core/cache/object-cache.reducer.ts
vendored
38
src/app/core/cache/object-cache.reducer.ts
vendored
@@ -1,18 +1,17 @@
|
|||||||
import { HALLink } from '../shared/hal-link.model';
|
/* eslint-disable max-classes-per-file */
|
||||||
import { HALResource } from '../shared/hal-resource.model';
|
|
||||||
import {
|
import {
|
||||||
|
AddPatchObjectCacheAction,
|
||||||
|
AddToObjectCacheAction,
|
||||||
|
ApplyPatchObjectCacheAction,
|
||||||
ObjectCacheAction,
|
ObjectCacheAction,
|
||||||
ObjectCacheActionTypes,
|
ObjectCacheActionTypes,
|
||||||
AddToObjectCacheAction,
|
|
||||||
RemoveFromObjectCacheAction,
|
RemoveFromObjectCacheAction,
|
||||||
ResetObjectCacheTimestampsAction,
|
ResetObjectCacheTimestampsAction
|
||||||
AddPatchObjectCacheAction,
|
|
||||||
ApplyPatchObjectCacheAction
|
|
||||||
} from './object-cache.actions';
|
} from './object-cache.actions';
|
||||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { CacheEntry } from './cache-entry';
|
import { CacheEntry } from './cache-entry';
|
||||||
import { ResourceType } from '../shared/resource-type';
|
|
||||||
import { applyPatch, Operation } from 'fast-json-patch';
|
import { applyPatch, Operation } from 'fast-json-patch';
|
||||||
|
import { CacheableObject } from './cacheable-object.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface to represent a JsonPatch
|
* An interface to represent a JsonPatch
|
||||||
@@ -29,11 +28,6 @@ export interface Patch {
|
|||||||
operations: Operation[];
|
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
|
* 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
|
* An entry in the ObjectCache
|
||||||
*/
|
*/
|
||||||
@@ -110,7 +85,6 @@ export class ObjectCacheEntry implements CacheEntry {
|
|||||||
alternativeLinks: string[];
|
alternativeLinks: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tslint:enable:max-classes-per-file */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ObjectCache State
|
* The ObjectCache State
|
||||||
|
@@ -7,7 +7,7 @@ import { Operation } from 'fast-json-patch';
|
|||||||
import { empty, of as observableOf } from 'rxjs';
|
import { empty, of as observableOf } from 'rxjs';
|
||||||
import { first } from 'rxjs/operators';
|
import { first } from 'rxjs/operators';
|
||||||
|
|
||||||
import { coreReducers, CoreState } from '../core.reducers';
|
import { coreReducers} from '../core.reducers';
|
||||||
import { RestRequestMethod } from '../data/rest-request-method';
|
import { RestRequestMethod } from '../data/rest-request-method';
|
||||||
import { Item } from '../shared/item.model';
|
import { Item } from '../shared/item.model';
|
||||||
import {
|
import {
|
||||||
@@ -20,10 +20,11 @@ import { Patch } from './object-cache.reducer';
|
|||||||
import { ObjectCacheService } from './object-cache.service';
|
import { ObjectCacheService } from './object-cache.service';
|
||||||
import { AddToSSBAction } from './server-sync-buffer.actions';
|
import { AddToSSBAction } from './server-sync-buffer.actions';
|
||||||
import { RemoveFromIndexBySubstringAction } from '../index/index.actions';
|
import { RemoveFromIndexBySubstringAction } from '../index/index.actions';
|
||||||
import { IndexName } from '../index/index.reducer';
|
|
||||||
import { HALLink } from '../shared/hal-link.model';
|
import { HALLink } from '../shared/hal-link.model';
|
||||||
import { storeModuleConfig } from '../../app.reducer';
|
import { storeModuleConfig } from '../../app.reducer';
|
||||||
import { TestColdObservable } from 'jasmine-marbles/src/test-observables';
|
import { TestColdObservable } from 'jasmine-marbles/src/test-observables';
|
||||||
|
import { IndexName } from '../index/index-name.model';
|
||||||
|
import { CoreState } from '../core-state.model';
|
||||||
|
|
||||||
describe('ObjectCacheService', () => {
|
describe('ObjectCacheService', () => {
|
||||||
let service: ObjectCacheService;
|
let service: ObjectCacheService;
|
||||||
|
7
src/app/core/cache/object-cache.service.ts
vendored
7
src/app/core/cache/object-cache.service.ts
vendored
@@ -5,7 +5,7 @@ import { combineLatest as observableCombineLatest, Observable, of as observableO
|
|||||||
|
|
||||||
import { distinctUntilChanged, filter, map, mergeMap, switchMap, take } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, map, mergeMap, switchMap, take } from 'rxjs/operators';
|
||||||
import { hasValue, isNotEmpty, isEmpty } from '../../shared/empty.util';
|
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 { coreSelector } from '../core.selectors';
|
||||||
import { RestRequestMethod } from '../data/rest-request-method';
|
import { RestRequestMethod } from '../data/rest-request-method';
|
||||||
import {
|
import {
|
||||||
@@ -22,11 +22,12 @@ import {
|
|||||||
RemoveFromObjectCacheAction
|
RemoveFromObjectCacheAction
|
||||||
} from './object-cache.actions';
|
} 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 { AddToSSBAction } from './server-sync-buffer.actions';
|
||||||
import { RemoveFromIndexBySubstringAction } from '../index/index.actions';
|
import { RemoveFromIndexBySubstringAction } from '../index/index.actions';
|
||||||
import { IndexName } from '../index/index.reducer';
|
|
||||||
import { HALLink } from '../shared/hal-link.model';
|
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
|
* The base selector function to select the object cache in the store
|
||||||
|
5
src/app/core/cache/response.models.ts
vendored
5
src/app/core/cache/response.models.ts
vendored
@@ -1,11 +1,11 @@
|
|||||||
import { RequestError } from '../data/request.models';
|
/* eslint-disable max-classes-per-file */
|
||||||
import { PageInfo } from '../shared/page-info.model';
|
import { PageInfo } from '../shared/page-info.model';
|
||||||
import { ConfigObject } from '../config/models/config.model';
|
import { ConfigObject } from '../config/models/config.model';
|
||||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||||
import { HALLink } from '../shared/hal-link.model';
|
import { HALLink } from '../shared/hal-link.model';
|
||||||
import { UnCacheableObject } from '../shared/uncacheable-object.model';
|
import { UnCacheableObject } from '../shared/uncacheable-object.model';
|
||||||
|
import { RequestError } from '../data/request-error.model';
|
||||||
|
|
||||||
/* tslint:disable:max-classes-per-file */
|
|
||||||
export class RestResponse {
|
export class RestResponse {
|
||||||
public toCache = true;
|
public toCache = true;
|
||||||
public timeCompleted: number;
|
public timeCompleted: number;
|
||||||
@@ -140,4 +140,3 @@ export class FilteredDiscoveryQueryResponse extends RestResponse {
|
|||||||
super(true, statusCode, statusText);
|
super(true, statusCode, statusText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* tslint:enable:max-classes-per-file */
|
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable max-classes-per-file */
|
||||||
import { Action } from '@ngrx/store';
|
import { Action } from '@ngrx/store';
|
||||||
|
|
||||||
import { type } from '../../shared/ngrx/type';
|
import { type } from '../../shared/ngrx/type';
|
||||||
@@ -12,7 +13,6 @@ export const ServerSyncBufferActionTypes = {
|
|||||||
EMPTY: type('dspace/core/cache/syncbuffer/EMPTY'),
|
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
|
* 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
|
* A type to encompass all ServerSyncBufferActions
|
||||||
|
@@ -83,7 +83,7 @@ describe('ServerSyncBufferEffects', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return a COMMIT action in response to an ADD action', () => {
|
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 }) => {
|
testScheduler.run(({ hot, expectObservable }) => {
|
||||||
actions = hot('a', {
|
actions = hot('a', {
|
||||||
a: {
|
a: {
|
||||||
|
12
src/app/core/cache/server-sync-buffer.effects.ts
vendored
12
src/app/core/cache/server-sync-buffer.effects.ts
vendored
@@ -1,6 +1,6 @@
|
|||||||
import { delay, exhaustMap, map, switchMap, take } from 'rxjs/operators';
|
import { delay, exhaustMap, map, switchMap, take } from 'rxjs/operators';
|
||||||
import { Injectable } from '@angular/core';
|
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 { coreSelector } from '../core.selectors';
|
||||||
import {
|
import {
|
||||||
AddToSSBAction,
|
AddToSSBAction,
|
||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
EmptySSBAction,
|
EmptySSBAction,
|
||||||
ServerSyncBufferActionTypes
|
ServerSyncBufferActionTypes
|
||||||
} from './server-sync-buffer.actions';
|
} from './server-sync-buffer.actions';
|
||||||
import { CoreState } from '../core.reducers';
|
|
||||||
import { Action, createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
import { Action, createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
||||||
import { ServerSyncBufferEntry, ServerSyncBufferState } from './server-sync-buffer.reducer';
|
import { ServerSyncBufferEntry, ServerSyncBufferState } from './server-sync-buffer.reducer';
|
||||||
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
|
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 { ObjectCacheEntry } from './object-cache.reducer';
|
||||||
import { Operation } from 'fast-json-patch';
|
import { Operation } from 'fast-json-patch';
|
||||||
import { NoOpAction } from '../../shared/ngrx/no-op.action';
|
import { NoOpAction } from '../../shared/ngrx/no-op.action';
|
||||||
|
import { CoreState } from '../core-state.model';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ServerSyncBufferEffects {
|
export class ServerSyncBufferEffects {
|
||||||
@@ -32,7 +32,7 @@ export class ServerSyncBufferEffects {
|
|||||||
* Then dispatch a CommitSSBAction
|
* Then dispatch a CommitSSBAction
|
||||||
* When the delay is running, no new AddToSSBActions are processed in this effect
|
* When the delay is running, no new AddToSSBActions are processed in this effect
|
||||||
*/
|
*/
|
||||||
@Effect() setTimeoutForServerSync = this.actions$
|
setTimeoutForServerSync = createEffect(() => this.actions$
|
||||||
.pipe(
|
.pipe(
|
||||||
ofType(ServerSyncBufferActionTypes.ADD),
|
ofType(ServerSyncBufferActionTypes.ADD),
|
||||||
exhaustMap((action: AddToSSBAction) => {
|
exhaustMap((action: AddToSSBAction) => {
|
||||||
@@ -42,7 +42,7 @@ export class ServerSyncBufferEffects {
|
|||||||
delay(timeoutInSeconds * 1000),
|
delay(timeoutInSeconds * 1000),
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When a CommitSSBAction is dispatched
|
* 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 of actions is not empty, also dispatch an EmptySSBAction
|
||||||
* When the list is empty dispatch a NO_ACTION placeholder action
|
* When the list is empty dispatch a NO_ACTION placeholder action
|
||||||
*/
|
*/
|
||||||
@Effect() commitServerSyncBuffer = this.actions$
|
commitServerSyncBuffer = createEffect(() => this.actions$
|
||||||
.pipe(
|
.pipe(
|
||||||
ofType(ServerSyncBufferActionTypes.COMMIT),
|
ofType(ServerSyncBufferActionTypes.COMMIT),
|
||||||
switchMap((action: CommitSSBAction) => {
|
switchMap((action: CommitSSBAction) => {
|
||||||
@@ -86,7 +86,7 @@ export class ServerSyncBufferEffects {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* private method to create an ApplyPatchObjectCacheAction based on a cache entry
|
* private method to create an ApplyPatchObjectCacheAction based on a cache entry
|
||||||
|
6
src/app/core/cache/typed-object.model.ts
vendored
Normal file
6
src/app/core/cache/typed-object.model.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { ResourceType } from '../shared/resource-type';
|
||||||
|
|
||||||
|
export abstract class TypedObject {
|
||||||
|
static type: ResourceType;
|
||||||
|
type: ResourceType;
|
||||||
|
}
|
@@ -3,11 +3,12 @@ import { TestScheduler } from 'rxjs/testing';
|
|||||||
import { getMockRequestService } from '../../shared/mocks/request.service.mock';
|
import { getMockRequestService } from '../../shared/mocks/request.service.mock';
|
||||||
import { ConfigService } from './config.service';
|
import { ConfigService } from './config.service';
|
||||||
import { RequestService } from '../data/request.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 { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub';
|
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock';
|
import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock';
|
||||||
|
import { FindListOptions } from '../data/find-list-options.model';
|
||||||
|
|
||||||
const LINK_NAME = 'test';
|
const LINK_NAME = 'test';
|
||||||
const BROWSE = 'search/findByCollection';
|
const BROWSE = 'search/findByCollection';
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user