feat(console): allow filter org by primary domain on instance (#7283)

* feat: i18n translations

* feat: add primary domain to filter-org component

* fix: add listOrgs service to admin and use it for org-table component

---------

Co-authored-by: Max Peintner <max@caos.ch>
This commit is contained in:
Miguel Cabrerizo 2024-01-30 17:09:47 +01:00 committed by GitHub
parent aa407c3c3e
commit 46bffd24ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 105 additions and 2 deletions

View File

@ -46,4 +46,27 @@
</div> </div>
</div> </div>
</div> </div>
<div class="domain-query">
<mat-checkbox
id="domain"
class="cb"
[checked]="getSubFilter(SubQuery.DOMAIN)"
(change)="changeCheckbox(SubQuery.DOMAIN, $event)"
>{{ 'FILTER.PRIMARYDOMAIN' | translate }}
</mat-checkbox>
<div class="subquery" *ngIf="getSubFilter(SubQuery.DOMAIN) as dq">
<cnsl-form-field class="filter-select-method">
<mat-select [value]="dq.getMethod()" (selectionChange)="setMethod(dq, $event)">
<mat-option *ngFor="let method of methods" [value]="method">
{{ 'FILTER.METHODS.' + method | translate }}
</mat-option>
</mat-select>
</cnsl-form-field>
<cnsl-form-field class="filter-input-value">
<input cnslInput name="value" [value]="dq.getDomain()" (change)="setValue(SubQuery.DOMAIN, dq, $event)" />
</cnsl-form-field>
</div>
</div>
</cnsl-filter> </cnsl-filter>

View File

@ -3,7 +3,7 @@ import { MatCheckboxChange } from '@angular/material/checkbox';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { take } from 'rxjs'; import { take } from 'rxjs';
import { TextQueryMethod } from 'src/app/proto/generated/zitadel/object_pb'; import { TextQueryMethod } from 'src/app/proto/generated/zitadel/object_pb';
import { OrgNameQuery, OrgQuery, OrgState, OrgStateQuery } from 'src/app/proto/generated/zitadel/org_pb'; import { OrgDomainQuery, OrgNameQuery, OrgQuery, OrgState, OrgStateQuery } from 'src/app/proto/generated/zitadel/org_pb';
import { UserNameQuery } from 'src/app/proto/generated/zitadel/user_pb'; import { UserNameQuery } from 'src/app/proto/generated/zitadel/user_pb';
import { FilterComponent } from '../filter/filter.component'; import { FilterComponent } from '../filter/filter.component';
@ -11,6 +11,7 @@ import { FilterComponent } from '../filter/filter.component';
enum SubQuery { enum SubQuery {
NAME, NAME,
STATE, STATE,
DOMAIN,
} }
@Component({ @Component({
@ -52,6 +53,13 @@ export class FilterOrgComponent extends FilterComponent implements OnInit {
orgStateQuery.setState(filter.stateQuery.state); orgStateQuery.setState(filter.stateQuery.state);
orgQuery.setStateQuery(orgStateQuery); orgQuery.setStateQuery(orgStateQuery);
return orgQuery; return orgQuery;
} else if (filter.domainQuery) {
const orgQuery = new OrgQuery();
const orgDomainQuery = new OrgDomainQuery();
orgDomainQuery.setDomain(filter.domainQuery.domain);
orgDomainQuery.setMethod(filter.domainQuery.method);
orgQuery.setDomainQuery(orgDomainQuery);
return orgQuery;
} else { } else {
return undefined; return undefined;
} }
@ -83,6 +91,14 @@ export class FilterOrgComponent extends FilterComponent implements OnInit {
osq.setStateQuery(sq); osq.setStateQuery(sq);
this.searchQueries.push(osq); this.searchQueries.push(osq);
break; break;
case SubQuery.DOMAIN:
const dq = new OrgDomainQuery();
dq.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
dq.setDomain('');
const odq = new OrgQuery();
odq.setDomainQuery(dq);
this.searchQueries.push(odq);
break;
} }
} else { } else {
switch (subquery) { switch (subquery) {
@ -98,6 +114,12 @@ export class FilterOrgComponent extends FilterComponent implements OnInit {
this.searchQueries.splice(index_sn, 1); this.searchQueries.splice(index_sn, 1);
} }
break; break;
case SubQuery.DOMAIN:
const index_pdn = this.searchQueries.findIndex((q) => (q as OrgQuery).toObject().domainQuery !== undefined);
if (index_pdn > -1) {
this.searchQueries.splice(index_pdn, 1);
}
break;
} }
} }
} }
@ -113,6 +135,10 @@ export class FilterOrgComponent extends FilterComponent implements OnInit {
(query as OrgStateQuery).setState(value); (query as OrgStateQuery).setState(value);
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []); this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
break; break;
case SubQuery.DOMAIN:
(query as OrgDomainQuery).setDomain(value);
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
break;
} }
} }
@ -132,6 +158,13 @@ export class FilterOrgComponent extends FilterComponent implements OnInit {
} else { } else {
return undefined; return undefined;
} }
case SubQuery.DOMAIN:
const pdn = this.searchQueries.find((q) => (q as OrgQuery).toObject().domainQuery !== undefined);
if (pdn) {
return (pdn as OrgQuery).getDomainQuery();
} else {
return undefined;
}
} }
} }

View File

@ -88,7 +88,7 @@ export class OrgTableComponent {
} }
return from( return from(
this.authService.listMyProjectOrgs(request.limit, request.offset, request.queries, sortingField, this.sort?.direction), this.adminService.listOrgs(request.limit, request.offset, request.queries, sortingField, this.sort?.direction),
).pipe( ).pipe(
map((resp) => { map((resp) => {
this.timestamp = resp.details?.viewTimestamp; this.timestamp = resp.details?.viewTimestamp;

View File

@ -159,6 +159,8 @@ import {
ListLoginPolicySecondFactorsResponse, ListLoginPolicySecondFactorsResponse,
ListMilestonesRequest, ListMilestonesRequest,
ListMilestonesResponse, ListMilestonesResponse,
ListOrgsRequest,
ListOrgsResponse,
ListProvidersRequest, ListProvidersRequest,
ListProvidersResponse, ListProvidersResponse,
ListSecretGeneratorsRequest, ListSecretGeneratorsRequest,
@ -314,6 +316,8 @@ import {
MilestoneQuery, MilestoneQuery,
MilestoneType, MilestoneType,
} from '../proto/generated/zitadel/milestone/v1/milestone_pb'; } from '../proto/generated/zitadel/milestone/v1/milestone_pb';
import { OrgFieldName, OrgQuery } from '../proto/generated/zitadel/org_pb';
import { SortDirection } from '@angular/material/sort';
export interface OnboardingActions { export interface OnboardingActions {
order: number; order: number;
@ -1303,4 +1307,33 @@ export class AdminService {
public listMilestones(req: ListMilestonesRequest): Promise<ListMilestonesResponse.AsObject> { public listMilestones(req: ListMilestonesRequest): Promise<ListMilestonesResponse.AsObject> {
return this.grpcService.admin.listMilestones(req, null).then((resp) => resp.toObject()); return this.grpcService.admin.listMilestones(req, null).then((resp) => resp.toObject());
} }
public listOrgs(
limit: number,
offset: number,
queriesList?: OrgQuery[],
sortingColumn?: OrgFieldName,
sortingDirection?: SortDirection,
): Promise<ListOrgsResponse.AsObject> {
const req = new ListOrgsRequest();
const query = new ListQuery();
if (limit) {
query.setLimit(limit);
}
if (offset) {
query.setOffset(offset);
}
if (sortingDirection) {
query.setAsc(sortingDirection === 'asc');
}
req.setQuery(query);
if (sortingColumn) {
req.setSortingColumn(sortingColumn);
}
if (queriesList) {
req.setQueriesList(queriesList);
}
return this.grpcService.admin.listOrgs(req, null).then((resp) => resp.toObject());
}
} }

View File

@ -221,6 +221,7 @@
"EMAIL": "електронна поща", "EMAIL": "електронна поща",
"USERNAME": "Потребителско име", "USERNAME": "Потребителско име",
"ORGNAME": "Наименование на организацията", "ORGNAME": "Наименование на организацията",
"PRIMARYDOMAIN": "Основен домейн",
"PROJECTNAME": "Име на проекта", "PROJECTNAME": "Име на проекта",
"RESOURCEOWNER": "Собственик на ресурс", "RESOURCEOWNER": "Собственик на ресурс",
"METHODS": { "METHODS": {

View File

@ -228,6 +228,7 @@
"EMAIL": "Email", "EMAIL": "Email",
"USERNAME": "Uživatelské jméno", "USERNAME": "Uživatelské jméno",
"ORGNAME": "Název organizace", "ORGNAME": "Název organizace",
"PRIMARYDOMAIN": "Primární doména",
"PROJECTNAME": "Název projektu", "PROJECTNAME": "Název projektu",
"RESOURCEOWNER": "Vlastník zdroje", "RESOURCEOWNER": "Vlastník zdroje",
"METHODS": { "METHODS": {

View File

@ -227,6 +227,7 @@
"EMAIL": "Email", "EMAIL": "Email",
"USERNAME": "Nutzername", "USERNAME": "Nutzername",
"ORGNAME": "Organisationsname", "ORGNAME": "Organisationsname",
"PRIMARYDOMAIN": "Primäre Domäne",
"PROJECTNAME": "Projektname", "PROJECTNAME": "Projektname",
"RESOURCEOWNER": "Ressourcenbesitzer", "RESOURCEOWNER": "Ressourcenbesitzer",
"METHODS": { "METHODS": {

View File

@ -228,6 +228,7 @@
"EMAIL": "Email", "EMAIL": "Email",
"USERNAME": "User Name", "USERNAME": "User Name",
"ORGNAME": "Organization Name", "ORGNAME": "Organization Name",
"PRIMARYDOMAIN": "Primary Domain",
"PROJECTNAME": "Project Name", "PROJECTNAME": "Project Name",
"RESOURCEOWNER": "Resource Owner", "RESOURCEOWNER": "Resource Owner",
"METHODS": { "METHODS": {

View File

@ -228,6 +228,7 @@
"EMAIL": "Email", "EMAIL": "Email",
"USERNAME": "Nombre de usuario", "USERNAME": "Nombre de usuario",
"ORGNAME": "Nombre de organización", "ORGNAME": "Nombre de organización",
"PRIMARYDOMAIN": "Dominio primario",
"PROJECTNAME": "Nombre de proyecto", "PROJECTNAME": "Nombre de proyecto",
"RESOURCEOWNER": "Propietario del recurso", "RESOURCEOWNER": "Propietario del recurso",
"METHODS": { "METHODS": {

View File

@ -227,6 +227,7 @@
"EMAIL": "Courriel", "EMAIL": "Courriel",
"USERNAME": "Nom de l'utilisateur", "USERNAME": "Nom de l'utilisateur",
"ORGNAME": "Nom de l'organisation", "ORGNAME": "Nom de l'organisation",
"PRIMARYDOMAIN": "Domaine principal",
"PROJECTNAME": "Nom du projet", "PROJECTNAME": "Nom du projet",
"RESOURCEOWNER": "Propriétaire des ressources", "RESOURCEOWNER": "Propriétaire des ressources",
"METHODS": { "METHODS": {

View File

@ -226,6 +226,7 @@
"EMAIL": "Email", "EMAIL": "Email",
"USERNAME": "User Name", "USERNAME": "User Name",
"ORGNAME": "Nome organizzazione", "ORGNAME": "Nome organizzazione",
"PRIMARYDOMAIN": "Dominio primario",
"PROJECTNAME": "Nome del progetto", "PROJECTNAME": "Nome del progetto",
"RESOURCEOWNER": "Resource Owner", "RESOURCEOWNER": "Resource Owner",
"METHODS": { "METHODS": {

View File

@ -228,6 +228,7 @@
"EMAIL": "Eメール", "EMAIL": "Eメール",
"USERNAME": "ユーザー名", "USERNAME": "ユーザー名",
"ORGNAME": "組織名", "ORGNAME": "組織名",
"PRIMARYDOMAIN": "プライマリドメイン",
"PROJECTNAME": "プロジェクト名", "PROJECTNAME": "プロジェクト名",
"RESOURCEOWNER": "リソース所有者", "RESOURCEOWNER": "リソース所有者",
"METHODS": { "METHODS": {

View File

@ -228,6 +228,7 @@
"EMAIL": "Е-пошта", "EMAIL": "Е-пошта",
"USERNAME": "Корисничко име", "USERNAME": "Корисничко име",
"ORGNAME": "Име на организацијата", "ORGNAME": "Име на организацијата",
"PRIMARYDOMAIN": "Примарен домен",
"PROJECTNAME": "Име на проектот", "PROJECTNAME": "Име на проектот",
"RESOURCEOWNER": "Сопственик на ресурсот", "RESOURCEOWNER": "Сопственик на ресурсот",
"METHODS": { "METHODS": {

View File

@ -228,6 +228,7 @@
"EMAIL": "E-mail", "EMAIL": "E-mail",
"USERNAME": "Gebruikersnaam", "USERNAME": "Gebruikersnaam",
"ORGNAME": "Organisatienaam", "ORGNAME": "Organisatienaam",
"PRIMARYDOMAIN": "Primair domein",
"PROJECTNAME": "Projectnaam", "PROJECTNAME": "Projectnaam",
"RESOURCEOWNER": "Eigenaar van de bron", "RESOURCEOWNER": "Eigenaar van de bron",
"METHODS": { "METHODS": {

View File

@ -227,6 +227,7 @@
"EMAIL": "Email", "EMAIL": "Email",
"USERNAME": "Nazwa Użytkownika", "USERNAME": "Nazwa Użytkownika",
"ORGNAME": "Nazwa Organizacji", "ORGNAME": "Nazwa Organizacji",
"PRIMARYDOMAIN": "Domena podstawowa",
"PROJECTNAME": "Nazwa Projektu", "PROJECTNAME": "Nazwa Projektu",
"RESOURCEOWNER": "Właściciel Zasobu", "RESOURCEOWNER": "Właściciel Zasobu",
"METHODS": { "METHODS": {

View File

@ -228,6 +228,7 @@
"EMAIL": "E-mail", "EMAIL": "E-mail",
"USERNAME": "Nome de Usuário", "USERNAME": "Nome de Usuário",
"ORGNAME": "Nome da Organização", "ORGNAME": "Nome da Organização",
"PRIMARYDOMAIN": "Domínio primário",
"PROJECTNAME": "Nome do Projeto", "PROJECTNAME": "Nome do Projeto",
"RESOURCEOWNER": "Proprietário do Recurso", "RESOURCEOWNER": "Proprietário do Recurso",
"METHODS": { "METHODS": {

View File

@ -224,6 +224,7 @@
"EMAIL": "Электронная почта", "EMAIL": "Электронная почта",
"USERNAME": "Имя пользователя", "USERNAME": "Имя пользователя",
"ORGNAME": "Название организации", "ORGNAME": "Название организации",
"PRIMARYDOMAIN": "Основной домен",
"PROJECTNAME": "название проекта", "PROJECTNAME": "название проекта",
"RESOURCEOWNER": "Владелец ресурса", "RESOURCEOWNER": "Владелец ресурса",
"METHODS": { "METHODS": {

View File

@ -227,6 +227,7 @@
"EMAIL": "邮箱", "EMAIL": "邮箱",
"USERNAME": "用户名", "USERNAME": "用户名",
"ORGNAME": "组织名称", "ORGNAME": "组织名称",
"PRIMARYDOMAIN": "主域",
"PROJECTNAME": "项目名称", "PROJECTNAME": "项目名称",
"RESOURCEOWNER": "资源所有者", "RESOURCEOWNER": "资源所有者",
"METHODS": { "METHODS": {