-
-
Notifications
You must be signed in to change notification settings - Fork 243
Generalize polling abstraction #3636
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 18 commits
5064689
394fcdd
242252b
4a74704
6698ebd
8906fb8
8edcf25
f688425
f72c570
28a98fe
1c297c2
9f0efe9
6404305
d4992ff
4b52a02
b932365
76449d6
5ccf0d3
997c806
ac31ea0
781c967
823aaa1
d7e9ba2
8a684fb
071f73b
8d5ff1e
d4ce6ac
77f3d2e
34234c6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,111 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
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 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 { | ||||||||||||||||||||||||||||||||||||||||||||||||
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( | ||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are your thoughts on making this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The mixin pattern where we expose this class via function doesn't seem to like any private or protected methods in the exposed class... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like maybe there are ways to get around it but I'm not sure if we want to pursue them if its not recommended. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, strange. Okay, no problem, then. |
||||||||||||||||||||||||||||||||||||||||||||||||
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>(); | ||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done here: 823aaa1 |
||||||||||||||||||||||||||||||||||||||||||||||||
pollingTokenSet.add(pollToken); | ||||||||||||||||||||||||||||||||||||||||||||||||
this.#pollingTokenSets.set(key, pollingTokenSet); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
if (pollingTokenSet.size === 1) { | ||||||||||||||||||||||||||||||||||||||||||||||||
// Start new polling only if it's the first token for this key | ||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: This comment repeats what the code already says:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done here: 823aaa1 |
||||||||||||||||||||||||||||||||||||||||||||||||
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) { | ||||||||||||||||||||||||||||||||||||||||||||||||
// TODO figure out why this is necessary | ||||||||||||||||||||||||||||||||||||||||||||||||
const nonNullKey = keyToDelete; | ||||||||||||||||||||||||||||||||||||||||||||||||
adonesky1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||
this._stopPollingByPollingTokenSetId(nonNullKey); | ||||||||||||||||||||||||||||||||||||||||||||||||
this.#pollingTokenSets.delete(nonNullKey); | ||||||||||||||||||||||||||||||||||||||||||||||||
this.#callbacks.get(nonNullKey)?.forEach((callback) => { | ||||||||||||||||||||||||||||||||||||||||||||||||
// for some reason this typescript can't tell that keyToDelete is not null here | ||||||||||||||||||||||||||||||||||||||||||||||||
callback(nonNullKey); | ||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||
this.#callbacks.get(nonNullKey)?.clear(); | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This happens because you're using
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. gotcha! Thanks for clearing that up! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done here: 823aaa1 |
||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
onPollingCompleteByNetworkClientId( | ||||||||||||||||||||||||||||||||||||||||||||||||
networkClientId: NetworkClientId, | ||||||||||||||||||||||||||||||||||||||||||||||||
callback: (networkClientId: NetworkClientId) => void, | ||||||||||||||||||||||||||||||||||||||||||||||||
options: Json = {}, | ||||||||||||||||||||||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||||||||||||||||||||||
const key = getKey(networkClientId, options); | ||||||||||||||||||||||||||||||||||||||||||||||||
const callbacks = this.#callbacks.get(key) || new Set<typeof callback>(); | ||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done here: 823aaa1 |
||||||||||||||||||||||||||||||||||||||||||||||||
callbacks.add(callback); | ||||||||||||||||||||||||||||||||||||||||||||||||
this.#callbacks.set(key, callbacks); | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
return AbstractPollingControllerBase; | ||||||||||||||||||||||||||||||||||||||||||||||||
} |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we explicitly write an interface which captures the polling behavior, and this abstract class then implements? Would make integration less coupled to the partial implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done here 997c806