Merge branch 'release' of github.com:appsmithorg/appsmith into release
This commit is contained in:
commit
8c5bd36350
|
|
@ -10,14 +10,14 @@
|
|||
</p>
|
||||
<p>
|
||||
|
||||
[](https://github.com/appsmithorg/appsmith/releases/latest)
|
||||
[](https://github.com/appsmithorg/appsmith/releases/latest)
|
||||
[](https://appsmith.com)
|
||||
[](https://discord.gg/rBTTVJp)
|
||||
[](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)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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']"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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 &&
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user