-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Display persistent configs in safe mode (#592)
* 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
Showing
6 changed files
with
420 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
// SPDX-FileCopyrightText: 2024 Gnuxie <Gnuxie@protonmail.com> | ||
// | ||
// 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); | ||
} | ||
} |
Oops, something went wrong.