-
Notifications
You must be signed in to change notification settings - Fork 14.1k
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
feat: Introduce a library for embedded iframe <-> host communication #18652
Merged
Merged
Changes from 5 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
ca41e1a
improved logging
suddjian b096c42
switchboard, and window size
suddjian 7a619b2
lint
suddjian d852d5e
tsconfig
suddjian f8cd9af
fix path
suddjian 96e5271
fix release script
suddjian 063db4f
tests, debug mode, and error handling
suddjian 15f8700
moar debug mode
suddjian 51f762c
package lock
suddjian 1d790d4
formatting
suddjian 7f63805
comment
suddjian 855e615
wording
suddjian 955766b
appease the coverage gods
suddjian b48e636
Async assertions must be awaited or returned
suddjian File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
33 changes: 33 additions & 0 deletions
33
superset-frontend/packages/superset-ui-switchboard/package.json
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,33 @@ | ||
{ | ||
"name": "@superset-ui/switchboard", | ||
"version": "0.18.25", | ||
"description": "Switchboard is a library to make it easier to communicate across browser windows using the MessageChannel API", | ||
"sideEffects": false, | ||
"main": "lib/index.js", | ||
"module": "esm/index.js", | ||
"files": [ | ||
"esm", | ||
"lib" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/apache/superset.git" | ||
}, | ||
"keywords": [ | ||
"switchboard", | ||
"iframe", | ||
"communication", | ||
"messagechannel", | ||
"messageport", | ||
"postmessage" | ||
], | ||
"author": "Superset", | ||
"license": "Apache-2.0", | ||
"bugs": { | ||
"url": "https://github.com/apache/superset/issues" | ||
}, | ||
"homepage": "https://github.com/apache/superset#readme", | ||
"publishConfig": { | ||
"access": "public" | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
superset-frontend/packages/superset-ui-switchboard/src/index.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,20 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
export * from './switchboard'; |
178 changes: 178 additions & 0 deletions
178
superset-frontend/packages/superset-ui-switchboard/src/switchboard.test.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,178 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
import { Switchboard } from './switchboard'; | ||
|
||
type EventHandler = (event: MessageEvent) => void; | ||
|
||
// A note on these fakes: | ||
// | ||
// jsdom doesn't supply a MessageChannel or a MessagePort, | ||
// so we have to build our own unless we want to unit test in-browser. | ||
// Might want to open a PR in jsdom: https://github.com/jsdom/jsdom/issues/2448 | ||
|
||
/** Matches the MessagePort api as closely as necessary (it's a small api) */ | ||
class FakeMessagePort { | ||
otherPort?: FakeMessagePort; | ||
|
||
isStarted = false; | ||
|
||
queue: MessageEvent[] = []; | ||
|
||
listeners: Set<EventHandler> = new Set(); | ||
|
||
dispatchEvent(event: MessageEvent) { | ||
if (this.isStarted) { | ||
this.listeners.forEach(listener => { | ||
try { | ||
listener(event); | ||
} catch (err) { | ||
// whatever browsers do here, idk | ||
} | ||
}); | ||
} else { | ||
this.queue.push(event); | ||
} | ||
return true; | ||
} | ||
|
||
addEventListener(eventType: 'message', handler: EventHandler) { | ||
this.listeners.add(handler); | ||
} | ||
|
||
removeEventListener(eventType: 'message', handler: EventHandler) { | ||
this.listeners.delete(handler); | ||
} | ||
|
||
postMessage(data: any) { | ||
this.otherPort!.dispatchEvent({ data } as MessageEvent); | ||
} | ||
|
||
start() { | ||
if (this.isStarted) return; | ||
this.isStarted = true; | ||
this.queue.forEach(event => { | ||
this.dispatchEvent(event); | ||
}); | ||
this.queue = []; | ||
} | ||
|
||
close() { | ||
this.isStarted = false; | ||
} | ||
|
||
onmessage = null; | ||
|
||
onmessageerror = null; | ||
} | ||
|
||
/** Matches the MessageChannel api as closely as necessary (an even smaller api than MessagePort) */ | ||
class FakeMessageChannel { | ||
port1: MessagePort; | ||
|
||
port2: MessagePort; | ||
|
||
constructor() { | ||
const port1 = new FakeMessagePort(); | ||
const port2 = new FakeMessagePort(); | ||
port1.otherPort = port2; | ||
port2.otherPort = port1; | ||
this.port1 = port1; | ||
this.port2 = port2; | ||
} | ||
} | ||
|
||
describe('comms', () => { | ||
beforeAll(() => { | ||
global.MessageChannel = FakeMessageChannel; // yolo | ||
console.debug = () => {}; // silencio bruno | ||
}); | ||
|
||
describe('emit', () => { | ||
it('triggers the method', async () => { | ||
const channel = new MessageChannel(); | ||
const ours = new Switchboard(channel.port1, 'ours'); | ||
const theirs = new Switchboard(channel.port2, 'theirs'); | ||
const handler = jest.fn(); | ||
|
||
theirs.defineMethod('someEvent', handler); | ||
theirs.start(); | ||
|
||
ours.emit('someEvent', 42); | ||
|
||
expect(handler).toHaveBeenCalledWith(42); | ||
}); | ||
}); | ||
|
||
describe('get', () => { | ||
it('returns the value', async () => { | ||
const channel = new MessageChannel(); | ||
const ours = new Switchboard(channel.port1, 'ours'); | ||
const theirs = new Switchboard(channel.port2, 'theirs'); | ||
theirs.defineMethod('theirMethod', ({ x }: { x: number }) => | ||
Promise.resolve(x + 42), | ||
); | ||
theirs.start(); | ||
|
||
const value = await ours.get('theirMethod', { x: 1 }); | ||
|
||
expect(value).toEqual(43); | ||
}); | ||
|
||
it('can handle one way concurrency', async () => { | ||
const channel = new MessageChannel(); | ||
const ours = new Switchboard(channel.port1, 'ours'); | ||
const theirs = new Switchboard(channel.port2, 'theirs'); | ||
theirs.defineMethod('theirMethod', () => Promise.resolve(42)); | ||
theirs.defineMethod( | ||
'theirMethod2', | ||
() => new Promise(resolve => setImmediate(() => resolve(420))), | ||
); | ||
theirs.start(); | ||
|
||
const [value1, value2] = await Promise.all([ | ||
ours.get('theirMethod'), | ||
ours.get('theirMethod2'), | ||
]); | ||
|
||
expect(value1).toEqual(42); | ||
expect(value2).toEqual(420); | ||
}); | ||
|
||
it('can handle two way concurrency', async () => { | ||
const channel = new MessageChannel(); | ||
const ours = new Switchboard(channel.port1, 'ours'); | ||
const theirs = new Switchboard(channel.port2, 'theirs'); | ||
theirs.defineMethod('theirMethod', () => Promise.resolve(42)); | ||
ours.defineMethod( | ||
'ourMethod', | ||
() => new Promise(resolve => setImmediate(() => resolve(420))), | ||
); | ||
theirs.start(); | ||
|
||
const [value1, value2] = await Promise.all([ | ||
ours.get('theirMethod'), | ||
theirs.get('ourMethod'), | ||
]); | ||
|
||
expect(value1).toEqual(42); | ||
expect(value2).toEqual(420); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
I didn't realize this before but
&
actually starts a process in the background instead of working likePromise.all
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.
I suggest use
&&
to execute that in series only if the predecessor was successful. In other words, stop as soon as an error occurs.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.
These aren't really dependent on each other's result. Running each of them regardless of the others' status can output more useful information in the case of an error. Ideally I'd like to run them in parallel and wait for all to complete before the command exits, but I am not familiar with a convenient way to do that without writing a script, and that seems like overkill since these all run in about a second.