import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { EVENT_FLOW_ADVANCE, EVENT_FLOW_INSPECTOR_TOGGLE } from "@goauthentik/common/constants"; import { AKElement } from "@goauthentik/elements/Base"; import "@goauthentik/elements/Expand"; import { msg } from "@lit/localize"; import { CSSResult, TemplateResult, css, html } from "lit"; import { customElement, property } from "lit/decorators.js"; import PFButton from "@patternfly/patternfly/components/Button/button.css"; import PFCard from "@patternfly/patternfly/components/Card/card.css"; import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css"; import PFNotificationDrawer from "@patternfly/patternfly/components/NotificationDrawer/notification-drawer.css"; import PFProgressStepper from "@patternfly/patternfly/components/ProgressStepper/progress-stepper.css"; import PFStack from "@patternfly/patternfly/layouts/Stack/stack.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; import { FlowInspection, FlowsApi, Stage } from "@goauthentik/api"; @customElement("ak-flow-inspector") export class FlowInspector extends AKElement { flowSlug: string; @property({ attribute: false }) state?: FlowInspection; @property({ attribute: false }) error?: Response; static get styles(): CSSResult[] { return [ PFBase, PFButton, PFStack, PFCard, PFNotificationDrawer, PFDescriptionList, PFProgressStepper, css` code.break { word-break: break-all; } pre { word-break: break-all; overflow-x: hidden; white-space: break-spaces; } `, ]; } constructor() { super(); this.flowSlug = window.location.pathname.split("/")[3]; window.addEventListener(EVENT_FLOW_ADVANCE, this.advanceHandler as EventListener); } disconnectedCallback(): void { super.disconnectedCallback(); window.removeEventListener(EVENT_FLOW_ADVANCE, this.advanceHandler as EventListener); } advanceHandler = (): void => { new FlowsApi(DEFAULT_CONFIG) .flowsInspectorGet({ flowSlug: this.flowSlug, }) .then((state) => { this.state = state; }) .catch((exc) => { this.error = exc; }); }; // getStage return a stage without flowSet, for brevity getStage(stage?: Stage): unknown { if (!stage) { return stage; } delete stage.flowSet; return stage; } renderAccessDenied(): TemplateResult { return html`

${msg("Flow inspector")}

${this.error?.statusText}
`; } render(): TemplateResult { if (this.error) { return this.renderAccessDenied(); } if (!this.state) { return html` `; } return html`

${msg("Flow inspector")}

${msg("Next stage")}
${msg("Stage name")}
${this.state.currentPlan?.nextPlannedStage ?.stageObj?.name || "-"}
${msg("Stage kind")}
${this.state.currentPlan?.nextPlannedStage ?.stageObj?.verboseName || "-"}
${msg("Stage object")}
${this.state.isCompleted ? html`
${msg("This flow is completed.")}
` : html`
${JSON.stringify(this.getStage(this.state.currentPlan?.nextPlannedStage?.stageObj), null, 4)}
`}
${msg("Plan history")}
    ${this.state.plans.map((plan) => { return html`
  1. ${plan.currentStage.stageObj?.name}
    ${plan.currentStage.stageObj?.verboseName}
  2. `; })} ${this.state.currentPlan?.currentStage && !this.state.isCompleted ? html`
  3. ${this.state.currentPlan?.currentStage ?.stageObj?.name}
    ${this.state.currentPlan?.currentStage ?.stageObj?.verboseName}
  4. ` : html``} ${this.state.currentPlan?.nextPlannedStage && !this.state.isCompleted ? html`
  5. ${this.state.currentPlan.nextPlannedStage .stageObj?.name}
    ${this.state.currentPlan?.nextPlannedStage ?.stageObj?.verboseName}
  6. ` : html``}
${msg("Current plan context")}
${JSON.stringify(this.state.currentPlan?.planContext, null, 4)}
${msg("Session ID")}
${this.state.currentPlan?.sessionId}
`; } }