## Description This PR migrates the plugins which were old DBForm config to UQI config. Plugins that got affected, 1. Snowflake 2. ArangoDB 3. Oracle 4. PostgreSQL 5. MySQL 6. Elasticsearch 7. DynamoDB 8. Redis 9. MSSQL 10. Redshift Fixes #35496 Fixes #35497 Fixes #35500 Fixes #35487 Fixes #35490 Fixes #35491 Fixes #35499 Fixes #35501 Fixes #35502 Fixes #35506 Fixes #35890 ## Automation /ok-to-test tags="@tag.Datasource" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/10897087023> > Commit: e8fe475a48aac1e2e1f3184181ae0bed154313ba > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=10897087023&attempt=1" target="_blank">Cypress dashboard</a>. > Tags: `@tag.Datasource` > Spec: > <hr>Tue, 17 Sep 2024 06:07:27 UTC <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Enhanced rendering logic in the Query Editor to support additional UI component types. - Improved handling of form evaluations and query configurations across various plugins, including ArangoDB, DynamoDB, Elasticsearch, and others. - Introduced structured layouts for editor configurations, enhancing clarity and usability in multiple database plugins. - **Bug Fixes** - Adjusted control flow in sagas to ensure proper handling of UI component types, improving user interactions. - **Chores** - Refined JSON configurations across multiple plugins for better organization and maintainability. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
498 lines
16 KiB
TypeScript
498 lines
16 KiB
TypeScript
import { ObjectsRegistry } from "../Objects/Registry";
|
|
import {
|
|
AppSidebar,
|
|
AppSidebarButton,
|
|
PageLeftPane,
|
|
PagePaneSegment,
|
|
} from "./EditorNavigation";
|
|
import * as _ from "../Objects/ObjectsCore";
|
|
import ApiEditor from "../../locators/ApiEditor";
|
|
|
|
type RightPaneTabs = "datasources" | "connections";
|
|
|
|
export class ApiPage {
|
|
public agHelper = ObjectsRegistry.AggregateHelper;
|
|
public locator = ObjectsRegistry.CommonLocators;
|
|
private assertHelper = ObjectsRegistry.AssertHelper;
|
|
|
|
// private datasources = ObjectsRegistry.DataSources;
|
|
|
|
_createapi = ".t--createBlankApiCard";
|
|
_resourceUrl = ".t--dataSourceField";
|
|
private _headerKey = (index: number) =>
|
|
".t--actionConfiguration\\.headers\\[" +
|
|
index +
|
|
"\\]\\.key\\." +
|
|
index +
|
|
"";
|
|
private _headerValue = (index: number) =>
|
|
".t--actionConfiguration\\.headers\\[" +
|
|
index +
|
|
"\\]\\.value\\." +
|
|
index +
|
|
"";
|
|
private _paramKey = (index: number) =>
|
|
".t--actionConfiguration\\.queryParameters\\[" +
|
|
index +
|
|
"\\]\\.key\\." +
|
|
index +
|
|
"";
|
|
public _paramValue = (index: number) =>
|
|
".t--actionConfiguration\\.queryParameters\\[" +
|
|
index +
|
|
"\\]\\.value\\." +
|
|
index +
|
|
"";
|
|
private _importedKey = (index: number, keyValueName: string) =>
|
|
`.t--${keyValueName}-key-${index}`;
|
|
private _importedValue = (index: number, keyValueName: string) =>
|
|
`.t--${keyValueName}-value-${index}`;
|
|
_bodyKey = (index: number) =>
|
|
".t--actionConfiguration\\.bodyFormData\\[0\\]\\.key\\." + index + "";
|
|
_bodyValue = (index: number) =>
|
|
`.t--actionConfiguration\\.bodyFormData\\[${index}\\]\\.value\\.${index}`;
|
|
_bodyTypeDropdown =
|
|
"//span[text()='Type'][@class='rc-select-selection-placeholder']/ancestor::div";
|
|
_apiRunBtn = ".t--apiFormRunBtn";
|
|
private _queryTimeout =
|
|
"//input[@name='actionConfiguration.timeoutInMillisecond']";
|
|
_responseBody = ".CodeMirror-code span.cm-string.cm-property";
|
|
private _blankAPI = "span:contains('REST API')";
|
|
private _apiVerbDropdown = ".t--apiFormHttpMethod div";
|
|
private _verbToSelect = (verb: string) =>
|
|
"//div[contains(@class, 'rc-select-item-option')]//div[contains(text(),'" +
|
|
verb +
|
|
"')]";
|
|
private _bodyTypeSelect = `//div[@data-testid="t--api-body-tab-switch"]`;
|
|
private _bodyTypeToSelect = (subTab: string) =>
|
|
"//div[contains(@class, 'rc-select-item-option')]//div[contains(text(),'" +
|
|
subTab +
|
|
"')]";
|
|
private _rightPaneTab = (tab: string) =>
|
|
"//span[contains(text(), '" + tab + "')]/parent::button";
|
|
_visibleTextSpan = (spanText: string) => "//span[text()='" + spanText + "']";
|
|
_visibleTextDiv = (divText: string) => "//div[text()='" + divText + "']";
|
|
_noBodyMessageDiv = "#NoBodyMessageDiv";
|
|
_noBodyMessage = "This request does not have a body";
|
|
_imageSrc = "//img/parent::div";
|
|
private _trashDelete = "[data-testid=t--trash-icon]";
|
|
private _onPageLoad = "input[name='executeOnLoad'][type='checkbox']";
|
|
private _confirmBeforeRunning =
|
|
"input[name='confirmBeforeExecute'][type='checkbox']";
|
|
private _paginationTypeLabels = ".t--apiFormPaginationType label";
|
|
_saveAsDS = ".t--store-as-datasource";
|
|
_responseStatus = ".t--response-status-code";
|
|
public _responseTabHeader = "[data-testid=t--tab-headers]";
|
|
public _headersTabContent = ".t--headers-tab";
|
|
public _autoGeneratedHeaderInfoIcon = (key: string) =>
|
|
`.t--auto-generated-${key}-info`;
|
|
_nextCursorValue = ".t--apiFormPaginationNextCursorValue";
|
|
_fileOperation = "[data-testid='t--file-operation']";
|
|
_addMore = ".t--addApiHeader";
|
|
public _editorDS = ".t--datasource-editor";
|
|
public _addMoreHeaderFieldButton = ".t--addApiHeader";
|
|
public jsonBody = `.t--apiFormPostBody`;
|
|
private _entityName = ".t--entity-name";
|
|
private curlImport = ".t--datasoucre-create-option-new_curl_import";
|
|
private _curlTextArea =
|
|
"//label[text()='Paste CURL Code Here']/parent::form/div";
|
|
|
|
CreateApi(
|
|
apiName = "",
|
|
apiVerb: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" = "GET",
|
|
aftDSSaved = false,
|
|
) {
|
|
if (aftDSSaved) ObjectsRegistry.DataSources.CreateQueryAfterDSSaved();
|
|
else {
|
|
AppSidebar.navigate(AppSidebarButton.Editor);
|
|
this.agHelper.RemoveUIElement("EvaluatedPopUp");
|
|
PageLeftPane.switchSegment(PagePaneSegment.Queries);
|
|
this.agHelper.GetHoverNClick(this.locator._createNew);
|
|
this.agHelper.GetNClick(this._blankAPI, 0, true);
|
|
this.agHelper.RemoveUIElement(
|
|
"Tooltip",
|
|
Cypress.env("MESSAGES").ADD_QUERY_JS_TOOLTIP(),
|
|
);
|
|
}
|
|
this.assertHelper.AssertNetworkStatus("@createNewApi", 201);
|
|
|
|
// cy.get("@createNewApi").then((response: any) => {
|
|
// expect(response.response.body.responseMeta.success).to.eq(true);
|
|
// cy.get(this.agHelper._actionName)
|
|
// .click()
|
|
// .invoke("text")
|
|
// .then((text) => {
|
|
// const someText = text;
|
|
// expect(someText).to.equal(response.response.body.data.name);
|
|
// });
|
|
// }); // to check if Api1 = Api1 when Create Api invoked
|
|
|
|
if (apiName) {
|
|
this.agHelper.RenameWithInPane(apiName);
|
|
this.agHelper.GetNAssertContains(this._entityName, apiName);
|
|
}
|
|
this.agHelper.AssertElementVisibility(this._resourceUrl);
|
|
if (apiVerb != "GET") this.SelectAPIVerb(apiVerb);
|
|
}
|
|
|
|
CreateAndFillApi(
|
|
url: string,
|
|
apiName = "",
|
|
queryTimeout = 10000,
|
|
apiVerb: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" = "GET",
|
|
aftDSSaved = false,
|
|
toVerifySave = true,
|
|
) {
|
|
this.CreateApi(apiName, apiVerb, aftDSSaved);
|
|
this.EnterURL(url, "", toVerifySave);
|
|
this.assertHelper.AssertNetworkStatus("@saveAction", 200);
|
|
this.AssertRunButtonDisability();
|
|
if (queryTimeout != 10000) this.SetAPITimeout(queryTimeout, toVerifySave);
|
|
}
|
|
|
|
AssertRunButtonDisability(disabled = false) {
|
|
this.agHelper.AssertElementEnabledDisabled(this._apiRunBtn, 0, disabled);
|
|
}
|
|
|
|
EnterURL(url: string, evaluatedValue = "", toVerifySave = true) {
|
|
this.agHelper.EnterValue(
|
|
url,
|
|
{
|
|
propFieldName: this._resourceUrl,
|
|
directInput: true,
|
|
inputFieldName: "",
|
|
apiOrQuery: "api",
|
|
},
|
|
toVerifySave,
|
|
);
|
|
this.agHelper.Sleep(); //Is needed for the entered url value to be registered, else failing locally & CI
|
|
evaluatedValue && this.agHelper.VerifyEvaluatedValue(evaluatedValue);
|
|
}
|
|
|
|
EnterHeader(hKey: string, hValue: string, index = 0) {
|
|
this.SelectPaneTab("Headers");
|
|
this.agHelper.EnterValue(hKey, {
|
|
propFieldName: this._headerKey(index),
|
|
directInput: true,
|
|
inputFieldName: "",
|
|
});
|
|
this.agHelper.PressEscape();
|
|
this.agHelper.EnterValue(hValue, {
|
|
propFieldName: this._headerValue(index),
|
|
directInput: true,
|
|
inputFieldName: "",
|
|
});
|
|
this.agHelper.PressEscape();
|
|
this.agHelper.AssertAutoSave();
|
|
}
|
|
|
|
EnterParams(pKey: string, pValue: string, index = 0, escape = true) {
|
|
this.SelectPaneTab("Params");
|
|
this.agHelper.EnterValue(pKey, {
|
|
propFieldName: this._paramKey(index),
|
|
directInput: true,
|
|
inputFieldName: "",
|
|
});
|
|
this.agHelper.PressEscape();
|
|
this.agHelper.EnterValue(pValue, {
|
|
propFieldName: this._paramValue(index),
|
|
directInput: true,
|
|
inputFieldName: "",
|
|
});
|
|
if (escape) {
|
|
this.agHelper.PressEscape();
|
|
}
|
|
this.agHelper.AssertAutoSave();
|
|
}
|
|
|
|
EnterBodyFormData(
|
|
subTab: "FORM_URLENCODED" | "MULTIPART_FORM_DATA",
|
|
bKey: string,
|
|
bValue: string,
|
|
type = "",
|
|
toTrash = false,
|
|
) {
|
|
this.SelectPaneTab("Body");
|
|
this.SelectSubTab(subTab);
|
|
if (toTrash) {
|
|
cy.get(this._trashDelete).first().click();
|
|
cy.xpath(this._visibleTextSpan("Add more")).click();
|
|
}
|
|
this.agHelper.EnterValue(bKey, {
|
|
propFieldName: this._bodyKey(0),
|
|
directInput: true,
|
|
inputFieldName: "",
|
|
});
|
|
this.agHelper.PressEscape();
|
|
|
|
if (type) {
|
|
cy.xpath(this._bodyTypeDropdown).eq(0).click();
|
|
cy.xpath(this._visibleTextDiv(type)).click();
|
|
}
|
|
this.agHelper.EnterValue(bValue, {
|
|
propFieldName: this._bodyValue(0),
|
|
directInput: true,
|
|
inputFieldName: "",
|
|
});
|
|
this.agHelper.PressEscape();
|
|
this.agHelper.AssertAutoSave();
|
|
}
|
|
|
|
RunAPI(
|
|
toValidateResponse = true,
|
|
waitTimeInterval = 20,
|
|
validateNetworkAssertOptions?: { expectedPath: string; expectedRes: any },
|
|
) {
|
|
this.agHelper.GetNClick(this._apiRunBtn, 0, true, waitTimeInterval);
|
|
toValidateResponse &&
|
|
this.assertHelper.AssertNetworkExecutionSuccess("@postExecute");
|
|
|
|
// Asserting Network result
|
|
validateNetworkAssertOptions?.expectedPath &&
|
|
validateNetworkAssertOptions?.expectedRes &&
|
|
this.agHelper.AssertNetworkDataNestedProperty(
|
|
"@postExecute",
|
|
validateNetworkAssertOptions.expectedPath,
|
|
validateNetworkAssertOptions.expectedRes,
|
|
);
|
|
}
|
|
|
|
SetAPITimeout(timeout: number, toVerifySave = true) {
|
|
this.SelectPaneTab("Settings");
|
|
cy.xpath(this._queryTimeout).clear().type(timeout.toString(), { delay: 0 }); //Delay 0 to work like paste!
|
|
toVerifySave && this.agHelper.AssertAutoSave();
|
|
this.SelectPaneTab("Headers");
|
|
}
|
|
|
|
ToggleOnPageLoadRun(enable = true || false) {
|
|
this.SelectPaneTab("Settings");
|
|
if (enable) this.agHelper.CheckUncheck(this._onPageLoad, true);
|
|
else this.agHelper.CheckUncheck(this._onPageLoad, false);
|
|
}
|
|
|
|
ToggleConfirmBeforeRunning(enable = true || false) {
|
|
this.SelectPaneTab("Settings");
|
|
if (enable) this.agHelper.CheckUncheck(this._confirmBeforeRunning, true);
|
|
else this.agHelper.CheckUncheck(this._confirmBeforeRunning, false);
|
|
}
|
|
|
|
SelectPaneTab(
|
|
tabName:
|
|
| "Headers"
|
|
| "Params"
|
|
| "Body"
|
|
| "Pagination"
|
|
| "Authentication"
|
|
| "Settings"
|
|
| "Response"
|
|
| "Errors"
|
|
| "Logs"
|
|
| "Inspect entity"
|
|
| "Query",
|
|
) {
|
|
this.agHelper.PressEscape();
|
|
this.agHelper.GetNClick(this._visibleTextSpan(tabName), 0, true);
|
|
}
|
|
|
|
SelectSubTab(
|
|
subTabName:
|
|
| "NONE"
|
|
| "JSON"
|
|
| "FORM_URLENCODED"
|
|
| "MULTIPART_FORM_DATA"
|
|
| "BINARY"
|
|
| "RAW",
|
|
) {
|
|
this.agHelper.GetNClick(this._bodyTypeSelect);
|
|
this.agHelper.GetNClick(this._bodyTypeToSelect(subTabName));
|
|
}
|
|
|
|
AssertRightPaneSelectedTab(tabName: RightPaneTabs) {
|
|
cy.xpath(this._rightPaneTab(tabName)).should(
|
|
"have.attr",
|
|
"aria-selected",
|
|
"true",
|
|
);
|
|
}
|
|
|
|
SelectRightPaneTab(tabName: RightPaneTabs) {
|
|
this.agHelper.GetNClick(this._rightPaneTab(tabName));
|
|
}
|
|
|
|
ValidateQueryParams(param: { key: string; value: string }) {
|
|
this.SelectPaneTab("Params");
|
|
this.agHelper.ValidateCodeEditorContent(this._paramKey(0), param.key);
|
|
this.agHelper.ValidateCodeEditorContent(this._paramValue(0), param.value);
|
|
}
|
|
|
|
ValidateHeaderParams(header: { key: string; value: string }, index = 0) {
|
|
this.SelectPaneTab("Headers");
|
|
this.agHelper.ValidateCodeEditorContent(this._headerKey(index), header.key);
|
|
this.agHelper.ValidateCodeEditorContent(
|
|
this._headerValue(index),
|
|
header.value,
|
|
);
|
|
}
|
|
|
|
ValidateImportedHeaderParams(
|
|
isAutoGeneratedHeader = false,
|
|
header: { key: string; value: string },
|
|
index = 0,
|
|
) {
|
|
let keyValueName = "Header";
|
|
if (isAutoGeneratedHeader) {
|
|
keyValueName = "autoGeneratedHeader";
|
|
}
|
|
|
|
this.SelectPaneTab("Headers");
|
|
this.ValidateImportedKeyValueContent(
|
|
this._importedKey(index, keyValueName),
|
|
header.key,
|
|
);
|
|
this.ValidateImportedKeyValueContent(
|
|
this._importedValue(index, keyValueName),
|
|
header.value,
|
|
);
|
|
}
|
|
|
|
public ValidateImportedKeyValueContent(
|
|
selector: string,
|
|
contentToValidate: any,
|
|
) {
|
|
this.agHelper.GetNAssertElementText(
|
|
selector,
|
|
contentToValidate,
|
|
"have.text",
|
|
);
|
|
}
|
|
|
|
public ValidateImportedKeyValueOverride(index: number, isOverriden = true) {
|
|
let assertion = "";
|
|
|
|
if (isOverriden) {
|
|
assertion = "have.css";
|
|
} else {
|
|
assertion = "not.have.css";
|
|
}
|
|
cy.get(this._importedKey(index, "autoGeneratedHeader")).should(
|
|
assertion,
|
|
"text-decoration",
|
|
"line-through solid rgb(76, 86, 100)",
|
|
);
|
|
cy.get(this._importedValue(index, "autoGeneratedHeader")).should(
|
|
assertion,
|
|
"text-decoration",
|
|
"line-through solid rgb(76, 86, 100)",
|
|
);
|
|
}
|
|
|
|
ValidateImportedHeaderParamsAbsence(
|
|
isAutoGeneratedHeader = false,
|
|
index = 0,
|
|
) {
|
|
let keyValueName = "Header";
|
|
if (isAutoGeneratedHeader) {
|
|
keyValueName = "autoGeneratedHeader";
|
|
}
|
|
|
|
this.SelectPaneTab("Headers");
|
|
this.ValidateImportedKeyValueAbsence(
|
|
this._importedKey(index, keyValueName),
|
|
);
|
|
this.ValidateImportedKeyValueAbsence(
|
|
this._importedValue(index, keyValueName),
|
|
);
|
|
}
|
|
|
|
public ValidateImportedKeyValueAbsence(selector: string) {
|
|
this.agHelper.AssertElementAbsence(selector);
|
|
}
|
|
|
|
ReadApiResponsebyKey(key: string) {
|
|
let apiResp = "";
|
|
cy.get(this._responseBody)
|
|
.contains(key)
|
|
.siblings("span")
|
|
.invoke("text")
|
|
.then((text) => {
|
|
apiResp = `${text
|
|
.match(/"(.*)"/)?.[0]
|
|
.split('"')
|
|
.join("")} `;
|
|
cy.log("Key value in api response is :" + apiResp);
|
|
cy.wrap(apiResp).as("apiResp");
|
|
});
|
|
}
|
|
|
|
SwitchToResponseTab(tabIdentifier: string) {
|
|
cy.get(tabIdentifier).click();
|
|
}
|
|
|
|
public SelectAPIVerb(verb: "GET" | "POST" | "PUT" | "DELETE" | "PATCH") {
|
|
cy.get(this._apiVerbDropdown).click();
|
|
cy.xpath(this._verbToSelect(verb)).should("be.visible").click();
|
|
}
|
|
|
|
public AssertAPIVerb(verb: "GET" | "POST" | "PUT" | "DELETE" | "PATCH") {
|
|
this.agHelper.AssertText(this._apiVerbDropdown, "text", verb);
|
|
}
|
|
|
|
ResponseStatusCheck(statusCode: string) {
|
|
this.agHelper.AssertElementVisibility(this._responseStatus);
|
|
this.agHelper.GetNAssertContains(this._responseStatus, statusCode);
|
|
}
|
|
|
|
public SelectPaginationTypeViaIndex(index: number) {
|
|
cy.get(this._paginationTypeLabels).eq(index).click({ force: true });
|
|
}
|
|
|
|
CreateAndFillGraphqlApi(url: string, apiName = "", queryTimeout = 10000) {
|
|
this.CreateGraphqlApi(apiName);
|
|
this.EnterURL(url);
|
|
this.agHelper.AssertAutoSave();
|
|
this.AssertRunButtonDisability();
|
|
if (queryTimeout != 10000) this.SetAPITimeout(queryTimeout);
|
|
}
|
|
|
|
CreateGraphqlApi(apiName = "") {
|
|
AppSidebar.navigate(AppSidebarButton.Editor);
|
|
PageLeftPane.switchSegment(PagePaneSegment.Queries);
|
|
PageLeftPane.switchToAddNew();
|
|
this.agHelper.GetNClickByContains(".ads-v2-listitem", "GraphQL API");
|
|
this.assertHelper.AssertNetworkStatus("@createNewApi", 201);
|
|
|
|
if (apiName) this.agHelper.RenameWithInPane(apiName);
|
|
cy.get(this._resourceUrl).should("be.visible");
|
|
}
|
|
|
|
AssertEmptyHeaderKeyValuePairsPresent(index: number) {
|
|
this.agHelper.AssertElementVisibility(this._headerKey(index));
|
|
this.agHelper.AssertElementVisibility(this._headerValue(index));
|
|
}
|
|
|
|
DebugError() {
|
|
this.agHelper.GetNClick(this._responseTabHeader);
|
|
cy.get(this._headersTabContent).contains("Debug").click();
|
|
}
|
|
|
|
public FillCurlNImport(value: string) {
|
|
AppSidebar.navigate(AppSidebarButton.Editor);
|
|
PageLeftPane.switchSegment(PagePaneSegment.Queries);
|
|
PageLeftPane.switchToAddNew();
|
|
this.agHelper.GetNClick(this.curlImport);
|
|
this.ImportCurlNRun(value);
|
|
}
|
|
|
|
public ImportCurlNRun(value: string) {
|
|
this.agHelper.UpdateTextArea(this._curlTextArea, value);
|
|
this.agHelper.Sleep(500); //Clicking import after value settled
|
|
cy.get(ApiEditor.curlImportBtn).click({ force: true });
|
|
cy.wait("@curlImport").should(
|
|
"have.nested.property",
|
|
"response.body.responseMeta.status",
|
|
201,
|
|
);
|
|
this.RunAPI();
|
|
}
|
|
}
|