Skip to content
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

[Connector Builder] Configuration UI MVP #20008

Merged
merged 56 commits into from
Dec 12, 2022

Conversation

lmossman
Copy link
Contributor

@lmossman lmossman commented Dec 1, 2022

What

This PR adds the first iteration of the Configuration UI to the Connector Builder, allowing users to fill out UI forms rather than hand-writing a YAML file.

I have recorded a loom where I go through what it looks like to build a simple connector in the new UI, and the various other features that have been added:
https://www.loom.com/share/9cf4ddb8ad894503bbc82cda5606aa2b

The main thing left to address for this first iteration is to better handle error cases, e.g. if a required field is not filled out and the user tries to test, currently they will see a somewhat confusing error message. This will be addressed in a later PR.

How

This PR is doing quite a bit -- apologies for such a large changeset. The motivation behind putting all of these changes in one PR was that I didn't want to add the new UI view until it reached at least minimum usability for building a simple connector like the Poke API.

However, I could have still created incremental PRs into this feature branch to get review as I went, so I will aim to do things that way in the future.

Recommended reading order

  1. Start at the top of the builder component structure and go down: ConnectorBuilderPage.tsx -> Builder.tsx -> BuilderSidebar.tsx -> Global/StreamConfigView.tsx -> BuilderField.tsx...
  2. Also important is reviewing the changes to the ConnectorBuilderStateService, as that is now handling both the UI form state and the yaml editor state.

🚨 User Impact 🚨

Users can now configure connectors through UI forms in the connector builder. More functionality to come soon!

@octavia-squidington-iv octavia-squidington-iv added area/platform issues related to the platform area/frontend Related to the Airbyte webapp labels Dec 1, 2022
Base automatically changed from lmossman/connector-builder-component-folder to master December 2, 2022 18:29
@octavia-squidington-iv octavia-squidington-iv added the CDK Connector Development Kit label Dec 6, 2022
@lmossman lmossman temporarily deployed to more-secrets December 6, 2022 02:53 Inactive
@lmossman lmossman temporarily deployed to more-secrets December 6, 2022 19:56 Inactive
@lmossman lmossman changed the base branch from master to lmossman/use-connector-manifest-schema-directly December 6, 2022 23:11
@lmossman lmossman temporarily deployed to more-secrets December 6, 2022 23:38 — with GitHub Actions Inactive
@lmossman lmossman temporarily deployed to more-secrets December 6, 2022 23:38 — with GitHub Actions Inactive
propagateDimensions
minSize={firstPanel.minWidth}
flex={firstPanel.flex}
onStopResize={(args) => {
firstPanel.onStopResize?.(args.component.props.flex);
}}
>
<PanelContainer className={firstPanel.className} overlay={firstPanel.overlay}>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Applying the styling to this container wasn't really doing much due to its nesting. What we really wanted users of this component to control was the ReflexElement for that panel, as that is the parent container of it.

This allowed me to fix the rounded corners of the testing side panel in the connector builder as well as the box shadow of it.

Base automatically changed from lmossman/use-connector-manifest-schema-directly to master December 7, 2022 01:29
Comment on lines +8 to +9
ConnectorManifest:
$ref: "../../../../airbyte-cdk/python/airbyte_cdk/sources/declarative/config_component_schema.json"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

FYI I added this file so that we could use orval to pull in the Connector Manifest json schema and generate typescript types from it. This is necessary to do rather than referencing the schema directly in the connector builder server openapi spec, because of the issue described here: #20248

@lmossman
Copy link
Contributor Author

lmossman commented Dec 8, 2022

Do we want to allow to test the connector if base url is not defined / invalid in some way we know?

No we do not -- this is what I am currently working on fixing and will be addressing in a follow-up PR. Basically, I will be making disabling the test button if they click it and there are any errors in the formik form, and will work on making it clear which views have errors that need to be fixed.

Weird idea - I found myself trying to do command+enter to re-test, what do you think?

