-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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]Add ability to convert from YAML manifest to UI #21142
Conversation
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.
A few things I noticed that are not checked (probably missing some):
- Nested cartesian slicers
- Whether a requestor is a custom requester
- Whether an extractor is custom extractor
- checkpoint interval and transformations which can be defined on a stream and would get lost
- error handler can be defined on the retriever and would get lost
Not sure whether it makes sense, but would it make sense to do the following:
- Define a scoped down json schema which defines only the parts we want to support - then as a first step in the conversion logic we check against this scoped down schema. This will get rid of a lot of custom checks, probably easier to maintain
- For the additional props we don't support in the UI - make sure they are kept around even if they are not editable (would work e.g. for the transformations or for the requester error handler). This should work already for the spec but it should probably become a common thing.
- For the inferred inputs: Your suggestion sounds good to me
- For the substream slicers: Yeah, this is unfortunately a little more complex. This is what I had in mind:
- Recursively go through all streams with its substream slicers (not actually converting them yet) and store all streams in serialized form in a set (
JSON.stringify(streamDeclaration)
- For the actual conversion, instead of iterating over
manifest.streams
, iterate over the set of collected streams (id can be the index as it's unique) - If a substream slicer is encountered, stringify the parent stream definition again and look it up from the set - this will give you the parent stream id.
- Recursively go through all streams with its substream slicers (not actually converting them yet) and store all streams in serialized form in a set (
@flash1293 I've pushed up some more commits here to handle converting the spec and auth components from YAML to UI. Could you take a look and let me know what you think? I haven't addressed the other parts of your feedback yet for a couple reasons:
What do you think? Am I overemphasizing the complexity of this? EDIT: After thinking about this a bit more, I think I may have been overemphasizing the complexity. Since this will allow users to go back and forth, and we'll only need to store a single source of truth in the db, it may be worth the bit of extra complexity here. |
result.inputs = Object.entries( | ||
manifestSpec.connection_specification.properties as Record<string, AirbyteJSONSchema> | ||
).flatMap(([key, definition]) => { | ||
if (Object.keys(authTypeToKeyToInferredInput[result.auth.type]).includes(key)) { |
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.
looks like the keys of that object are the properties of the authenticator object, not the name of the input (in this case it's causing a mismatch between api_key and api_token). Should probably be
if (Object.values(authTypeToKeyToInferredInput[result.auth.type])
.map((input) => input.key).includes(key)) {
Otherwise you will end up with two api_keys when switching between UI and YAML
const inlineSchemaLoader = manifestSchemaLoader as InlineSchemaLoader; | ||
|
||
if (inlineSchemaLoader.schema) { | ||
return JSON.stringify(inlineSchemaLoader.schema); |
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 think it's worth it for two reasons:
Definitely, we can just fail on these conditions for now and get better in conversion later - it's lossy anyway, so it's not a fundamental change. |
… handle inferred input overrides properly
@flash1293 I think I've addressed all of the feedback here except for storing unsupported fields (I'll look into that next week). This should be working with both inputs and substream slicers now. Also, I didn't end up needing to check for nested Cartesian slicers, because the manifest schema already doesn't allow that. Notice that Cartsian isn't listed here: airbyte/airbyte-cdk/python/airbyte_cdk/sources/declarative/declarative_component_schema.yaml Lines 134 to 138 in 189891c
Keeping the PR in draft because I still want to add unit tests for the conversion logic, but I think it would be useful to get another review on the implementation. This should be mergeable even before #21211 is complete, as I've added checks for refs and $options which will catch it from trying to convert if those exist in the YAML |
} from "./types"; | ||
import { formatJson } from "./utils"; | ||
|
||
export const convertToBuilderFormValues = ( |
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.
Decided to move this and its related methods into its own file, because types.ts was starting to get pretty large
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 that, definitely overdue!
const tracebackIndex = errorMessage.indexOf(" - Traceback"); | ||
if (tracebackIndex >= 0) { | ||
errorMessage = errorMessage.substring(0, tracebackIndex); | ||
} |
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.
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.
Looks like this is merged in
id: index.toString(), | ||
name: stream.name ?? "", | ||
urlPath: requester.path, | ||
httpMethod: (requester.http_method as "GET" | "POST" | undefined) ?? "GET", |
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.
Not broken by you, but I just realized we do not set the http_method on the requester when doing form state -> manifest conversion. This is a bug right?
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.
Yep, good catch. I'll include the fix in this PR since it's just one line
import { editor } from "monaco-editor/esm/vs/editor/editor.api"; | ||
import { useEffect, useMemo, useRef, useState } from "react"; | ||
|
||
import { CodeEditor } from "components/ui/CodeEditor"; | ||
|
||
import { ConnectorManifest } from "core/request/ConnectorManifest"; | ||
// import { useConfirmationModalService } from "hooks/services/ConfirmationModal"; |
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.
leftover
Overall this looks great! Due to the high redundancy in the configuration I don't really see users switching between UI and YAML at the moment in a super effective way, but that's something we can gradually improve over time in multiple ways. It should work just fine for the initial use case we had in mind about only storing the manifest and not the builder form values along with it. |
}, | ||
}); | ||
|
||
describe("Conversion throws error when", () => { |
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 originally started to add tests for every case that throws a ManifestCompatibilityError, but that felt overly excessive and just means that we would be repeating all of those cases in yet another place, so I decided against it.
I think its more important that we test the more complex conversion cases anyway.
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.
Agreed, the complex ones are the relevant bit here.
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.
Tested and works really well! LGTM
|
||
it("manifest contains refs", () => { | ||
const convert = () => { | ||
// have to use a string manifest here, because refs aren't in the schema |
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.
nit: You can also use a regular object, then convince Typescript with as unknown as ConnectorManifest
}, | ||
}); | ||
|
||
describe("Conversion throws error when", () => { |
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.
Agreed, the complex ones are the relevant bit here.
…21142) * save * save more progress * try setting values directly * toggle editor * fix primary key * enforce consistency in name and primary key * refactor conversion method to be more readable * save progress * allow custom input keys to be used for inferred auth values * fix isMatch bug and remove console logs * fix type issues with reflect * properly handle undefined * format schema and gracefully handle non-inline schemas * verify no custom components * refactor and fix request options type * rest of refactor * move manifest to builder form conversion logic into its own file, and handle inferred input overrides properly * convert substream slicers * restore warning modal for switching back to UI * remove console logs * remove unneeded traceback filtering * set http method when converting to manifest * remove commented import * add unsupported fields to builder form values * save check stream values from manifest * save progress * add more tests * save record filter in unsupported fields * use type coersion instead of yaml strings
What
Resolves #21285
Adds logic to handle converting from a YAML manifest to the connector builder UI form values.
Also updates the confirmation modal service to allow sending FormattedMessage values along with the i18n id