Skip to content

Feat/query triggers #1443

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

Merged
merged 5 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 81 additions & 8 deletions client/packages/lowcoder/src/comps/queries/queryComp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,25 @@ interface AfterExecuteQueryAction {
result: QueryResult;
}

const TriggerTypeOptions = [
const CommonTriggerOptions = [
{ label: trans("query.triggerTypeInputChange"), value: "onInputChange"},
{ label: trans("query.triggerTypeQueryExec"), value: "onQueryExecution"},
{ label: trans("query.triggerTypeTimeout"), value: "onTimeout"},
]

export const TriggerTypeOptions = [
{ label: trans("query.triggerTypePageLoad"), value: "onPageLoad"},
...CommonTriggerOptions,
{ label: trans("query.triggerTypeAuto"), value: "automatic" },
{ label: trans("query.triggerTypeManual"), value: "manual" },
] as const;

export const JSTriggerTypeOptions = [
...CommonTriggerOptions,
{ label: trans("query.triggerTypePageLoad"), value: "automatic" },
{ label: trans("query.triggerTypeManual"), value: "manual" },
];

export type TriggerType = ValueFromOption<typeof TriggerTypeOptions>;

const EventOptions = [
Expand Down Expand Up @@ -151,6 +166,13 @@ const childrenMap = {
},
}),
cancelPrevious: withDefault(BoolPureControl, false),
// use only for onQueryExecution trigger
depQueryName: SimpleNameComp,
// use only for onTimeout trigger, triggers query after x time passed on page load
delayTime: millisecondsControl({
left: 0,
defaultValue: 5 * 1000,
})
};

