PromucFlow_constructor/app/client/src/WidgetQueryGenerators/PostgreSQL/index.ts

221 lines
5.6 KiB
TypeScript
Raw Normal View History

feat: Table one click binding for MongoDB and Postgres (#23629) > Pull Request Template > > Use this template to quickly create a well written pull request. Delete all quotes before creating the pull request. > ## Description > Add a TL;DR when description is extra long (helps content team) > > Please include a summary of the changes and which issue has been fixed. Please also include relevant motivation > and context. List any dependencies that are required for this change > > Links to Notion, Figma or any other documents that might be relevant to the PR > > #### PR fixes following issue(s) Fixes # (issue number) > if no issue exists, please create an issue and ask the maintainers about this first > > #### Media > A video or a GIF is preferred. when using Loom, don’t embed because it looks like it’s a GIF. instead, just link to the video > > #### Type of change > Please delete options that are not relevant. - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) - Breaking change (fix or feature that would cause existing functionality to not work as expected) - Chore (housekeeping or task changes that don't impact user perception) - This change requires a documentation update > > > ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [x] Manual - [x] Jest - [x] Cypress > > #### Test Plan > One Click Binding - https://github.com/appsmithorg/TestSmith/issues/2390 > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Test-plan-implementation#speedbreaker-features-to-consider-for-every-change) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans/_edit#areas-of-interest) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --------- Co-authored-by: Vemparala Surya Vamsi <vamsi@appsmith.com>
2023-06-01 17:26:05 +00:00
import { BaseQueryGenerator } from "../BaseQueryGenerator";
import { format } from "sql-formatter";
import { QUERY_TYPE } from "../types";
import type {
WidgetQueryGenerationConfig,
WidgetQueryGenerationFormConfig,
ActionConfigurationPostgreSQL,
} from "../types";
export default abstract class PostgreSQL extends BaseQueryGenerator {
private static buildSelect(
widgetConfig: WidgetQueryGenerationConfig,
formConfig: WidgetQueryGenerationFormConfig,
) {
const { select } = widgetConfig;
//if no table name do not build query
if (!select || !formConfig.tableName) {
return;
}
const { limit, offset, orderBy, sortOrder, where } = select;
const querySegments = [
{
isValuePresent: formConfig.tableName,
template: "SELECT * FROM $1",
params: {
1: formConfig.tableName,
},
},
{
isValuePresent: formConfig.searchableColumn && where,
template: "WHERE $2 ilike $3",
params: {
2: `"${formConfig.searchableColumn}"`,
3: `'%{{${where}}}%'`,
},
},
{
isValuePresent: orderBy,
template: "ORDER BY $4 $5",
params: {
4: `"{{${orderBy} || '${formConfig.primaryColumn}'}}"`,
5: `{{${sortOrder} ? "" : "DESC"}}`,
},
},
{
isValuePresent: limit,
template: "LIMIT $6",
params: {
6: `{{${limit}}}`,
},
},
{
isValuePresent: offset,
template: "OFFSET $7",
params: {
7: `{{${offset}}}`,
},
},
];
const { params, template } = querySegments
//we need to filter out query segments which are not defined
.filter(({ isValuePresent }) => !!isValuePresent)
.reduce(
(acc, curr) => {
const { params, template } = curr;
return {
template: acc.template + " " + template,
params: { ...acc.params, ...params },
};
},
{ template: "", params: {} },
);
//formats sql string
const res = format(template, {
params,
language: "postgresql",
});
return {
type: QUERY_TYPE.SELECT,
name: "Select_query",
payload: {
body: res,
},
dynamicBindingPathList: [
{
key: "body",
},
],
};
}
private static buildUpdate(
widgetConfig: WidgetQueryGenerationConfig,
formConfig: WidgetQueryGenerationFormConfig,
) {
const { update } = widgetConfig;
//if no table name do not build query
if (!update || !update.where || !formConfig.tableName) {
return;
}
const { value, where } = update;
return {
type: QUERY_TYPE.UPDATE,
name: "Update_query",
payload: {
body: `UPDATE ${formConfig.tableName} SET ${formConfig.columns
.map((column) => `"${column}"= '{{${value}.${column}}}'`)
.join(", ")} WHERE "${formConfig.primaryColumn}"= {{${where}.${
formConfig.primaryColumn
}}};`,
},
dynamicBindingPathList: [
{
key: "body",
},
],
};
}
private static buildInsert(
widgetConfig: WidgetQueryGenerationConfig,
formConfig: WidgetQueryGenerationFormConfig,
) {
const { create } = widgetConfig;
//if no table name do not build query
if (!create || !create.value || !formConfig.tableName) {
return;
}
return {
type: QUERY_TYPE.CREATE,
name: "Insert_query",
payload: {
body: `INSERT INTO ${formConfig.tableName} (${formConfig.columns.map(
(a) => `"${a}"`,
)}) VALUES (${formConfig.columns
.map((d) => `'{{${create.value}.${d}}}'`)
.toString()})`,
},
dynamicBindingPathList: [
{
key: "body",
},
],
};
}
private static buildTotal(
widgetConfig: WidgetQueryGenerationConfig,
formConfig: WidgetQueryGenerationFormConfig,
) {
const { select, totalRecord } = widgetConfig;
//if no table name do not build query
if (!totalRecord) {
return;
}
return {
type: QUERY_TYPE.TOTAL_RECORD,
name: "Total_record_query",
payload: {
body: `SELECT COUNT(*) from ${formConfig.tableName}${
formConfig.searchableColumn
? ` where ${formConfig.searchableColumn} ilike '%{{${select?.where}}}%'`
: ""
};`,
},
dynamicBindingPathList: [
{
key: "body",
},
],
};
}
public static build(
widgetConfig: WidgetQueryGenerationConfig,
formConfig: WidgetQueryGenerationFormConfig,
pluginInitalValues: { actionConfiguration: ActionConfigurationPostgreSQL },
) {
const allBuildConfigs = [];
if (widgetConfig.select) {
allBuildConfigs.push(this.buildSelect(widgetConfig, formConfig));
}
if (widgetConfig.update) {
allBuildConfigs.push(this.buildUpdate(widgetConfig, formConfig));
}
if (widgetConfig.create) {
allBuildConfigs.push(this.buildInsert(widgetConfig, formConfig));
}
if (widgetConfig.totalRecord) {
allBuildConfigs.push(this.buildTotal(widgetConfig, formConfig));
}
return allBuildConfigs
.filter((val) => !!val)
.map((val) => ({
...val,
payload: {
...(val?.payload || {}),
...(pluginInitalValues?.actionConfiguration || {}),
pluginSpecifiedTemplates: [
{
value: false,
},
],
},
}));
}
static getTotalRecordExpression(binding: string) {
return `${binding}[0].count`;
}
}