Interesting, I hadn't considered hotkeys for testing, but I think that could definitely be useful since re-testing is pretty common. Created an issue for that here: https://github.com/airbytehq/airbyte-internal-issues/issues/1184


I think with that I have addressed all of your comments. Could you take another look @flash1293

Copy link
Contributor

@flash1293 flash1293 left a comment

Choose a reason for hiding this comment

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

I focused mostly on small css stuff. In general I think we should invest in general layout components that make it easier to achieve common layouts to make these things much easier, but I will bring that up separately with the frontend chapter.

Besides that and a few nits it starts to look pretty good.

Phrase is not translated in modal:
Screenshot 2022-12-09 at 15 38 34

A more general one - while testing I noticed the UI sends a request for every keystroke which seems like a lot for production systems:
Kapture 2022-12-09 at 15 56 06

Can we throttle these calls?

gap: variables.$spacing-xl;
}

.addButton {
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 it's OK for now, but I don't like how much you need to fight the stylings of the underlying button component.

It seems like it would be possible to make the round icon button a prop on the regular button - this way you don't need to overrule styles with !important but you can selectively apply the right styles in the right moment.

If you think that makes sense I can create an issue for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I think that makes sense - having a prop on the button to easily make it round would be really nice here. If you could create an issue for that, that would be great!

</Text>

<AddStreamButton
className={styles.addStreamButton}
Copy link
Contributor

Choose a reason for hiding this comment

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

You are adding some styles here and then there are additional styles in the AddStreamButton component itself. Can we consolidate these in a single place?

Copy link
Contributor Author

@lmossman lmossman Dec 9, 2022

Choose a reason for hiding this comment

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

This is a pattern I've been doing for a little while, but let me know if you think I shouldn't be doing it:

  • When I create a custom component, I've usually been trying to keep positional styling (e.g. the addStreamButton style in BuilderSidebar.module.scss which just sets margin values) in the same .module.scss file as the parent component that holds the custom component, because that feels like the most natural place to control the position.
  • I then put the styling code for the internals of the custom component (e.g. the addButton style in the AddStreamButton.module.scss file) in the custom component's own scss file, since that feels like the most natural place to style internals of a component.

Basically it felt weird to me to put positional styling inside the custom component's own styles, because if I want to use that custom component in another place in the future then I would need to lift those positional styles back up to the parent anyway. I guess that doesn't really apply so much here because the "Add stream" button isn't likely to be reused somewhere else, so I'll consolidate these styles into the AddStreamButton component's styles now.

But what do you think of that pattern overall?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was actually able to solve this specific case in I think a much more idiomatic way, see commit here: b9580b4

I just used the flex property on the heading to make it grow to fill the rest of the flexbox, so I didn't need to apply the margin styles to the AddStreamButton anymore to make it positioned correctly. So maybe when I find myself wanting to do the above pattern, I should first ask myself if I can solve it in a more idiomatic way like this (though I'm sure not every case will end up like this)

Copy link
Contributor

Choose a reason for hiding this comment

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

But what do you think of that pattern overall?

Thanks for the explanation, that makes a lot of sense to me as a pattern - Ideally I hope we can abstract away most of these concerns into library components, but I'll bring it up in the chapter sync.

