influxdata / influxdata/ui

EPIC: Interactive flux architecture

Open
#5,821 0 comments 0 reactions 1 assignee Claimed by @rockstar View on GitHub
kind/bug team/automation team/ui
Dominant language
TypeScript
Stars
117
Forks
51
Avg merge
2d 15h
Merged PRs (30d)
4

Description

Problem
--

The LSP code is loaded asynchronously, with no signalling to readiness. The editor is loaded asynchronously as well, but since it is a react component and doesn't require I/O, it _usually_ completes before the LSP is ready. The editor _immediately_ begins sending messages to the LSP, whether it's ready or not. The way this is implemented is that [there is a buffer that sits in front of the lsp and buffers all messages to the LSP](https://github.com/influxdata/ui/blob/933bcd5178712a1e5b854904f425ad5d6f3d8eb6/src/languageSupport/languages/flux/lsp/worker/buffer.ts).

Additionally, Query Composition brings with it the complexity that other UI elements are now interacting with LSP extensions. This has its own, similar timing issues, but with the added complexity that it also has dependencies on the state of the editor itself having sent `didOpen` to the lsp, i.e. you can't send query composition commands to the server about a file it has no idea about. [This is currently handled with a manual `setTimeout` of 3 seconds before operating](https://github.com/influxdata/ui/issues/5305) which breaks quite terribly on slow computers and slow connections.

#5305 was an attempt to address these issues, but they'd be adding more buffering, and essentially an entire proxy in front of the LSP to try and synthesize events in a situation where, if all things were evented properly in the first place, we wouldn't need.

Proposal
--

There are two architectural pieces to this, (1) the serializing and eventing of the lsp startup and various connections, and (2) the api needed by components to make calls into the LSP extensions. When evaluating solutions, both requirements were used when evaluating fit-for-purpose.

**Interactive Flux State**
The flux LSP, combined with the Monaco editor implementation, provides what should be referred to as "interactive flux." It provides the interface for auto completion and syntax highlighting, as well as query composition and other extensions that the LSP provides. The bootstrap of this interactive flux environment requires the following flow:

```mermaid
flowchart TD
A(Initial State)-->|Load worker code and instantiate LSP| B(LSP Ready)
B-->|Instantiate Monaco Editor and send `didOpen`| C(Editor Ready)
```

While it's important to address and speak to these states, the only states that matter outside of the editor are the first and last states. The editor _must_ be in charge of initializing the LSP and rendering the editor _only_ when the lsp is ready[^1]. Any UI elements that use Interactive Flux should not be enabled and functional until "Editor Ready" state is reached. As soon as that state is reached, those elements can be rendered/enabled[^2] and may start interacting with the LSP extensions.

**UI communication with the LSP**
Once the "editor ready" state has been reached, an api object will provide typed interfaces to the LSP extensions (query composition at the time of this writing). This API object _must_ be the only object that manually constructs json-rpc messages and sends them to the LSP object; components and other interfaces will only see this object, and it's interaction with LSP will be opaque. An example for this object may look like the following.

```typescript
interface InteractiveFlux {
compositionInit(bucket: string, measurement?: string, fields?: string[], tagValues?: string[]) -> Promise;
compositionAddTagValue(tagValue: string) -> Promise;
...
}
```

The specifics to this interface aren't entirely to be described, because it will need to be flexible as we add more to the interface. The important piece, here, is that these functions return `Promise`, so user interface elements can be disabled while these operations occur, and we can know when the operation has completed[^3]. There are a number of benefits to this, but the most important bit is that there is *one* place that is interacting with the LSP; the LSP interface can change without a need to change many components and flows in the process.

_How_ this `InteractiveFlux` object is made available is left to the implementation. Whether it is provided in a callback or in a context, is still not quite clear, and should not block getting started on this functionality.

[^1]: There may be design ramifications for an error or timeout state where a plain text box is rendered instead of the full monaco editor.
[^2]: Each component will need to have its own logic and rules for whether it is rendered at all, whether it's visible, or whether it's disabled. Providing the api to know is the purpose of this interface.
[^3]: Error states are beyond the scope of this issue, and will deserve their own epic.

The following tasks can serve as a starting point for breaking up this work.

- [ ] #5844
- [ ] #5859
- [ ] Implement Interactive Flux interface
- [ ] Provide Interactive Flux interface to other components

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.