let QueryCompTmp = withTypeAndChildren<typeof QueryMap, ToInstanceType<typeof childrenMap>>(
Expand All @@ -174,6 +196,7 @@ export type QueryChildrenType = InstanceType<typeof QueryCompTmp> extends MultiB
? X
: never;

let blockInputChangeQueries = true;
/**
* Logic to automatically trigger execution
*/
Expand Down Expand Up @@ -222,11 +245,17 @@ QueryCompTmp = class extends QueryCompTmp {
const isJsQuery = this.children.compType.getView() === "js";
const notExecuted = this.children.lastQueryStartTime.getView() === -1;
const isAutomatic = getTriggerType(this) === "automatic";
const isPageLoadTrigger = getTriggerType(this) === "onPageLoad";
const isInputChangeTrigger = getTriggerType(this) === "onInputChange";

if (
action.type === CompActionTypes.UPDATE_NODES_V2 &&
isAutomatic &&
(!isJsQuery || (isJsQuery && notExecuted)) // query which has deps can be executed on page load(first time)
action.type === CompActionTypes.UPDATE_NODES_V2
&& (
isAutomatic
|| isInputChangeTrigger
|| (isPageLoadTrigger && notExecuted)
)
// && (!isJsQuery || (isJsQuery && notExecuted)) // query which has deps can be executed on page load(first time)
) {
const next = super.reduce(action);
const depends = this.children.comp.node()?.dependValues();
Expand All @@ -250,6 +279,18 @@ QueryCompTmp = class extends QueryCompTmp {
const dependsChanged = !_.isEqual(preDepends, depends);
const dslNotChanged = _.isEqual(preDsl, dsl);

if(isInputChangeTrigger && blockInputChangeQueries && dependsChanged) {
// block executing input change queries initially on page refresh
setTimeout(() => {
blockInputChangeQueries = false;
}, 500)

return setFieldsNoTypeCheck(next, {
[lastDependsKey]: depends,
[lastDslKey]: dsl,
});
}

// If the dsl has not changed, but the dependent node value has changed, then trigger the query execution
// FIXME, this should be changed to a reference judgement, but for unknown reasons if the reference is modified once, it will change twice.
if (dependsChanged) {
Expand Down Expand Up @@ -277,24 +318,33 @@ function QueryView(props: QueryViewProps) {
useEffect(() => {
// Automatically load when page load
if (
getTriggerType(comp) === "automatic" &&
(
getTriggerType(comp) === "automatic"
|| getTriggerType(comp) === "onPageLoad"
) &&
(comp as any).isDepReady &&
!comp.children.isNewCreate.value
) {
setTimeout(() => {
comp.dispatch(deferAction(executeQueryAction({})));
}, 300);
}

if(getTriggerType(comp) === "onTimeout") {
setTimeout(() => {
comp.dispatch(deferAction(executeQueryAction({})));
}, comp.children.delayTime.getView());
}
}, []);

useFixedDelay(
() =>
getPromiseAfterDispatch(comp.dispatch, executeQueryAction({}), {
notHandledError: trans("query.fixedDelayError"),
}),
getTriggerType(comp) === "automatic" && comp.children.periodic.getView()
? comp.children.periodicTime.getView()
: null
getTriggerType(comp) === "automatic" && comp.children.periodic.getView()
? comp.children.periodicTime.getView()
: null
);

return null;
Expand Down Expand Up @@ -609,6 +659,29 @@ export const QueryComp = withExposingConfigs(QueryCompTmp, [
const QueryListTmpComp = list(QueryComp);

class QueryListComp extends QueryListTmpComp implements BottomResListComp {
override reduce(action: CompAction): this {
if (isCustomAction<AfterExecuteQueryAction>(action, "afterExecQuery")) {
if (action.path?.length === 1 && !isNaN(parseInt(action.path[0]))) {
const queryIdx = parseInt(action.path[0]);
const queryComps = this.getView();
const queryName = queryComps?.[queryIdx]?.children.name.getView();
const dependentQueries = queryComps.filter((query, idx) => {
if (queryIdx === idx) return false;
if (
getTriggerType(query) === 'onQueryExecution'
&& query.children.depQueryName.getView() === queryName
) {
return true;
}
})
dependentQueries?.forEach((query) => {
query.dispatch(deferAction(executeQueryAction({})));
})
}
}
return super.reduce(action);
}

nameAndExposingInfo(): NameAndExposingInfo {
const result: NameAndExposingInfo = {};
Object.values(this.children).forEach((comp) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { useSelector } from "react-redux";
import { getDataSource, getDataSourceTypes } from "redux/selectors/datasourceSelectors";
import { BottomResTypeEnum } from "types/bottomRes";
import { EditorContext } from "../../editorState";
import { QueryComp } from "../queryComp";
import { JSTriggerTypeOptions, QueryComp, TriggerType, TriggerTypeOptions } from "../queryComp";
import { ResourceDropdown } from "../resourceDropdown";
import { NOT_SUPPORT_GUI_SQL_QUERY, SQLQuery } from "../sqlQuery/SQLQuery";
import { StreamQuery } from "../httpQuery/streamQuery";
Expand All @@ -37,6 +37,7 @@ import styled from "styled-components";
import { DataSourceButton } from "pages/datasource/pluginPanel";
import { Tooltip, Divider } from "antd";
import { uiCompRegistry } from "comps/uiCompRegistry";
import { InputTypeEnum } from "@lowcoder-ee/comps/comps/moduleContainerComp/ioComp/inputListItemComp";

const Wrapper = styled.div`
width: 100%;
Expand Down Expand Up @@ -226,6 +227,42 @@ export const QueryGeneralPropertyView = (props: {
comp.children.datasourceId.dispatchChangeValueAction(QUICK_REST_API_ID);
}

const triggerOptions = useMemo(() => {
if (datasourceType === "js" || datasourceType === "streamApi") {
return JSTriggerTypeOptions;
}
return TriggerTypeOptions;
}, [datasourceType]);

const getQueryOptions = useMemo(() => {
const options: { label: string; value: string }[] =
editorState
?.queryCompInfoList()
.map((info) => ({
label: info.name,
value: info.name,
}))
.filter((option) => {
// Filter out the current query under query
if (editorState.selectedBottomResType === BottomResTypeEnum.Query) {
return option.value !== editorState.selectedBottomResName;
}
return true;
}) || [];

// input queries
editorState
?.getModuleLayoutComp()
?.getInputs()
.forEach((i) => {
const { name, type } = i.getView();
if (type === InputTypeEnum.Query) {
options.push({ label: name, value: name });
}
});
return options;
}, [editorState]);

return (
<QueryPropertyViewWrapper>
<QuerySectionWrapper>
Expand Down Expand Up @@ -329,26 +366,38 @@ export const QueryGeneralPropertyView = (props: {
</QueryConfigWrapper>

{placement === "editor" && (
<TriggerTypeStyled>
<Dropdown
placement={"bottom"}
label={trans("query.triggerType")}
options={
[
{
label:
(children.compType.getView() === "js" || children.compType.getView() === "streamApi")
? trans("query.triggerTypePageLoad")
: trans("query.triggerTypeAuto"),
value: "automatic",
},
{ label: trans("query.triggerTypeManual"), value: "manual" },
] as const
}
value={children.triggerType.getView()}
onChange={(value) => children.triggerType.dispatchChangeValueAction(value)}
/>
</TriggerTypeStyled>
<>
<TriggerTypeStyled>
<Dropdown
placement={"bottom"}
label={trans("query.triggerType")}
options={triggerOptions}
value={children.triggerType.getView()}
onChange={(value) => children.triggerType.dispatchChangeValueAction(value as TriggerType)}
/>
</TriggerTypeStyled>
{children.triggerType.getView() === 'onQueryExecution' && (
<TriggerTypeStyled>
<Dropdown
showSearch={true}
placement={"bottom"}
value={children.depQueryName.getView()}
options={getQueryOptions}
label={trans("eventHandler.selectQuery")}
onChange={(value) => children.depQueryName.dispatchChangeValueAction(value)}
/>
</TriggerTypeStyled>
)}
{children.triggerType.getView() === 'onTimeout' && (
<TriggerTypeStyled>
{children.delayTime.propertyView({
label: trans("query.delayTime"),
placeholder: "5s",
placement: "bottom",
})}
</TriggerTypeStyled>
)}
</>
)}
</QuerySectionWrapper>

Expand Down
4 changes: 4 additions & 0 deletions client/packages/lowcoder/src/i18n/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,10 @@ export const en = {
"triggerTypeAuto": "Inputs Change or On Page Load",
"triggerTypePageLoad": "When the Application (Page) loads",
"triggerTypeManual": "Only when you trigger it manually",
"triggerTypeInputChange": "When Inputs Change",
"triggerTypeQueryExec": "After Query Execution",
"triggerTypeTimeout": "After the Timeout Interval",
"delayTime": "Delay Time",
"chooseDataSource": "Choose Data Source",
"method": "Method",
"updateExceptionDataSourceTitle": "Update Failing Data Source",
Expand Down
Loading