From f4c0df176ef5590dae0a0fea595e7079c55a63c7 Mon Sep 17 00:00:00 2001 From: William Welling Date: Thu, 12 Oct 2017 19:08:03 -0500 Subject: [PATCH] metadata service --- package.json | 49 ++- .../collection-page-routing.module.ts | 7 +- .../community-page-routing.module.ts | 7 +- .../+item-page/item-page-routing.module.ts | 5 +- .../search-page.component.spec.ts | 2 +- src/app/app.component.spec.ts | 8 +- src/app/app.component.ts | 9 +- src/app/core/core.module.ts | 43 ++- src/app/core/data/data.service.ts | 7 +- .../core/metadata/metadata.service.spec.ts | 114 ++++++ src/app/core/metadata/metadata.service.ts | 354 ++++++++++++++++++ src/app/footer/footer.component.spec.ts | 2 +- src/app/shared/error/error.component.spec.ts | 6 +- .../shared/loading/loading.component.spec.ts | 6 +- src/app/shared/mocks/mock-action.ts | 6 + src/app/shared/mocks/mock-active-router.ts | 34 ++ .../shared/mocks/mock-host-window-service.ts | 19 + src/app/shared/mocks/mock-item.ts | 164 ++++++++ src/app/shared/mocks/mock-metadata-service.ts | 9 + src/app/shared/mocks/mock-normalized-item.ts | 114 ++++++ src/app/shared/mocks/mock-router.ts | 4 + src/app/shared/mocks/mock-store.ts | 23 ++ src/app/shared/mocks/mock-translate-loader.ts | 8 + .../pagination/pagination.component.spec.ts | 22 +- yarn.lock | 166 ++++---- 25 files changed, 1018 insertions(+), 170 deletions(-) create mode 100644 src/app/core/metadata/metadata.service.spec.ts create mode 100644 src/app/core/metadata/metadata.service.ts create mode 100644 src/app/shared/mocks/mock-action.ts create mode 100644 src/app/shared/mocks/mock-active-router.ts create mode 100644 src/app/shared/mocks/mock-host-window-service.ts create mode 100644 src/app/shared/mocks/mock-item.ts create mode 100644 src/app/shared/mocks/mock-metadata-service.ts create mode 100644 src/app/shared/mocks/mock-normalized-item.ts create mode 100644 src/app/shared/mocks/mock-router.ts create mode 100644 src/app/shared/mocks/mock-store.ts create mode 100644 src/app/shared/mocks/mock-translate-loader.ts diff --git a/package.json b/package.json index 6f761ef47c..9a5e024941 100644 --- a/package.json +++ b/package.json @@ -12,15 +12,14 @@ }, "scripts": { "global": "npm install -g @angular/cli marked node-gyp nodemon node-nightly npm-check-updates npm-run-all rimraf typescript ts-node typedoc webpack webpack-bundle-analyzer pm2 rollup", - "clean:coverage": "yarn run rimraf coverage", - "clean:dist": "yarn run rimraf dist", - "clean:doc": "yarn run rimraf doc", - "clean:log": "yarn run rimraf *.log*", - "clean:json": "yarn run rimraf *.records.json", - "clean:node": "yarn run rimraf node_modules", + "clean:coverage": "rimraf coverage", + "clean:dist": "rimraf dist", + "clean:doc": "rimraf doc", + "clean:log": "rimraf *.log*", + "clean:json": "rimraf *.records.json", + "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", "clean": "yarn run clean:prod && yarn run clean:node", - "rimraf": "rimraf", "prebuild": "yarn run clean:dist", "prebuild:aot": "yarn run prebuild", "prebuild:prod": "yarn run prebuild", @@ -69,15 +68,15 @@ "coverage": "http-server -c-1 -o -p 9875 ./coverage" }, "dependencies": { - "@angular/animations": "4.4.4", - "@angular/common": "4.4.4", - "@angular/core": "4.4.4", - "@angular/forms": "4.4.4", - "@angular/http": "4.4.4", - "@angular/platform-browser": "4.4.4", - "@angular/platform-browser-dynamic": "4.4.4", - "@angular/platform-server": "4.4.4", - "@angular/router": "4.4.4", + "@angular/animations": "4.4.5", + "@angular/common": "4.4.5", + "@angular/core": "4.4.5", + "@angular/forms": "4.4.5", + "@angular/http": "4.4.5", + "@angular/platform-browser": "4.4.5", + "@angular/platform-browser-dynamic": "4.4.5", + "@angular/platform-server": "4.4.5", + "@angular/router": "4.4.5", "@angularclass/bootloader": "1.0.1", "@angularclass/idle-preload": "1.0.4", "@ng-bootstrap/ng-bootstrap": "1.0.0-beta.5", @@ -89,11 +88,11 @@ "@ngx-translate/http-loader": "2.0.0", "body-parser": "1.18.2", "bootstrap": "v4.0.0-beta", - "cerialize": "0.1.16", + "cerialize": "0.1.18", "compression": "1.7.1", "cookie-parser": "1.4.3", "core-js": "2.5.1", - "express": "4.16.1", + "express": "4.16.2", "express-session": "1.15.6", "font-awesome": "4.7.0", "http-server": "0.10.0", @@ -103,7 +102,7 @@ "methods": "1.1.2", "morgan": "1.9.0", "ngx-pagination": "3.0.1", - "pem": "1.12.2", + "pem": "1.12.3", "reflect-metadata": "0.1.10", "rxjs": "5.4.3", "ts-md5": "1.2.2", @@ -111,10 +110,10 @@ "zone.js": "0.8.18" }, "devDependencies": { - "@angular/compiler": "4.4.4", - "@angular/compiler-cli": "4.4.4", + "@angular/compiler": "4.4.5", + "@angular/compiler-cli": "4.4.5", "@ngrx/store-devtools": "4.0.0", - "@ngtools/webpack": "1.7.2", + "@ngtools/webpack": "1.7.4", "@types/cookie-parser": "1.4.1", "@types/deep-freeze": "0.1.1", "@types/express": "4.0.37", @@ -168,13 +167,13 @@ "postcss-apply": "0.8.0", "postcss-cli": "4.1.1", "postcss-cssnext": "3.0.2", - "postcss-loader": "2.0.6", + "postcss-loader": "2.0.7", "postcss-responsive-type": "1.0.0", "postcss-smart-import": "0.7.5", "protractor": "5.1.2", "protractor-istanbul-plugin": "2.0.0", "raw-loader": "0.5.1", - "resolve-url-loader": "2.1.0", + "resolve-url-loader": "2.1.1", "rimraf": "2.6.2", "rollup": "0.50.0", "rollup-plugin-commonjs": "8.2.1", @@ -191,7 +190,7 @@ "tslint": "5.7.0", "typedoc": "0.9.0", "typescript": "2.5.3", - "webpack": "3.6.0", + "webpack": "3.7.1", "webpack-bundle-analyzer": "2.9.0", "webpack-dev-middleware": "1.12.0", "webpack-dev-server": "2.9.1", diff --git a/src/app/+collection-page/collection-page-routing.module.ts b/src/app/+collection-page/collection-page-routing.module.ts index c62643acc2..fc7f58b6fb 100644 --- a/src/app/+collection-page/collection-page-routing.module.ts +++ b/src/app/+collection-page/collection-page-routing.module.ts @@ -2,12 +2,15 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { CollectionPageComponent } from './collection-page.component'; +import { NormalizedCollection } from '../core/cache/models/normalized-collection.model'; @NgModule({ imports: [ RouterModule.forChild([ - { path: ':id', component: CollectionPageComponent, pathMatch: 'full' } + { path: ':id', component: CollectionPageComponent, pathMatch: 'full', data: { type: NormalizedCollection } } ]) ] }) -export class CollectionPageRoutingModule { } +export class CollectionPageRoutingModule { + +} diff --git a/src/app/+community-page/community-page-routing.module.ts b/src/app/+community-page/community-page-routing.module.ts index e090a93087..e761717808 100644 --- a/src/app/+community-page/community-page-routing.module.ts +++ b/src/app/+community-page/community-page-routing.module.ts @@ -2,12 +2,15 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { CommunityPageComponent } from './community-page.component'; +import { NormalizedCommunity } from '../core/cache/models/normalized-community.model'; @NgModule({ imports: [ RouterModule.forChild([ - { path: ':id', component: CommunityPageComponent, pathMatch: 'full' } + { path: ':id', component: CommunityPageComponent, pathMatch: 'full', data: { type: NormalizedCommunity } } ]) ] }) -export class CommunityPageRoutingModule { } +export class CommunityPageRoutingModule { + +} diff --git a/src/app/+item-page/item-page-routing.module.ts b/src/app/+item-page/item-page-routing.module.ts index 9dca4d0f6e..e315b18a9c 100644 --- a/src/app/+item-page/item-page-routing.module.ts +++ b/src/app/+item-page/item-page-routing.module.ts @@ -3,12 +3,13 @@ import { RouterModule } from '@angular/router'; import { ItemPageComponent } from './simple/item-page.component'; import { FullItemPageComponent } from './full/full-item-page.component'; +import { NormalizedItem } from '../core/cache/models/normalized-item.model'; @NgModule({ imports: [ RouterModule.forChild([ - { path: ':id', component: ItemPageComponent, pathMatch: 'full' }, - { path: ':id/full', component: FullItemPageComponent } + { path: ':id', component: ItemPageComponent, pathMatch: 'full', data: { type: NormalizedItem } }, + { path: ':id/full', component: FullItemPageComponent, data: { type: NormalizedItem } } ]) ] }) diff --git a/src/app/+search-page/search-page.component.spec.ts b/src/app/+search-page/search-page.component.spec.ts index a3e314db69..0b00021ed6 100644 --- a/src/app/+search-page/search-page.component.spec.ts +++ b/src/app/+search-page/search-page.component.spec.ts @@ -104,7 +104,7 @@ describe('SearchPageComponent', () => { (comp as any).updateSearchResults({}); expect(comp.results as any).toBe(mockResults); - }); + }); }); }); diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index 99cf1068e4..0854c5756e 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -1,4 +1,3 @@ -// ... test imports import { async, ComponentFixture, @@ -23,14 +22,18 @@ import { AppComponent } from './app.component'; import { HostWindowState } from './shared/host-window.reducer'; import { HostWindowResizeAction } from './shared/host-window.actions'; -import { MockTranslateLoader } from './shared/testing/mock-translate-loader'; import { BrowserTransferStateModule } from '../modules/transfer-state/browser-transfer-state.module'; import { BrowserTransferStoreModule } from '../modules/transfer-store/browser-transfer-store.module'; +import { MetadataService } from './core/metadata/metadata.service'; + import { GLOBAL_CONFIG, ENV_CONFIG } from '../config'; import { NativeWindowRef, NativeWindowService } from './shared/window.service'; +import { MockTranslateLoader } from './shared/mocks/mock-translate-loader'; +import { MockMetadataService } from './shared/mocks/mock-metadata-service'; + let comp: AppComponent; let fixture: ComponentFixture; let de: DebugElement; @@ -57,6 +60,7 @@ describe('App component', () => { providers: [ { provide: GLOBAL_CONFIG, useValue: ENV_CONFIG }, { provide: NativeWindowService, useValue: new NativeWindowRef() }, + { provide: MetadataService, useValue: new MockMetadataService() }, AppComponent ], schemas: [CUSTOM_ELEMENTS_SCHEMA] diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 9ea58365df..221c1c37d1 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -12,12 +12,10 @@ import { TranslateService } from '@ngx-translate/core'; import { Store } from '@ngrx/store'; import { TransferState } from '../modules/transfer-state/transfer-state'; - import { HostWindowState } from './shared/host-window.reducer'; - import { HostWindowResizeAction } from './shared/host-window.actions'; - import { NativeWindowRef, NativeWindowService } from './shared/window.service'; +import { MetadataService } from './core/metadata/metadata.service'; import { GLOBAL_CONFIG, GlobalConfig } from '../config'; @@ -35,13 +33,16 @@ export class AppComponent implements OnInit { @Inject(NativeWindowService) private _window: NativeWindowRef, private translate: TranslateService, private cache: TransferState, - private store: Store + private store: Store, + private metadata: MetadataService ) { // this language will be used as a fallback when a translation isn't found in the current language translate.setDefaultLang('en'); // the lang to use, if the lang isn't available, it will use the current loader to get them translate.use('en'); + metadata.listenForRouteChange(); + if (config.debug) { console.info(config); } diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 9742a6b500..b782f1d4fc 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -1,29 +1,35 @@ -import { NgModule, Optional, SkipSelf, ModuleWithProviders } from '@angular/core'; +import { + NgModule, + Optional, + SkipSelf, + ModuleWithProviders +} from '@angular/core'; import { CommonModule } from '@angular/common'; -import { isNotEmpty } from '../shared/empty.util'; -import { DSpaceRESTv2Service } from './dspace-rest-v2/dspace-rest-v2.service'; -import { ObjectCacheService } from './cache/object-cache.service'; -import { ResponseCacheService } from './cache/response-cache.service'; -import { CollectionDataService } from './data/collection-data.service'; -import { ItemDataService } from './data/item-data.service'; -import { RequestService } from './data/request.service'; -import { RemoteDataBuildService } from './cache/builders/remote-data-build.service'; -import { CommunityDataService } from './data/community-data.service'; -import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; -import { coreEffects } from './core.effects'; -import { EffectsModule } from '@ngrx/effects'; import { StoreModule } from '@ngrx/store'; +import { EffectsModule } from '@ngrx/effects'; + +import { coreEffects } from './core.effects'; import { coreReducers } from './core.reducers'; -import { DSOResponseParsingService } from './data/dso-response-parsing.service'; -import { RootResponseParsingService } from './data/root-response-parsing.service'; + +import { isNotEmpty } from '../shared/empty.util'; import { ApiService } from '../shared/api.service'; - +import { CollectionDataService } from './data/collection-data.service'; +import { CommunityDataService } from './data/community-data.service'; +import { DSOResponseParsingService } from './data/dso-response-parsing.service'; +import { DSpaceRESTv2Service } from './dspace-rest-v2/dspace-rest-v2.service'; import { HostWindowService } from '../shared/host-window.service'; -import { NativeWindowFactory, NativeWindowService } from '../shared/window.service'; - +import { ItemDataService } from './data/item-data.service'; +import { MetadataService } from './metadata/metadata.service'; +import { ObjectCacheService } from './cache/object-cache.service'; +import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; +import { RemoteDataBuildService } from './cache/builders/remote-data-build.service'; +import { RequestService } from './data/request.service'; +import { ResponseCacheService } from './cache/response-cache.service'; +import { RootResponseParsingService } from './data/root-response-parsing.service'; import { ServerResponseService } from '../shared/server-response.service'; +import { NativeWindowFactory, NativeWindowService } from '../shared/window.service'; const IMPORTS = [ CommonModule, @@ -47,6 +53,7 @@ const PROVIDERS = [ DSpaceRESTv2Service, HostWindowService, ItemDataService, + MetadataService, ObjectCacheService, PaginationComponentOptions, RemoteDataBuildService, diff --git a/src/app/core/data/data.service.ts b/src/app/core/data/data.service.ts index e48e7a8bb8..d1054d69ef 100644 --- a/src/app/core/data/data.service.ts +++ b/src/app/core/data/data.service.ts @@ -3,7 +3,10 @@ import { CacheableObject } from '../cache/object-cache.reducer'; import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { RemoteData } from './remote-data'; import { - FindAllOptions, FindAllRequest, FindByIDRequest, RestRequest, + FindAllOptions, + FindAllRequest, + FindByIDRequest, + RestRequest, RootEndpointRequest } from './request.models'; import { Store } from '@ngrx/store'; @@ -51,7 +54,7 @@ export abstract class DataService } public isEnabledOnRestApi(): Observable { - return this.getEndpointMap() + return this.getEndpointMap() .map((map: EndpointMap) => isNotEmpty(map[this.linkName])) .startWith(undefined) .distinctUntilChanged(); diff --git a/src/app/core/metadata/metadata.service.spec.ts b/src/app/core/metadata/metadata.service.spec.ts new file mode 100644 index 0000000000..6e76874604 --- /dev/null +++ b/src/app/core/metadata/metadata.service.spec.ts @@ -0,0 +1,114 @@ +import { ComponentFixture, TestBed, async, fakeAsync, inject, tick } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { Location, CommonModule } from '@angular/common'; +import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { By, Meta } from '@angular/platform-browser'; +import { Router } from '@angular/router'; + +import { Store, StoreModule } from '@ngrx/store'; + +import { Observable } from 'rxjs/Observable'; + +import { MetadataService } from './metadata.service'; + +import { CoreState } from '../core.reducers'; + +import { GlobalConfig } from '../../../config/global-config.interface'; +import { ENV_CONFIG, GLOBAL_CONFIG } from '../../../config'; + +import { ObjectCacheService } from '../cache/object-cache.service'; +import { RequestService } from '../data/request.service'; +import { ResponseCacheService } from '../cache/response-cache.service'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; + +import { NormalizedItem } from '../cache/models/normalized-item.model'; + +import { MockRouter } from '../../shared/mocks/mock-router'; +import { MockNormalizedItem } from '../../shared/mocks/mock-normalized-item'; +import { MockItem } from '../../shared/mocks/mock-item'; + +/* tslint:disable:max-classes-per-file */ +@Component({ + template: `` +}) +class TestComponent { + constructor(private metadata: MetadataService) { + metadata.listenForRouteChange(); + } +} + +@Component({ template: '' }) class DummyItemComponent { } +/* tslint:enable:max-classes-per-file */ + +describe('MetadataService', () => { + let metadataService: MetadataService; + + let meta: Meta; + + let store: Store; + + let objectCacheService: ObjectCacheService; + let responseCacheService: ResponseCacheService; + let requestService: RequestService; + let remoteDataBuildService: RemoteDataBuildService; + + let location: Location; + let router: Router; + let fixture: ComponentFixture; + + beforeEach(() => { + + store = new Store(undefined, undefined, undefined); + spyOn(store, 'dispatch'); + + objectCacheService = new ObjectCacheService(store); + responseCacheService = new ResponseCacheService(store); + requestService = new RequestService(objectCacheService, responseCacheService, store); + remoteDataBuildService = new RemoteDataBuildService(objectCacheService, responseCacheService, requestService); + + TestBed.configureTestingModule({ + imports: [ + CommonModule, + StoreModule.forRoot({}), + RouterTestingModule.withRoutes([ + { path: 'items/:id', component: DummyItemComponent, pathMatch: 'full', data: { type: NormalizedItem } } + ]) + ], + declarations: [ + TestComponent, + DummyItemComponent + ], + providers: [ + { provide: ObjectCacheService, useValue: objectCacheService }, + { provide: ResponseCacheService, useValue: responseCacheService }, + { provide: RequestService, useValue: requestService }, + { provide: RemoteDataBuildService, useValue: remoteDataBuildService }, + { provide: GLOBAL_CONFIG, useValue: ENV_CONFIG }, + Meta, + MetadataService + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }); + meta = TestBed.get(Meta); + metadataService = TestBed.get(MetadataService); + + router = TestBed.get(Router); + location = TestBed.get(Location); + + fixture = TestBed.createComponent(TestComponent); + fixture.detectChanges(); + }); + + beforeEach(() => { + spyOn(objectCacheService, 'getByUUID').and.returnValue(Observable.create((observer) => { + observer.next(MockNormalizedItem); + })); + spyOn(remoteDataBuildService, 'build').and.returnValue(MockItem); + }); + + it('upon navigation should call meta tag setters', () => { + router.navigate(['/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357']); + }); + +}); diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts new file mode 100644 index 0000000000..9e8c6747f5 --- /dev/null +++ b/src/app/core/metadata/metadata.service.ts @@ -0,0 +1,354 @@ +import 'rxjs/add/operator/first' +import 'rxjs/add/operator/take' + +import { Inject, Injectable } from '@angular/core'; +import { + ActivatedRoute, + Event, + NavigationEnd, + Params, + Router +} from '@angular/router'; +import { Meta, MetaDefinition } from '@angular/platform-browser'; + +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; +import { Observable } from 'rxjs/Observable'; + +import { Bitstream } from '../shared/bitstream.model'; +import { CacheableObject } from '../cache/object-cache.reducer'; +import { DSpaceObject } from '../shared/dspace-object.model'; +import { Item } from '../shared/item.model'; +import { Metadatum } from '../shared/metadatum.model'; +import { ObjectCacheService } from '../cache/object-cache.service'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; + +import { GLOBAL_CONFIG, GlobalConfig } from '../../../config'; + +@Injectable() +export class MetadataService { + + private initialized: boolean; + + private tagStore: Map; + + private currentObject: BehaviorSubject; + + constructor( + private router: Router, + private objectCacheService: ObjectCacheService, + private remoteDataBuildService: RemoteDataBuildService, + private meta: Meta, + @Inject(GLOBAL_CONFIG) private envConfig: GlobalConfig + ) { + this.meta.addTags([ + { property: 'og:title', content: 'DSpace Angular Universal' } + ]); + this.initialized = false; + this.tagStore = new Map(); + } + + public listenForRouteChange(): void { + const subscription = this.router.events + .filter((event) => event instanceof NavigationEnd) + .map(() => this.router.routerState.root) + .map((route: ActivatedRoute) => { + route = this.getCurrentRoute(route); + return { params: route.params, data: route.data }; + }).subscribe((routeInfo: any) => { + if (routeInfo.params.value.id && routeInfo.data.value.type) { + this.objectCacheService.getByUUID(routeInfo.params.value.id, routeInfo.data.value.type) + .first().subscribe((normalizedObject: CacheableObject) => { + const dspaceObject = this.remoteDataBuildService.build(normalizedObject) as DSpaceObject; + if (!this.initialized) { + this.initialize(dspaceObject); + } + this.currentObject.next(dspaceObject); + }); + } else { + this.clearMetaTags(); + } + }); + } + + private initialize(dspaceObject: DSpaceObject): void { + this.currentObject = new BehaviorSubject(dspaceObject); + const subscription = this.currentObject.asObservable().distinctUntilKeyChanged('uuid').subscribe(() => { + this.setMetaTags(); + }); + this.initialized = true; + } + + private getCurrentRoute(route: ActivatedRoute): ActivatedRoute { + while (route.firstChild) { + route = route.firstChild; + } + return route; + } + + private setMetaTags(): void { + + this.clearMetaTags(); + + this.setCitationTitleTag(); + this.setCitationAuthorTags(); + this.setCitationDateTag(); + this.setCitationISSNTag(); + this.setCitationISBNTag(); + + this.setCitationLanguageTag(); + this.setCitationKeywordsTag(); + + this.setCitationAbstractUrlTag(); + this.setCitationPdfUrlTag(); + + if (this.isDissertation()) { + this.setCitationDissertationNameTag(); + this.setCitationDissertationInstitutionTag(); + } + + if (this.isTechReport()) { + this.setCitationTechReportInstitutionTag(); + } + + // this.setCitationJournalTitleTag(); + // this.setCitationVolumeTag(); + // this.setCitationIssueTag(); + // this.setCitationFirstPageTag(); + // this.setCitationLastPageTag(); + // this.setCitationDOITag(); + // this.setCitationPMIDTag(); + + // this.setCitationFullTextTag(); + + // this.setCitationConferenceTag(); + + // this.setCitationPatentCountryTag(); + // this.setCitationPatentNumberTag(); + + } + + /** + * Add to the + */ + private setCitationTitleTag(): void { + const value = this.getMetaTagValue('dc.title'); + this.addMetaTag('citation_title', value); + } + + /** + * Add to the + */ + private setCitationAuthorTags(): void { + const values: string[] = this.getMetaTagValues(['dc.author', 'dc.contributor.author', 'dc.creator']); + this.addMetaTags('citation_author', values); + } + + /** + * Add to the + */ + private setCitationDateTag(): void { + const value = this.getFirstMetaTagValue(['dc.date.copyright', 'dc.date.issued', 'dc.date.available', 'dc.date.accessioned']); + this.addMetaTag('citation_date', value); + } + + /** + * Add to the + */ + private setCitationISSNTag(): void { + const value = this.getMetaTagValue('dc.identifier.issn'); + this.addMetaTag('citation_issn', value); + } + + /** + * Add to the + */ + private setCitationISBNTag(): void { + const value = this.getMetaTagValue('dc.identifier.isbn'); + this.addMetaTag('citation_isbn', value); + } + + /** + * Add to the + */ + private setCitationLanguageTag(): void { + const value = this.getMetaTagValue('dc.language.iso'); + this.addMetaTag('citation_language', value); + } + + /** + * Add to the + */ + private setCitationDissertationNameTag(): void { + const value = this.getMetaTagValue('dc.title'); + this.addMetaTag('citation_dissertation_name', value); + } + + /** + * Add to the + */ + private setCitationDissertationInstitutionTag(): void { + const value = this.getMetaTagValue('dc.publisher'); + this.addMetaTag('citation_dissertation_institution', value); + } + + /** + * Add to the + */ + private setCitationTechReportInstitutionTag(): void { + const value = this.getMetaTagValue('dc.publisher'); + this.addMetaTag('citation_technical_report_institution', value); + } + + /** + * Add to the + */ + private setCitationKeywordsTag(): void { + const value = this.getMetaTagValuesAndCombine('dc.subject'); + this.addMetaTag('citation_keywords', value); + } + + /** + * Add to the + */ + private setCitationAbstractUrlTag(): void { + if (this.currentObject.value instanceof Item) { + const value = [this.envConfig.ui.baseUrl, this.router.url].join(''); + this.addMetaTag('citation_abstract_html_url', value); + } + } + + /** + * Add to the + */ + private setCitationPdfUrlTag(): void { + if (this.currentObject.value instanceof Item) { + const item = this.currentObject.value as Item; + // NOTE: Observable resolves many times with same data + // taking only two, fist one is empty array + const subscription = item.getFiles().take(2).subscribe((bitstreams: Bitstream[]) => { + for (const bitstream of bitstreams) { + if (bitstream.mimetype === 'application/pdf') { + this.addMetaTag('citation_abstract_html_url', bitstream.content); + break; + } + } + }); + } + } + + /** + * Returns true if this._item is a dissertation + * + * @returns {boolean} + * true if this._item has a dc.type equal to 'Thesis' + */ + private isDissertation(): boolean { + let isDissertation = false; + for (const metadatum of this.currentObject.value.metadata) { + if (metadatum.key === 'dc.type') { + isDissertation = metadatum.value === 'Thesis'; + break; + } + } + return isDissertation; + } + + /** + * Returns true if this._item is a technical report + * + * @returns {boolean} + * true if this._item has a dc.type equal to 'Technical Report' + */ + private isTechReport(): boolean { + let isTechReport = false; + for (const metadatum of this.currentObject.value.metadata) { + if (metadatum.key === 'dc.type') { + isTechReport = metadatum.value === 'Technical Report'; + break; + } + } + return isTechReport; + } + + private getMetaTagValue(key: string): string { + let value: string; + for (const metadatum of this.currentObject.value.metadata) { + if (metadatum.key === key) { + value = metadatum.value; + } + } + return value; + } + + private getFirstMetaTagValue(keys: string[]): string { + let value: string; + for (const metadatum of this.currentObject.value.metadata) { + for (const key of keys) { + if (key === metadatum.key) { + value = metadatum.value; + break; + } + } + if (value !== undefined) { + break; + } + } + return value; + } + + private getMetaTagValuesAndCombine(key: string): string { + return this.getMetaTagValues([key]).join('; '); + } + + private getMetaTagValues(keys: string[]): string[] { + const values: string[] = []; + for (const metadatum of this.currentObject.value.metadata) { + for (const key of keys) { + if (key === metadatum.key) { + values.push(metadatum.value); + } + } + } + return values; + } + + private addMetaTag(property: string, content: string): void { + if (content) { + const tag = { property, content } as MetaDefinition; + this.meta.addTag(tag); + this.storeTag(property, tag); + } + } + + private addMetaTags(property: string, content: string[]): void { + for (const value of content) { + this.addMetaTag(property, value); + } + } + + private storeTag(key: string, tag: MetaDefinition): void { + const tags: MetaDefinition[] = this.getTags(key); + tags.push(tag); + this.setTags(key, tags); + } + + private getTags(key: string): MetaDefinition[] { + let tags: MetaDefinition[] = this.tagStore.get(key); + if (tags === undefined) { + tags = []; + } + return tags; + } + + private setTags(key: string, tags: MetaDefinition[]): void { + this.tagStore.set(key, tags); + } + + private clearMetaTags() { + this.tagStore.forEach((tags: MetaDefinition[], property: string) => { + this.meta.removeTag("property='" + property + "'"); + }); + this.tagStore.clear(); + } + +} diff --git a/src/app/footer/footer.component.spec.ts b/src/app/footer/footer.component.spec.ts index 6d56726764..dde432e1ef 100644 --- a/src/app/footer/footer.component.spec.ts +++ b/src/app/footer/footer.component.spec.ts @@ -21,7 +21,7 @@ import { Store, StoreModule } from '@ngrx/store'; // Load the implementations that should be tested import { FooterComponent } from './footer.component'; -import { MockTranslateLoader } from '../shared/testing/mock-translate-loader'; +import { MockTranslateLoader } from '../shared/mocks/mock-translate-loader'; let comp: FooterComponent; let fixture: ComponentFixture; diff --git a/src/app/shared/error/error.component.spec.ts b/src/app/shared/error/error.component.spec.ts index a0226f7f86..7335f93aed 100644 --- a/src/app/shared/error/error.component.spec.ts +++ b/src/app/shared/error/error.component.spec.ts @@ -4,7 +4,7 @@ import { DebugElement } from '@angular/core'; import { TranslateModule, TranslateLoader, TranslateService } from '@ngx-translate/core'; -import { MockTranslateLoader } from '../testing/mock-translate-loader'; +import { MockTranslateLoader } from '../mocks/mock-translate-loader'; import { ErrorComponent } from './error.component'; @@ -25,8 +25,8 @@ describe('ErrorComponent (inline template)', () => { } }), ], - declarations: [ ErrorComponent ], // declare the test component - providers: [ TranslateService ] + declarations: [ErrorComponent], // declare the test component + providers: [TranslateService] }).compileComponents(); // compile template and css })); diff --git a/src/app/shared/loading/loading.component.spec.ts b/src/app/shared/loading/loading.component.spec.ts index 0b758b8218..aca9673282 100644 --- a/src/app/shared/loading/loading.component.spec.ts +++ b/src/app/shared/loading/loading.component.spec.ts @@ -4,7 +4,7 @@ import { DebugElement } from '@angular/core'; import { TranslateModule, TranslateLoader, TranslateService } from '@ngx-translate/core'; -import { MockTranslateLoader } from '../testing/mock-translate-loader'; +import { MockTranslateLoader } from '../mocks/mock-translate-loader'; import { LoadingComponent } from './loading.component'; @@ -25,8 +25,8 @@ describe('LoadingComponent (inline template)', () => { } }), ], - declarations: [ LoadingComponent ], // declare the test component - providers: [ TranslateService ] + declarations: [LoadingComponent], // declare the test component + providers: [TranslateService] }).compileComponents(); // compile template and css })); diff --git a/src/app/shared/mocks/mock-action.ts b/src/app/shared/mocks/mock-action.ts new file mode 100644 index 0000000000..0f619c8aff --- /dev/null +++ b/src/app/shared/mocks/mock-action.ts @@ -0,0 +1,6 @@ +import { Action } from '@ngrx/store'; + +export class MockAction implements Action { + type = null; + payload: {}; +} diff --git a/src/app/shared/mocks/mock-active-router.ts b/src/app/shared/mocks/mock-active-router.ts new file mode 100644 index 0000000000..391b9c3426 --- /dev/null +++ b/src/app/shared/mocks/mock-active-router.ts @@ -0,0 +1,34 @@ +import { Params } from '@angular/router'; + +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; + +export class MockActivatedRoute { + + private _testParams?: any; + + // ActivatedRoute.params is Observable + private subject?: BehaviorSubject = new BehaviorSubject(this.testParams); + + params = this.subject.asObservable(); + queryParams = this.subject.asObservable(); + + constructor(params?: Params) { + if (params) { + this.testParams = params; + } else { + this.testParams = {}; + } + } + + // Test parameters + get testParams() { return this._testParams; } + set testParams(params: {}) { + this._testParams = params; + this.subject.next(params); + } + + // ActivatedRoute.snapshot.params + get snapshot() { + return { params: this.testParams }; + } +} diff --git a/src/app/shared/mocks/mock-host-window-service.ts b/src/app/shared/mocks/mock-host-window-service.ts new file mode 100644 index 0000000000..104e712682 --- /dev/null +++ b/src/app/shared/mocks/mock-host-window-service.ts @@ -0,0 +1,19 @@ +import { Observable } from 'rxjs/Observable'; + +// declare a stub service +export class MockHostWindowService { + + private width: number; + + constructor(width) { + this.setWidth(width); + } + + setWidth(width) { + this.width = width; + } + + isXs(): Observable { + return Observable.of(this.width < 576); + } +} diff --git a/src/app/shared/mocks/mock-item.ts b/src/app/shared/mocks/mock-item.ts new file mode 100644 index 0000000000..43ad178485 --- /dev/null +++ b/src/app/shared/mocks/mock-item.ts @@ -0,0 +1,164 @@ +import { Observable } from 'rxjs/Observable'; + +import { Item } from '../../core/shared/item.model'; + +export const MockItem: Item = Object.assign(new Item(), { + handle: '10673/6', + lastModified: '2017-04-24T19:44:08.178+0000', + isArchived: true, + isDiscoverable: true, + isWithdrawn: false, + bitstreams: { + self: { + _isScalar: true, + value: '1507836003548', + scheduler: null + }, + requestPending: Observable.create((observer) => { + observer.next(false); + }), + responsePending: Observable.create((observer) => { + observer.next(false); + }), + isSuccessFul: Observable.create((observer) => { + observer.next(true); + }), + errorMessage: Observable.create((observer) => { + observer.next(''); + }), + statusCode: Observable.create((observer) => { + observer.next(202); + }), + pageInfo: Observable.create((observer) => { + observer.next({}); + }), + payload: Observable.create((observer) => { + observer.next([]); + }) + }, + self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357', + id: '0ec7ff22-f211-40ab-a69e-c819b0b1f357', + uuid: '0ec7ff22-f211-40ab-a69e-c819b0b1f357', + type: 'item', + name: 'Test PowerPoint Document', + metadata: [ + { + key: 'dc.creator', + language: 'en_US', + value: 'Doe, Jane' + }, + { + key: 'dc.date.accessioned', + language: null, + value: '1650-06-26T19:58:25Z' + }, + { + key: 'dc.date.available', + language: null, + value: '1650-06-26T19:58:25Z' + }, + { + key: 'dc.date.issued', + language: null, + value: '1650-06-26' + }, + { + key: 'dc.identifier.issn', + language: 'en_US', + value: '123456789' + }, + { + key: 'dc.identifier.uri', + language: null, + value: 'http://dspace7.4science.it/xmlui/handle/10673/6' + }, + { + key: 'dc.description.abstract', + language: 'en_US', + value: 'This is really just a sample abstract. If it was a real abstract it would contain useful information about this test document. Sorry though, nothing useful in this paragraph. You probably shouldn\'t have even bothered to read it!' + }, + { + key: 'dc.description.provenance', + language: 'en', + value: 'Made available in DSpace on 2012-06-26T19:58:25Z (GMT). No. of bitstreams: 2\r\ntest_ppt.ppt: 12707328 bytes, checksum: a353fc7d29b3c558c986f7463a41efd3 (MD5)\r\ntest_ppt.pptx: 12468572 bytes, checksum: 599305edb4ebee329667f2c35b14d1d6 (MD5)' + }, + { + key: 'dc.description.provenance', + language: 'en', + value: 'Restored into DSpace on 2013-06-13T09:17:34Z (GMT).' + }, + { + key: 'dc.description.provenance', + language: 'en', + value: 'Restored into DSpace on 2013-06-13T11:04:16Z (GMT).' + }, + { + key: 'dc.description.provenance', + language: 'en', + value: 'Restored into DSpace on 2017-04-24T19:44:08Z (GMT).' + }, + { + key: 'dc.language', + language: 'en_US', + value: 'en' + }, + { + key: 'dc.rights', + language: 'en_US', + value: '© Jane Doe' + }, + { + key: 'dc.subject', + language: 'en_US', + value: 'keyword1' + }, + { + key: 'dc.subject', + language: 'en_US', + value: 'keyword2' + }, + { + key: 'dc.subject', + language: 'en_US', + value: 'keyword3' + }, + { + key: 'dc.title', + language: 'en_US', + value: 'Test PowerPoint Document' + }, + { + key: 'dc.type', + language: 'en_US', + value: 'text' + } + ], + owningCollection: { + self: { + _isScalar: true, + value: 'https://dspace7.4science.it/dspace-spring-rest/api/core/collections/1c11f3f1-ba1f-4f36-908a-3f1ea9a557eb', + scheduler: null + }, + requestPending: Observable.create((observer) => { + observer.next(false); + }), + responsePending: Observable.create((observer) => { + observer.next(false); + }), + isSuccessFul: Observable.create((observer) => { + observer.next(true); + }), + errorMessage: Observable.create((observer) => { + observer.next(''); + }), + statusCode: Observable.create((observer) => { + observer.next(202); + }), + pageInfo: Observable.create((observer) => { + observer.next({}); + }), + payload: Observable.create((observer) => { + observer.next([]); + }) + } +}) diff --git a/src/app/shared/mocks/mock-metadata-service.ts b/src/app/shared/mocks/mock-metadata-service.ts new file mode 100644 index 0000000000..8456e92b92 --- /dev/null +++ b/src/app/shared/mocks/mock-metadata-service.ts @@ -0,0 +1,9 @@ + +export class MockMetadataService { + + // tslint:disable-next-line:no-empty + public listenForRouteChange(): void { + + } + +} diff --git a/src/app/shared/mocks/mock-normalized-item.ts b/src/app/shared/mocks/mock-normalized-item.ts new file mode 100644 index 0000000000..d8716b4bf2 --- /dev/null +++ b/src/app/shared/mocks/mock-normalized-item.ts @@ -0,0 +1,114 @@ +import { NormalizedItem } from '../../core/cache/models/normalized-item.model'; + +export const MockNormalizedItem: NormalizedItem = Object.assign(new NormalizedItem(), { + handle: '10673/6', + lastModified: new Date('2017-04-24T19:44:08.178+0000'), + isArchived: true, + isDiscoverable: true, + isWithdrawn: false, + bitstreams: [ + 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/a7cd7d97-4e40-41db-80a8-fac908b63bb8', + 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/9ff3df0d-1709-472f-8c00-d3e8db2153c8', + 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/d660a4b8-e7cc-45cd-b026-35f98c5bd3ba' + ], + self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/items/0ec7ff22-f211-40ab-a69e-c819b0b1f357', + id: '0ec7ff22-f211-40ab-a69e-c819b0b1f357', + uuid: '0ec7ff22-f211-40ab-a69e-c819b0b1f357', + type: 'item', + name: 'Test PowerPoint Document', + metadata: [ + { + key: 'dc.creator', + language: 'en_US', + value: 'Doe, Jane L' + }, + { + key: 'dc.date.accessioned', + language: null, + value: '1650-06-26T19:58:25Z' + }, + { + key: 'dc.date.available', + language: null, + value: '1650-06-26T19:58:25Z' + }, + { + key: 'dc.date.issued', + language: null, + value: '1650-06-26' + }, + { + key: 'dc.identifier.issn', + language: 'en_US', + value: '123456789' + }, + { + key: 'dc.identifier.uri', + language: null, + value: 'http://dspace7.4science.it/xmlui/handle/10673/6' + }, + { + key: 'dc.description.abstract', + language: 'en_US', + value: 'This is really just a sample abstract. If it was a real abstract it would contain useful information about this test document. Sorry though, nothing useful in this paragraph. You probably shouldn\'t have even bothered to read it!' + }, + { + key: 'dc.description.provenance', + language: 'en', + value: 'Made available in DSpace on 2012-06-26T19:58:25Z (GMT). No. of bitstreams: 2\r\ntest_ppt.ppt: 12707328 bytes, checksum: a353fc7d29b3c558c986f7463a41efd3 (MD5)\r\ntest_ppt.pptx: 12468572 bytes, checksum: 599305edb4ebee329667f2c35b14d1d6 (MD5)' + }, + { + key: 'dc.description.provenance', + language: 'en', + value: 'Restored into DSpace on 2013-06-13T09:17:34Z (GMT).' + }, + { + key: 'dc.description.provenance', + language: 'en', + value: 'Restored into DSpace on 2013-06-13T11:04:16Z (GMT).' + }, + { + key: 'dc.description.provenance', + language: 'en', + value: 'Restored into DSpace on 2017-04-24T19:44:08Z (GMT).' + }, + { + key: 'dc.language', + language: 'en_US', + value: 'en' + }, + { + key: 'dc.rights', + language: 'en_US', + value: '© Jane Doe' + }, + { + key: 'dc.subject', + language: 'en_US', + value: 'keyword1' + }, + { + key: 'dc.subject', + language: 'en_US', + value: 'keyword2' + }, + { + key: 'dc.subject', + language: 'en_US', + value: 'keyword3' + }, + { + key: 'dc.title', + language: 'en_US', + value: 'Test PowerPoint Document' + }, + { + key: 'dc.type', + language: 'en_US', + value: 'text' + } + ], + owningCollection: [ + 'https://dspace7.4science.it/dspace-spring-rest/api/core/collections/1c11f3f1-ba1f-4f36-908a-3f1ea9a557eb' + ] +}) diff --git a/src/app/shared/mocks/mock-router.ts b/src/app/shared/mocks/mock-router.ts new file mode 100644 index 0000000000..054c63d4c0 --- /dev/null +++ b/src/app/shared/mocks/mock-router.ts @@ -0,0 +1,4 @@ +export class MockRouter { + // noinspection TypeScriptUnresolvedFunction + navigate = jasmine.createSpy('navigate'); +} diff --git a/src/app/shared/mocks/mock-store.ts b/src/app/shared/mocks/mock-store.ts new file mode 100644 index 0000000000..c619b5aa77 --- /dev/null +++ b/src/app/shared/mocks/mock-store.ts @@ -0,0 +1,23 @@ +import { Action } from '@ngrx/store'; +import { Observable } from 'rxjs/Observable'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; + +export class MockStore extends BehaviorSubject { + + constructor(private _initialState: T) { + super(_initialState); + } + + dispatch = (action: Action): void => { + console.info(); + } + + select = (pathOrMapFn: any): Observable => { + return Observable.of(this.getValue()); + } + + nextState(_newState: T) { + this.next(_newState); + } + +} diff --git a/src/app/shared/mocks/mock-translate-loader.ts b/src/app/shared/mocks/mock-translate-loader.ts new file mode 100644 index 0000000000..6e22066f8a --- /dev/null +++ b/src/app/shared/mocks/mock-translate-loader.ts @@ -0,0 +1,8 @@ +import { TranslateLoader } from '@ngx-translate/core'; +import { Observable } from 'rxjs/Observable'; + +export class MockTranslateLoader implements TranslateLoader { + getTranslation(lang: string): Observable { + return Observable.of({}); + } +} diff --git a/src/app/shared/pagination/pagination.component.spec.ts b/src/app/shared/pagination/pagination.component.spec.ts index 92d7ba693f..a4b9e5fcea 100644 --- a/src/app/shared/pagination/pagination.component.spec.ts +++ b/src/app/shared/pagination/pagination.component.spec.ts @@ -34,10 +34,10 @@ import Spy = jasmine.Spy; import { PaginationComponent } from './pagination.component'; import { PaginationComponentOptions } from './pagination-component-options.model'; -import { MockTranslateLoader } from '../testing/mock-translate-loader'; -import { HostWindowServiceStub } from '../testing/host-window-service-stub'; -import { ActivatedRouteStub } from '../testing/active-router-stub'; -import { RouterStub } from '../testing/router-stub'; +import { MockTranslateLoader } from '../mocks/mock-translate-loader'; +import { MockHostWindowService } from '../mocks/mock-host-window-service'; +import { MockActivatedRoute } from '../mocks/mock-active-router'; +import { MockRouter } from '../mocks/mock-router'; import { HostWindowService } from '../host-window.service'; import { EnumKeysPipe } from '../utils/enum-keys-pipe'; @@ -45,7 +45,7 @@ import { SortOptions } from '../../core/cache/models/sort-options.model'; import { GLOBAL_CONFIG, ENV_CONFIG } from '../../../config'; -function createTestComponent(html: string, type: { new (...args: any[]): T }): ComponentFixture { +function createTestComponent(html: string, type: { new(...args: any[]): T }): ComponentFixture { TestBed.overrideComponent(type, { set: { template: html } }); @@ -123,19 +123,19 @@ describe('Pagination component', () => { let testFixture: ComponentFixture; let de: DebugElement; let html; - let hostWindowServiceStub: HostWindowServiceStub; + let hostWindowServiceStub: MockHostWindowService; - let activatedRouteStub: ActivatedRouteStub; - let routerStub: RouterStub; + let activatedRouteStub: MockActivatedRoute; + let routerStub: MockRouter; // Define initial state and test state const _initialState = { width: 1600, height: 770 }; // async beforeEach beforeEach(async(() => { - activatedRouteStub = new ActivatedRouteStub(); - routerStub = new RouterStub(); - hostWindowServiceStub = new HostWindowServiceStub(_initialState.width); + activatedRouteStub = new MockActivatedRoute(); + routerStub = new MockRouter(); + hostWindowServiceStub = new MockHostWindowService(_initialState.width); TestBed.configureTestingModule({ imports: [ diff --git a/yarn.lock b/yarn.lock index 109d1eb310..39e4bf575d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,79 +2,79 @@ # yarn lockfile v1 -"@angular/animations@4.4.4": - version "4.4.4" - resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-4.4.4.tgz#a2f9353604347abe15df98292058842f52f08bc2" +"@angular/animations@4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-4.4.5.tgz#5a5a551d757e5a5560098f6f8535c102d93954d7" dependencies: tslib "^1.7.1" -"@angular/common@4.4.4": - version "4.4.4" - resolved "https://registry.yarnpkg.com/@angular/common/-/common-4.4.4.tgz#ae0a818aaa0c6a3f0901e7b80bd94e1c22eb9365" +"@angular/common@4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@angular/common/-/common-4.4.5.tgz#bd5179dc922adbf4c3ea6dfb19e73cb849ffdc37" dependencies: tslib "^1.7.1" -"@angular/compiler-cli@4.4.4": - version "4.4.4" - resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-4.4.4.tgz#063080a497d9175396825050222c717da184f6cf" +"@angular/compiler-cli@4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-4.4.5.tgz#61fa0336acd1a208c5f1c5c6d4df679e99953248" dependencies: - "@angular/tsc-wrapped" "4.4.4" + "@angular/tsc-wrapped" "4.4.5" minimist "^1.2.0" reflect-metadata "^0.1.2" -"@angular/compiler@4.4.4": - version "4.4.4" - resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-4.4.4.tgz#326eb0029d9a3541aaca124def9adc51c36f2b41" +"@angular/compiler@4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-4.4.5.tgz#8721a5910f2bb52f09e2d404cad264f35ede5902" dependencies: tslib "^1.7.1" -"@angular/core@4.4.4": - version "4.4.4" - resolved "https://registry.yarnpkg.com/@angular/core/-/core-4.4.4.tgz#bd37ecf54158f97489996c9386bd222f80a32f5c" +"@angular/core@4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-4.4.5.tgz#54acbcbda11719f883c786a906974abeb132f1a0" dependencies: tslib "^1.7.1" -"@angular/forms@4.4.4": - version "4.4.4" - resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-4.4.4.tgz#4db3790509b6b10f1db8a7c1b7f52187cf64cfd4" +"@angular/forms@4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-4.4.5.tgz#e9552086232aab2ce1d08ef198b62204ea13c43b" dependencies: tslib "^1.7.1" -"@angular/http@4.4.4": - version "4.4.4" - resolved "https://registry.yarnpkg.com/@angular/http/-/http-4.4.4.tgz#667faf616bb624168eafae6ee92e5eba23a9d1f2" +"@angular/http@4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@angular/http/-/http-4.4.5.tgz#2c735ed842401fc2356419268e288dcf2396e84f" dependencies: tslib "^1.7.1" -"@angular/platform-browser-dynamic@4.4.4": - version "4.4.4" - resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.4.4.tgz#c3c9eb854a528556a07054127932e527fa932e14" +"@angular/platform-browser-dynamic@4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.4.5.tgz#774dbdc1d90f775dbf1e319f6ed42b260623b61f" dependencies: tslib "^1.7.1" -"@angular/platform-browser@4.4.4": - version "4.4.4" - resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-4.4.4.tgz#a3898e2e7ba9d84ffa0d47144c6971179c75aee6" +"@angular/platform-browser@4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-4.4.5.tgz#74eb91c0b758126f26d53ee56c7cf4668bd9cac5" dependencies: tslib "^1.7.1" -"@angular/platform-server@4.4.4": - version "4.4.4" - resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-4.4.4.tgz#73ee41fa1cec8628fcc03174727b27cb0031b22a" +"@angular/platform-server@4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-4.4.5.tgz#76f23b2c384ed7395dc1793cf85978883ba2cb50" dependencies: parse5 "^3.0.1" tslib "^1.7.1" xhr2 "^0.1.4" -"@angular/router@4.4.4": - version "4.4.4" - resolved "https://registry.yarnpkg.com/@angular/router/-/router-4.4.4.tgz#7be391096e843cb3e04f9f05d1d65a88df9bc7cf" +"@angular/router@4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@angular/router/-/router-4.4.5.tgz#f73130cf487d9a32cc1988afda59665f44a28a89" dependencies: tslib "^1.7.1" -"@angular/tsc-wrapped@4.4.4": - version "4.4.4" - resolved "https://registry.yarnpkg.com/@angular/tsc-wrapped/-/tsc-wrapped-4.4.4.tgz#9841821e55616b826ca160250fe85e15fc74ffc3" +"@angular/tsc-wrapped@4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@angular/tsc-wrapped/-/tsc-wrapped-4.4.5.tgz#30a0cbb43a663aa75dca984894be4813778ddc9c" dependencies: tsickle "^0.21.0" @@ -106,9 +106,9 @@ version "4.0.3" resolved "https://registry.yarnpkg.com/@ngrx/store/-/store-4.0.3.tgz#36abacdfa19bfb8506e40de80bae06050a1e15e9" -"@ngtools/webpack@1.7.2": - version "1.7.2" - resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-1.7.2.tgz#3fc4de01786dcc2f50d8cbaaa117311e56799977" +"@ngtools/webpack@1.7.4": + version "1.7.4" + resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-1.7.4.tgz#5015c47ebd339045dd89a1bef0497f4524d2c8ed" dependencies: enhanced-resolve "^3.1.0" loader-utils "^1.0.2" @@ -192,7 +192,11 @@ version "2.0.29" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-2.0.29.tgz#5002e14f75e2d71e564281df0431c8c1b4a2a36a" -"@types/node@*", "@types/node@8.0.26": +"@types/node@*": + version "8.0.34" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.34.tgz#55f801fa2ddb2a40dd6dfc15ecfe1dde9c129fe9" + +"@types/node@8.0.26": version "8.0.26" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.26.tgz#4d58be925306fd22b1141085535a0268b8beb189" @@ -568,7 +572,7 @@ atob@~1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/atob/-/atob-1.1.3.tgz#95f13629b12c3a51a5d215abdce2aa9f32f80773" -autoprefixer@7.1.5: +autoprefixer@7.1.5, autoprefixer@^7.1.1: version "7.1.5" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-7.1.5.tgz#d65d14b83c7cd1dd7bc801daa00557addf5a06b2" dependencies: @@ -590,17 +594,6 @@ autoprefixer@^6.3.1: postcss "^5.2.16" postcss-value-parser "^3.2.3" -autoprefixer@^7.1.1: - version "7.1.4" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-7.1.4.tgz#960847dbaa4016bc8e8e52ec891cbf8f1257a748" - dependencies: - browserslist "^2.4.0" - caniuse-lite "^1.0.30000726" - normalize-range "^0.1.2" - num2fraction "^1.2.2" - postcss "^6.0.11" - postcss-value-parser "^3.2.3" - awesome-typescript-loader@3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/awesome-typescript-loader/-/awesome-typescript-loader-3.2.3.tgz#aa2119b7c808a031e2b28945b031450a8975367f" @@ -793,11 +786,7 @@ blocking-proxy@0.0.5: dependencies: minimist "^1.2.0" -bluebird@^3.3.0, bluebird@^3.4.7: - version "3.5.0" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" - -bluebird@^3.5.1: +bluebird@^3.3.0, bluebird@^3.4.7, bluebird@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" @@ -975,14 +964,7 @@ browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: caniuse-db "^1.0.30000639" electron-to-chromium "^1.2.7" -browserslist@^2.0.0, browserslist@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.4.0.tgz#693ee93d01e66468a6348da5498e011f578f87f8" - dependencies: - caniuse-lite "^1.0.30000718" - electron-to-chromium "^1.3.18" - -browserslist@^2.5.0: +browserslist@^2.0.0, browserslist@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.5.1.tgz#68e4bc536bbcc6086d62843a2ffccea8396821c6" dependencies: @@ -1099,11 +1081,7 @@ caniuse-lite@1.0.30000697: version "1.0.30000697" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000697.tgz#125fb00604b63fbb188db96a667ce2922dcd6cdd" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000718, caniuse-lite@^1.0.30000726: - version "1.0.30000740" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000740.tgz#f2c4c04d6564eb812e61006841700ad557f6f973" - -caniuse-lite@^1.0.30000744: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000744: version "1.0.30000745" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000745.tgz#20d6fede1157a4935133502946fc7e0e6b880da5" @@ -1126,11 +1104,11 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" -cerialize@0.1.16: - version "0.1.16" - resolved "https://registry.yarnpkg.com/cerialize/-/cerialize-0.1.16.tgz#88678bffbd7817a90aa5b58a8c66d6bdca3035be" +cerialize@0.1.18: + version "0.1.18" + resolved "https://registry.yarnpkg.com/cerialize/-/cerialize-0.1.18.tgz#d0f4f1b61cec7e4ed16a3eda0cac2bc99787414d" dependencies: - typescript "^2.1.6" + typescript "^2.5.0" chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" @@ -2042,7 +2020,7 @@ ejs@^2.5.6: version "2.5.7" resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a" -electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.18, electron-to-chromium@^1.3.24: +electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.24: version "1.3.24" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.24.tgz#9b7b88bb05ceb9fa016a177833cc2dde388f21b6" @@ -2414,9 +2392,9 @@ express-session@1.15.6: uid-safe "~2.1.5" utils-merge "1.0.1" -express@4.16.1, express@^4.13.3, express@^4.15.2: - version "4.16.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.16.1.tgz#6b33b560183c9b253b7b62144df33a4654ac9ed0" +express@4.16.2, express@^4.13.3, express@^4.15.2: + version "4.16.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c" dependencies: accepts "~1.3.4" array-flatten "1.1.1" @@ -5129,9 +5107,9 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" -pem@1.12.2: - version "1.12.2" - resolved "https://registry.yarnpkg.com/pem/-/pem-1.12.2.tgz#d2f1744c9ff8144f795f96d0c54a4b2be6f43b0c" +pem@1.12.3: + version "1.12.3" + resolved "https://registry.yarnpkg.com/pem/-/pem-1.12.3.tgz#b1fb5c8b79da8d18146c27fee79b0d4ddf9905b3" dependencies: md5 "^2.2.1" os-tmpdir "^1.0.1" @@ -5484,12 +5462,12 @@ postcss-load-plugins@^2.3.0: cosmiconfig "^2.1.1" object-assign "^4.1.0" -postcss-loader@2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.0.6.tgz#8c7e0055a3df1889abc6bad52dd45b2f41bbc6fc" +postcss-loader@2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.0.7.tgz#4d2da1489cee0a14f72c0d9440c9ee7eded34345" dependencies: loader-utils "^1.1.0" - postcss "^6.0.2" + postcss "^6.0.0" postcss-load-config "^1.2.0" schema-utils "^0.3.0" @@ -5755,7 +5733,7 @@ postcss-zindex@^2.0.1: postcss "^5.0.4" uniqs "^2.0.0" -postcss@6.0.13, postcss@^6.0.0, postcss@^6.0.1, postcss@^6.0.11, postcss@^6.0.13, postcss@^6.0.2, postcss@^6.0.3, postcss@^6.0.5, postcss@^6.0.6, postcss@^6.0.8: +postcss@6.0.13, postcss@^6.0.0, postcss@^6.0.1, postcss@^6.0.11, postcss@^6.0.13, postcss@^6.0.3, postcss@^6.0.5, postcss@^6.0.6, postcss@^6.0.8: version "6.0.13" resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.13.tgz#b9ecab4ee00c89db3ec931145bd9590bbf3f125f" dependencies: @@ -6320,9 +6298,9 @@ requires-port@1.0.x, requires-port@1.x.x: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" -resolve-url-loader@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-2.1.0.tgz#27c95cc16a4353923fdbdc2dbaf5eef22232c477" +resolve-url-loader@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-2.1.1.tgz#5354e87381aae348371e555172c50816708e6c1c" dependencies: adjust-sourcemap-loader "^1.1.0" camelcase "^4.0.0" @@ -7419,7 +7397,7 @@ typescript@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.1.tgz#c3ccb16ddaa0b2314de031e7e6fee89e5ba346bc" -typescript@2.5.3, typescript@^2.1.6: +typescript@2.5.3, typescript@^2.5.0: version "2.5.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.3.tgz#df3dcdc38f3beb800d4bc322646b04a3f6ca7f0d" @@ -7827,9 +7805,9 @@ webpack-sources@^1.0.1: source-list-map "^2.0.0" source-map "~0.5.3" -webpack@3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.6.0.tgz#a89a929fbee205d35a4fa2cc487be9cbec8898bc" +webpack@3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.7.1.tgz#6046b5c415ff7df7a0dc54c5b6b86098e8b952da" dependencies: acorn "^5.0.0" acorn-dynamic-import "^2.0.0"