diff --git a/app/client/cypress/fixtures/datasources.json b/app/client/cypress/fixtures/datasources.json index c7064b4d41..25e2bf48d6 100644 --- a/app/client/cypress/fixtures/datasources.json +++ b/app/client/cypress/fixtures/datasources.json @@ -34,10 +34,19 @@ "redshift-databaseName": "fakeapi", "redshift-username": "root", "redshift-password": "Redshift$123", + "smtp-host":"host.docker.internal", "smtp-port":"25", "smtp-username":"root", "smtp-password":"root", + + "OAuth_Username":"testuser@appsmith.com", + "OAuth_Host":"http://localhost:6001", + "OAuth_ApiUrl": "http://host.docker.internal:6001", + "OAUth_AccessTokenUrl": "http://host.docker.internal:6001/oauth/token", + "OAuth_AuthUrl": "http://localhost:6001/oauth/authorize", + "OAuth_RedirectUrl":"http://localhost/api/v1/datasources/authorize", + "restapi-url": "https://my-json-server.typicode.com/typicode/demo/posts", "connection-type": "Replica set", "database-url": "appsmith-f9fe4.firebaseio.com", diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Onboarding/CreateNewApp_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Onboarding/CreateNewApp_spec.js index ec71c8ba19..8b6192c1a8 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Onboarding/CreateNewApp_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Onboarding/CreateNewApp_spec.js @@ -2,18 +2,17 @@ const explorerLocators = require("../../../../locators/explorerlocators.json"); const guidedTourLocators = require("../../../../locators/GuidedTour.json"); const commonlocators = require("../../../../locators/commonlocators.json"); const homePage = require("../../../../locators/HomePage"); -import { ObjectsRegistry } from "../../../../support/Objects/Registry"; -let datasources = ObjectsRegistry.DataSources; +import * as _ from "../../../../support/Objects/ObjectsCore"; describe("Creating new app after discontinuing guided tour should not start the same", function() { it("1. Creating new app after discontinuing guided tour should not start the same", function() { // Start guided tour - cy.get(commonlocators.homeIcon).click({ force: true }); - datasources.CloseReconnectDataSourceModal(); // Check if reconnect data source modal is visible and close it + _.homePage.NavigateToHome(); + _.dataSources.CloseReconnectDataSourceModal(); // Check if reconnect data source modal is visible and close it cy.get(guidedTourLocators.welcomeTour) .click() .wait(2000); - datasources.CloseReconnectDataSourceModal(); // Check if reconnect data source modal is visible and close it + _.dataSources.CloseReconnectDataSourceModal(); // Check if reconnect data source modal is visible and close it cy.get("body").then(($ele) => { if ($ele.find(guidedTourLocators.welcomeTour).length) { cy.get(guidedTourLocators.welcomeTour) @@ -21,7 +20,7 @@ describe("Creating new app after discontinuing guided tour should not start the .wait(2000); } }); - datasources.CloseReconnectDataSourceModal(); // Check if reconnect data source modal is visible and close it + _.dataSources.CloseReconnectDataSourceModal(); // Check if reconnect data source modal is visible and close it cy.get("body").then(($ele) => { if ($ele.find(guidedTourLocators.startBuilding).length == 0) { diff --git a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/Datasources/AuthenticatedApiWithOAuth_spec.ts b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/Datasources/AuthenticatedApiWithOAuth_spec.ts new file mode 100644 index 0000000000..e6ac5593b6 --- /dev/null +++ b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/Datasources/AuthenticatedApiWithOAuth_spec.ts @@ -0,0 +1,36 @@ +import * as _ from "../../../../support/Objects/ObjectsCore"; +//import * as _ from "@ObjectsCore" + +describe("Authentiacted Api with OAuth 2.O authorization code test cases", function() { + it("1. Create & Save an Authenticated API with OAuth 2.O authorization code", function() { + // Create OAuth client + cy.fixture("datasources").then((datasourceFormData: any) => { + _.dataSources.CreateOAuthClient("authorization_code"); + // Create datasource + _.agHelper.GenerateUUID(); + cy.get("@guid").then((uid) => { + cy.get("@OAuthClientID").then((clientId: any) => { + cy.get("@OAuthClientSecret").then((clientSecret: any) => { + _.dataSources.CreateOAuthDatasource( + "TED_OAuth_Api_" + uid, + "AuthCode", + clientId, + clientSecret, + ); + //Create API from datasource + _.apiPage.CreateAndFillApi( + datasourceFormData["OAuth_ApiUrl"] + "/api/echo/get?ASDSA=ASDSA", + "EchoAPI_" + uid, + 10000, + "GET", + true, + ); + }); + }); + }); + }); + //Run API & Validate Response + _.apiPage.RunAPI(); + _.apiPage.ResponseStatusCheck("200"); + }); +}); diff --git a/app/client/cypress/support/Pages/ApiPage.ts b/app/client/cypress/support/Pages/ApiPage.ts index 845735099a..027a50152c 100644 --- a/app/client/cypress/support/Pages/ApiPage.ts +++ b/app/client/cypress/support/Pages/ApiPage.ts @@ -7,7 +7,7 @@ export class ApiPage { public locator = ObjectsRegistry.CommonLocators; private _createapi = ".t--createBlankApiCard"; - private _resourceUrl = ".t--dataSourceField"; + _resourceUrl = ".t--dataSourceField"; private _headerKey = (index: number) => ".t--actionConfiguration\\.headers\\[0\\]\\.key\\." + index + ""; private _headerValue = (index: number) => @@ -26,7 +26,7 @@ export class ApiPage { ".t--actionConfiguration\\.bodyFormData\\[0\\]\\.value\\." + index + ""; _bodyTypeDropdown = "//span[text()='Type'][@class='bp3-button-text']/parent::button"; - private _apiRunBtn = ".t--apiFormRunBtn"; + _apiRunBtn = ".t--apiFormRunBtn"; private _queryTimeout = "//input[@name='actionConfiguration.timeoutInMillisecond']"; _responseBody = ".CodeMirror-code span.cm-string.cm-property"; @@ -57,13 +57,18 @@ export class ApiPage { public _responseTabHeader = "[data-cy=t--tab-headers]"; public _autoGeneratedHeaderInfoIcon = (key: string) => `.t--auto-generated-${key}-info`; + private _createQuery = ".t--create-query"; CreateApi( apiName = "", apiVerb: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" = "GET", + aftDSSaved = false, ) { - cy.get(this.locator._createNew).click({ force: true }); - cy.get(this._blankAPI).click({ force: true }); + if (aftDSSaved) this.agHelper.GetNClick(this._createQuery); + else { + cy.get(this.locator._createNew).click({ force: true }); + cy.get(this._blankAPI).click({ force: true }); + } this.agHelper.ValidateNetworkStatus("@createNewApi", 201); // cy.get("@createNewApi").then((response: any) => { @@ -87,10 +92,10 @@ export class ApiPage { apiName = "", queryTimeout = 10000, apiVerb: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" = "GET", + aftDSSaved = false, ) { - this.CreateApi(apiName, apiVerb); + this.CreateApi(apiName, apiVerb, aftDSSaved); this.EnterURL(url); - this.agHelper.AssertAutoSave(); //this.agHelper.Sleep(2000);// Added because api name edit takes some time to reflect in api sidebar after the call passes. cy.get(this._apiRunBtn).should("not.be.disabled"); if (queryTimeout != 10000) this.SetAPITimeout(queryTimeout); @@ -370,7 +375,7 @@ export class ApiPage { ResponseStatusCheck(statusCode: string) { this.agHelper.AssertElementVisible(this._responseStatus); - this.agHelper.GetNAssertContains(this._responseStatus, statusCode) + this.agHelper.GetNAssertContains(this._responseStatus, statusCode); } public SelectPaginationTypeViaIndex(index: number) { cy.get(this._paginationTypeLabels) diff --git a/app/client/cypress/support/Pages/DataSources.ts b/app/client/cypress/support/Pages/DataSources.ts index 148f20ba2e..5107ac35d9 100644 --- a/app/client/cypress/support/Pages/DataSources.ts +++ b/app/client/cypress/support/Pages/DataSources.ts @@ -123,17 +123,22 @@ export class DataSources { public _urlInputControl = "input[name='url']"; // Authenticated API locators + private _authApiDatasource = ".t--createAuthApiDatasource"; private _authType = "[data-cy=authType]"; private _oauth2 = ".t--dropdown-option:contains('OAuth 2.0')"; private _accessTokenUrl = "[data-cy='authentication.accessTokenUrl'] input"; + private _scope = "[data-cy='authentication.scopeString'] input"; private _clientID = "[data-cy='authentication.clientId'] input"; private _clientSecret = "[data-cy='authentication.clientSecret'] input"; + private _clientCredentails = + ".t--dropdown-option:contains('Client Credentials')"; private _authorizationCode = ".t--dropdown-option:contains('Authorization Code')"; private _grantType = "[data-cy='authentication.grantType']"; private _authorizationURL = "[data-cy='authentication.authorizationUrl'] input"; - + private _consent = '[name="confirm"]'; + private _consentSubmit = "//button[text()='Submit']"; public _datasourceModalSave = ".t--datasource-modal-save"; public _datasourceModalDoNotSave = ".t--datasource-modal-do-not-save"; public _deleteDatasourceButton = ".t--delete-datasource"; @@ -481,7 +486,7 @@ export class DataSources { } public NavigateToActiveTab() { - this.agHelper.GetElement(this.locator._body).then(($body) => { + this.agHelper.GetElement(this.locator._body).then(($body) => { if ($body.find(this._selectedActiveTab).length == 0) { this.NavigateToDSCreateNew(); this.agHelper.GetNClick(this._activeTab, 0, true); @@ -900,4 +905,111 @@ export class DataSources { uri, ); } + + public CreateOAuthClient(grantType: string) { + let clientId, clientSecret; + + // Login to TED OAuth + let formData = new FormData(); + formData.append("username", datasourceFormData["OAuth_Username"]); + cy.request("POST", datasourceFormData["OAuth_Host"], formData).then( + (response) => { + expect(response.status).to.equal(200); + }, + ); + + // Create client + let clientData = new FormData(); + clientData.append("client_name", "appsmith_cs_post"); + clientData.append("client_uri", "http://localhost/"); + clientData.append("scope", "profile"); + clientData.append("redirect_uri", datasourceFormData["OAuth_RedirectUrl"]); + clientData.append("grant_type", grantType); + clientData.append("response_type", "code"); + clientData.append("token_endpoint_auth_method", "client_secret_post"); + cy.request( + "POST", + datasourceFormData["OAuth_Host"] + "/create_client", + clientData, + ).then((response) => { + expect(response.status).to.equal(200); + }); + + // Get Client Credentials + cy.request("GET", datasourceFormData["OAuth_Host"]).then((response) => { + clientId = response.body.split("client_id: "); + clientId = clientId[1].split("client_secret: "); + clientSecret = clientId[1].split(""); + clientSecret = clientSecret[0].trim(); + clientId = clientId[0].trim(); + cy.wrap(clientId).as("OAuthClientID"); + cy.wrap(clientSecret).as("OAuthClientSecret"); + }); + } + + public CreateOAuthDatasource( + datasourceName: string, + grantType: "ClientCredentials" | "AuthCode", + clientId: string, + clientSecret: string, + ) { + this.NavigateToDSCreateNew(); + //Click on Authenticated API + cy.get(this._authApiDatasource).click({ force: true }); + this.FillAPIOAuthForm(datasourceName, grantType, clientId, clientSecret); + + // save datasource + this.agHelper.Sleep(500); + this.agHelper.GetNClick(this._saveAndAuthorizeDS); + + //Accept consent + this.agHelper.GetNClick(this._consent); + this.agHelper.GetNClick(this._consentSubmit); + + //Validate save + this.agHelper.ValidateNetworkStatus("@saveDatasource", 201); + } + + public FillAPIOAuthForm( + dsName: string, + grantType: "ClientCredentials" | "AuthCode", + clientId: string, + clientSecret: string, + ) { + this.agHelper.RenameWithInPane(dsName, false); + // Fill Auth Form + this.agHelper.UpdateInput( + this.locator._inputFieldByName("URL"), + datasourceFormData["OAuth_ApiUrl"], + ); + this.agHelper.GetNClick(this._authType); + this.agHelper.GetNClick(this._oauth2); + this.agHelper.GetNClick(this._grantType); + if (grantType == "ClientCredentials") + this.agHelper.GetNClick(this._clientCredentails); + else if (grantType == "AuthCode") + this.agHelper.GetNClick(this._authorizationCode); + + this.agHelper.UpdateInput( + this.locator._inputFieldByName("Access Token URL"), + datasourceFormData["OAUth_AccessTokenUrl"], + ); + + this.agHelper.UpdateInput( + this.locator._inputFieldByName("Client ID"), + clientId, + ); + this.agHelper.UpdateInput( + this.locator._inputFieldByName("Client Secret"), + clientSecret, + ); + this.agHelper.UpdateInput( + this.locator._inputFieldByName("Scope(s)"), + "profile", + ); + this.agHelper.UpdateInput( + this.locator._inputFieldByName("Authorization URL"), + datasourceFormData["OAuth_AuthUrl"], + ); + } }