diff --git a/packages/composer-playground/src/app/basic-modals/delete-confirm/delete-confirm.component.html b/packages/composer-playground/src/app/basic-modals/delete-confirm/delete-confirm.component.html index 13a5200548..8b01001254 100644 --- a/packages/composer-playground/src/app/basic-modals/delete-confirm/delete-confirm.component.html +++ b/packages/composer-playground/src/app/basic-modals/delete-confirm/delete-confirm.component.html @@ -12,16 +12,18 @@

{{headerMessage}}

diff --git a/packages/composer-playground/src/app/basic-modals/delete-confirm/delete-confirm.component.spec.ts b/packages/composer-playground/src/app/basic-modals/delete-confirm/delete-confirm.component.spec.ts index 829cc32fce..82530834b8 100644 --- a/packages/composer-playground/src/app/basic-modals/delete-confirm/delete-confirm.component.spec.ts +++ b/packages/composer-playground/src/app/basic-modals/delete-confirm/delete-confirm.component.spec.ts @@ -66,5 +66,14 @@ describe('DeleteComponent', () => { component['fileName'].should.equal('test_name'); component['fileType'].should.equal('File'); })); + + it('should not initialise file name if provided', fakeAsync( () => { + component['fileName'] = 'test_name'; + component.ngOnInit(); + + tick(); + + component['fileName'].should.equal('test_name'); + })); }); }); diff --git a/packages/composer-playground/src/app/basic-modals/delete-confirm/delete-confirm.component.ts b/packages/composer-playground/src/app/basic-modals/delete-confirm/delete-confirm.component.ts index a8e5b5d152..489944a286 100644 --- a/packages/composer-playground/src/app/basic-modals/delete-confirm/delete-confirm.component.ts +++ b/packages/composer-playground/src/app/basic-modals/delete-confirm/delete-confirm.component.ts @@ -17,14 +17,16 @@ export class DeleteComponent implements OnInit { } ngOnInit() { - this.fileName = this.deleteFile.displayID; + if (!this.fileName) { + this.fileName = this.deleteFile.displayID; - if (this.deleteFile.model) { - this.fileType = 'Model File'; - } else if (this.deleteFile.script) { - this.fileType = 'Script File'; - } else { - this.fileType = 'File'; + if (this.deleteFile.model) { + this.fileType = 'Model File'; + } else if (this.deleteFile.script) { + this.fileType = 'Script File'; + } else { + this.fileType = 'File'; + } } } } diff --git a/packages/composer-playground/src/app/identity/identity.component.html b/packages/composer-playground/src/app/identity/identity.component.html index e8672d73ac..a515572c51 100644 --- a/packages/composer-playground/src/app/identity/identity.component.html +++ b/packages/composer-playground/src/app/identity/identity.component.html @@ -7,24 +7,23 @@

My Wallet

+ Add to Wallet -
+
ID Name
Status
-
+
+
{{id}}
- In use - + In Use
In my wallet @@ -35,6 +34,36 @@

My Wallet

+
+
+

All IDs for {{ deployedPackageName }}

+
+ +
+
+
ID Name
+
Issued to
+
Status
+
+
+
+
+
+ {{ id.name }} +
+
+ {{ id.participant.$identifier }} +
+
+ {{ id.state }} +
+
+
+ +
+
+
+
diff --git a/packages/composer-playground/src/app/identity/identity.component.scss b/packages/composer-playground/src/app/identity/identity.component.scss index f6a106587d..f3ce23806f 100644 --- a/packages/composer-playground/src/app/identity/identity.component.scss +++ b/packages/composer-playground/src/app/identity/identity.component.scss @@ -10,10 +10,17 @@ identity { display: flex; flex-direction: column; + .selected-border { + width: $border-width-large; + margin-right: $space-medium; + } + .identity-title { display: flex; padding: $space-smedium; - + padding-left: 0px; + padding-right: $space-medium; + margin-top: $space-medium; & > * { font-weight: bold; } @@ -23,14 +30,22 @@ identity { flex-basis: 48%; } + .cell-24 { + flex-basis: 24%; + } + .identity { background-color: $white; box-shadow: 0 2px 5px -1px $fifth-highlight; - margin-bottom: $space-medium; + margin-bottom: $space-smedium; display: flex; - padding: 8px; - height:45px; - cursor: pointer; + padding: 0px; + padding-right: $space-medium; + height: 50px; + line-height: 50px; + cursor: default; + overflow: hidden; + border-radius: $border-width-medium; &:hover { .actions { @@ -38,8 +53,13 @@ identity { } } + .selected-border--selected { + background-color: $second-highlight; + } + .actions { display: none; + text-align: right; } } diff --git a/packages/composer-playground/src/app/identity/identity.component.spec.ts b/packages/composer-playground/src/app/identity/identity.component.spec.ts index f73091d6a1..ec5776faaf 100644 --- a/packages/composer-playground/src/app/identity/identity.component.spec.ts +++ b/packages/composer-playground/src/app/identity/identity.component.spec.ts @@ -12,6 +12,7 @@ import { IdentityComponent } from './identity.component'; import { AlertService } from '../basic-modals/alert.service'; import { IdentityService } from '../services/identity.service'; import { ClientService } from '../services/client.service'; +import { BusinessNetworkConnection } from 'composer-client'; import * as chai from 'chai'; @@ -40,6 +41,7 @@ describe(`IdentityComponent`, () => { let mockClientService; let mockConnectionProfileService; let mockWalletService; + let mockBusinessNetworkConnection; beforeEach(() => { @@ -49,13 +51,22 @@ describe(`IdentityComponent`, () => { mockClientService = sinon.createStubInstance(ClientService); mockConnectionProfileService = sinon.createStubInstance(ConnectionProfileService); mockWalletService = sinon.createStubInstance(WalletService); + mockBusinessNetworkConnection = sinon.createStubInstance(BusinessNetworkConnection); - mockAlertService.errorStatus$ = { - next: sinon.stub() - }; - + mockAlertService.errorStatus$ = {next: sinon.stub()}; + mockAlertService.successStatus$ = {next: sinon.stub()}; mockAlertService.busyStatus$ = {next: sinon.stub()}; + mockClientService.ensureConnected.returns(Promise.resolve(true)); + mockBusinessNetworkConnection.getIdentityRegistry.returns(Promise.resolve({ + getAll: sinon.stub().returns([{name: 'idOne'}, {name: 'idTwo'}]) + })); + mockClientService.getBusinessNetworkConnection.returns(mockBusinessNetworkConnection); + + mockClientService.getMetaData.returns({ + getName: sinon.stub().returns('name') + }); + TestBed.configureTestingModule({ imports: [FormsModule], declarations: [ @@ -83,7 +94,7 @@ describe(`IdentityComponent`, () => { }); it('should load the component', () => { - let loadMock = sinon.stub(component, 'loadIdentities'); + let loadMock = sinon.stub(component, 'loadAllIdentities'); component.ngOnInit(); @@ -91,16 +102,17 @@ describe(`IdentityComponent`, () => { }); }); - describe('load identities', () => { + describe('load all identities', () => { it('should load the identities', fakeAsync(() => { mockIdentityService.getCurrentIdentities.returns(Promise.resolve(['idOne', 'idTwo'])); mockIdentityService.getCurrentIdentity.returns(Promise.resolve('my identity')); - component.loadIdentities(); + component.loadAllIdentities(); tick(); - component['identities'].should.deep.equal(['idOne', 'idTwo']); + component['myIdentities'].should.deep.equal(['idOne', 'idTwo']); + component['allIdentities'].should.deep.equal([{name: 'idOne'}, {name: 'idTwo'}]); component['currentIdentity'].should.equal('my identity'); })); @@ -109,11 +121,12 @@ describe(`IdentityComponent`, () => { mockIdentityService.getCurrentIdentities.returns(Promise.resolve(['idOne', 'idTwo'])); mockIdentityService.getCurrentIdentity.returns(Promise.reject('some error')); - component.loadIdentities(); + component.loadAllIdentities(); tick(); - component['identities'].should.deep.equal(['idOne', 'idTwo']); + component['myIdentities'].should.deep.equal(['idOne', 'idTwo']); + component['allIdentities'].should.deep.equal([{name: 'idOne'}, {name: 'idTwo'}]); should.not.exist(component['currentIdentity']); mockAlertService.errorStatus$.next.should.have.been.called; @@ -126,13 +139,13 @@ describe(`IdentityComponent`, () => { result: Promise.resolve() }); - let mockLoadIdentities = sinon.stub(component, 'loadIdentities'); + let mockLoadAllIdentities = sinon.stub(component, 'loadAllIdentities'); component.addId(); tick(); - mockLoadIdentities.should.have.been.called; + mockLoadAllIdentities.should.have.been.called; mockModal.open.should.have.been.called; })); @@ -141,14 +154,14 @@ describe(`IdentityComponent`, () => { result: Promise.reject('some error') }); - let mockLoadIdentities = sinon.stub(component, 'loadIdentities'); + let mockLoadAllIdentities = sinon.stub(component, 'loadAllIdentities'); component.addId(); tick(); mockAlertService.errorStatus$.next.should.have.been.called; - mockLoadIdentities.should.not.have.been.called; + mockLoadAllIdentities.should.not.have.been.called; mockModal.open.should.have.been.called; })); @@ -157,14 +170,14 @@ describe(`IdentityComponent`, () => { result: Promise.reject(1) }); - let mockLoadIdentities = sinon.stub(component, 'loadIdentities'); + let mockLoadAllIdentities = sinon.stub(component, 'loadAllIdentities'); component.addId(); tick(); mockAlertService.errorStatus$.next.should.not.have.been.called; - mockLoadIdentities.should.not.have.been.called; + mockLoadAllIdentities.should.not.have.been.called; mockModal.open.should.have.been.called; })); }); @@ -175,7 +188,7 @@ describe(`IdentityComponent`, () => { }); it('should issue id', fakeAsync(() => { - let mockLoadIdentities = sinon.stub(component, 'loadIdentities'); + let mockLoadAllIdentities = sinon.stub(component, 'loadAllIdentities'); mockModal.open.onFirstCall().returns({ result: Promise.resolve({userID: 'myId', userSecret: 'mySecret'}) }); @@ -189,11 +202,11 @@ describe(`IdentityComponent`, () => { tick(); - mockLoadIdentities.should.have.been.called; + mockLoadAllIdentities.should.have.been.called; })); it('should handle error in id creation', fakeAsync(() => { - let mockLoadIdentities = sinon.stub(component, 'loadIdentities'); + let mockLoadAllIdentities = sinon.stub(component, 'loadAllIdentities'); mockModal.open.onFirstCall().returns({ result: Promise.reject('some error') @@ -207,11 +220,11 @@ describe(`IdentityComponent`, () => { mockAlertService.errorStatus$.next.should.have.been.called; - mockLoadIdentities.should.have.been.called; + mockLoadAllIdentities.should.have.been.called; })); it('should handle escape being pressed', fakeAsync(() => { - let mockLoadIdentities = sinon.stub(component, 'loadIdentities'); + let mockLoadAllIdentities = sinon.stub(component, 'loadAllIdentities'); mockModal.open.onFirstCall().returns({ result: Promise.reject(1) @@ -225,11 +238,11 @@ describe(`IdentityComponent`, () => { mockAlertService.errorStatus$.next.should.not.have.been.called; - mockLoadIdentities.should.have.been.called; + mockLoadAllIdentities.should.have.been.called; })); it('should handle id in id displaying', fakeAsync(() => { - let mockLoadIdentities = sinon.stub(component, 'loadIdentities'); + let mockLoadAllIdentities = sinon.stub(component, 'loadAllIdentities'); mockModal.open.onFirstCall().returns({ result: Promise.resolve({userID: 'myId', userSecret: 'mySecret'}) @@ -244,13 +257,13 @@ describe(`IdentityComponent`, () => { tick(); - mockLoadIdentities.should.not.have.been.called; + mockLoadAllIdentities.should.not.have.been.called; mockAlertService.errorStatus$.next.should.have.been.called; })); it('should not issue identity if cancelled', fakeAsync(() => { - let mockLoadIdentities = sinon.stub(component, 'loadIdentities'); + let mockLoadAllIdentities = sinon.stub(component, 'loadAllIdentities'); mockModal.open.onFirstCall().returns({ result: Promise.resolve() @@ -261,13 +274,14 @@ describe(`IdentityComponent`, () => { tick(); mockAlertService.errorStatus$.next.should.not.have.been.called; - mockLoadIdentities.should.have.been.called; + mockLoadAllIdentities.should.have.been.called; })); }); describe('setCurrentIdentity', () => { it('should set the current identity', fakeAsync(() => { + component.loadAllIdentities = sinon.stub(); mockClientService.ensureConnected.returns(Promise.resolve()); component.setCurrentIdentity('bob'); @@ -308,12 +322,59 @@ describe(`IdentityComponent`, () => { }); describe('removeIdentity', () => { + it('should open the delete-confirm modal', fakeAsync(() => { + + mockModal.open = sinon.stub().returns({ + componentInstance: {}, + result: Promise.resolve() + }); + + component.removeIdentity('fred'); + tick(); + + mockModal.open.should.have.been.called; + })); + + it('should open the delete-confirm modal and handle error', fakeAsync(() => { + + mockModal.open = sinon.stub().returns({ + componentInstance: {}, + result: Promise.reject('some error') + }); + + component.removeIdentity('fred'); + tick(); + + mockAlertService.busyStatus$.next.should.have.been.called; + mockAlertService.errorStatus$.next.should.have.been.called; + })); + + it('should open the delete-confirm modal and handle cancel', fakeAsync(() => { + + mockModal.open = sinon.stub().returns({ + componentInstance: {}, + result: Promise.reject(null) + }); + + component.removeIdentity('fred'); + tick(); + + mockAlertService.busyStatus$.next.should.not.have.been.called; + mockAlertService.errorStatus$.next.should.not.have.been.called; + })); + it('should remove the identity from the wallet', fakeAsync(() => { + + mockModal.open = sinon.stub().returns({ + componentInstance: {}, + result: Promise.resolve(true) + }); + mockConnectionProfileService.getCurrentConnectionProfile.returns('myProfile'); mockWalletService.removeFromWallet.returns(Promise.resolve()); - let mockLoadIdentities = sinon.stub(component, 'loadIdentities'); + let mockLoadAllIdentities = sinon.stub(component, 'loadAllIdentities'); component.removeIdentity('fred'); @@ -321,16 +382,25 @@ describe(`IdentityComponent`, () => { mockConnectionProfileService.getCurrentConnectionProfile.should.have.been.called; mockWalletService.removeFromWallet.should.have.been.calledWith('myProfile', 'fred'); - mockLoadIdentities.should.have.been.called; + mockLoadAllIdentities.should.have.been.called; + mockAlertService.busyStatus$.next.should.have.been.called; + mockAlertService.successStatus$.next.should.have.been.called; + mockAlertService.errorStatus$.next.should.not.have.been.called; })); - it('should handle error', fakeAsync(() => { + it('should handle error when remvoing from wallet', fakeAsync(() => { + + mockModal.open = sinon.stub().returns({ + componentInstance: {}, + result: Promise.resolve(true) + }); + mockConnectionProfileService.getCurrentConnectionProfile.returns('myProfile'); mockWalletService.removeFromWallet.returns(Promise.reject('some error')); - let mockLoadIdentities = sinon.stub(component, 'loadIdentities'); + let mockLoadAllIdentities = sinon.stub(component, 'loadAllIdentities'); component.removeIdentity('fred'); @@ -338,9 +408,101 @@ describe(`IdentityComponent`, () => { mockConnectionProfileService.getCurrentConnectionProfile.should.have.been.called; mockWalletService.removeFromWallet.should.have.been.calledWith('myProfile', 'fred'); - mockLoadIdentities.should.not.have.been.called; + mockLoadAllIdentities.should.not.have.been.called; + mockAlertService.busyStatus$.next.should.have.been.called; mockAlertService.errorStatus$.next.should.have.been.calledWith('some error'); })); }); + + describe('revokeIdentity', () => { + it('should open the delete-confirm modal', fakeAsync(() => { + + mockModal.open = sinon.stub().returns({ + componentInstance: {}, + result: Promise.resolve() + }); + + component.revokeIdentity({name: 'fred'}); + tick(); + + mockModal.open.should.have.been.called; + })); + + it('should open the delete-confirm modal and handle error', fakeAsync(() => { + + mockModal.open = sinon.stub().returns({ + componentInstance: {}, + result: Promise.reject('some error') + }); + + component.revokeIdentity({name: 'fred'}); + tick(); + + mockAlertService.busyStatus$.next.should.have.been.called; + mockAlertService.errorStatus$.next.should.have.been.called; + })); + + it('should open the delete-confirm modal and handle cancel', fakeAsync(() => { + + mockModal.open = sinon.stub().returns({ + componentInstance: {}, + result: Promise.reject(null) + }); + + component.revokeIdentity({name: 'fred'}); + tick(); + + mockAlertService.busyStatus$.next.should.not.have.been.called; + mockAlertService.errorStatus$.next.should.not.have.been.called; + })); + + it('should revoke the identity from the client service and then remove the identity from the wallet', fakeAsync(() => { + + mockModal.open = sinon.stub().returns({ + componentInstance: {}, + result: Promise.resolve(true) + }); + + mockClientService.revokeIdentity.returns(Promise.resolve()); + + let mockRemoveIdentity = sinon.stub(component, 'removeIdentity').returns(Promise.resolve()); + let mockLoadAllIdentities = sinon.stub(component, 'loadAllIdentities').returns(Promise.resolve()); + + component.revokeIdentity({name: 'fred'}); + + tick(); + + mockClientService.revokeIdentity.should.have.been.called; + mockRemoveIdentity.should.have.been.calledWith('fred'); + mockLoadAllIdentities.should.have.been.called; + + mockAlertService.busyStatus$.next.should.have.been.called; + mockAlertService.successStatus$.next.should.have.been.called; + + })); + + it('should handle error', fakeAsync(() => { + mockModal.open = sinon.stub().returns({ + componentInstance: {}, + result: Promise.resolve(true) + }); + + mockClientService.revokeIdentity.returns(Promise.reject('some error')); + + let mockRemoveIdentity = sinon.stub(component, 'removeIdentity'); + let mockLoadAllIdentities = sinon.stub(component, 'loadAllIdentities'); + + component.revokeIdentity({name: 'fred'}); + + tick(); + + mockClientService.revokeIdentity.should.have.been.called; + mockRemoveIdentity.should.not.have.been.called; + mockLoadAllIdentities.should.not.have.been.called; + + mockAlertService.busyStatus$.next.should.have.been.called; + mockAlertService.errorStatus$.next.should.have.been.called; + })); + }); }); diff --git a/packages/composer-playground/src/app/identity/identity.component.ts b/packages/composer-playground/src/app/identity/identity.component.ts index 71c889077c..d87a63263c 100644 --- a/packages/composer-playground/src/app/identity/identity.component.ts +++ b/packages/composer-playground/src/app/identity/identity.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { DeleteComponent } from '../basic-modals/delete-confirm/delete-confirm.component'; import { AddIdentityComponent } from './add-identity'; import { IssueIdentityComponent } from './issue-identity'; @@ -20,8 +21,10 @@ import { WalletService } from '../services/wallet.service'; }) export class IdentityComponent implements OnInit { - identities: string[]; + myIdentities: string[]; + allIdentities: Object[]; // array of all IDs currentIdentity: string = null; + private deployedPackageName; constructor(private modalService: NgbModal, private alertService: AlertService, @@ -33,14 +36,27 @@ export class IdentityComponent implements OnInit { } ngOnInit(): Promise { - return this.loadIdentities(); + this.deployedPackageName = this.clientService.getMetaData().getName(); + return this.loadAllIdentities(); } - loadIdentities() { + loadAllIdentities() { + return this.loadMyIdentities() + .then(() => { + return this.clientService.ensureConnected(); + }).then(() => { + return this.clientService.getBusinessNetworkConnection().getIdentityRegistry(); + }).then((registry) => { + return registry.getAll(); + }).then((ids) => { + this.allIdentities = ids; + }); + } + + loadMyIdentities() { return this.identityService.getCurrentIdentities() .then((currentIdentities) => { - this.identities = currentIdentities; - + this.myIdentities = currentIdentities; return this.identityService.getCurrentIdentity(); }) .then((currentIdentity) => { @@ -53,7 +69,7 @@ export class IdentityComponent implements OnInit { addId() { this.modalService.open(AddIdentityComponent).result.then((result) => { - return this.loadIdentities(); + return this.loadAllIdentities(); }, (reason) => { if (reason && reason !== 1) { // someone hasn't pressed escape this.alertService.errorStatus$.next(reason); @@ -76,7 +92,7 @@ export class IdentityComponent implements OnInit { } }) .then(() => { - return this.loadIdentities(); + return this.loadAllIdentities(); }, (reason) => { this.alertService.errorStatus$.next(reason); }); @@ -94,6 +110,7 @@ export class IdentityComponent implements OnInit { return this.clientService.ensureConnected(true) .then(() => { this.alertService.busyStatus$.next(null); + return this.loadAllIdentities(); }) .catch((error) => { this.alertService.busyStatus$.next(null); @@ -102,13 +119,98 @@ export class IdentityComponent implements OnInit { } removeIdentity(userID: string) { + + // show confirm/delete dialog first before taking action + const confirmModalRef = this.modalService.open(DeleteComponent); + confirmModalRef.componentInstance.headerMessage = 'Remove ID'; + confirmModalRef.componentInstance.fileAction = 'remove'; + confirmModalRef.componentInstance.fileType = 'ID'; + confirmModalRef.componentInstance.fileName = userID; + confirmModalRef.componentInstance.deleteMessage = 'Take care when removing IDs: you usually cannot re-add them. Make sure you leave at least one ID that can be used to issue new IDs.'; + confirmModalRef.componentInstance.confirmButtonText = 'Remove'; + let profileName = this.connectionProfileService.getCurrentConnectionProfile(); - return this.walletService.removeFromWallet(profileName, userID) - .then(() => { - return this.loadIdentities(); - }) - .catch((error) => { - this.alertService.errorStatus$.next(error); + + confirmModalRef.result + .then((result) => { + if (result) { + this.alertService.busyStatus$.next({ + title: 'Removing ID', + text: 'Removing identity ' + userID + ' from your wallet' + }); + + return this.walletService.removeFromWallet(profileName, userID) + .then(() => { + return this.loadAllIdentities(); + }) + .then(() => { + // Send alert + this.alertService.busyStatus$.next(null); + this.alertService.successStatus$.next({ + title: 'Removal Successful', + text: userID + ' was successfully removed.', + icon: '#icon-trash_32' + }); + }) + .catch((error) => { + this.alertService.errorStatus$.next(error); + }); + } + }, (reason) => { + // runs this when user presses 'cancel' button on the modal + if (reason && reason !== 1) { + this.alertService.busyStatus$.next(null); + this.alertService.errorStatus$.next(reason); + } + }); + } + + revokeIdentity(identity) { + + // show confirm/delete dialog first before taking action + const confirmModalRef = this.modalService.open(DeleteComponent); + confirmModalRef.componentInstance.headerMessage = 'Revoke Identity'; + confirmModalRef.componentInstance.fileAction = 'revoke the permissions for'; + confirmModalRef.componentInstance.fileType = 'identity'; + confirmModalRef.componentInstance.fileName = identity.name; + confirmModalRef.componentInstance.deleteMessage = 'Are you sure you want to do this?'; + confirmModalRef.componentInstance.confirmButtonText = 'Revoke'; + + confirmModalRef.result + .then((result) => { + if (result) { + this.alertService.busyStatus$.next({ + title: 'Revoking identity within business network', + text: 'Revoking identity ' + identity.name + }); + + return this.clientService.revokeIdentity(identity) + .then(() => { + return this.removeIdentity(identity.name); + }) + .then(() => { + return this.loadAllIdentities(); + }) + .then(() => { + // Send alert + this.alertService.busyStatus$.next(null); + this.alertService.successStatus$.next({ + title: 'Revoke Successful', + text: identity.name + ' was successfully revoked.', + icon: '#icon-trash_32' + }); + }) + .catch((error) => { + this.alertService.busyStatus$.next(null); + this.alertService.errorStatus$.next(error); + }); + } + }, (reason) => { + // runs this when user presses 'cancel' button on the modal + if (reason && reason !== 1) { + this.alertService.busyStatus$.next(null); + this.alertService.errorStatus$.next(reason); + } }); } } diff --git a/packages/composer-playground/src/app/services/client.service.spec.ts b/packages/composer-playground/src/app/services/client.service.spec.ts index cea9cc8a7d..3a4d4ed746 100644 --- a/packages/composer-playground/src/app/services/client.service.spec.ts +++ b/packages/composer-playground/src/app/services/client.service.spec.ts @@ -1013,6 +1013,23 @@ describe('ClientService', () => { }))); }); + describe('revokeIdentity', () => { + it('should call the revokeIdentity() function for the relevant BusinessNetworkConnection', fakeAsync(inject([ClientService], (service: ClientService) => { + + // (1).should.equal(1); + + let mockGetBusinessNetwork = sinon.stub(service, 'getBusinessNetworkConnection').returns({ + revokeIdentity: sinon.stub().returns(Promise.resolve()) + }); + + service.revokeIdentity({fake : 'identity'}); + + tick(); + + mockGetBusinessNetwork().revokeIdentity.should.have.been.calledWith({fake : 'identity'}); + }))); + }); + describe('createNewBusinessNetwork', () => { it('should alert on failure', inject([ClientService], (service: ClientService) => { // Set up mocks diff --git a/packages/composer-playground/src/app/services/client.service.ts b/packages/composer-playground/src/app/services/client.service.ts index 4f434cbf69..c412119248 100644 --- a/packages/composer-playground/src/app/services/client.service.ts +++ b/packages/composer-playground/src/app/services/client.service.ts @@ -7,7 +7,7 @@ import { IdentityService } from './identity.service'; import { AlertService } from '../basic-modals/alert.service'; import { BusinessNetworkConnection } from 'composer-client'; -import { BusinessNetworkDefinition, Util, ModelFile, Script, AclFile, QueryFile } from 'composer-common'; +import { BusinessNetworkDefinition, Util, ModelFile, Script, AclFile, QueryFile, Resource } from 'composer-common'; /* tslint:disable-next-line:no-var-requires */ const sampleBusinessNetworkArchive = require('basic-sample-network/dist/basic-sample-network.bna'); @@ -345,8 +345,9 @@ export class ClientService { }); } - revokeIdentity(userID: string) { - return this.getBusinessNetworkConnection().revokeIdentity(userID); + revokeIdentity(identity) { + // identity should be the full ValidatedResource object + return this.getBusinessNetworkConnection().revokeIdentity(identity); } createNewBusinessNetwork(name, version, description, packageJson, readme) { diff --git a/packages/composer-playground/src/assets/styles/base/_typography.scss b/packages/composer-playground/src/assets/styles/base/_typography.scss index 366a7ca390..9500091ff3 100644 --- a/packages/composer-playground/src/assets/styles/base/_typography.scss +++ b/packages/composer-playground/src/assets/styles/base/_typography.scss @@ -16,6 +16,7 @@ font-family: $font-stack; font-size: 1.25em; //should be 20px font-weight: 300; + line-height: 48px; } h2 { font-family: $font-stack; diff --git a/packages/composer-playground/src/assets/styles/elements/_button.scss b/packages/composer-playground/src/assets/styles/elements/_button.scss index fc354798a5..4d38f99000 100644 --- a/packages/composer-playground/src/assets/styles/elements/_button.scss +++ b/packages/composer-playground/src/assets/styles/elements/_button.scss @@ -137,6 +137,11 @@ color: $secondary-text; min-width: 0; padding: 0 $space-smedium; + + &:hover { + color: $white; + background-color: $second-highlight; + } } &.action {