Skip to content

Conversation

@tgriesser
Copy link
Member

@tgriesser tgriesser commented Oct 5, 2021

Summary

Adds Socket.io bindings for pushing data from the DataContext to trigger a refresh of any queries that need updating on the client. Triggered with ctx.emitter.toApp() / ctx.emitter.toLaunchpad(), will trigger a refetch of any queries currently being executed.

Additionally, does a lot of work to pass the GraphQL & App server port info to the frontend clients, which enables better e2e testing of the launchpad & app. Created DataContextShell which is the bare minimum API threaded through the open_project / ServerBase without needing a major refactor of tests - we just auto-create the DataContextShell if one isn't passed, but we will ensure one is passed from any new codepaths. This approach will provide a starting point to extract more server logic into the data-context layer.

We are testing against the dist'ed .html files, by passing the ports via the query string, otherwise the ports will be passed via injection when the HTML is rendered. This injection / testing pattern necessitates that we use hash-routing within the main app area to keep things simple.

Moves the data-context setup to makeDataContext, rather than having it in the lib/gui/events, since we're going to need to create the data context in other locations as well.

Upgrades ts-node to give us better sourcemapping when we're debugging, and adds typescript-cached-transpile to make transpilation more consistent / possible to debug between the app & plugins (since we're now using the plugins to test the app itsself, the sourcemaps need to line up).

UNIFY-413

Setting up bi-directional data communication w/ Sockets

  • Electron IPC for Launchpad
  • Socket.io layer for app
  • Urql exchange for auto-refetch when a signal comes in

@tgriesser tgriesser requested a review from a team as a code owner October 5, 2021 13:51
@cypress-bot
Copy link
Contributor

cypress-bot bot commented Oct 5, 2021

Thanks for taking the time to open a PR!

