Files
zitadel/console/src/app/modules/actions-two/actions-two-actions/actions-two-actions.component.ts
Ramon 3348acdbab feat: Actions V2 improvements in console (#9759)
# Which Problems Are Solved
This PR allows one to edit the order of Actions V2 Targets in an
Execution. Editing of Targets was also added back again.

# How the Problems Are Solved
One of the changes is the addition of the CorrectlyTypedExecution which
restricts the Grpc types a bit more to make working with them easier.
Some fields may be optional in the Grpc Protobuf but in reality are
always set.
Typings were generally improved to make them more accurate and safer to
work with.

# Additional Changes
Removal of the Actions V2 Feature flag as it will be enabled by default
anyways.

# Additional Context
This pr used some advanced Angular Signals logic which is very
interesting for future PR's.
- Part of the tasks from #7248

---------

Co-authored-by: Max Peintner <peintnerm@gmail.com>
(cherry picked from commit 56e0df67d5)
2025-04-29 13:04:56 +02:00

114 lines
3.5 KiB
TypeScript

import { ChangeDetectionStrategy, Component, DestroyRef } from '@angular/core';
import { ActionService } from 'src/app/services/action.service';
import { lastValueFrom, Observable, of, Subject } from 'rxjs';
import { catchError, map, startWith, switchMap } from 'rxjs/operators';
import { ToastService } from 'src/app/services/toast.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import {
ActionTwoAddActionDialogComponent,
ActionTwoAddActionDialogData,
ActionTwoAddActionDialogResult,
CorrectlyTypedExecution,
correctlyTypeExecution,
} from '../actions-two-add-action/actions-two-add-action-dialog.component';
import { MatDialog } from '@angular/material/dialog';
import { MessageInitShape } from '@bufbuild/protobuf';
import { SetExecutionRequestSchema } from '@zitadel/proto/zitadel/action/v2beta/action_service_pb';
import { Target } from '@zitadel/proto/zitadel/action/v2beta/target_pb';
@Component({
selector: 'cnsl-actions-two-actions',
templateUrl: './actions-two-actions.component.html',
styleUrls: ['./actions-two-actions.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ActionsTwoActionsComponent {
protected readonly refresh$ = new Subject<true>();
protected readonly executions$: Observable<CorrectlyTypedExecution[]>;
protected readonly targets$: Observable<Target[]>;
constructor(
private readonly actionService: ActionService,
private readonly toast: ToastService,
private readonly destroyRef: DestroyRef,
private readonly dialog: MatDialog,
) {
this.executions$ = this.getExecutions$();
this.targets$ = this.getTargets$();
}
private getExecutions$() {
return this.refresh$.pipe(
startWith(true),
switchMap(() => {
return this.actionService.listExecutions({});
}),
map(({ result }) => result.map(correctlyTypeExecution)),
catchError((err) => {
this.toast.showError(err);
return of([]);
}),
);
}
private getTargets$() {
return this.refresh$.pipe(
startWith(true),
switchMap(() => {
return this.actionService.listTargets({});
}),
map(({ result }) => result),
catchError((err) => {
this.toast.showError(err);
return of([]);
}),
);
}
public async openDialog(execution?: CorrectlyTypedExecution): Promise<void> {
const request$ = this.dialog
.open<ActionTwoAddActionDialogComponent, ActionTwoAddActionDialogData, ActionTwoAddActionDialogResult>(
ActionTwoAddActionDialogComponent,
{
width: '400px',
data: execution
? {
execution,
}
: {},
},
)
.afterClosed()
.pipe(takeUntilDestroyed(this.destroyRef));
const request = await lastValueFrom(request$);
if (!request) {
return;
}
try {
await this.actionService.setExecution(request);
await new Promise((res) => setTimeout(res, 1000));
this.refresh$.next(true);
} catch (error) {
console.error(error);
this.toast.showError(error);
}
}
public async deleteExecution(execution: CorrectlyTypedExecution) {
const deleteReq: MessageInitShape<typeof SetExecutionRequestSchema> = {
condition: execution.condition,
targets: [],
};
try {
await this.actionService.setExecution(deleteReq);
await new Promise((res) => setTimeout(res, 1000));
this.refresh$.next(true);
} catch (error) {
console.error(error);
this.toast.showError(error);
}
}
}