-
Notifications
You must be signed in to change notification settings - Fork 1.7k
plugin: message channels for dynamic plugins #2703
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
Conversation
|
Added tests, cr address. Ready for another look! |
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.
Thanks for making this module better! Also thanks for adding test cases I missed!
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.
Is this for readability of the module? Or does it have functional differences?
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.
Functional difference, and the test I added covers it!
Basically, if a frame receives message {id: 0, ...}, it doesn't know whether it's the reply-message to something it previously sent, or whether it's a brand new incoming message from another frame.
In both cases, the origin is a foreign frame, but we need to distinguish that the former case should resolve the responseWait, while the latter should not.
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.
consider using the fluent alternative.
expect(longTaskCb).to.have.been.calledOnce;
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.
consider adding assertion on the payload. expect(longTaskCb).to.have.been.calledWith(...)
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 x2
Yes, asserting the payload is a good idea
|
Comments addressed, ready for another look. |
|
I really think we need more consensus on what plugin architecture we're aiming for before checking in code. The two open docs (Stephan's and Erik's) are a good start, as is the new weekly meeting. Let's use those venues, as well as perhaps the TB design review, to solidify our plans--and then return to this. |
|
@davidsoergel would this draft PR (#2705) be what you are seeking? Just do note that this work is time sensitive. |
|
@davidsoergel as per offline discussion, I've moved plugin_util/ under tensorboard/components/experiments/, to indicate that this is current work is unstable. I opted to not put it at the root level (tensorboard/experiments/) because there happens to be another build target "plugin_util" at the root level BUILD that has nothing todo with components. Would this be fine with you? |
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.
components/experimental is good, thanks.
I don't fully follow the logic re id collisions and isReply. It's fine given the experimental status, but I'm looking forward to whiteboarding/etc. to understand this once other dust settles. Thanks!
| } | ||
| } | ||
|
|
||
| export const broadcast = _broadcast; |
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.
Is the purpose of this pattern (as opposed to just export function broadcast(...) {...}) to prevent monkey-patching? Just curious if I should adopt this generally.
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.
The underscored version is just there to allow us to export it via es6 (which is used/imported by the test) and export it under a namespace, at the same time.
| export const listen = _listen; | ||
| export const unlisten = _unlisten; | ||
|
|
||
| namespace tf_plugin { |
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.
Namespaces are disallowed by go/tsstyle (publicly: https://github.com/google/gts/blob/83cb107010f7a956bb008291066304a9f26a0f34/tslint-rules.json#L43). TensorBoard contains a fair number of them anyway, but let's not add more if possible.
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.
Good point. @stephanwlee, could you weigh in on this?
There does seem to be alot of existing namespaces, ever since this PR, but I think the Angular ng-tensorboard/ folder avoids this.
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.
- plugin-host (at least until the tf_ng_tensorboard work is finished) will be loaded inside Polymer context which uses the namespace
- (in anticipation of inevitable question) in ideal world, we want to share base code between host and guest impl except it is super hard to make ts_library build rule work with tf_web_library. The latter is used in mainly for polymer based TensorBoard. For now, since the experimental API is only used in context of Polymer anyways, I decided to just deprecate the [es]module style and use namespace everywhere as v0.
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.
Thanks for the explanation. It's unfortunate that Polymer code depends on these files. I'll land as-is for now, but it seems we all agree that 'namespaces' are not ideal, and should be avoided if possible.
This iterates on the experimental code in plugin_util/ for communication with IFrames. This PR does NOT address the public API.
Using
window.postMessageandwindow.addEventListener('message', ...)works, butHaving multiple concurrent IPCs (e.g. a frame cannot be a host and a guest, a host cannot have multiple guests) required today's
idPrefixsystem. Random-number-generated idPrefixes are prepended to message ids to distinguish which frame it comes from, to resolve the appropriateresponseWait.This PR uses MessageChannels, instead of random-numbers, to establish a private channel between frames.
The IPC now requires an
isReply: trueto be present on reply messages. This allows a frame to distinguish a case where (it receives a foreign message with id=X) vs (it receives a reply for its own previous message with id=X).The flow looks like
WANT_LGTM=all