Skip to content

Commit

Permalink
Merge pull request #1457 from lowcoder-org/feat/alasql
Browse files Browse the repository at this point in the history
Feat/alasql
  • Loading branch information
FalkWolsky authored Jan 22, 2025
2 parents c3068a6 + 3a4ab7c commit 10c17e2
Show file tree
Hide file tree
Showing 14 changed files with 231 additions and 3 deletions.
1 change: 1 addition & 0 deletions client/packages/lowcoder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@types/react-signature-canvas": "^1.0.2",
"@types/react-test-renderer": "^18.0.0",
"@types/react-virtualized": "^9.21.21",
"alasql": "^4.6.2",
"animate.css": "^4.1.1",
"antd": "^5.20.0",
"axios": "^1.7.7",
Expand Down
8 changes: 8 additions & 0 deletions client/packages/lowcoder/src/components/ResCreatePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,13 @@ const ResButton = (props: {
compType: "streamApi",
},
},
alasql: {
label: trans("query.quickAlasql"),
type: BottomResTypeEnum.Query,
extra: {
compType: "alasql",
},
},
graphql: {
label: trans("query.quickGraphql"),
type: BottomResTypeEnum.Query,
Expand Down Expand Up @@ -319,6 +326,7 @@ export function ResCreatePanel(props: ResCreateModalProps) {
<DataSourceListWrapper $placement={placement}>
<ResButton size={buttonSize} identifier={"restApi"} onSelect={onSelect} />
<ResButton size={buttonSize} identifier={"streamApi"} onSelect={onSelect} />
<ResButton size={buttonSize} identifier={"alasql"} onSelect={onSelect} />
<ResButton size={buttonSize} identifier={"graphql"} onSelect={onSelect} />
{datasource.map((i) => (
<ResButton size={buttonSize} key={i.id} identifier={i} onSelect={onSelect} />
Expand Down
134 changes: 134 additions & 0 deletions client/packages/lowcoder/src/comps/queries/httpQuery/alasqlQuery.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { QueryConfigItemWrapper, QueryConfigLabel, QueryConfigWrapper } from "components/query";
import { simpleMultiComp } from "comps/generators/multi";
import { JSONValue } from "../../../util/jsonTypes";
import { ParamsStringControl } from "../../controls/paramsControl";
import { dropdownControl } from "@lowcoder-ee/comps/controls/dropdownControl";
import { QueryResult } from "../queryComp";
import { QUERY_EXECUTION_ERROR, QUERY_EXECUTION_OK } from "@lowcoder-ee/constants/queryConstants";
import { getDynamicStringSegments, isDynamicSegment } from "lowcoder-core";
import alasql from "alasql";
import { trans } from "i18n";

const childrenMap = {
databaseType: dropdownControl(
[
{ label: "Data Query", value: "dataQuery" },
{ label: "Local Database", value: "localDB" },
] as const,
"dataQuery"
),
database: dropdownControl(
[
{ label: "Local Storage", value: "LOCALSTORAGE" },
{ label: "IndexedDB", value: "INDEXEDDB" },
] as const,
"LOCALSTORAGE"
),
sql: ParamsStringControl,
};

const AlaSqlTmpQuery = simpleMultiComp(childrenMap);

// TODO: Support multiple queries
export class AlaSqlQuery extends AlaSqlTmpQuery {
override getView() {
const children = this.children;
const params = [ ...children.sql.getQueryParams() ];
const databaseType = children.databaseType.getView();
const selectedDB = children.database.getView();
const paramsMap: Record<string, any> = {};
params.forEach(({key, value}) => {
paramsMap[key] = value();
});

const sqlQuery = children.sql.children.text.unevaledValue.replace(/ +/g, ' ');
const isCreateDBQuery = sqlQuery.toUpperCase().startsWith('CREATE DATABASE');

return async (p: { args?: Record<string, unknown> }): Promise<QueryResult> => {
try {
let result: JSONValue;
const timer = performance.now();

if (databaseType === 'localDB' && isCreateDBQuery) {
const updatedQuery = `${sqlQuery.slice(0, 6)} ${selectedDB} ${sqlQuery.slice(6)}`;
const tableName = updatedQuery.split(' ').pop()?.replace(';', '');
result = alasql(updatedQuery);
result = alasql(`ATTACH ${selectedDB} DATABASE ${tableName};`);
} else {
let segments = getDynamicStringSegments(sqlQuery);
let dataArr: any = [];
segments = segments.map((segment) => {
if (isDynamicSegment(segment)) {
const key = segment.replace('{{','').replace('}}','');
dataArr.push(paramsMap[key]);
return '?';
}
return segment;
})
result = alasql(segments.join(' '), dataArr);
}

return {
data: result as JSONValue,
code: QUERY_EXECUTION_OK,
success: true,
runTime: Number((performance.now() - timer).toFixed()),
};
} catch (e) {
return {
success: false,
data: "",
code: QUERY_EXECUTION_ERROR,
message: (e as any).message || "",
};
}
};
}

propertyView(props: { datasourceId: string }) {
return <PropertyView {...props} comp={this} />;
}
}

const PropertyView = (props: { comp: InstanceType<typeof AlaSqlQuery>; datasourceId: string }) => {
const { comp } = props;
const { children } = comp;

return (
<>
<QueryConfigWrapper>
<QueryConfigLabel>{trans("query.databaseType")}</QueryConfigLabel>
<QueryConfigItemWrapper>
{children.databaseType.propertyView({
styleName: "medium",
width: "100%",
})}
</QueryConfigItemWrapper>
</QueryConfigWrapper>

{children.databaseType.getView() === 'localDB' && (
<QueryConfigWrapper>
<QueryConfigLabel>{trans("query.chooseDatabase")}</QueryConfigLabel>
<QueryConfigItemWrapper>
{children.database.propertyView({
styleName: "medium",
width: "100%",
})}
</QueryConfigItemWrapper>
</QueryConfigWrapper>
)}

<QueryConfigWrapper>
<QueryConfigItemWrapper>
{children.sql.propertyView({
placement: "bottom",
placeholder: "SELECT * FROM users WHERE user_id = {{userId}}::uuid",
styleName: "medium",
language: "sql",
enableMetaCompletion: true,
})}
</QueryConfigItemWrapper>
</QueryConfigWrapper>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,7 @@ function useDatasourceStatus(datasourceId: string, datasourceType: ResourceType)
datasourceType === "js" ||
datasourceType === "streamApi" ||
datasourceType === "libraryQuery" ||
datasourceType === "alasql" ||
datasourceId === QUICK_REST_API_ID ||
datasourceId === QUICK_GRAPHQL_ID
) {
Expand Down
16 changes: 16 additions & 0 deletions client/packages/lowcoder/src/comps/queries/resourceDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ const QuickGraphqlValue: ResourceOptionValue = {
type: "graphql",
};

const QuickAlasqlValue: ResourceOptionValue = {
id: "",
type: "alasql",
};

interface ResourceDropdownProps {
changeResource: (datasourceId: string, value: string) => void;
selectedResource: ResourceOptionValue;
Expand Down Expand Up @@ -265,6 +270,17 @@ export const ResourceDropdown = (props: ResourceDropdownProps) => {
<SelectOptionLabel>{trans("query.quickStreamAPI")} </SelectOptionLabel>
</SelectOptionContains>
</SelectOption>

<SelectOption
key={JSON.stringify(QuickAlasqlValue)}
label={trans("query.quickAlasql")}
value={JSON.stringify(QuickAlasqlValue)}
>
<SelectOptionContains>
{getBottomResIcon("restApi")}
<SelectOptionLabel>{trans("query.quickAlasql")} </SelectOptionLabel>
</SelectOptionContains>
</SelectOption>

<SelectOption
key={JSON.stringify(QuickGraphqlValue)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ export const NOT_SUPPORT_GUI_SQL_QUERY: string[] = [
"snowflake",
"tdengine",
"dameng",
"alasql",
];
const SUPPORT_UPSERT_SQL_QUERY: string[] = [
"mysql",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const databasePlugins: Partial<DatasourceType>[] = [
"clickHouse",
"snowflake",
"mariadb",
"alasql",
];

export const apiPluginsForQueryLibrary: Partial<DatasourceType>[] = [
Expand Down
2 changes: 1 addition & 1 deletion client/packages/lowcoder/src/constants/libConstants.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const libNames = new Set(["uuid", "numbro", "Papa", "supabase"]);
export const libNames = new Set(["uuid", "numbro", "Papa", "supabase", "alasql"]);
5 changes: 4 additions & 1 deletion client/packages/lowcoder/src/constants/queryConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { GraphqlQuery } from "../comps/queries/httpQuery/graphqlQuery";
import { toPluginQuery } from "comps/queries/pluginQuery/pluginQuery";
import { MultiCompConstructor } from "lowcoder-core";
import { DataSourcePluginMeta } from "lowcoder-sdk/dataSource";
import { AlaSqlQuery } from "@lowcoder-ee/comps/queries/httpQuery/alasqlQuery";

export type DatasourceType =
| "mysql"
Expand All @@ -29,13 +30,15 @@ export type DatasourceType =
| "googleSheets"
| "graphql"
| "snowflake"
| "mariadb";
| "mariadb"
| "alasql";

export type ResourceType = DatasourceType | "js" | "libraryQuery" | "view";

export const QueryMap = {
js: JSQuery,
mysql: SQLQuery,
alasql: AlaSqlQuery,
restApi: HttpQuery,
streamApi: StreamQuery,
mongodb: MongoQuery,
Expand Down
1 change: 1 addition & 0 deletions client/packages/lowcoder/src/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ declare global {
numbro: any;
Papa: any;
uuid: any;
alasql: any;
}
}
3 changes: 3 additions & 0 deletions client/packages/lowcoder/src/i18n/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,9 @@ export const en = {
"quickRestAPI": "REST Query",
"quickStreamAPI": "Stream Query",
"quickGraphql": "GraphQL Query",
"quickAlasql": "Local SQL Query",
"databaseType": "Database Type",
"chooseDatabase": "Choose Database",
"lowcoderAPI": "Lowcoder API",
"executeJSCode": "Run JavaScript Code",
"importFromQueryLibrary": "Import from Query Library",
Expand Down
2 changes: 2 additions & 0 deletions client/packages/lowcoder/src/index.sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import numbro from "numbro";
import Papa from "papaparse";
import * as uuid from "uuid";
import * as supabase from "@supabase/supabase-js";
import * as alasql from "alasql";

import * as styledNameExports from "styled-components";
import styledDefault from "styled-components";
Expand Down Expand Up @@ -136,3 +137,4 @@ window.numbro = numbro;
window.Papa = Papa;
window.uuid = uuid;
window.supabase = supabase;
window.alasql = alasql;
2 changes: 2 additions & 0 deletions client/packages/lowcoder/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import ResizeObserver from "resize-observer-polyfill";
import numbro from "numbro";
import Papa from "papaparse";
import * as supabase from "@supabase/supabase-js";
import * as alasql from "alasql";

import * as uuid from "uuid";
import "regenerator-runtime/runtime";
Expand All @@ -18,6 +19,7 @@ window.numbro = numbro;
window.Papa = Papa;
window.uuid = uuid;
window.supabase = supabase;
window.alasql = alasql;

// for chrome 63
if (!window.ResizeObserver) {
Expand Down
57 changes: 56 additions & 1 deletion client/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5932,6 +5932,18 @@ __metadata:
languageName: node
linkType: hard

"alasql@npm:^4.6.2":
version: 4.6.2
resolution: "alasql@npm:4.6.2"
dependencies:
cross-fetch: 4.1.0
yargs: 16
bin:
alasql: bin/alasql-cli.js
checksum: cc68e87eeaa72ddaec5f20c4ca631e2a8ddb45e38d4b7de41cb14661ead657b1afec8d9530160f66fe5253e9724db9ada5fc63ba2c5bcacf5b8f9583c7b0870f
languageName: node
linkType: hard

"animate.css@npm:^4.1.1":
version: 4.1.1
resolution: "animate.css@npm:4.1.1"
Expand Down Expand Up @@ -7309,6 +7321,17 @@ __metadata:
languageName: node
linkType: hard

"cliui@npm:^7.0.2":
version: 7.0.4
resolution: "cliui@npm:7.0.4"
dependencies:
string-width: ^4.2.0
strip-ansi: ^6.0.0
wrap-ansi: ^7.0.0
checksum: ce2e8f578a4813806788ac399b9e866297740eecd4ad1823c27fd344d78b22c5f8597d548adbcc46f0573e43e21e751f39446c5a5e804a12aace402b7a315d7f
languageName: node
linkType: hard

"cliui@npm:^8.0.1":
version: 8.0.1
resolution: "cliui@npm:8.0.1"
Expand Down Expand Up @@ -7870,6 +7893,15 @@ coolshapes-react@lowcoder-org/coolshapes-react:
languageName: node
linkType: hard

"cross-fetch@npm:4.1.0":
version: 4.1.0
resolution: "cross-fetch@npm:4.1.0"
dependencies:
node-fetch: ^2.7.0
checksum: c02fa85d59f83e50dbd769ee472c9cc984060c403ee5ec8654659f61a525c1a655eef1c7a35e365c1a107b4e72d76e786718b673d1cb3c97f61d4644cb0a9f9d
languageName: node
linkType: hard

"cross-fetch@npm:^3.1.5":
version: 3.1.8
resolution: "cross-fetch@npm:3.1.8"
Expand Down Expand Up @@ -14090,6 +14122,7 @@ coolshapes-react@lowcoder-org/coolshapes-react:
"@types/regenerator-runtime": ^0.13.1
"@types/uuid": ^8.3.4
"@vitejs/plugin-react": ^2.2.0
alasql: ^4.6.2
animate.css: ^4.1.1
antd: ^5.20.0
axios: ^1.7.7
Expand Down Expand Up @@ -15684,7 +15717,7 @@ coolshapes-react@lowcoder-org/coolshapes-react:
languageName: node
linkType: hard

"node-fetch@npm:^2.6.12":
"node-fetch@npm:^2.6.12, node-fetch@npm:^2.7.0":
version: 2.7.0
resolution: "node-fetch@npm:2.7.0"
dependencies:
Expand Down Expand Up @@ -22446,13 +22479,35 @@ coolshapes-react@lowcoder-org/coolshapes-react:
languageName: node
linkType: hard

"yargs-parser@npm:^20.2.2":
version: 20.2.9
resolution: "yargs-parser@npm:20.2.9"
checksum: 8bb69015f2b0ff9e17b2c8e6bfe224ab463dd00ca211eece72a4cd8a906224d2703fb8a326d36fdd0e68701e201b2a60ed7cf81ce0fd9b3799f9fe7745977ae3
languageName: node
linkType: hard

"yargs-parser@npm:^21.1.1":
version: 21.1.1
resolution: "yargs-parser@npm:21.1.1"
checksum: ed2d96a616a9e3e1cc7d204c62ecc61f7aaab633dcbfab2c6df50f7f87b393993fe6640d017759fe112d0cb1e0119f2b4150a87305cc873fd90831c6a58ccf1c
languageName: node
linkType: hard

"yargs@npm:16":
version: 16.2.0
resolution: "yargs@npm:16.2.0"
dependencies:
cliui: ^7.0.2
escalade: ^3.1.1
get-caller-file: ^2.0.5
require-directory: ^2.1.1
string-width: ^4.2.0
y18n: ^5.0.5
yargs-parser: ^20.2.2
checksum: b14afbb51e3251a204d81937c86a7e9d4bdbf9a2bcee38226c900d00f522969ab675703bee2a6f99f8e20103f608382936034e64d921b74df82b63c07c5e8f59
languageName: node
linkType: hard

"yargs@npm:^17.3.1, yargs@npm:^17.5.1":
version: 17.7.2
resolution: "yargs@npm:17.7.2"
Expand Down

0 comments on commit 10c17e2

Please sign in to comment.