@tgriesser tgriesser requested review from BlueWinds and jennifer-shehane and removed request for a team, BlueWinds and jennifer-shehane October 5, 2021 13:51
@tgriesser tgriesser marked this pull request as draft October 5, 2021 13:51
@tgriesser tgriesser changed the title data context cleanup & IPC bindings for data push Data context cleanup & IPC bindings for data push Oct 5, 2021
* unified-desktop-gui:
  chore: improving component test infra (#18378)
  chore(launchpad): launchpad UI tweaks (#18369)
  feat: wire up add project button (#18320)
  feat(unify): adding larger defaults for the Launchpad window (#18248)
  fix(app): allow launching app dev server w/o relying on `supportFile` to bundle deps (#18354)
  feat(unify): reporter dark theme (#18313)
  feat(launchpad): UNIFY-371 header menu (#18338)
* unified-desktop-gui:
  feat(unification): adding specs list for the app (#18391)
  chore: merge in develop to unified-desktop-gui (#18388)

on('task', {
withCtx (fnString: string) {
return new Function('ctx', `return (${fnString})(ctx)`).call(undefined, ctx)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cute

.finally(() => {
_stopServer()
windows.focusMainWindow()
require('./windows').focusMainWindow()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reminder for me to clean this up and pass this as a callback function to avoid requiring electron

@brian-mann
Copy link
Member

We also need to refactor lib/gui/events to become only a source of events, and move the handler logic to gql ctx

* unified-desktop-gui: (40 commits)
  feat: index.html configurability and storybook support (#18242)
  fix: remove .json check from require_async, prevent child_process spawn (#18416)
  percy snapshot the tooltip visually, prevent it from being hidden
  fix: failing tests from #18372 (#18414)
  fix: `everyNthFrame` should only be applied for Chrome 89+ (#18392)
  feat(app): render spec list, command log, iframe (#18372)
  fix: drag and drop to be correct directory (#18400)
  refactor: Add GitDataSource, FileDataSource, toast for errors (#18385)
  docs: General updates to contributing guide (#18283)
  Add shorter --ct alias for --component
  Add --e2e and --component CLI options
  chore: Update Chrome (beta) to 95.0.4638.40 (#18389)
  chore: use circleci timings split for e2e tests (#18367)
  fix: fixed title (#18370)
  chore(deps): update dependency electron to v14 🌟 (#18384)
  chore(server): share client route (#18215)
  fix: Prevent Cypress from crashing when argument parsing "spec: {}" (#18312)
  chore: update husky dev dependency to v7 (#18345)
  feat: add defineConfig function to help type config (#18302)
  chore: Update Chrome (stable) to 94.0.4606.71 (#18324)
  ...
* unified-desktop-gui:
  chore(unify): optimize deps automatically, clean up unused files (#18429)
  fixing stub types and tests (#18428)
  feat: expose project config to project nodes (#18424)
  feat(launchpad): Adding generic error screen (#18405)
@tgriesser tgriesser changed the title Data context cleanup & IPC bindings for data push refactor: Data context cleanup & IPC bindings for data push Oct 10, 2021
@tgriesser tgriesser requested a review from brian-mann October 11, 2021 04:07
import type { Exchange, Operation } from '@urql/core'
import type { Socket } from '@packages/socket/lib/browser'

export const pubSubExchange = (io: Socket): Exchange => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where the queries are watched for refresh on push signal.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we actually using this somewhere or not yet? It would be good to see an example of "how to push things". I'm guessing there might be one here, but there is a lot of files - might be good to include a link to the file in the PR description so everyone can easily see it.

@tgriesser tgriesser requested a review from lmiller1990 October 11, 2021 14:31
@tgriesser tgriesser marked this pull request as ready for review October 11, 2021 14:33
@tgriesser tgriesser requested a review from ImCesar October 11, 2021 14:33
Copy link
Contributor

@lmiller1990 lmiller1990 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some misc comments - I couldn't immediately see an example of where we are actually pushing something from the server here? Do we have an example of "how to do it" or not yet?

}

clearActiveProject () {
async clearActiveProject () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silly question but can this not just be

clearActiveProject () {
  this.ctx.appData.activeProject = null
  return this.api.closeActiveProject()
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it can be, I think I might have written this way was because it was unclear what closeActiveProject is returning and didn't want to imply that you could rely on whatever the return value from that was

}

export function makeUrqlClient (): Client {
export function makeUrqlClient (target: 'launchpad' | 'app'): Client {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we have these typed in resolve-dist, but probably not a big deal to duplicate them. But might be good to have a single place to define these?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe... for simple types like this I tend to not care about duplication too much


module.exports = {
interface EventsStartArgs extends LaunchArgs {
onFocusTests: () => void
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we started defining these already, full text search for export interface OpenProjectLaunchOptions, might be good to have them all in one place (ideally in @types

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is specifically extending LaunchArgs which is defined in @types with values that are local here / specific to Events.start


const { getBrowsers } = browserUtils

interface MakeDataContextOptions {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice, I am glad this is isn't in gui/events anymore.

msg: nonNull(stringArg()),
},
resolve: (root, args, ctx) => {
ctx.emitter.toLaunchpad(args.msg)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of having this as a mutation? Isn't the idea to be able to push things to GraphQL independantly of GraphQL (eg, via some other event/trigger)?

What is the best pattern here? Do we just grab the data context (import it however) and call emiiter.toLaunchpad, or would we be making a GraphQL mutation request to the server to access the emitter?

An example might be doing a background check to see if the git status of a file has changed via some watcher. How would we notify GraphQL that it needs to re-query to get the latest data? Woudl we make an actual request to GraphQL or just grab the context and call emitter.toApp('get:new-files') (for example)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was just for testing, we can delete this

Copy link
Member Author

@tgriesser tgriesser Oct 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An example might be doing a background check to see if the git status of a file has changed via some watcher. How would we notify GraphQL that it needs to re-query to get the latest data? Woudl we make an actual request to GraphQL or just grab the context and call emitter.toApp('get:new-files') (for example)?

Currently we just call ctx.emitter.toApp() from wherever in the background (watching, or whatever) and it'll signal the app to refetch any queries. Going for simplicitly rather than optimizing / narrowing the data fetched until it seems necessary due to perf or similar.

@tgriesser tgriesser merged commit d841e13 into unified-desktop-gui Oct 11, 2021
@tgriesser tgriesser deleted the tgriesser/unify/ui-data-push branch October 11, 2021 16:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants