2021-11-18 01:26:32 +00:00
|
|
|
// Intended to be used with message lists
|
|
|
|
|
|
|
|
import { stringify } from "querystring";
|
2021-11-21 01:06:50 +00:00
|
|
|
import { AutoVerifier, AutoVerifierChangesType } from "./auto-verifier";
|
2021-11-18 01:26:32 +00:00
|
|
|
import { Changes, WithEquals } from "./data-types";
|
|
|
|
|
2021-11-21 01:06:50 +00:00
|
|
|
export interface PartialMessageListQuery {
|
2021-11-18 01:26:32 +00:00
|
|
|
channelId: string;
|
|
|
|
messageId: string | null;
|
|
|
|
number: number;
|
|
|
|
}
|
|
|
|
|
2021-11-21 01:06:50 +00:00
|
|
|
export interface IDQuery {
|
|
|
|
id: string;
|
|
|
|
}
|
|
|
|
|
2021-11-18 01:26:32 +00:00
|
|
|
export class AutoVerifierWithArg<T, K> {
|
|
|
|
private tokenAutoVerifiers = new Map<string, AutoVerifier<T>>();
|
|
|
|
private tokenQueries = new Map<string, K>();
|
|
|
|
|
|
|
|
// TODO: Clear out old autoverifiers that haven't been used for a while.
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
private tokenizer: (query: K) => string, // must be one-to-one mapping
|
|
|
|
private primaryFunc: (query: K) => Promise<T | null>,
|
|
|
|
private trustedFunc: (query: K) => Promise<T | null>,
|
|
|
|
private verifyFunc: (query: K, primaryResult: T | null, trustedResult: T | null) => Promise<void>
|
|
|
|
) {}
|
|
|
|
|
2021-11-21 01:06:50 +00:00
|
|
|
static createStandardPartialMessageListAutoVerifier<T extends WithEquals<T> & { id: string }>(
|
|
|
|
primaryFunc: (query: PartialMessageListQuery) => Promise<T[] | null>,
|
|
|
|
trustedFunc: (query: PartialMessageListQuery) => Promise<T[] | null>,
|
|
|
|
changesFunc: (query: PartialMessageListQuery, changesType: AutoVerifierChangesType, changes: Changes<T>) => Promise<void>
|
2021-11-18 01:26:32 +00:00
|
|
|
) {
|
2021-11-21 01:06:50 +00:00
|
|
|
return new AutoVerifierWithArg<T[], PartialMessageListQuery>(
|
2021-11-18 01:26:32 +00:00
|
|
|
query => `ch#${query.channelId} m#${query.messageId}->${query.number}`,
|
|
|
|
query => primaryFunc(query),
|
|
|
|
query => trustedFunc(query),
|
2021-11-21 01:06:50 +00:00
|
|
|
async (query: PartialMessageListQuery, primaryResult: T[] | null, trustedResult: T[] | null) => {
|
|
|
|
let changes = AutoVerifier.getChanges<T>(primaryResult, trustedResult);
|
|
|
|
let changesType = AutoVerifier.getListChangesType<T>(primaryResult, trustedResult, changes);
|
|
|
|
await changesFunc(query, changesType, changes);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
static createStandardIDQueriedSingleAutoVerifier<T extends WithEquals<T> & { id: string }>(
|
|
|
|
primaryFunc: (query: IDQuery) => Promise<T | null>,
|
|
|
|
trustedFunc: (query: IDQuery) => Promise<T | null>,
|
|
|
|
changesFunc: (query: IDQuery, changesType: AutoVerifierChangesType, primaryResult: T | null, trustedResult: T | null) => Promise<void>
|
|
|
|
) {
|
|
|
|
return new AutoVerifierWithArg<T, IDQuery>(
|
|
|
|
query => `id#${query.id}`,
|
|
|
|
query => primaryFunc(query),
|
|
|
|
query => trustedFunc(query),
|
|
|
|
async (query: IDQuery, primaryResult: T | null, trustedResult: T | null) => {
|
|
|
|
let changesType = AutoVerifier.getSingleChangesType<T>(primaryResult, trustedResult);
|
|
|
|
await changesFunc(query, changesType, primaryResult, trustedResult);
|
2021-11-18 01:26:32 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
unverifyAll(): void {
|
|
|
|
for (let autoVerifier of this.tokenAutoVerifiers.values()) {
|
|
|
|
autoVerifier.unverify();
|
|
|
|
}
|
|
|
|
this.tokenAutoVerifiers.clear();
|
|
|
|
this.tokenQueries.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
unverifySome(toUnverifyFilter: (query: K) => boolean) {
|
|
|
|
let tokensToUnverify = Array.from(this.tokenQueries.values())
|
|
|
|
.filter(toUnverifyFilter)
|
|
|
|
.map(query => this.tokenizer(query));
|
|
|
|
for (let token of tokensToUnverify) {
|
|
|
|
this.tokenAutoVerifiers.get(token)?.unverify();
|
|
|
|
this.tokenAutoVerifiers.delete(token);
|
|
|
|
this.tokenQueries.delete(token);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async fetchAndVerifyIfNeded(query: K): Promise<T | null> {
|
|
|
|
let token = this.tokenizer(query);
|
|
|
|
this.tokenQueries.set(token, query);
|
|
|
|
let autoVerifier = this.tokenAutoVerifiers.get(token);
|
|
|
|
if (!autoVerifier) {
|
|
|
|
autoVerifier = new AutoVerifier<T>(
|
|
|
|
async () => await this.primaryFunc(query),
|
|
|
|
async () => await this.trustedFunc(query),
|
|
|
|
async (primaryResult: T | null, trustedResult: T | null) => await this.verifyFunc(query, primaryResult, trustedResult),
|
|
|
|
);
|
|
|
|
this.tokenAutoVerifiers.set(token, autoVerifier);
|
|
|
|
}
|
|
|
|
return await autoVerifier.fetchAndVerifyIfNeeded();
|
|
|
|
}
|
|
|
|
}
|