-
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 paginator #20698
Changes from all commits
699470e
827532b
4899673
a1b62f1
39e8ba8
2352ef9
df492fa
5a99c7a
62fc08c
7b26b78
71a88f1
d7bee54
162ecf2
def60b0
ff9aa76
3678956
b16d4a8
67fb9dd
bb42007
9bb1812
587ca2f
81cf108
094a045
d1c8c80
aebac20
1e39be4
09cf875
ec65d67
f9a3fa1
603820a
cb2f299
d1672a0
7255784
65cd42b
10d7893
fc89001
24eaaff
de604f6
e696e57
5952f70
b7c08f6
dd04e61
d6f10b4
5ffe20e
b82ba2a
b9580b4
1587243
62a60e5
092821d
7b8dec8
6d68db9
91bc357
4ffb969
70afdf8
f656506
c437d30
3cdbc70
831f574
e2730fe
269ae82
901fc63
4944d3f
3255298
333fb0b
cec206f
ba28014
53ad55f
5300933
935188b
b694dc5
e7d81ea
3de021a
552fc65
ace1299
7fa1752
7196409
4a37a67
10fa73e
7e48ba5
75db697
b340ef4
98c146c
9835a5c
c352df0
ed43eab
7958ef1
beb4968
b3cfb8f
49f55a1
04d87cf
b796201
18498c7
9ffa34e
8c4b6c7
c0350d3
e98ebc1
fb1ec21
a17ab05
7b6470e
23f4fbe
d4975bf
32213f8
3e20324
3ee0ffb
0576478
5e45f59
f101602
5fb62e2
7cce350
20e8044
50bddcb
0ca37ad
8095fda
fbeb2e1
1bccbbd
eaec279
a338590
2429d29
6dd02d9
84803ce
4bd0bdc
ca0219b
134fc17
ed9860e
e029279
e964eb8
a23ffb8
e3c578d
24bfc32
e2450c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,4 +18,5 @@ | |
.form { | ||
flex: 1; | ||
padding: variables.$spacing-xl; | ||
overflow: auto; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { useField } from "formik"; | ||
|
||
import { RequestOption } from "core/request/ConnectorManifest"; | ||
|
||
import { injectIntoValues } from "../types"; | ||
import { BuilderField } from "./BuilderField"; | ||
|
||
interface InjectRequestOptionFieldsProps { | ||
path: string; | ||
descriptor: string; | ||
excludeInjectIntoValues?: string[]; | ||
} | ||
|
||
export const InjectRequestOptionFields: React.FC<InjectRequestOptionFieldsProps> = ({ | ||
path, | ||
descriptor, | ||
excludeInjectIntoValues, | ||
}) => { | ||
const [field, , helpers] = useField<RequestOption>(path); | ||
|
||
return ( | ||
<> | ||
<BuilderField | ||
type="enum" | ||
path={`${path}.inject_into`} | ||
options={ | ||
excludeInjectIntoValues | ||
? injectIntoValues.filter((val) => !excludeInjectIntoValues.includes(val)) | ||
: injectIntoValues | ||
} | ||
onChange={(newValue) => { | ||
if (newValue === "path") { | ||
helpers.setValue({ inject_into: newValue, field_name: undefined }); | ||
} | ||
}} | ||
label="Inject into" | ||
tooltip={`Configures where the ${descriptor} should be set on the HTTP requests`} | ||
/> | ||
{field.value.inject_into !== "path" && ( | ||
<BuilderField | ||
type="string" | ||
path={`${path}.field_name`} | ||
label="Field name" | ||
tooltip={`Configures which key should be used in the location that the ${descriptor} is being injected into`} | ||
/> | ||
)} | ||
</> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
import { useField } from "formik"; | ||
|
||
import GroupControls from "components/GroupControls"; | ||
import { ControlLabels } from "components/LabeledControl"; | ||
|
||
import { BuilderCard } from "./BuilderCard"; | ||
import { BuilderField } from "./BuilderField"; | ||
import { BuilderOneOf } from "./BuilderOneOf"; | ||
import { InjectRequestOptionFields } from "./InjectRequestOptionFields"; | ||
import { ToggleGroupField } from "./ToggleGroupField"; | ||
|
||
interface PaginationSectionProps { | ||
streamFieldPath: (fieldPath: string) => string; | ||
} | ||
|
||
export const PaginationSection: React.FC<PaginationSectionProps> = ({ streamFieldPath }) => { | ||
const [field, , helpers] = useField(streamFieldPath("paginator")); | ||
const [pageSizeField] = useField(streamFieldPath("paginator.strategy.page_size")); | ||
const [, , pageSizeOptionHelpers] = useField(streamFieldPath("paginator.pageSizeOption")); | ||
|
||
const handleToggle = (newToggleValue: boolean) => { | ||
if (newToggleValue) { | ||
helpers.setValue({ | ||
strategy: { | ||
type: "OffsetIncrement", | ||
}, | ||
pageTokenOption: { | ||
inject_into: "request_parameter", | ||
}, | ||
}); | ||
} else { | ||
helpers.setValue(undefined); | ||
} | ||
}; | ||
const toggledOn = field.value !== undefined; | ||
|
||
const pageTokenOption = ( | ||
<GroupControls | ||
label={ | ||
<ControlLabels | ||
label="Page token option" | ||
infoTooltipContent="Configures how the page token will be sent in requests to the source API" | ||
/> | ||
} | ||
> | ||
<InjectRequestOptionFields path={streamFieldPath("paginator.pageTokenOption")} descriptor="page token" /> | ||
</GroupControls> | ||
); | ||
|
||
const pageSizeOption = ( | ||
<ToggleGroupField | ||
label="Page size option" | ||
tooltip="Configures how the page size will be sent in requests to the source API" | ||
fieldPath={streamFieldPath("paginator.pageSizeOption")} | ||
initialValues={{ | ||
inject_into: "request_parameter", | ||
field_name: "", | ||
}} | ||
> | ||
<InjectRequestOptionFields | ||
path={streamFieldPath("paginator.pageSizeOption")} | ||
descriptor="page size" | ||
excludeInjectIntoValues={["path"]} | ||
/> | ||
</ToggleGroupField> | ||
); | ||
|
||
return ( | ||
<BuilderCard | ||
toggleConfig={{ | ||
label: ( | ||
<ControlLabels | ||
label="Pagination" | ||
infoTooltipContent="Configure how pagination is handled by your connector" | ||
/> | ||
), | ||
toggledOn, | ||
onToggle: handleToggle, | ||
}} | ||
> | ||
<BuilderOneOf | ||
path={streamFieldPath("paginator.strategy")} | ||
label="Mode" | ||
tooltip="Pagination method to use for requests sent to the API" | ||
options={[ | ||
{ | ||
label: "Offset Increment", | ||
typeValue: "OffsetIncrement", | ||
children: ( | ||
<> | ||
<BuilderField | ||
type="number" | ||
path={streamFieldPath("paginator.strategy.page_size")} | ||
label="Page size" | ||
tooltip="Set the size of each page" | ||
/> | ||
{pageSizeOption} | ||
{pageTokenOption} | ||
</> | ||
), | ||
}, | ||
{ | ||
label: "Page Increment", | ||
typeValue: "PageIncrement", | ||
children: ( | ||
<> | ||
<BuilderField | ||
type="number" | ||
path={streamFieldPath("paginator.strategy.page_size")} | ||
label="Page size" | ||
tooltip="Set the size of each page" | ||
/> | ||
<BuilderField | ||
type="number" | ||
path={streamFieldPath("paginator.strategy.start_from_page")} | ||
label="Start from page" | ||
tooltip="Page number to start requesting pages from" | ||
optional | ||
/> | ||
{pageSizeOption} | ||
{pageTokenOption} | ||
</> | ||
), | ||
}, | ||
{ | ||
label: "Cursor Pagination", | ||
typeValue: "CursorPagination", | ||
children: ( | ||
<> | ||
<BuilderField | ||
type="string" | ||
path={streamFieldPath("paginator.strategy.cursor_value")} | ||
label="Cursor value" | ||
tooltip="Value of the cursor to send in requests to the API" | ||
/> | ||
<BuilderField | ||
type="string" | ||
path={streamFieldPath("paginator.strategy.stop_condition")} | ||
label="Stop condition" | ||
tooltip="Condition that determines when to stop requesting further pages" | ||
optional | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like we have a problem with optional number fields - clearing them out will turn them into an empty string which will fail the validation. |
||
/> | ||
<BuilderField | ||
type="number" | ||
path={streamFieldPath("paginator.strategy.page_size")} | ||
onChange={(newValue) => { | ||
if (newValue === undefined || newValue === "") { | ||
pageSizeOptionHelpers.setValue(undefined); | ||
} | ||
}} | ||
label="Page size" | ||
tooltip="Set the size of each page" | ||
optional | ||
/> | ||
Comment on lines
+143
to
+154
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I decided to keep the page size field here outside of the pageSizeOption group, because it is technically allowed by the low code framework to set a page size here, but not set a page_size_option There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's slightly weird that starting to type into the page size input suddenly pops in the page size request options, but I think we can start like this. |
||
{pageSizeField.value && pageSizeField.value !== "" && pageSizeOption} | ||
{pageTokenOption} | ||
</> | ||
), | ||
}, | ||
]} | ||
/> | ||
</BuilderCard> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ import { BuilderConfigView } from "./BuilderConfigView"; | |
import { BuilderField } from "./BuilderField"; | ||
import { BuilderTitle } from "./BuilderTitle"; | ||
import { KeyValueListField } from "./KeyValueListField"; | ||
import { PaginationSection } from "./PaginationSection"; | ||
import styles from "./StreamConfigView.module.scss"; | ||
|
||
interface StreamConfigViewProps { | ||
|
@@ -84,10 +85,18 @@ export const StreamConfigView: React.FC<StreamConfigViewProps> = ({ streamNum }) | |
<BuilderField | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like these are not required, should they be marked as optional in the UI? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes they should, good catch |
||
type="array" | ||
path={streamFieldPath("fieldPointer")} | ||
label="Field Pointer" | ||
label="Record selector" | ||
tooltip="Pointer into the response that should be extracted as the final record" | ||
/> | ||
<BuilderField | ||
type="array" | ||
path={streamFieldPath("primaryKey")} | ||
label="Primary key" | ||
tooltip="Pointer into the response that should be used as the primary key when deduplicating records in the destination" | ||
optional | ||
/> | ||
</BuilderCard> | ||
<PaginationSection streamFieldPath={streamFieldPath} /> | ||
<BuilderCard> | ||
<KeyValueListField | ||
path={streamFieldPath("requestOptions.requestParameters")} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
@use "scss/variables"; | ||
|
||
.label { | ||
display: flex; | ||
align-items: center; | ||
gap: variables.$spacing-md; | ||
height: 34px; | ||
|
||
label { | ||
padding-bottom: 0; | ||
} | ||
} |
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 similar situation here - you need to set the page size if yopu want to use the page size option:
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 putting the request options behind a checkbox like you suggested and looking for those to make page size optional or required would work.