const handleViewSelect = (selectedView: BuilderView, streamName?: string) => {
setSelectedView(selectedView);
if (selectedView !== "global" && streamName !== undefined) {
setSelectedStream(streamName);
Copy link
Contributor

Choose a reason for hiding this comment

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

We are auto-selecting the testing panel for the stream when switching the view, but we don't do it the other way around (switching the view when switching the selectedStream). Would it make sense to do so?

Related to that - would it make sense to lift the selected view into the ConnectorBuilderStateService as well? It's conceptually very similar to the selected stream, it confused me at first why it's handled in such a different fashion.

Not a must-have though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good suggestion -- I've added this behavior. The one slightly nuanced aspect of it is that if the Global Configuration view is currently selected while the user changes streams in the testing panel dropdown, I don't think we want it to switch the view to that stream, because the user might want to test out different streams while iterating on global configuration values. So I've encoded that behavior as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

Makes a lot of sense to me!

@lmossman
Copy link
Contributor Author

lmossman commented Dec 9, 2022

Can we throttle these calls?

I've pushed this commit to debounce setting the json manifest from the yaml editor, which seems to solve that problem: 5ffe20e

@lmossman
Copy link
Contributor Author

lmossman commented Dec 10, 2022

@flash1293 I made a couple more changes in the last few commits, which do the following:

  1. If a stream's name is set to an empty/pure whitespace string, it now shows an (empty) placeholder in the view select button and test panel stream dropdown
  2. I switched the tracking of the selected test panel stream to track the index instead of the stream name, as this was necessary to ensure that if a user changes the name of a stream, that stream stays selected in the test panel

I believe I have also addressed all of the other feedback so this should be ready for another look

Copy link
Contributor

@flash1293 flash1293 left a comment

Choose a reason for hiding this comment

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

For the scope of this PR it LGTM, great work!

Two notes that should be addressed, can happen later on:

  • The "one request per keystroke" also happens for the test input - this hasn't been touched here, so we can address it separately, just something I noticed. Not sure whether we even need to re-fetch the streams in this situation, but I might miss something
  • The "add stream" modal doesn't really add any value, it's just making the user flow more complex - it seems like it would be easier to add an empty, untouched stream and switch to the view so the user can fill it in without any modal involved. I realize that this is not according to the current designs so happy to discuss it in some forum before implementing. Let me know if I should create an issue for it.

@lmossman
Copy link
Contributor Author

The "one request per keystroke" also happens for the test input - this hasn't been touched here, so we can address it separately, just something I noticed. Not sure whether we even need to re-fetch the streams in this situation, but I might miss something

One reason to refetch the streams in this situation is if the user has implemented a stream in which the name or the URL use some interpolated user-input value. Then the name/url of that stream depend on the value they type into the test input menu, so they need to be refetched to resolve those interpolated references.

I won't address it in this PR but maybe that is something you can improve in your PR to use the Connector Form for the test input menu?

The "add stream" modal doesn't really add any value, it's just making the user flow more complex - it seems like it would be easier to add an empty, untouched stream and switch to the view so the user can fill it in without any modal involved. I realize that this is not according to the current designs so happy to discuss it in some forum before implementing. Let me know if I should create an issue for it.

I think the benefits of using a modal are that simplifies the set of fields that the user is presented with upon creating a new stream to only the ones that are absolutely required (name and path), and doesn't allow them to create a stream unless they supply those values. If we just create an empty stream and focus that view, it may be a bit more overwhelming (especially once we add more components to the stream view). Though that may be lessened if we have collapsible components.

@flash1293 could you create a ticket to capture this question? We can discuss it in the next low-code builder sync

@flash1293
Copy link
Contributor

One reason to refetch the streams in this situation is if the user has implemented a stream in which the name or the URL use some interpolated user-input value. Then the name/url of that stream depend on the value they type into the test input menu, so they need to be refetched to resolve those interpolated references.

Makes sense, thanks

I won't address it in this PR but maybe that is something you can improve in your PR to use the Connector Form for the test input menu?

Sounds good, I'm gonna take care of it.

I think the benefits of using a modal are that simplifies the set of fields that the user is presented with upon creating a new stream to only the ones that are absolutely required (name and path), and doesn't allow them to create a stream unless they supply those values. If we just create an empty stream and focus that view, it may be a bit more overwhelming (especially once we add more components to the stream view). Though that may be lessened if we have collapsible components.

I see your point, I still think we can shape the general form in a way to make it more approachable without resorting to modals.

@flash1293
Copy link
Contributor

I see your point, I still think we can shape the general form in a way to make it more approachable without resorting to modals.

Added it to the next sync

@lmossman lmossman merged commit 7afc689 into master Dec 12, 2022
@lmossman lmossman deleted the lmossman/connector-builder-config-ui branch December 12, 2022 21:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/frontend Related to the Airbyte webapp area/platform issues related to the platform
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants