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 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>

View File

@ -3,7 +3,7 @@ import { MatCheckboxChange } from '@angular/material/checkbox';
import { ActivatedRoute, Router } from '@angular/router';
import { take } from 'rxjs';
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 { FilterComponent } from '../filter/filter.component';
@ -11,6 +11,7 @@ import { FilterComponent } from '../filter/filter.component';
enum SubQuery {
NAME,
STATE,
DOMAIN,
}
@Component({
@ -52,6 +53,13 @@ export class FilterOrgComponent extends FilterComponent implements OnInit {
orgStateQuery.setState(filter.stateQuery.state);
orgQuery.setStateQuery(orgStateQuery);
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 {
return undefined;
}
@ -83,6 +91,14 @@ export class FilterOrgComponent extends FilterComponent implements OnInit {
osq.setStateQuery(sq);
this.searchQueries.push(osq);
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 {
switch (subquery) {
@ -98,6 +114,12 @@ export class FilterOrgComponent extends FilterComponent implements OnInit {
this.searchQueries.splice(index_sn, 1);
}
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);
this.filterChanged.emit(this.searchQueries ? this.searchQueries : []);
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 {
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(
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(
map((resp) => {
this.timestamp = resp.details?.viewTimestamp;

View File

@ -159,6 +159,8 @@ import {
ListLoginPolicySecondFactorsResponse,
ListMilestonesRequest,
ListMilestonesResponse,
ListOrgsRequest,
ListOrgsResponse,
ListProvidersRequest,
ListProvidersResponse,
ListSecretGeneratorsRequest,
@ -314,6 +316,8 @@ import {
MilestoneQuery,
MilestoneType,
} 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 {
order: number;
@ -1303,4 +1307,33 @@ export class AdminService {
public listMilestones(req: ListMilestonesRequest): Promise<ListMilestonesResponse.AsObject> {
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": "електронна поща",
"USERNAME": "Потребителско име",
"ORGNAME": "Наименование на организацията",
"PRIMARYDOMAIN": "Основен домейн",
"PROJECTNAME": "Име на проекта",
"RESOURCEOWNER": "Собственик на ресурс",
"METHODS": {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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