## Description Added ESLint rule to force blank lines between statements. Fixes #`Issue Number` _or_ Fixes `Issue URL` > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="@tag.All" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!CAUTION] > 🔴 🔴 🔴 Some tests have failed. > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/10924926728> > Commit: 34f57714a1575ee04e94e03cbcaf95e57a96c86c > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=10924926728&attempt=1&selectiontype=test&testsstatus=failed&specsstatus=fail" target="_blank">Cypress dashboard</a>. > Tags: @tag.All > Spec: > The following are new failures, please fix them before merging the PR: <ol> > <li>cypress/e2e/Regression/ClientSide/Anvil/AnvilModal_spec.ts > <li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilButtonWidgetSnapshot_spec.ts > <li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilCheckboxGroupWidgetSnapshot_spec.ts > <li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilCurrencyInputWidgetSnapshot_spec.ts > <li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilIconButtonWidgetSnapshot_spec.ts > <li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilInlineButtonWidgetSnapshot_spec.ts > <li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilInputWidgetSnapshot_spec.ts > <li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilParagraphWidgetSnapshot_spec.ts > <li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilPhoneInputWidgetSnapshot_spec.ts > <li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilStatsWidgetSnapshot_spec.ts > <li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilSwitchGroupWidgetSnapshot_spec.ts > <li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilSwitchWidgetSnapshot_spec.ts > <li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilTableWidgetSnapshot_spec.ts > <li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilToolbarButtonWidgetSnapshot_spec.ts > <li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilZoneSectionWidgetSnapshot_spec.ts</ol> > <a href="https://internal.appsmith.com/app/cypress-dashboard/identified-flaky-tests-65890b3c81d7400d08fa9ee3?branch=master" target="_blank">List of identified flaky tests</a>. > <hr>Wed, 18 Sep 2024 16:33:36 UTC <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No --------- Co-authored-by: Valera Melnikov <valera@appsmith.com>
330 lines
8.7 KiB
TypeScript
330 lines
8.7 KiB
TypeScript
import type { Plugin } from "api/PluginApi";
|
|
import {
|
|
DATASOURCE_DB_FORM,
|
|
DATASOURCE_REST_API_FORM,
|
|
DATASOURCE_SAAS_FORM,
|
|
} from "ee/constants/forms";
|
|
import { DB_NOT_SUPPORTED } from "ee/utils/Environments";
|
|
import { diff } from "deep-diff";
|
|
import { PluginName, PluginPackageName, PluginType } from "entities/Action";
|
|
import type {
|
|
Datasource,
|
|
DatasourceStructure,
|
|
DatasourceTable,
|
|
QueryTemplate,
|
|
} from "entities/Datasource";
|
|
import { AuthenticationStatus, AuthType } from "entities/Datasource";
|
|
import { get, isArray } from "lodash";
|
|
import store from "store";
|
|
import { getPlugin } from "ee/selectors/entitiesSelector";
|
|
import type { AppState } from "ee/reducers";
|
|
import {
|
|
DATASOURCES_ALLOWED_FOR_PREVIEW_MODE,
|
|
MOCK_DB_TABLE_NAMES,
|
|
SQL_DATASOURCES,
|
|
} from "constants/QueryEditorConstants";
|
|
import {
|
|
NOSQL_PLUGINS_DEFAULT_TEMPLATE_TYPE,
|
|
SQL_PLUGINS_DEFAULT_TEMPLATE_TYPE,
|
|
} from "constants/Datasource";
|
|
export function isCurrentFocusOnInput() {
|
|
return (
|
|
["input", "textarea"].indexOf(
|
|
document.activeElement?.tagName?.toLowerCase() || "",
|
|
) >= 0
|
|
);
|
|
}
|
|
|
|
/**
|
|
* This method returns boolean if the propertyControl is focused.
|
|
* @param domElement
|
|
* @returns
|
|
*/
|
|
export function shouldFocusOnPropertyControl(
|
|
domElement?: HTMLDivElement | null,
|
|
) {
|
|
let isCurrentFocusOnProperty = false;
|
|
|
|
if (domElement) {
|
|
isCurrentFocusOnProperty = domElement.contains(document.activeElement);
|
|
}
|
|
|
|
return !(isCurrentFocusOnInput() || isCurrentFocusOnProperty);
|
|
}
|
|
|
|
/**
|
|
* Returns a focusable field of PropertyControl.
|
|
* @param element
|
|
* @returns
|
|
*/
|
|
export function getPropertyControlFocusElement(
|
|
element: HTMLElement | null,
|
|
): HTMLElement | undefined {
|
|
if (element?.children) {
|
|
const [, propertyInputElement] = element.children;
|
|
|
|
if (propertyInputElement) {
|
|
const uiInputElement = propertyInputElement.querySelector(
|
|
'button:not([tabindex="-1"]), input, [tabindex]:not([tabindex="-1"])',
|
|
) as HTMLElement | undefined;
|
|
|
|
if (uiInputElement) {
|
|
return uiInputElement;
|
|
}
|
|
|
|
const codeEditorInputElement =
|
|
propertyInputElement.getElementsByClassName("CodeEditorTarget")[0] as
|
|
| HTMLElement
|
|
| undefined;
|
|
|
|
if (codeEditorInputElement) {
|
|
return codeEditorInputElement;
|
|
}
|
|
|
|
const lazyCodeEditorInputElement =
|
|
propertyInputElement.getElementsByClassName("LazyCodeEditor")[0] as
|
|
| HTMLElement
|
|
| undefined;
|
|
|
|
if (lazyCodeEditorInputElement) {
|
|
return lazyCodeEditorInputElement;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if :
|
|
* - authentication type is not oauth2 or is not a Google Sheet Plugin
|
|
* - authentication type is oauth2 and authorized status success and is a Google Sheet Plugin
|
|
* @param datasource Datasource
|
|
* @param plugin Plugin
|
|
* @param currentEnvironment string
|
|
* @param validStatusArr Array<AuthenticationStatus>
|
|
* @returns boolean
|
|
*/
|
|
export function isDatasourceAuthorizedForQueryCreation(
|
|
datasource: Datasource,
|
|
plugin: Plugin,
|
|
currentEnvironment: string,
|
|
validStatusArr: Array<AuthenticationStatus> = [AuthenticationStatus.SUCCESS],
|
|
): boolean {
|
|
if (!datasource || !datasource.hasOwnProperty("datasourceStorages"))
|
|
return false;
|
|
|
|
const isGoogleSheetPlugin = isGoogleSheetPluginDS(plugin?.packageName);
|
|
const envSupportedDs = !DB_NOT_SUPPORTED.includes(plugin?.type || "");
|
|
|
|
if (!datasource.datasourceStorages.hasOwnProperty(currentEnvironment)) {
|
|
if (envSupportedDs) return false;
|
|
|
|
const envs = Object.keys(datasource.datasourceStorages);
|
|
|
|
if (envs.length === 0) return false;
|
|
|
|
currentEnvironment = envs[0];
|
|
}
|
|
|
|
const datasourceStorage = datasource.datasourceStorages[currentEnvironment];
|
|
|
|
if (
|
|
!datasourceStorage ||
|
|
!datasourceStorage.hasOwnProperty("id") ||
|
|
!datasourceStorage.hasOwnProperty("datasourceConfiguration")
|
|
)
|
|
return false;
|
|
|
|
const authType = get(
|
|
datasourceStorage,
|
|
"datasourceConfiguration.authentication.authenticationType",
|
|
);
|
|
|
|
if (isGoogleSheetPlugin) {
|
|
const authStatus = get(
|
|
datasourceStorage,
|
|
"datasourceConfiguration.authentication.authenticationStatus",
|
|
) as AuthenticationStatus;
|
|
const isAuthorized =
|
|
authType === AuthType.OAUTH2 &&
|
|
!!authStatus &&
|
|
validStatusArr.includes(authStatus);
|
|
|
|
return isAuthorized;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Determines whether plugin is google sheet or not
|
|
* @param pluginPackageName string
|
|
* @returns boolean
|
|
*/
|
|
export function isGoogleSheetPluginDS(pluginPackageName?: string) {
|
|
return pluginPackageName === PluginPackageName.GOOGLE_SHEETS;
|
|
}
|
|
|
|
/**
|
|
* Returns datasource property value from datasource?.datasourceConfiguration?.properties
|
|
* @param datasource Datasource
|
|
* @param propertyKey string
|
|
* @returns string | null
|
|
*/
|
|
export function getDatasourcePropertyValue(
|
|
datasource: Datasource,
|
|
propertyKey: string,
|
|
currentEnvironment: string,
|
|
): string | null {
|
|
if (!datasource) {
|
|
return null;
|
|
}
|
|
|
|
const properties = get(
|
|
datasource,
|
|
`datasourceStorages.${currentEnvironment}.datasourceConfiguration.properties`,
|
|
);
|
|
|
|
if (!!properties && properties.length > 0) {
|
|
const propertyObj = properties.find((prop) => prop.key === propertyKey);
|
|
|
|
if (!!propertyObj) {
|
|
return propertyObj.value;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
export function getFormName(plugin: Plugin): string {
|
|
const pluginType = plugin?.type;
|
|
|
|
if (!!pluginType) {
|
|
switch (pluginType) {
|
|
case PluginType.DB:
|
|
case PluginType.REMOTE:
|
|
case PluginType.AI:
|
|
return DATASOURCE_DB_FORM;
|
|
case PluginType.SAAS:
|
|
return DATASOURCE_SAAS_FORM;
|
|
case PluginType.API:
|
|
return DATASOURCE_REST_API_FORM;
|
|
}
|
|
}
|
|
|
|
return DATASOURCE_DB_FORM;
|
|
}
|
|
|
|
// TODO: Fix this the next time the file is edited
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
export function getFormDiffPaths(initialValues: any, currentValues: any) {
|
|
const difference = diff(initialValues, currentValues);
|
|
const diffPaths: string[] = [];
|
|
|
|
if (!!difference) {
|
|
difference.forEach((diff) => {
|
|
if (!!diff.path && isArray(diff.path)) {
|
|
diffPaths.push(diff.path.join("."));
|
|
}
|
|
});
|
|
}
|
|
|
|
return diffPaths;
|
|
}
|
|
|
|
/**
|
|
* Returns mock datasource default table name to be populated in query editor, based on plugin name
|
|
* @param pluginId string
|
|
* @returns string
|
|
*/
|
|
export function getSQLPluginsMockTableName(pluginId: string) {
|
|
const state: AppState = store.getState();
|
|
const plugin: Plugin | undefined = getPlugin(state, pluginId);
|
|
|
|
switch (plugin?.name) {
|
|
case PluginName.POSTGRES: {
|
|
return "public.users";
|
|
}
|
|
default: {
|
|
return "";
|
|
}
|
|
}
|
|
}
|
|
|
|
export function getDefaultTemplateActionConfig(
|
|
plugin: Plugin,
|
|
dsPreviewTable?: string,
|
|
dsStructure?: DatasourceStructure,
|
|
isMock?: boolean,
|
|
) {
|
|
if (!!dsStructure) {
|
|
let defaultTableName = "";
|
|
let templateTitle = "";
|
|
let queryTemplate: QueryTemplate | undefined = undefined;
|
|
|
|
if (SQL_DATASOURCES.includes(plugin?.name)) {
|
|
templateTitle = SQL_PLUGINS_DEFAULT_TEMPLATE_TYPE;
|
|
} else if (plugin?.name === PluginName.MONGO) {
|
|
templateTitle = NOSQL_PLUGINS_DEFAULT_TEMPLATE_TYPE;
|
|
}
|
|
|
|
if (isMock) {
|
|
switch (plugin?.name) {
|
|
case PluginName.MONGO: {
|
|
defaultTableName = MOCK_DB_TABLE_NAMES.MOVIES;
|
|
break;
|
|
}
|
|
case PluginName.POSTGRES: {
|
|
defaultTableName = MOCK_DB_TABLE_NAMES.USERS;
|
|
break;
|
|
}
|
|
default: {
|
|
defaultTableName = "";
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
if (SQL_DATASOURCES.includes(plugin?.name)) {
|
|
defaultTableName = !!dsPreviewTable
|
|
? dsPreviewTable
|
|
: !!dsStructure.tables && dsStructure.tables.length > 0
|
|
? dsStructure.tables[0].name
|
|
: "";
|
|
}
|
|
}
|
|
|
|
const table: DatasourceTable | undefined = dsStructure.tables?.find(
|
|
(table: DatasourceTable) => table.name === defaultTableName,
|
|
);
|
|
|
|
queryTemplate = table?.templates?.find(
|
|
(template: QueryTemplate) => template.title === templateTitle,
|
|
);
|
|
|
|
// Reusing same functionality as QueryTemplate.tsx to populate actionConfiguration
|
|
if (!!queryTemplate) {
|
|
return {
|
|
body: queryTemplate.body,
|
|
pluginSpecifiedTemplates: queryTemplate.pluginSpecifiedTemplates,
|
|
formData: queryTemplate.configuration,
|
|
...queryTemplate.actionConfiguration,
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export const isEnabledForPreviewData = (
|
|
datasource: Datasource,
|
|
plugin: Plugin,
|
|
) => {
|
|
const isGoogleSheetPlugin = isGoogleSheetPluginDS(plugin?.packageName);
|
|
|
|
return (
|
|
DATASOURCES_ALLOWED_FOR_PREVIEW_MODE.includes(plugin?.name || "") ||
|
|
(plugin?.name === PluginName.MONGO &&
|
|
!!(datasource as Datasource)?.isMock) ||
|
|
isGoogleSheetPlugin
|
|
);
|
|
};
|