-
-
Notifications
You must be signed in to change notification settings - Fork 186
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Generalize polling abstraction (#3636)
Ready for review: **I will add changelog entries when/if we decide to roll with this approach.** Alternative approach to refactoring the PollingController per this [feedback](#3623 (comment)) on [PollingTracker enhancement work](#3623)
- Loading branch information
Showing
16 changed files
with
844 additions
and
600 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
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
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
138 changes: 138 additions & 0 deletions
138
packages/polling-controller/src/AbstractPollingController.ts
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,138 @@ | ||
import type { NetworkClientId } from '@metamask/network-controller'; | ||
import type { Json } from '@metamask/utils'; | ||
import stringify from 'fast-json-stable-stringify'; | ||
import { v4 as random } from 'uuid'; | ||
|
||
export type IPollingController = { | ||
startPollingByNetworkClientId( | ||
networkClientId: NetworkClientId, | ||
options: Json, | ||
): string; | ||
|
||
stopAllPolling(): void; | ||
|
||
stopPollingByPollingToken(pollingToken: string): void; | ||
|
||
onPollingCompleteByNetworkClientId( | ||
networkClientId: NetworkClientId, | ||
callback: (networkClientId: NetworkClientId) => void, | ||
options: Json, | ||
): void; | ||
|
||
_executePoll(networkClientId: NetworkClientId, options: Json): Promise<void>; | ||
_startPollingByNetworkClientId( | ||
networkClientId: NetworkClientId, | ||
options: Json, | ||
): void; | ||
_stopPollingByPollingTokenSetId(key: PollingTokenSetId): void; | ||
}; | ||
|
||
export const getKey = ( | ||
networkClientId: NetworkClientId, | ||
options: Json, | ||
): PollingTokenSetId => `${networkClientId}:${stringify(options)}`; | ||
|
||
export type PollingTokenSetId = `${NetworkClientId}:${string}`; | ||
|
||
type Constructor = new (...args: any[]) => object; | ||
|
||
/** | ||
* AbstractPollingControllerBaseMixin | ||
* | ||
* @param Base - The base class to mix onto. | ||
* @returns The composed class. | ||
*/ | ||
export function AbstractPollingControllerBaseMixin<TBase extends Constructor>( | ||
Base: TBase, | ||
) { | ||
abstract class AbstractPollingControllerBase | ||
extends Base | ||
implements IPollingController | ||
{ | ||
readonly #pollingTokenSets: Map<PollingTokenSetId, Set<string>> = new Map(); | ||
|
||
#callbacks: Map< | ||
PollingTokenSetId, | ||
Set<(PollingTokenSetId: PollingTokenSetId) => void> | ||
> = new Map(); | ||
|
||
abstract _executePoll( | ||
networkClientId: NetworkClientId, | ||
options: Json, | ||
): Promise<void>; | ||
|
||
abstract _startPollingByNetworkClientId( | ||
networkClientId: NetworkClientId, | ||
options: Json, | ||
): void; | ||
|
||
abstract _stopPollingByPollingTokenSetId(key: PollingTokenSetId): void; | ||
|
||
startPollingByNetworkClientId( | ||
networkClientId: NetworkClientId, | ||
options: Json = {}, | ||
): string { | ||
const pollToken = random(); | ||
const key = getKey(networkClientId, options); | ||
const pollingTokenSet = | ||
this.#pollingTokenSets.get(key) ?? new Set<string>(); | ||
pollingTokenSet.add(pollToken); | ||
this.#pollingTokenSets.set(key, pollingTokenSet); | ||
|
||
if (pollingTokenSet.size === 1) { | ||
this._startPollingByNetworkClientId(networkClientId, options); | ||
} | ||
|
||
return pollToken; | ||
} | ||
|
||
stopAllPolling() { | ||
this.#pollingTokenSets.forEach((tokenSet, _key) => { | ||
tokenSet.forEach((token) => { | ||
this.stopPollingByPollingToken(token); | ||
}); | ||
}); | ||
} | ||
|
||
stopPollingByPollingToken(pollingToken: string) { | ||
if (!pollingToken) { | ||
throw new Error('pollingToken required'); | ||
} | ||
|
||
let keyToDelete: PollingTokenSetId | null = null; | ||
for (const [key, tokenSet] of this.#pollingTokenSets) { | ||
if (tokenSet.delete(pollingToken)) { | ||
if (tokenSet.size === 0) { | ||
keyToDelete = key; | ||
} | ||
break; | ||
} | ||
} | ||
|
||
if (keyToDelete) { | ||
this._stopPollingByPollingTokenSetId(keyToDelete); | ||
this.#pollingTokenSets.delete(keyToDelete); | ||
const callbacks = this.#callbacks.get(keyToDelete); | ||
if (callbacks) { | ||
for (const callback of callbacks) { | ||
// eslint-disable-next-line n/callback-return | ||
callback(keyToDelete); | ||
} | ||
callbacks.clear(); | ||
} | ||
} | ||
} | ||
|
||
onPollingCompleteByNetworkClientId( | ||
networkClientId: NetworkClientId, | ||
callback: (networkClientId: NetworkClientId) => void, | ||
options: Json = {}, | ||
) { | ||
const key = getKey(networkClientId, options); | ||
const callbacks = this.#callbacks.get(key) ?? new Set<typeof callback>(); | ||
callbacks.add(callback); | ||
this.#callbacks.set(key, callbacks); | ||
} | ||
} | ||
return AbstractPollingControllerBase; | ||
} |
Oops, something went wrong.