Merge branch 'release' of github.com:appsmithorg/appsmith into release

This commit is contained in:
Arpit Mohan 2020-07-29 13:04:55 +05:30
commit 8c5bd36350
10 changed files with 95 additions and 38 deletions

View File

@ -10,14 +10,14 @@
</p>
<p>
[![GitHub release](https://img.shields.io/github/release/getappsmith/appsmith/all?logo=GitHub)](https://github.com/appsmithorg/appsmith/releases/latest)
[![GitHub release](https://img.shields.io/github/v/release/appsmithorg/appsmith.svg?logo=GitHub)](https://github.com/appsmithorg/appsmith/releases/latest)
[![Website](https://img.shields.io/website?url=https%3A%2F%2Fappsmith.com&logo=Appsmith)](https://appsmith.com)
[![Chat on Discord](https://img.shields.io/badge/chat-Discord-violet?logo=discord)](https://discord.gg/rBTTVJp)
[![Docs](https://img.shields.io/badge/docs-v1.x-brightgreen.svg?style=flat)](https://docs.appsmith.com)
</p>
</p>
<p>
<sub>Built with ❤︎ & empathy</sub>
<sub>Built with empathy, not just ❤︎ </sub>
</p>
</div>
@ -37,7 +37,7 @@ Appsmith provides a better way of building internal tools by visualising them as
## Features
* **Build custom UI**: Drag & drop, resize and style widgets **without HTLM / CSS**. [Read more](https://docs.appsmith.com/core-concepts/building-the-ui)
* **Build custom UI**: Drag & drop, resize and style widgets **without HTML / CSS**. [Read more](https://docs.appsmith.com/core-concepts/building-the-ui)
* **Query data**: Query & update your database directly from the UI. Supports **postgres, mongo, REST & GraphQL APIs**. [Read more](https://docs.appsmith.com/core-concepts/building-the-ui/displaying-api-data)
* **JS Logic**: Write snippets of business logic using JS to transform data, manipuate UI or trigger workflows
* **Data Workflows**: Simple configuration to create flows when users interact with the UI. [Read more](https://docs.appsmith.com/core-concepts/building-the-ui/calling-apis-from-widgets)

View File

@ -37,5 +37,9 @@
"prevUrl": ".data.previous}}",
"methodsWithParam": "users?page=2",
"invalidHeader": "invalid",
"invalidValue": "invalid"
}
"invalidValue": "invalid",
"Put": "PUT",
"Get": "GET",
"next": "?page=2&pageSize=10",
"prev": "?page=1&pageSize=10"
}

View File

@ -30,9 +30,10 @@ describe("API Panel Test Functionality", function() {
.click({ force: true })
.focus()
.type(json, { force: true });
cy.WaitAutoSave();
cy.RunAPI();
cy.validateRequest(testdata.baseUrl2, testdata.methodput, testdata.Put);
});
cy.WaitAutoSave();
cy.RunAPI();
cy.ResponseStatusCheck("200 OK");
cy.log("Response code check successful");
cy.ResponseCheck("updatedAt");
@ -55,9 +56,10 @@ describe("API Panel Test Functionality", function() {
.click({ force: true })
.focus()
.type(json, { force: true });
cy.WaitAutoSave();
cy.RunAPI();
cy.validateRequest(testdata.baseUrl2, testdata.methodpost, testdata.Post);
});
cy.WaitAutoSave();
cy.RunAPI();
cy.ResponseStatusCheck("201 CREATED");
cy.log("Response code check successful");
cy.ResponseCheck("createdAt");
@ -80,9 +82,14 @@ describe("API Panel Test Functionality", function() {
.click({ force: true })
.focus()
.type(json, { force: true });
cy.WaitAutoSave();
cy.RunAPI();
cy.validateRequest(
testdata.baseUrl2,
testdata.methodpatch,
testdata.Patch,
);
});
cy.WaitAutoSave();
cy.RunAPI();
cy.ResponseStatusCheck("200 OK");
cy.log("Response code check successful");
cy.ResponseCheck("updatedAt");
@ -101,6 +108,11 @@ describe("API Panel Test Functionality", function() {
);
cy.WaitAutoSave();
cy.RunAPI();
cy.validateRequest(
testdata.baseUrl2,
testdata.methodpatch,
testdata.Delete,
);
cy.ResponseStatusCheck("204 NO_CONTENT");
cy.log("Response code check successful");
});
@ -112,6 +124,7 @@ describe("API Panel Test Functionality", function() {
cy.enterDatasourceAndPath(testdata.baseUrl, testdata.methods);
cy.WaitAutoSave();
cy.RunAPI();
cy.validateRequest(testdata.baseUrl, testdata.methods, testdata.Get);
cy.ResponseStatusCheck(testdata.successStatusCode);
cy.log("Response code check successful");
cy.ResponseCheck(testdata.responsetext);
@ -120,12 +133,22 @@ describe("API Panel Test Functionality", function() {
cy.selectPaginationType(apiwidget.paginationWithUrl);
cy.enterUrl(apiname, apiwidget.panigationNextUrl, testdata.nextUrl);
cy.clickTest(apiwidget.TestNextUrl);
cy.validateRequest(
testdata.baseUrl,
testdata.methods.concat(testdata.next),
testdata.Get,
);
cy.ResponseStatusCheck(testdata.successStatusCode);
cy.log("Response code check successful");
cy.ResponseCheck("Josh M Krantz");
cy.log("Response data check successful");
cy.enterUrl(apiname, apiwidget.panigationPrevUrl, testdata.prevUrl);
cy.clickTest(apiwidget.TestPreUrl);
cy.validateRequest(
testdata.baseUrl,
testdata.methods.concat(testdata.prev),
testdata.Get,
);
cy.ResponseStatusCheck(testdata.successStatusCode);
cy.log("Response code check successful");
cy.ResponseCheck(testdata.responsetext);
@ -138,6 +161,7 @@ describe("API Panel Test Functionality", function() {
cy.enterDatasourceAndPath(testdata.baseUrl, testdata.queryAndValue);
cy.WaitAutoSave();
cy.RunAPI();
cy.validateRequest(testdata.baseUrl, testdata.queryAndValue, testdata.Get);
cy.ResponseStatusCheck("200 OK");
cy.log("Response code check successful");
cy.ResponseCheck(testdata.responsetext3);
@ -157,6 +181,7 @@ describe("API Panel Test Functionality", function() {
);
cy.WaitAutoSave();
cy.RunAPI();
cy.validateRequest(testdata.baseUrl, testdata.methods, testdata.Get);
cy.ResponseStatusCheck("5000");
cy.log("Response code check successful");
cy.ResponseCheck("Invalid value for Content-Type");

View File

@ -9,8 +9,8 @@ describe("API Panel Test Functionality", function() {
cy.CreateAPI("FirstAPI");
cy.log("Creation of FirstAPI Action successful");
cy.enterDatasourceAndPath(testdata.baseUrl, testdata.methods);
cy.WaitAutoSave();
cy.RunAPI();
cy.SaveAndRunAPI();
cy.validateRequest(testdata.baseUrl, testdata.methods, testdata.Get);
cy.ResponseStatusCheck(testdata.successStatusCode);
cy.get(apiwidget.createApiOnSideBar)
.first()

View File

@ -34,5 +34,12 @@
"TestNextUrl": ".t--apiFormPaginationNextTest",
"TestPreUrl": ".t--apiFormPaginationPrevTest",
"EditApiName": "img[alt='Edit pen']",
"ApiName": ".t--action-name-edit-field span"
"ApiName": ".t--action-name-edit-field span",
"Request": "//li[text()='Request']",
"RequestURL": "(//span[@class='bp3-tree-node-label']/span)[1]",
"RequestMethod": "(//span[@class='bp3-tree-node-label']/span)[2]",
"content-Type": "(//span[@class='bp3-tree-node-label']/span)[3]",
"requestBody": "(//div[contains(@class,'bp3-collapse-body')]//textarea)[1]",
"showrequest": "span:contains('Show Request')",
"Responsetab": "//li[text()='Response Body']"
}

View File

@ -377,6 +377,17 @@ Cypress.Commands.add("SaveAndRunAPI", () => {
cy.RunAPI();
});
Cypress.Commands.add("validateRequest", (baseurl, path, verb) => {
cy.xpath(apiwidget.Request)
.should("be.visible")
.click({ force: true });
cy.xpath(apiwidget.RequestURL).contains(baseurl.concat(path));
cy.xpath(apiwidget.RequestMethod).contains(verb);
cy.xpath(apiwidget.Responsetab)
.should("be.visible")
.click({ force: true });
});
Cypress.Commands.add("SelectAction", action => {
cy.get(ApiEditor.ApiVerb)
.first()
@ -471,9 +482,12 @@ Cypress.Commands.add("selectPaginationType", option => {
});
Cypress.Commands.add("clickTest", testbutton => {
cy.wait(2000);
cy.wait("@saveAction");
cy.get(testbutton)
.first()
.click({ force: true });
cy.wait("@postExecute");
});
Cypress.Commands.add("enterUrl", (apiname, url, value) => {

View File

@ -34,14 +34,14 @@ class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
label: VALIDATION_TYPES.TEXT,
selectedRowIndex: VALIDATION_TYPES.NUMBER,
searchText: VALIDATION_TYPES.TEXT,
// columnActions: VALIDATION_TYPES.ARRAY_ACTION_SELECTOR,
// onRowSelected: VALIDATION_TYPES.ACTION_SELECTOR,
// onPageChange: VALIDATION_TYPES.ACTION_SELECTOR,
filteredTableData: VALIDATION_TYPES.TABLE_DATA,
};
}
static getDerivedPropertiesMap() {
return {
selectedRow: "{{this.tableData[this.selectedRowIndex]}}",
filteredTableData:
"{{!this.onSearchTextChanged ? this.tableData.filter((item) => Object.values(item).join(', ').toUpperCase().includes(this.searchText.toUpperCase())) : this.tableData}}",
selectedRow: "{{this.filteredTableData[this.selectedRowIndex]}}",
};
}
@ -51,6 +51,8 @@ class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
pageSize: undefined,
selectedRowIndex: -1,
searchText: "",
// The following meta property is used for rendering the table.
filteredTableData: [],
};
}
@ -198,26 +200,10 @@ class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
return updatedTableData;
};
searchTableData = (tableData: object[]) => {
if (!tableData || !tableData.length) {
return [];
}
const searchKey =
this.props.searchText !== undefined
? this.props.searchText.toString().toUpperCase()
: "";
return tableData.filter((item: object) => {
return Object.values(item)
.join(", ")
.toUpperCase()
.includes(searchKey);
});
};
getPageView() {
const { tableData, hiddenColumns } = this.props;
const { tableData, hiddenColumns, filteredTableData } = this.props;
const tableColumns = this.getTableColumns(tableData);
const filteredTableData = this.searchTableData(tableData);
// Use the filtered data to render the table.
const transformedData = this.transformData(filteredTableData, tableColumns);
const serverSidePaginationEnabled = (this.props
.serverSidePaginationEnabled &&

View File

@ -146,7 +146,7 @@ public class DatasourceContextServiceImpl implements DatasourceContextService {
@Override
public AuthenticationDTO decryptSensitiveFields(AuthenticationDTO authenticationDTO) {
if (authenticationDTO.getPassword() != null) {
if (authenticationDTO != null && authenticationDTO.getPassword() != null) {
authenticationDTO.setPassword(encryptionService.decryptString(authenticationDTO.getPassword()));
}
return authenticationDTO;

View File

@ -18,6 +18,7 @@ import com.appsmith.server.repositories.OrganizationRepository;
import com.appsmith.server.repositories.PageRepository;
import com.appsmith.server.services.ActionService;
import com.appsmith.server.services.ApplicationPageService;
import com.appsmith.server.services.DatasourceContextService;
import com.appsmith.server.services.DatasourceService;
import com.appsmith.server.services.OrganizationService;
import com.appsmith.server.services.SessionUserService;
@ -51,6 +52,7 @@ public class ExamplesOrganizationCloner {
private final SessionUserService sessionUserService;
private final UserService userService;
private final ApplicationPageService applicationPageService;
private final DatasourceContextService datasourceContextService;
public Mono<Organization> cloneExamplesOrganization() {
return sessionUserService
@ -230,6 +232,9 @@ public class ExamplesOrganizationCloner {
makePristine(datasource);
datasource.setOrganizationId(toOrganizationId);
datasource.setName(datasource.getName());
if (datasource.getDatasourceConfiguration() != null) {
datasourceContextService.decryptSensitiveFields(datasource.getDatasourceConfiguration().getAuthentication());
}
return Mono.zip(
Mono.just(templateDatasourceId),
datasourceService.create(datasource)

View File

@ -1,5 +1,6 @@
package com.appsmith.server.solutions;
import com.appsmith.external.models.AuthenticationDTO;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.Property;
import com.appsmith.server.constants.FieldName;
@ -17,6 +18,7 @@ import com.appsmith.server.services.ActionService;
import com.appsmith.server.services.ApplicationPageService;
import com.appsmith.server.services.ApplicationService;
import com.appsmith.server.services.DatasourceService;
import com.appsmith.server.services.EncryptionService;
import com.appsmith.server.services.OrganizationService;
import com.appsmith.server.services.PageService;
import com.appsmith.server.services.SessionUserService;
@ -88,6 +90,9 @@ public class ExamplesOrganizationClonerTests {
@Autowired
private PluginRepository pluginRepository;
@Autowired
private EncryptionService encryptionService;
@MockBean
private PluginExecutorHelper pluginExecutorHelper;
@ -324,6 +329,9 @@ public class ExamplesOrganizationClonerTests {
final Datasource ds2 = new Datasource();
ds2.setName("datasource 2");
ds2.setOrganizationId(organization.getId());
ds2.setDatasourceConfiguration(new DatasourceConfiguration());
ds2.getDatasourceConfiguration().setAuthentication(new AuthenticationDTO());
ds2.getDatasourceConfiguration().getAuthentication().setPassword("answer-to-life");
return Mono.when(
datasourceService.create(ds1),
@ -354,6 +362,14 @@ public class ExamplesOrganizationClonerTests {
new Property("X-Answer", "42")
);
final Datasource ds2 = data.datasources.stream()
.filter(datasource -> "datasource 2".equals(datasource.getName()))
.findFirst()
.orElseThrow();
assertThat(ds2.getDatasourceConfiguration().getAuthentication()).isNotNull();
assertThat(ds2.getDatasourceConfiguration().getAuthentication().getPassword())
.isEqualTo(encryptionService.encryptString("answer-to-life"));
assertThat(data.applications).isEmpty();
assertThat(data.actions).isEmpty();
})