diff --git a/packages/composer-playground/src/app/login/login.component.spec.ts b/packages/composer-playground/src/app/login/login.component.spec.ts index b425e04ce9..0be268e964 100644 --- a/packages/composer-playground/src/app/login/login.component.spec.ts +++ b/packages/composer-playground/src/app/login/login.component.spec.ts @@ -457,6 +457,8 @@ describe(`LoginComponent`, () => { loadIdentityCardsStub = sinon.stub(component, 'loadIdentityCards'); loadIdentityCardsStub.returns(Promise.resolve()); + + mockIdentityCardService.getAllCardsForBusinessNetwork.returns(new Map()); }); it('should open the delete-confirm modal', fakeAsync(() => { @@ -522,6 +524,13 @@ describe(`LoginComponent`, () => { })); it('should undeploy and refresh the identity cards after successfully calling identityCardService.deleteIdentityCard()', fakeAsync(() => { + let myMap = new Map(); + + let idCardOne = new IdCard({userName: 'bob', businessNetwork: 'bn'}, {name: 'cp1', type: 'web'}); + + myMap.set('idCardOne', idCardOne); + + mockIdentityCardService.getAllCardsForBusinessNetwork.returns(myMap); mockIdCard.getConnectionProfile.returns({type: 'web'}); mockIdCards.set('myCardRef', mockIdCard); @@ -548,6 +557,40 @@ describe(`LoginComponent`, () => { mockAlertService.errorStatus$.next.should.not.have.been.called; })); + it('should not undeploy if more than one identity', fakeAsync(() => { + let myMap = new Map(); + + let idCardOne = new IdCard({userName: 'bob', businessNetwork: 'bn'}, {name: 'cp1', type: 'web'}); + let idCardTwo = new IdCard({userName: 'fred', businessNetwork: 'bn'}, {name: 'cp1', type: 'web'}); + + myMap.set('myCardRef', idCardOne); + myMap.set('idCardTwo', idCardTwo); + + mockIdentityCardService.getAllCardsForBusinessNetwork.returns(myMap); + mockIdCard.getConnectionProfile.returns({type: 'web'}); + mockIdCards.set('myCardRef', mockIdCard); + + component['idCards'] = mockIdCards; + mockIdentityCardService.deleteIdentityCard.returns(Promise.resolve()); + + mockModal.open = sinon.stub().returns({ + componentInstance: {}, + result: Promise.resolve(true) + }); + + component.removeIdentity('myCardRef'); + tick(); + + // check services called + mockAdminService.connect.should.not.have.been.called; + mockAdminService.undeploy.should.not.have.been.called; + mockIdentityCardService.deleteIdentityCard.should.have.been.calledWith('myCardRef'); + loadIdentityCardsStub.should.have.been.called; + + mockAlertService.successStatus$.next.should.have.been.called; + mockAlertService.errorStatus$.next.should.not.have.been.called; + })); + it('should handle errors when calling identityCardService.deleteIdentityCard()', fakeAsync(() => { component['idCards'] = mockIdCards; mockIdentityCardService.deleteIdentityCard.returns(Promise.reject('some error')); diff --git a/packages/composer-playground/src/app/login/login.component.ts b/packages/composer-playground/src/app/login/login.component.ts index 01a94a119a..ed5ba4520e 100644 --- a/packages/composer-playground/src/app/login/login.component.ts +++ b/packages/composer-playground/src/app/login/login.component.ts @@ -287,8 +287,9 @@ export class LoginComponent implements OnInit { .then((result) => { if (result) { let deletePromise: Promise; - if (card.getConnectionProfile().type === 'web') { - deletePromise = this.adminService.connect(cardRef, card, true) + let cards = this.identityCardService.getAllCardsForBusinessNetwork(card.getBusinessNetworkName(), this.identityCardService.getQualifiedProfileName(card.getConnectionProfile())); + if (card.getConnectionProfile().type === 'web' && cards.size === 1) { + deletePromise = this.adminService.connect(cardRef, card, true) .then(() => { return this.adminService.undeploy(card.getBusinessNetworkName()); }); diff --git a/packages/composer-playground/src/app/version-check/version-check.component.spec.ts b/packages/composer-playground/src/app/version-check/version-check.component.spec.ts index ed3ed01b2b..9cd2cd5cd6 100644 --- a/packages/composer-playground/src/app/version-check/version-check.component.spec.ts +++ b/packages/composer-playground/src/app/version-check/version-check.component.spec.ts @@ -2,15 +2,17 @@ /* tslint:disable:no-unused-expression */ /* tslint:disable:no-var-requires */ /* tslint:disable:max-classes-per-file */ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { DebugElement, NgZone, EventEmitter } from '@angular/core'; +import { DebugElement, NgZone } from '@angular/core'; import { VersionCheckComponent } from './version-check.component'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { LocalStorageService } from 'angular-2-local-storage'; import * as sinon from 'sinon'; +import { IdentityCardService } from '../services/identity-card.service'; +import { IdCard } from 'composer-common'; describe('VersionCheckComponent', () => { let component: VersionCheckComponent; @@ -24,19 +26,21 @@ describe('VersionCheckComponent', () => { }; let localStorageServiceMock; - let identityCardStorageServiceMock; + let identityCardServiceMock; - let reload; + let indexDBMock = sinon.stub(indexedDB, 'deleteDatabase').returns(Promise.resolve()); beforeEach(async(() => { localStorageServiceMock = sinon.createStubInstance(LocalStorageService); + identityCardServiceMock = sinon.createStubInstance(IdentityCardService); TestBed.configureTestingModule({ declarations: [VersionCheckComponent], providers: [ {provide: NgbActiveModal, useValue: ngbActiveModalMock}, {provide: NgZone, useValue: new NgZone({})}, - {provide: LocalStorageService, useValue: localStorageServiceMock} + {provide: LocalStorageService, useValue: localStorageServiceMock}, + {provide: IdentityCardService, useValue: identityCardServiceMock} ] }).compileComponents(); })); @@ -48,6 +52,18 @@ describe('VersionCheckComponent', () => { element = debug.nativeElement; fixture.detectChanges(); + + let cardOne = new IdCard({userName : 'bob', businessNetwork: 'bn1'}, {name : 'cp1', type: 'hlfv1' }); + let cardTwo = new IdCard({userName : 'fred', businessNetwork: 'bn2'}, {name : 'cp1', type: 'web' }); + let cardThree = new IdCard({userName : 'jim'}, {name : 'cp1', type: 'web' }); + + let cardMap: Map = new Map(); + + cardMap.set('cardOne', cardOne); + cardMap.set('cardTwo', cardTwo); + cardMap.set('cardThree', cardThree); + + identityCardServiceMock.getIdentityCards.returns(Promise.resolve(cardMap)); }); it('should create component', () => { @@ -58,24 +74,31 @@ describe('VersionCheckComponent', () => { element.textContent.should.contain('Invalid version!'); }); - it('should clear all local storage', () => { + it('should clear all local storage and remove indexDB', fakeAsync(() => { let runOutsideAngularStub = sinon.stub(fixture.ngZone, 'runOutsideAngular'); localStorageServiceMock.clearAll.returns(true); component.clearLocalStorage(); + tick(); + + indexDBMock.should.have.been.calledTwice; + indexDBMock.firstCall.should.have.been.calledWith('_pouch_Composer:bn2'); + indexDBMock.secondCall.should.have.been.calledWith('_pouch_Composer'); + localStorageServiceMock.clearAll.should.have.been.called; runOutsideAngularStub.should.have.been.called; - }); + })); - it('should handle unsupported browser for clearLocalStorage', () => { + it('should handle unsupported browser for clearLocalStorage', fakeAsync(() => { let runOutsideAngularStub = sinon.stub(fixture.ngZone, 'runOutsideAngular'); localStorageServiceMock.clearAll.returns(false); (() => { component.clearLocalStorage(); + tick(); }).should.throw(Error, 'Failed to clear local storage'); runOutsideAngularStub.should.not.have.been.called; - }); + })); }); diff --git a/packages/composer-playground/src/app/version-check/version-check.component.ts b/packages/composer-playground/src/app/version-check/version-check.component.ts index 5f01cb5287..5d7e85313b 100644 --- a/packages/composer-playground/src/app/version-check/version-check.component.ts +++ b/packages/composer-playground/src/app/version-check/version-check.component.ts @@ -1,6 +1,8 @@ import { Component, NgZone } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { LocalStorageService } from 'angular-2-local-storage'; +import { IdentityCardService } from '../services/identity-card.service'; +import { IdCard } from 'composer-common'; @Component({ selector: 'version-check-modal', @@ -11,17 +13,38 @@ export class VersionCheckComponent { constructor(public activeModal: NgbActiveModal, private zone: NgZone, - private localStorageService: LocalStorageService) { + private localStorageService: LocalStorageService, + private identityCardService: IdentityCardService) { } public clearLocalStorage() { - if (this.localStorageService.clearAll()) { - this.zone.runOutsideAngular(() => { - location.reload(); - }); - } else { - throw new Error('Failed to clear local storage'); - } - } + this.identityCardService.getIdentityCards(true).then((idCards: Map) => { + let cardRefs = Array.from(idCards.keys()) + .filter((cardRef) => { + return idCards.get(cardRef).getConnectionProfile().type === 'web'; + }); + return cardRefs.reduce((promise, cardRef) => { + return promise.then(() => { + let idCard = idCards.get(cardRef); + let bn = idCard.getBusinessNetworkName(); + if (bn) { + return indexedDB.deleteDatabase('_pouch_Composer:' + bn); + } + }); + }, Promise.resolve(null)) + .then(() => { + return indexedDB.deleteDatabase('_pouch_Composer'); + }) + .then(() => { + if (this.localStorageService.clearAll()) { + this.zone.runOutsideAngular(() => { + location.reload(); + }); + } else { + throw new Error('Failed to clear local storage'); + } + }); + }); + } } diff --git a/packages/composer-website/jekylldocs/index.html b/packages/composer-website/jekylldocs/index.html index 9b7d17999b..6882d66639 100644 --- a/packages/composer-website/jekylldocs/index.html +++ b/packages/composer-website/jekylldocs/index.html @@ -35,7 +35,7 @@

Build Blockchain applications and business networks your way

-

Update Nov 9th: Version 0.15 has been released. To deliver major enhancements to identity management, this update may break existing projects. Please see the release notes.

+

Update Nov 28th: Version 0.16 has been released. Please see the release notes.