feat(console): rename org (#4542)

* rename org

* add data-e2e

* e2e test

* restore state after

* use ngIf instead of hasrole directive and initialized regex

* rm h2 check

* Update e2e/cypress/e2e/organization/organizations.cy.ts

Co-authored-by: Elio Bischof <eliobischof@gmail.com>

* Update console/src/assets/i18n/de.json

Co-authored-by: Elio Bischof <eliobischof@gmail.com>

* Update console/src/assets/i18n/de.json

Co-authored-by: Elio Bischof <eliobischof@gmail.com>

* Update console/src/assets/i18n/en.json

Co-authored-by: Elio Bischof <eliobischof@gmail.com>

* change e2e test

* org param

* reintroduct org param

* use org query param

* org rename test

* no initial focus on button

* contain name

Co-authored-by: Elio Bischof <eliobischof@gmail.com>
This commit is contained in:
Max Peintner 2022-10-20 14:08:13 +02:00 committed by GitHub
parent 2bfa51da1c
commit 6e89b7d0a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 186 additions and 15 deletions

View File

@ -8,7 +8,7 @@ import { DomSanitizer } from '@angular/platform-browser';
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
import { LangChangeEvent, TranslateService } from '@ngx-translate/core';
import { Observable, of, Subject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import { filter, map, takeUntil } from 'rxjs/operators';
import { accountCard, adminLineAnimation, navAnimations, routeAnimations, toolbarAnimation } from './animations';
import { Org } from './proto/generated/zitadel/org_pb';
@ -191,6 +191,11 @@ export class AppComponent implements OnDestroy {
}
});
this.activatedRoute.queryParams.pipe(filter((params) => !!params.org)).subscribe((params) => {
const { org } = params;
this.authService.getActiveOrg(org);
});
this.authenticationService.authenticationChanged.pipe(takeUntil(this.destroy$)).subscribe((authenticated) => {
if (authenticated) {
this.authService

View File

@ -5,7 +5,7 @@
<div mat-dialog-content>
<cnsl-form-field class="formfield">
<cnsl-label>{{ data.labelKey | translate }}</cnsl-label>
<input cnslInput [(ngModel)]="name" />
<input data-e2e="name" cnslInput [(ngModel)]="name" />
</cnsl-form-field>
</div>
<div mat-dialog-actions class="action">
@ -13,7 +13,7 @@
{{ 'ACTIONS.CLOSE' | translate }}
</button>
<button [disabled]="!name" cdkFocusInitial color="primary" mat-raised-button (click)="closeDialog(name)">
<button data-e2e="dialog-submit" [disabled]="!name" color="primary" mat-raised-button (click)="closeDialog(name)">
{{ 'ACTIONS.RENAME' | translate }}
</button>
</div>

View File

@ -7,7 +7,7 @@
</a>
<div class="cnsl-title-row-wrapper">
<div class="cnsl-title-row">
<h2 class="cnsl-title">{{ title }}</h2>
<h2 data-e2e="top-view-title" class="cnsl-title">{{ title }}</h2>
<div
class="cnsl-state-dot"
*ngIf="isActive || isInactive"
@ -28,6 +28,7 @@
<ng-container *ngIf="hasActions">
<button
data-e2e="actions"
class="cnsl-actions-trigger-desk cnsl-action-button"
mat-raised-button
color="primary"

View File

@ -1,13 +1,14 @@
<cnsl-top-view
*ngIf="['org.write:' + org?.id, 'org.write$'] | hasRole as hasWrite$"
[hasBackButton]="false"
title="{{ org?.name }}"
[isActive]="org?.state === OrgState.ORG_STATE_ACTIVE"
[isInactive]="org?.state === OrgState.ORG_STATE_INACTIVE"
[hasContributors]="true"
stateTooltip="{{ 'ORG.STATE.' + org?.state | translate }}"
[hasActions]="['org.write:' + org?.id, 'org.write$'] | hasRole | async"
[hasActions]="hasWrite$ | async"
>
<ng-template topActions cnslHasRole [hasRole]="['org.write:' + org?.id, 'org.write$']">
<ng-container topActions *ngIf="hasWrite$ | async">
<button
mat-menu-item
*ngIf="org?.state === OrgState.ORG_STATE_ACTIVE"
@ -23,7 +24,11 @@
>
{{ 'ORG.PAGES.REACTIVATE' | translate }}
</button>
</ng-template>
<button data-e2e="rename" mat-menu-item (click)="renameOrg()">
{{ 'ORG.PAGES.RENAME.ACTION' | translate }}
</button>
</ng-container>
<cnsl-contributors
topContributors
[totalResult]="totalMemberResult"

View File

@ -18,6 +18,7 @@ import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service';
import { Buffer } from 'buffer';
import { NameDialogComponent } from 'src/app/modules/name-dialog/name-dialog.component';
@Component({
selector: 'cnsl-org-detail',
@ -44,7 +45,7 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
public InfoSectionType: any = InfoSectionType;
constructor(
auth: GrpcAuthService,
private auth: GrpcAuthService,
private dialog: MatDialog,
public mgmtService: ManagementService,
private toast: ToastService,
@ -235,4 +236,49 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
this.loadMetadata();
});
}
public renameOrg(): void {
const dialogRef = this.dialog.open(NameDialogComponent, {
data: {
name: this.org?.name,
titleKey: 'ORG.PAGES.RENAME.TITLE',
descKey: 'ORG.PAGES.RENAME.DESCRIPTION',
labelKey: 'ORG.PAGES.NAME',
},
width: '400px',
});
dialogRef.afterClosed().subscribe((name) => {
if (name) {
this.updateOrg(name);
}
});
}
public updateOrg(name: string): void {
if (this.org) {
this.mgmtService
.updateOrg(name)
.then(() => {
this.toast.showInfo('ORG.TOAST.UPDATED', true);
if (this.org) {
this.org.name = name;
}
this.mgmtService
.getMyOrg()
.then((resp) => {
if (resp.org) {
this.org = resp.org;
this.auth.setActiveOrg(resp.org);
}
})
.catch((error) => {
this.toast.showError(error);
});
})
.catch((error) => {
this.toast.showError(error);
});
}
}
}

View File

@ -19,6 +19,7 @@ import { InfoSectionModule } from 'src/app/modules/info-section/info-section.mod
import { InputModule } from 'src/app/modules/input/input.module';
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
import { MetadataModule } from 'src/app/modules/metadata/metadata.module';
import { NameDialogModule } from 'src/app/modules/name-dialog/name-dialog.module';
import { SettingsGridModule } from 'src/app/modules/settings-grid/settings-grid.module';
import { SharedModule } from 'src/app/modules/shared/shared.module';
import { TopViewModule } from 'src/app/modules/top-view/top-view.module';
@ -52,6 +53,7 @@ import { OrgRoutingModule } from './org-routing.module';
WarnDialogModule,
MemberCreateDialogModule,
MatMenuModule,
NameDialogModule,
ChangesModule,
MatProgressSpinnerModule,
MetadataModule,

View File

@ -425,6 +425,8 @@ import {
UpdateOrgIDPResponse,
UpdateOrgMemberRequest,
UpdateOrgMemberResponse,
UpdateOrgRequest,
UpdateOrgResponse,
UpdateProjectGrantMemberRequest,
UpdateProjectGrantMemberResponse,
UpdateProjectGrantRequest,
@ -2353,6 +2355,12 @@ export class ManagementService {
return this.grpcService.mgmt.updateApp(req, null).then((resp) => resp.toObject());
}
public updateOrg(name: string): Promise<UpdateOrgResponse.AsObject> {
const req = new UpdateOrgRequest();
req.setName(name);
return this.grpcService.mgmt.updateOrg(req, null).then((resp) => resp.toObject());
}
public updateOIDCAppConfig(req: UpdateOIDCAppConfigRequest): Promise<UpdateOIDCAppConfigResponse.AsObject> {
return this.grpcService.mgmt.updateOIDCAppConfig(req, null).then((resp) => resp.toObject());
}

View File

@ -777,6 +777,12 @@
"ORGDETAIL_TITLE": "Gebe den Namen und die Domain für die neue Organisation ein.",
"ORGDETAIL_TITLE_WITHOUT_DOMAIN": "Geben Sie den Namen der neuen Organisation ein.",
"ORGDETAILUSER_TITLE": "Organisationsbesitzer hinzufügen",
"RENAME": {
"ACTION": "Umbenennen",
"TITLE": "Organisation umbenennen",
"DESCRIPTION": "Geben Sie den neuen Namen Ihrer Organisation an.",
"BTN": "Umbenennen"
},
"ORGDOMAIN": {
"TITLE": "Verifikation der Domain der Organisation",
"VERIFICATION": "Überprüfe den Besitz Deiner Domain, indem Du eine Bestätigungsdatei herunterlädst und unter der angegebenen URL speicherst, oder indem Du sie mit einem DNS-Eintrag verifizierst.",
@ -828,6 +834,7 @@
"DESCRIPTION": "Definiere hier die Benutzer, die Operationen auf Deinen Organisationen vornehmen dürfen."
},
"TOAST": {
"UPDATED": "Organisation geändert",
"DEACTIVATED": "Organisation deaktiviert.",
"REACTIVATED": "Organisation reaktiviert.",
"DOMAINADDED": "Domain hinzugefügt.",

View File

@ -777,6 +777,12 @@
"ORGDETAIL_TITLE": "Enter the name and domain of your new organization.",
"ORGDETAIL_TITLE_WITHOUT_DOMAIN": "Enter the name of your new organization.",
"ORGDETAILUSER_TITLE": "Configure Organization Owner",
"RENAME": {
"ACTION": "Rename",
"TITLE": "Rename Organization",
"DESCRIPTION": "Enter the new name for your organization",
"BTN": "Rename"
},
"ORGDOMAIN": {
"TITLE": "Organization Domain Ownership Verification",
"VERIFICATION": "To verify the ownership of your domain, you need to download a verification file and upload it at the provided URL listed below, or place a TXT Record DNS entry for the provided URL. To complete, click the button to verify.",
@ -828,6 +834,7 @@
"DESCRIPTION": "Define the users who can change your organizations preferences."
},
"TOAST": {
"UPDATED": "Organization updated successfully.",
"DEACTIVATED": "Organization deactivated.",
"REACTIVATED": "Organization reactivated.",
"DOMAINADDED": "Added domain.",

View File

@ -777,6 +777,12 @@
"ORGDETAIL_TITLE": "Saisissez le nom et le domaine de votre nouvelle organisation.",
"ORGDETAIL_TITLE_WITHOUT_DOMAIN": "Saisissez le nom de votre nouvelle organisation.",
"ORGDETAILUSER_TITLE": "Configurer le propriétaire de l'organisation",
"RENAME": {
"ACTION": "Renommer",
"TITLE": "Renommer l'organisation",
"DESCRIPTION": "Entrez le nouveau nom de votre organisation",
"BTN": "Renommer"
},
"ORGDOMAIN": {
"TITLE": "Vérification de la propriété du domaine de l'organisation",
"VERIFICATION": "Pour vérifier la propriété de votre domaine, vous devez télécharger un fichier de vérification et le charger à l'URL indiquée ci-dessous, ou placer une entrée DNS TXT Record pour l'URL indiquée. Pour terminer, cliquez sur le bouton de vérification.",
@ -828,6 +834,7 @@
"DESCRIPTION": "Définissez les utilisateurs qui peuvent modifier les préférences de votre organisation."
},
"TOAST": {
"UPDATED": "L'organisation a été mise à jour",
"DEACTIVATED": "Organisation désactivée.",
"REACTIVATED": "Organisation réactivée.",
"DOMAINADDED": "Domaine ajouté.",

View File

@ -777,6 +777,12 @@
"ORGDETAIL_TITLE": "Inserisci il nome e il dominio della tua nuova organizzazione.",
"ORGDETAIL_TITLE_WITHOUT_DOMAIN": "Inserisci il nome della tua nuova organizzazione.",
"ORGDETAILUSER_TITLE": "Configurare il proprietario dell'organizzazione",
"RENAME": {
"ACTION": "Rinomina",
"TITLE": "Rinomina organizzazione",
"DESCRIPTION": "Inserisci il nuovo nome per la tua organizzazione",
"BTN": "Rinomina"
},
"ORGDOMAIN": {
"TITLE": "Verificazione della propriet\u00e0 del dominio dell'organizzazione",
"VERIFICATION": "Verifica la propriet\u00e0 del tuo dominio. \u00c8 necessario scaricare un file di verifica e caricarlo all'URL fornito elencato di seguito, o inserire una voce DNS TXT Record per l'URL fornito. Per completare, clicca sul pulsante di verifica.",
@ -828,6 +834,7 @@
"DESCRIPTION": "Definisci gli utenti che possono cambiare le preferenze delle tue organizzazioni."
},
"TOAST": {
"UPDATED": "L'organizzazione es stata aggiornata.",
"DEACTIVATED": "Organizzazione disattivata.",
"REACTIVATED": "Organizzazione riattivata.",
"DOMAINADDED": "Aggiunto dominio.",

View File

@ -777,6 +777,12 @@
"ORGDETAIL_TITLE": "输入新组织的名称和域名。",
"ORGDETAIL_TITLE_WITHOUT_DOMAIN": "输入新组织的名称。",
"ORGDETAILUSER_TITLE": "配置组织所有者",
"RENAME": {
"ACTION": "改名",
"TITLE": "重命名组织",
"DESCRIPTION": "输入组织的新名称",
"BTN": "改名"
},
"ORGDOMAIN": {
"TITLE": "组织域所有权验证",
"VERIFICATION": "要验证您对域的所有权,您需要下载验证文件并将其上传到下面列出的提供的 URL或者为提供的域名添加一条类型为TXT的DNS解析记录。完成后请单击按钮进行验证。",
@ -828,6 +834,7 @@
"DESCRIPTION": "定义可以更改组织首选项的用户。"
},
"TOAST": {
"UPDATED": "组织已更新",
"DEACTIVATED": "组织已停用。",
"REACTIVATED": "组织重新启用。",
"DOMAINADDED": "域名已添加。",

View File

@ -1,4 +1,39 @@
import { ensureOrgExists } from 'support/api/orgs';
import { apiAuth } from '../../support/api/apiauth';
import { v4 as uuidv4 } from 'uuid';
const orgPath = `/org`;
const orgNameOnCreation = 'e2eorgrename';
const testOrgNameChange = uuidv4();
describe('organizations', () => {
describe('rename', () => {
beforeEach(() => {
apiAuth()
.as('api')
.then((api) => {
ensureOrgExists(api, orgNameOnCreation)
.as('newOrgId')
.then((newOrgId) => {
cy.visit(`${orgPath}?org=${newOrgId}`).as('orgsite');
});
});
});
it('should rename the organization', () => {
cy.get('[data-e2e="actions"]').click();
cy.get('[data-e2e="rename"]', { timeout: 1000 }).should('be.visible').click();
cy.get('[data-e2e="name"]').focus().clear().type(testOrgNameChange);
cy.get('[data-e2e="dialog-submit"]').click();
cy.get('.data-e2e-success');
cy.shouldNotExist({ selector: '.data-e2e-failure' });
cy.visit(orgPath);
cy.get('[data-e2e="top-view-title"').should('contain', testOrgNameChange);
});
});
it('should add an organization with the personal account as org owner');
describe('changing the current organization', () => {
it('should update displayed organization details');

View File

@ -18,7 +18,7 @@ export function ensureOrgExists(api: API, name: string): Cypress.Chainable<numbe
() => `${api.mgmtBaseURL}/orgs`,
'POST',
{ name: name },
(org: any) => org?.name === name,
(org) => org?.name === name,
(res) => res.id,
);
}

45
e2e/package-lock.json generated
View File

@ -14,6 +14,7 @@
"mochawesome": "^7.1.3",
"prettier": "^2.7.1",
"typescript": "^4.8.4",
"uuid": "^9.0.0",
"wait-on": "^6.0.1"
},
"devDependencies": {
@ -60,6 +61,15 @@
"node": ">= 6"
}
},
"node_modules/@cypress/request/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true,
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/@cypress/xvfb": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz",
@ -1945,6 +1955,14 @@
"node": ">=12"
}
},
"node_modules/mochawesome/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@ -2594,9 +2612,9 @@
}
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
"bin": {
"uuid": "dist/bin/uuid"
}
@ -2794,6 +2812,14 @@
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^8.3.2"
},
"dependencies": {
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true
}
}
},
"@cypress/xvfb": {
@ -4174,6 +4200,13 @@
"mochawesome-report-generator": "^6.2.0",
"strip-ansi": "^6.0.1",
"uuid": "^8.3.2"
},
"dependencies": {
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
}
}
},
"mochawesome-report-generator": {
@ -4690,9 +4723,9 @@
"dev": true
},
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
},
"validator": {
"version": "13.7.0",

View File

@ -17,6 +17,7 @@
"mochawesome": "^7.1.3",
"prettier": "^2.7.1",
"typescript": "^4.8.4",
"uuid": "^9.0.0",
"wait-on": "^6.0.1"
},
"devDependencies": {