diff --git a/.codecov.yml b/.codecov.yml index 3dba42ef37..326dd3e0b2 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -17,10 +17,9 @@ coverage: # Configuration for patch-level checks. This checks the relative coverage of the new PR code ONLY. patch: default: - # For each PR, make sure the coverage of the new code is within 1% of current overall coverage. - # We let 'patch' be more lenient as we only require *project* coverage to not drop significantly. - target: auto - threshold: 1% + # Enable informational mode, which just provides info to reviewers & always passes + # https://docs.codecov.io/docs/commit-status#section-informational + informational: true # Turn PR comments "off". This feature adds the code coverage summary as a # comment on each PR. See https://docs.codecov.io/docs/pull-request-comments diff --git a/.editorconfig b/.editorconfig index 70ce43b68e..15d4c87b14 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,3 +12,6 @@ trim_trailing_whitespace = true [*.md] insert_final_newline = false trim_trailing_whitespace = false + +[*.ts] +quote_type = single diff --git a/package.json b/package.json index 5ceb899322..60473fbffc 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "clean:bld": "rimraf build", "clean:node": "rimraf node_modules", "clean:prod": "yarn run clean:coverage && yarn run clean:doc && yarn run clean:dist && yarn run clean:log && yarn run clean:json && yarn run clean:bld", - "clean": "yarn run clean:prod && yarn run clean:node && yarn run clean:env", + "clean": "yarn run clean:prod && yarn run clean:env && yarn run clean:node", "clean:env": "rimraf src/environments/environment.ts", "sync-i18n": "yarn run config:dev && ts-node --project ./tsconfig.ts-node.json scripts/sync-i18n-files.ts" }, @@ -88,6 +88,7 @@ "debug-loader": "^0.0.1", "deepmerge": "^4.2.2", "express": "4.16.2", + "express-rate-limit": "^5.1.3", "fast-json-patch": "^2.0.7", "file-saver": "^1.3.8", "filesize": "^6.1.0", diff --git a/scripts/set-env.ts b/scripts/set-env.ts index 0aa106538c..5eee22a4be 100644 --- a/scripts/set-env.ts +++ b/scripts/set-env.ts @@ -54,13 +54,6 @@ import(environmentFilePath) function generateEnvironmentFile(file: GlobalConfig): void { file.production = production; buildBaseUrls(file); - - // TODO remove workaround in beta 5 - if (file.rest.nameSpace.match("(.*)/api/?$") !== null) { - file.rest.nameSpace = getNameSpace(file.rest.nameSpace); - console.log(colors.white.bgMagenta.bold(`The rest.nameSpace property in your environment file or in your DSPACE_REST_NAMESPACE environment variable ends with '/api'.\nThis is deprecated. As '/api' isn't configurable on the rest side, it shouldn't be repeated in every environment file.\nPlease change the rest nameSpace to '${file.rest.nameSpace}'`)); - } - const contents = `export const environment = ` + JSON.stringify(file); writeFile(targetPath, contents, (err) => { if (err) { @@ -119,16 +112,5 @@ function getPort(port: number): string { } function getNameSpace(nameSpace: string): string { - // TODO remove workaround in beta 5 - const apiMatches = nameSpace.match("(.*)/api/?$"); - if (apiMatches != null) { - let newValue = '/' - if (hasValue(apiMatches[1])) { - newValue = apiMatches[1]; - } - return newValue; - } - else { - return nameSpace ? nameSpace.charAt(0) === '/' ? nameSpace : '/' + nameSpace : ''; - } + return nameSpace ? nameSpace.charAt(0) === '/' ? nameSpace : '/' + nameSpace : ''; } diff --git a/server.ts b/server.ts index c640a95ef4..202d5a58bc 100644 --- a/server.ts +++ b/server.ts @@ -28,12 +28,13 @@ import * as compression from 'compression'; import * as cookieParser from 'cookie-parser'; import { join } from 'path'; -import { enableProdMode, NgModuleFactory, Type } from '@angular/core'; +import { enableProdMode } from '@angular/core'; import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens'; import { environment } from './src/environments/environment'; import { createProxyMiddleware } from 'http-proxy-middleware'; -import { hasValue, hasNoValue } from './src/app/shared/empty.util'; +import { hasNoValue, hasValue } from './src/app/shared/empty.util'; +import { UIServerConfig } from './src/config/ui-server-config.interface'; /* * Set path for the browser application's dist folder @@ -121,6 +122,19 @@ function cacheControl(req, res, next) { next(); } +/** + * Checks if the rateLimiter property is present + * When it is present, the rateLimiter will be enabled. When it is undefined, the rateLimiter will be disabled. + */ +if (hasValue((environment.ui as UIServerConfig).rateLimiter)) { + const RateLimit = require('express-rate-limit'); + const limiter = new RateLimit({ + windowMs: (environment.ui as UIServerConfig).rateLimiter.windowMs, + max: (environment.ui as UIServerConfig).rateLimiter.max + }); + app.use(limiter); +} + /* * Serve static resources (images, i18n messages, …) */ @@ -209,8 +223,9 @@ if (environment.ui.ssl) { certificate: certificate }); } else { + console.warn('Disabling certificate validation and proceeding with a self-signed certificate. If this is a production server, it is recommended that you configure a valid certificate instead.'); - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; // lgtm[js/disabling-certificate-validation] pem.createCertificate({ days: 1, diff --git a/src/app/+admin/admin-search-page/admin-search-results/admin-search-result-grid-element/collection-search-result/collection-admin-search-result-grid-element.component.spec.ts b/src/app/+admin/admin-search-page/admin-search-results/admin-search-result-grid-element/collection-search-result/collection-admin-search-result-grid-element.component.spec.ts index 34db71db77..8ca02fa8ad 100644 --- a/src/app/+admin/admin-search-page/admin-search-results/admin-search-result-grid-element/collection-search-result/collection-admin-search-result-grid-element.component.spec.ts +++ b/src/app/+admin/admin-search-page/admin-search-results/admin-search-result-grid-element/collection-search-result/collection-admin-search-result-grid-element.component.spec.ts @@ -13,6 +13,7 @@ import { Collection } from '../../../../../core/shared/collection.model'; import { By } from '@angular/platform-browser'; import { RouterTestingModule } from '@angular/router/testing'; import { getCollectionEditRoute } from '../../../../../+collection-page/collection-page-routing-paths'; +import { LinkService } from '../../../../../core/cache/builders/link.service'; describe('CollectionAdminSearchResultGridElementComponent', () => { let component: CollectionAdminSearchResultGridElementComponent; @@ -26,6 +27,11 @@ describe('CollectionAdminSearchResultGridElementComponent', () => { searchResult.indexableObject = new Collection(); searchResult.indexableObject.uuid = id; } + + const linkService = jasmine.createSpyObj('linkService', { + resolveLink: {} + }); + beforeEach(async(() => { init(); TestBed.configureTestingModule({ @@ -39,6 +45,7 @@ describe('CollectionAdminSearchResultGridElementComponent', () => { providers: [ { provide: TruncatableService, useValue: mockTruncatableService }, { provide: BitstreamDataService, useValue: {} }, + { provide: LinkService, useValue: linkService} ] }) .compileComponents(); diff --git a/src/app/+admin/admin-search-page/admin-search-results/admin-search-result-grid-element/community-search-result/community-admin-search-result-grid-element.component.spec.ts b/src/app/+admin/admin-search-page/admin-search-results/admin-search-result-grid-element/community-search-result/community-admin-search-result-grid-element.component.spec.ts index 85c81d55a4..6a834dd753 100644 --- a/src/app/+admin/admin-search-page/admin-search-results/admin-search-result-grid-element/community-search-result/community-admin-search-result-grid-element.component.spec.ts +++ b/src/app/+admin/admin-search-page/admin-search-results/admin-search-result-grid-element/community-search-result/community-admin-search-result-grid-element.component.spec.ts @@ -14,8 +14,8 @@ import { RouterTestingModule } from '@angular/router/testing'; import { CommunityAdminSearchResultGridElementComponent } from './community-admin-search-result-grid-element.component'; import { CommunitySearchResult } from '../../../../../shared/object-collection/shared/community-search-result.model'; import { Community } from '../../../../../core/shared/community.model'; -import { CommunityAdminSearchResultListElementComponent } from '../../admin-search-result-list-element/community-search-result/community-admin-search-result-list-element.component'; import { getCommunityEditRoute } from '../../../../../+community-page/community-page-routing-paths'; +import { LinkService } from '../../../../../core/cache/builders/link.service'; describe('CommunityAdminSearchResultGridElementComponent', () => { let component: CommunityAdminSearchResultGridElementComponent; @@ -29,6 +29,11 @@ describe('CommunityAdminSearchResultGridElementComponent', () => { searchResult.indexableObject = new Community(); searchResult.indexableObject.uuid = id; } + + const linkService = jasmine.createSpyObj('linkService', { + resolveLink: {} + }); + beforeEach(async(() => { init(); TestBed.configureTestingModule({ @@ -42,6 +47,7 @@ describe('CommunityAdminSearchResultGridElementComponent', () => { providers: [ { provide: TruncatableService, useValue: mockTruncatableService }, { provide: BitstreamDataService, useValue: {} }, + { provide: LinkService, useValue: linkService} ], schemas: [NO_ERRORS_SCHEMA] }) diff --git a/src/app/+collection-page/collection-page.component.html b/src/app/+collection-page/collection-page.component.html index 98552ed40b..beb7413415 100644 --- a/src/app/+collection-page/collection-page.component.html +++ b/src/app/+collection-page/collection-page.component.html @@ -3,37 +3,41 @@ *ngVar="(collectionRD$ | async) as collectionRD">