Skip to content

Commit

Permalink
Display persistent configs in safe mode (#592)
Browse files Browse the repository at this point in the history
* Initial attempt at this, but I don't like it.

We've managed to cnofuse rendering/command layer again just because
we started with the rendering rather than the command lol.

* Remove side effects from config renderers.

* Improve rendering of persistent configs in safe mode.

We should really also expand by default, and array properties in
details instead. Otherwise a new user might not expand by default and
might not know what's wrong.

* Remove <details> from configs themselves.

* Color blind indicators for config status.

* When config are bodged just render their reasons on the same line.

* Hide stack trace behind details.

* Update for MPS 1.6.0.
  • Loading branch information
Gnuxie authored Oct 4, 2024
1 parent cb88aa5 commit 39eb298
Show file tree
Hide file tree
Showing 6 changed files with 420 additions and 34 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@
"js-yaml": "^4.1.0",
"jsdom": "^24.0.0",
"matrix-appservice-bridge": "^9.0.1",
"matrix-protection-suite": "npm:@gnuxie/matrix-protection-suite@1.5.2",
"matrix-protection-suite-for-matrix-bot-sdk": "npm:@gnuxie/matrix-protection-suite-for-matrix-bot-sdk@1.5.2",
"matrix-protection-suite": "npm:@gnuxie/matrix-protection-suite@1.6.0",
"matrix-protection-suite-for-matrix-bot-sdk": "npm:@gnuxie/matrix-protection-suite-for-matrix-bot-sdk@1.6.0",
"parse-duration": "^1.0.2",
"pg": "^8.8.0",
"shell-quote": "^1.7.3",
Expand Down
28 changes: 22 additions & 6 deletions src/safemode/DraupnirSafeMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@ import {
} from "../commands/interface-manager/MatrixPromptForAccept";
import { makeCommandDispatcherTimelineListener } from "./ManagementRoom";
import { SafeModeToggle } from "./SafeModeToggle";
import { Result } from "@gnuxie/typescript-result";
import { Result, isError } from "@gnuxie/typescript-result";
import {
renderSafeModeStatusInfo,
safeModeStatusInfo,
} from "./commands/StatusCommand";
import { wrapInRoot } from "../commands/interface-manager/MatrixHelpRenderer";
import { sendAndAnnotateWithRecoveryOptions } from "./commands/RecoverCommand";
import { StandardPersistentConfigEditor } from "./PersistentConfigEditor";

export class SafeModeDraupnir implements MatrixAdaptorContext {
public reactionHandler: MatrixReactionHandler;
Expand Down Expand Up @@ -118,11 +119,26 @@ export class SafeModeDraupnir implements MatrixAdaptorContext {

public startupComplete(): void {
void Task(
sendAndAnnotateWithRecoveryOptions(
this,
wrapInRoot(renderSafeModeStatusInfo(safeModeStatusInfo(this))),
{}
)
(async () => {
const editor = new StandardPersistentConfigEditor(this.client);
const configStatus = await editor.supplementStatusWithSafeModeCause(
this.cause
);
if (isError(configStatus)) {
return configStatus.elaborate(
"Failed to fetch draupnir's persistent configuration"
);
}
return await sendAndAnnotateWithRecoveryOptions(
this,
wrapInRoot(
renderSafeModeStatusInfo(
safeModeStatusInfo(this.cause, configStatus.ok)
)
),
{}
);
})()
);
}

Expand Down
143 changes: 143 additions & 0 deletions src/safemode/PersistentConfigEditor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// SPDX-FileCopyrightText: 2024 Gnuxie <[email protected]>
//
// SPDX-License-Identifier: AFL-3.0

import { Ok, Result, isError } from "@gnuxie/typescript-result";
import {
ConfigDescription,
ConfigParseError,
ConfigPropertyUseError,
MJOLNIR_PROTECTED_ROOMS_EVENT_TYPE,
MJOLNIR_WATCHED_POLICY_ROOMS_EVENT_TYPE,
MjolnirEnabledProtectionsDescription,
MjolnirEnabledProtectionsEventType,
MjolnirPolicyRoomsDescription,
MjolnirProtectedRoomsDescription,
PersistentConfigData,
StandardPersistentConfigData,
} from "matrix-protection-suite";
import {
BotSDKAccountDataConfigBackend,
MatrixSendClient,
} from "matrix-protection-suite-for-matrix-bot-sdk";
import { SafeModeCause, SafeModeReason } from "./SafeModeCause";

export type PersistentConfigStatus = {
description: ConfigDescription;
data: unknown;
error: ConfigParseError | undefined;
};

export interface PersistentConfigEditor {
getConfigAdaptors(): PersistentConfigData[];
requestConfigStatus(): Promise<Result<PersistentConfigStatus[]>>;
/**
* requestConfigStatus, but be sure to update the PeristentConfigStatus list
* with the ConfigPropertyUseError in the safe mode cause, if there is one.
*
* This is because `ConfigPropertyUseError`'s will not show up in parsing,
* only when creating Draupnir itself, and so they won't show up from just requesting
* the config alone.
*/
supplementStatusWithSafeModeCause(
cause: SafeModeCause
): Promise<Result<PersistentConfigStatus[]>>;
}

export class StandardPersistentConfigEditor implements PersistentConfigEditor {
private readonly configAdaptors: PersistentConfigData[] = [];
public constructor(client: MatrixSendClient) {
// We do some sweepy sweepy casting here because the ConfigMirror has methods
// that accept a specific shape, and obviously that means the type parameter
// becomes contravariant. I think the only way to fix this is to make the mirrors
// only work with the general shape rather than the specific one, in the way that
// the `remove` methods do, but I'm not convinced that works either, as those
// methods accept a Record that at least has the keys from the specific shape
// of the config. OK that's not why, because I tried to remove the toMirror method.
// I don't understand why it won't work then...
this.configAdaptors = [
new StandardPersistentConfigData(
MjolnirPolicyRoomsDescription as unknown as ConfigDescription,
new BotSDKAccountDataConfigBackend(
client,
MJOLNIR_WATCHED_POLICY_ROOMS_EVENT_TYPE
)
),
new StandardPersistentConfigData(
MjolnirProtectedRoomsDescription as unknown as ConfigDescription,
new BotSDKAccountDataConfigBackend(
client,
MJOLNIR_PROTECTED_ROOMS_EVENT_TYPE
)
),
new StandardPersistentConfigData(
MjolnirEnabledProtectionsDescription as unknown as ConfigDescription,
new BotSDKAccountDataConfigBackend(
client,
MjolnirEnabledProtectionsEventType
)
),
];
}
getConfigAdaptors(): PersistentConfigData[] {
return this.configAdaptors;
}

public async requestConfigStatus(): Promise<
Result<PersistentConfigStatus[]>
> {
const info: PersistentConfigStatus[] = [];
for (const adaptor of this.configAdaptors) {
const dataResult = await adaptor.requestConfig();
if (isError(dataResult)) {
if (dataResult.error instanceof ConfigParseError) {
info.push({
description: adaptor.description,
data: dataResult.error.config,
error: dataResult.error,
});
} else {
return dataResult;
}
} else {
info.push({
description: adaptor.description,
data: dataResult.ok,
error: undefined,
});
}
}
return Ok(info);
}
public async supplementStatusWithSafeModeCause(
cause: SafeModeCause
): Promise<Result<PersistentConfigStatus[]>> {
const info = await this.requestConfigStatus();
if (isError(info)) {
return info;
}
if (cause.reason === SafeModeReason.ByRequest) {
return Ok(info.ok);
}
if (!(cause.error instanceof ConfigPropertyUseError)) {
return Ok(info.ok);
}
const relevantStatus = info.ok.find(
(status) =>
status.description ===
(cause.error as ConfigPropertyUseError).configDescription
);
if (relevantStatus === undefined) {
throw new TypeError(
"The cause of the safe mode error was not found in the configuration status."
);
}
relevantStatus.error = new ConfigParseError(
"There was a problem when using a property in the configuration.",
relevantStatus.description as unknown as ConfigDescription,
[cause.error],
relevantStatus.data
);
return Ok(info.ok);
}
}
Loading

0 comments on commit 39eb298

Please sign in to comment.