Merge branch 'release' into feature/acl-spring-object
# Conflicts: # appsmith-server/src/main/java/com/appsmith/server/services/MarketplaceService.java
This commit is contained in:
commit
a8f32d8b2a
|
|
@ -1,6 +1,5 @@
|
|||
package com.appsmith.external.models;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
|
@ -28,7 +27,6 @@ public class Provider extends BaseDomain {
|
|||
|
||||
String documentationUrl; //URL which points to the homepage of the documentations here
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
|
||||
String credentialSteps; //How to generate/get the credentials to run the APIs which belong to this provider
|
||||
|
||||
List<String> categories; //Category names here
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import com.appsmith.external.plugins.BasePlugin;
|
|||
import com.appsmith.external.plugins.PluginExecutor;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.MongoClientURI;
|
||||
import com.mongodb.MongoTimeoutException;
|
||||
import com.mongodb.client.ClientSession;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bson.Document;
|
||||
|
|
@ -26,6 +28,8 @@ import org.springframework.util.StringUtils;
|
|||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
|
@ -153,7 +157,14 @@ public class MongoPlugin extends BasePlugin {
|
|||
public static MongoClientURI buildClientURI(DatasourceConfiguration datasourceConfiguration) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
boolean isSrv = Connection.Type.REPLICA_SET.equals(datasourceConfiguration.getConnection().getType());
|
||||
final Connection connection = datasourceConfiguration.getConnection();
|
||||
final List<Endpoint> endpoints = datasourceConfiguration.getEndpoints();
|
||||
|
||||
// Use SRV mode if using REPLICA_SET, AND a port is not specified in the first endpoint. In SRV mode, the
|
||||
// host and port details of individual shards will be obtained from the TXT records of the first endpoint.
|
||||
boolean isSrv = Connection.Type.REPLICA_SET.equals(connection.getType())
|
||||
&& endpoints.get(0).getPort() == null;
|
||||
|
||||
if (isSrv) {
|
||||
builder.append("mongodb+srv://");
|
||||
} else {
|
||||
|
|
@ -163,13 +174,13 @@ public class MongoPlugin extends BasePlugin {
|
|||
AuthenticationDTO authentication = datasourceConfiguration.getAuthentication();
|
||||
if (authentication != null) {
|
||||
builder
|
||||
.append(authentication.getUsername())
|
||||
.append(urlEncode(authentication.getUsername()))
|
||||
.append(':')
|
||||
.append(authentication.getPassword())
|
||||
.append(urlEncode(authentication.getPassword()))
|
||||
.append('@');
|
||||
}
|
||||
|
||||
for (Endpoint endpoint : datasourceConfiguration.getEndpoints()) {
|
||||
for (Endpoint endpoint : endpoints) {
|
||||
builder.append(endpoint.getHost());
|
||||
if (endpoint.getPort() != null) {
|
||||
builder.append(':').append(endpoint.getPort());
|
||||
|
|
@ -183,11 +194,22 @@ public class MongoPlugin extends BasePlugin {
|
|||
// Delete the trailing comma.
|
||||
builder.deleteCharAt(builder.length() - 1);
|
||||
|
||||
boolean addedFinalSlash = false;
|
||||
if (authentication != null) {
|
||||
builder.append('/').append(authentication.getDatabaseName());
|
||||
addedFinalSlash = true;
|
||||
}
|
||||
|
||||
return new MongoClientURI(builder.toString());
|
||||
if (connection.getSsl() != null) {
|
||||
if (!addedFinalSlash) {
|
||||
builder.append('/');
|
||||
}
|
||||
builder.append("?ssl=true");
|
||||
}
|
||||
|
||||
final String uri = builder.toString();
|
||||
log.info("MongoPlugin URI: `{}`.", uri);
|
||||
return new MongoClientURI(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -207,13 +229,9 @@ public class MongoPlugin extends BasePlugin {
|
|||
invalids.add("Missing endpoint(s).");
|
||||
|
||||
} else if (Connection.Type.REPLICA_SET.equals(datasourceConfiguration.getConnection().getType())) {
|
||||
if (endpoints.size() > 1) {
|
||||
invalids.add("Direct connections cannot be used with multiple endpoints." +
|
||||
" Please provide a single endpoint.");
|
||||
}
|
||||
|
||||
if (endpoints.get(0).getPort() != null) {
|
||||
invalids.add("Port should not be set for REPLICA_SET connections.");
|
||||
if (endpoints.size() == 1 && endpoints.get(0).getPort() != null) {
|
||||
invalids.add("REPLICA_SET connections should not be given a port." +
|
||||
" If you are trying to specify all the shards, please add more than one.");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -253,12 +271,28 @@ public class MongoPlugin extends BasePlugin {
|
|||
public Mono<DatasourceTestResult> testDatasource(DatasourceConfiguration datasourceConfiguration) {
|
||||
return datasourceCreate(datasourceConfiguration)
|
||||
.map(mongoClient -> {
|
||||
ClientSession clientSession = null;
|
||||
|
||||
try {
|
||||
if (mongoClient != null) {
|
||||
// Not using try-with-resources here since we want to close the *session* before closing the
|
||||
// MongoClient instance.
|
||||
clientSession = ((MongoClient) mongoClient).startSession();
|
||||
|
||||
} catch (MongoTimeoutException e) {
|
||||
log.warn("Timeout connecting to MongoDB from MongoPlugin.", e);
|
||||
return new DatasourceTestResult("Timed out trying to connect to MongoDB host.");
|
||||
|
||||
} catch (Exception e) {
|
||||
return new DatasourceTestResult(e.getMessage());
|
||||
|
||||
} finally {
|
||||
if (clientSession != null) {
|
||||
clientSession.close();
|
||||
}
|
||||
if (mongoClient instanceof MongoClient) {
|
||||
((MongoClient) mongoClient).close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Error closing MongoDB connection that was made for testing.", e);
|
||||
|
||||
}
|
||||
|
||||
return new DatasourceTestResult();
|
||||
|
|
@ -266,6 +300,10 @@ public class MongoPlugin extends BasePlugin {
|
|||
.onErrorResume(error -> Mono.just(new DatasourceTestResult(error.getMessage())));
|
||||
}
|
||||
|
||||
private static String urlEncode(String text) {
|
||||
return URLEncoder.encode(text, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,193 @@
|
|||
{
|
||||
"form": [
|
||||
{
|
||||
"sectionName": "General",
|
||||
"children": [
|
||||
{
|
||||
"label": "Connection Mode",
|
||||
"configProperty": "datasourceConfiguration.connection.mode",
|
||||
"controlType": "DROP_DOWN",
|
||||
"options": [
|
||||
{
|
||||
"label": "Read Only",
|
||||
"value": "READ_ONLY"
|
||||
},
|
||||
{
|
||||
"label": "Read / Write",
|
||||
"value": "READ_WRITE"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Connection Type",
|
||||
"configProperty": "datasourceConfiguration.connection.type",
|
||||
"controlType": "DROP_DOWN",
|
||||
"options": [
|
||||
{
|
||||
"label": "Direct Connection",
|
||||
"value": "DIRECT"
|
||||
},
|
||||
{
|
||||
"label": "Replica set",
|
||||
"value": "REPLICA_SET"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"sectionName": null,
|
||||
"children": [
|
||||
{
|
||||
"label": "Host Address",
|
||||
"configProperty": "datasourceConfiguration.endpoints[*].host",
|
||||
"controlType": "KEYVALUE_ARRAY"
|
||||
},
|
||||
{
|
||||
"label": "Port",
|
||||
"configProperty": "datasourceConfiguration.endpoints[*].port",
|
||||
"dataType": "NUMBER",
|
||||
"controlType": "KEYVALUE_ARRAY"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Default Database Name",
|
||||
"placeholder": "(Optional)",
|
||||
"configProperty": "datasourceConfiguration.authentication.databaseName",
|
||||
"controlType": "INPUT_TEXT"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"sectionName": "Authentication",
|
||||
"children": [
|
||||
{
|
||||
"label": "Authentication Database Name",
|
||||
"configProperty": "datasourceConfiguration.authentication.databaseName",
|
||||
"controlType": "INPUT_TEXT"
|
||||
},
|
||||
{
|
||||
"label": "Authentication Type",
|
||||
"configProperty": "datasourceConfiguration.authentication.authType",
|
||||
"controlType": "DROP_DOWN",
|
||||
"options": [
|
||||
{
|
||||
"label": "SCRAM-SHA-1",
|
||||
"value": "SCRAM_SHA_1"
|
||||
},
|
||||
{
|
||||
"label": "SCRAM-SHA-256",
|
||||
"value": "SCRAM_SHA_256"
|
||||
},
|
||||
{
|
||||
"label": "MONGODB-CR",
|
||||
"value": "MONGODB_CR"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"sectionName": null,
|
||||
"children": [
|
||||
{
|
||||
"label": "Username",
|
||||
"configProperty": "datasourceConfiguration.authentication.username",
|
||||
"controlType": "INPUT_TEXT"
|
||||
},
|
||||
{
|
||||
"label": "Password",
|
||||
"configProperty": "datasourceConfiguration.authentication.password",
|
||||
"dataType": "PASSWORD",
|
||||
"controlType": "INPUT_TEXT"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"sectionName": "SSL (optional)",
|
||||
"children": [
|
||||
{
|
||||
"label": "Authentication Mechanism",
|
||||
"configProperty": "datasourceConfiguration.connection.ssl.authType",
|
||||
"controlType": "DROP_DOWN",
|
||||
"options": [
|
||||
{
|
||||
"label": "CA Certificate",
|
||||
"value": "CA_CERTIFICATE"
|
||||
},
|
||||
{
|
||||
"label": "Self Signed Certificate",
|
||||
"value": "SELF_SIGNED_CERTIFICATE"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "CA Certificate",
|
||||
"configProperty": "datasourceConfiguration.connection.ssl.caCertificate",
|
||||
"controlType": "FILE_PICKER"
|
||||
},
|
||||
{
|
||||
"label": "PEM Certificate",
|
||||
"configProperty": "datasourceConfiguration.connection.ssl.pemCertificate",
|
||||
"controlType": "FILE_PICKER"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"sectionName": "SSH Tunnel (optional)",
|
||||
"children": [
|
||||
{
|
||||
"label": "Enable SSH Tunneling",
|
||||
"configProperty": "sshTunneling",
|
||||
"controlType": "SWITCH"
|
||||
},
|
||||
{
|
||||
"sectionName": null,
|
||||
"children": [
|
||||
{
|
||||
"label": "SSH Address",
|
||||
"configProperty": "datasourceConfiguration.sshProxy.host",
|
||||
"controlType": "INPUT_TEXT"
|
||||
},
|
||||
{
|
||||
"label": "Port",
|
||||
"configProperty": "datasourceConfiguration.sshProxy.port",
|
||||
"controlType": "INPUT_TEXT"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Username",
|
||||
"configProperty": "datasourceConfiguration.sshProxy.username",
|
||||
"controlType": "INPUT_TEXT"
|
||||
},
|
||||
{
|
||||
"label": "Authentication Type",
|
||||
"configProperty": "datasourceConfiguration.authenticationType",
|
||||
"controlType": "DROP_DOWN",
|
||||
"options": [
|
||||
{
|
||||
"label": "Password",
|
||||
"value": "PASSWORD"
|
||||
},
|
||||
{
|
||||
"label": "Identity File",
|
||||
"value": "IDENTITY_FILE"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Password",
|
||||
"configProperty": "datasourceConfiguration.ssh.tunnelPassword",
|
||||
"dataType": "PASSWORD",
|
||||
"controlType": "INPUT_TEXT"
|
||||
},
|
||||
{
|
||||
"label": "Passphrase",
|
||||
"configProperty": "datasourceConfiguration.ssh.tunnelPassphrase",
|
||||
"dataType": "PASSWORD",
|
||||
"controlType": "INPUT_TEXT"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
{
|
||||
"form": [
|
||||
{
|
||||
"sectionName": "General",
|
||||
"id": 1,
|
||||
"children": [
|
||||
{
|
||||
"label": "Connection Mode",
|
||||
"configProperty": "datasourceConfiguration.connection.mode",
|
||||
"controlType": "DROP_DOWN",
|
||||
"isRequired": true,
|
||||
"options": [
|
||||
{
|
||||
"label": "Read Only",
|
||||
"value": "READ_ONLY"
|
||||
},
|
||||
{
|
||||
"label": "Read / Write",
|
||||
"value": "READ_WRITE"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"sectionName": "Connection",
|
||||
"id": 2,
|
||||
"children": [
|
||||
{
|
||||
"sectionName": null,
|
||||
"children": [
|
||||
{
|
||||
"label": "Host Address",
|
||||
"configProperty": "datasourceConfiguration.endpoints[*].host",
|
||||
"controlType": "KEYVALUE_ARRAY"
|
||||
},
|
||||
{
|
||||
"label": "Port",
|
||||
"configProperty": "datasourceConfiguration.endpoints[*].port",
|
||||
"dataType": "NUMBER",
|
||||
"controlType": "KEYVALUE_ARRAY"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Database Name",
|
||||
"configProperty": "datasourceConfiguration.authentication.databaseName",
|
||||
"controlType": "INPUT_TEXT"
|
||||
},
|
||||
{
|
||||
"sectionName": null,
|
||||
"children": [
|
||||
{
|
||||
"label": "Username",
|
||||
"configProperty": "datasourceConfiguration.authentication.username",
|
||||
"controlType": "INPUT_TEXT"
|
||||
},
|
||||
{
|
||||
"label": "Password",
|
||||
"configProperty": "datasourceConfiguration.authentication.password",
|
||||
"dataType": "PASSWORD",
|
||||
"controlType": "INPUT_TEXT"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"sectionName": "SSL (optional)",
|
||||
"children": [
|
||||
{
|
||||
"label": "SSL Mode",
|
||||
"configProperty": "datasourceConfiguration.connection.ssl.authType",
|
||||
"controlType": "DROP_DOWN",
|
||||
"options": [
|
||||
{
|
||||
"label": "Allow",
|
||||
"value": "ALLOW"
|
||||
},
|
||||
{
|
||||
"label": "Prefer",
|
||||
"value": "PREFER"
|
||||
},
|
||||
{
|
||||
"label": "Require",
|
||||
"value": "REQUIRE"
|
||||
},
|
||||
{
|
||||
"label": "Disable",
|
||||
"value": "DISABLE"
|
||||
},
|
||||
{
|
||||
"label": "Verify-CA",
|
||||
"value": "VERIFY_CA"
|
||||
},
|
||||
{
|
||||
"label": "Verify-Full",
|
||||
"value": "VERIFY_FULL"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"sectionName": null,
|
||||
"children": [
|
||||
{
|
||||
"label": "Key File",
|
||||
"configProperty": "datasourceConfiguration.connection.ssl.keyFile",
|
||||
"controlType": "FILE_PICKER"
|
||||
},
|
||||
{
|
||||
"label": "Certificate",
|
||||
"configProperty": "datasourceConfiguration.connection.ssl.certificateFile",
|
||||
"controlType": "FILE_PICKER"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"sectionName": null,
|
||||
"children": [
|
||||
{
|
||||
"label": "CA Certificate",
|
||||
"configProperty": "datasourceConfiguration.connection.ssl.caCertificateFile",
|
||||
"controlType": "FILE_PICKER"
|
||||
},
|
||||
{
|
||||
"label": "PEM Certificate",
|
||||
"configProperty": "datasourceConfiguration.connection.ssl.pemCertificate.file",
|
||||
"controlType": "FILE_PICKER"
|
||||
},
|
||||
{
|
||||
"label": "PEM Passphrase",
|
||||
"configProperty": "datasourceConfiguration.connection.ssl.pemCertificate.password",
|
||||
"dataType": "PASSWORD",
|
||||
"controlType": "INPUT_TEXT"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"sectionName": "SSH (optional)",
|
||||
"id": 4,
|
||||
"children": [
|
||||
{
|
||||
"label": "Enable SSH",
|
||||
"configProperty": "enableSSH",
|
||||
"controlType": "SWITCH"
|
||||
},
|
||||
{
|
||||
"sectionName": null,
|
||||
"children": [
|
||||
{
|
||||
"label": "Tunnel Host",
|
||||
"configProperty": "datasourceConfiguration.sshProxy.host",
|
||||
"controlType": "INPUT_TEXT"
|
||||
},
|
||||
{
|
||||
"label": "Tunnel Port",
|
||||
"configProperty": "datasourceConfiguration.sshProxy.port",
|
||||
"controlType": "INPUT_TEXT"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Username",
|
||||
"configProperty": "datasourceConfiguration.sshProxy.username",
|
||||
"controlType": "INPUT_TEXT"
|
||||
},
|
||||
{
|
||||
"label": "Password",
|
||||
"configProperty": "datasourceConfiguration.sshProxy.password",
|
||||
"dataType": "PASSWORD",
|
||||
"controlType": "INPUT_TEXT"
|
||||
},
|
||||
{
|
||||
"label": "Authentication Type",
|
||||
"configProperty": "datasourceConfiguration.sshProxy.authType",
|
||||
"controlType": "DROP_DOWN",
|
||||
"options": [
|
||||
{
|
||||
"label": "Password",
|
||||
"value": "PASSWORD"
|
||||
},
|
||||
{
|
||||
"label": "Identity File",
|
||||
"value": "IDENTITY_FILE"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"form": [
|
||||
{
|
||||
"sectionName": "General",
|
||||
"id": 1,
|
||||
"children": [
|
||||
{
|
||||
"label": "Rapid Api Connection Name",
|
||||
"configProperty": "connectionName",
|
||||
"controlType": "INPUT_TEXT"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -241,8 +241,13 @@ public class RestApiPlugin extends BasePlugin {
|
|||
return Mono.just(new DatasourceTestResult("Invalid URL: '" + e.getMessage() + "'."));
|
||||
}
|
||||
|
||||
int port = url.getPort();
|
||||
if (port <= 0) {
|
||||
return Mono.just(new DatasourceTestResult("Invalid Port: '" + port + "'."));
|
||||
}
|
||||
|
||||
try (Socket socket = new Socket()) {
|
||||
socket.connect(new InetSocketAddress(url.getHost(), url.getPort()), 300);
|
||||
socket.connect(new InetSocketAddress(url.getHost(), port), 300);
|
||||
|
||||
} catch (IOException e) {
|
||||
return Mono.just(
|
||||
|
|
@ -262,7 +267,8 @@ public class RestApiPlugin extends BasePlugin {
|
|||
if (StringUtils.isNotEmpty(key)) {
|
||||
String value = header.getValue();
|
||||
webClientBuilder.defaultHeader(key, value);
|
||||
if (key.equals("Content-Type") && value.equals(MediaType.APPLICATION_JSON_VALUE)) {
|
||||
|
||||
if (key.toLowerCase().equals(HttpHeaders.CONTENT_TYPE.toLowerCase()) && value.equals(MediaType.APPLICATION_JSON_VALUE)) {
|
||||
isContentTypeJson = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"form": [
|
||||
{
|
||||
"sectionName": "General",
|
||||
"id": 1,
|
||||
"children": [
|
||||
{
|
||||
"label": "Rapid Api Connection Name",
|
||||
"configProperty": "connectionName",
|
||||
"controlType": "INPUT_TEXT"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import com.appsmith.server.domains.Datasource;
|
|||
import com.appsmith.server.dtos.ResponseDTO;
|
||||
import com.appsmith.server.services.DatasourceService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
|
@ -41,10 +42,6 @@ public class DatasourceController extends BaseController<DatasourceService, Data
|
|||
return Mono.just(new DatasourceTestResult(datasource1.getInvalids()));
|
||||
}
|
||||
})
|
||||
.map(testResult -> {
|
||||
ResponseDTO<DatasourceTestResult> response = new ResponseDTO<>();
|
||||
response.setData(testResult);
|
||||
return response;
|
||||
});
|
||||
.map(testResult -> new ResponseDTO<>(HttpStatus.OK.value(), testResult, null));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,13 @@
|
|||
package com.appsmith.server.controllers;
|
||||
|
||||
import com.appsmith.external.models.ActionConfiguration;
|
||||
import com.appsmith.external.models.ActionExecutionResult;
|
||||
import com.appsmith.external.models.ApiTemplate;
|
||||
import com.appsmith.external.models.ApiTemplateConfiguration;
|
||||
import com.appsmith.external.models.DatasourceConfiguration;
|
||||
import com.appsmith.external.models.Property;
|
||||
import com.appsmith.external.models.Provider;
|
||||
import com.appsmith.external.models.Statistics;
|
||||
import com.appsmith.server.constants.Url;
|
||||
import com.appsmith.server.domains.Action;
|
||||
import com.appsmith.server.domains.Datasource;
|
||||
import com.appsmith.server.dtos.ProviderPaginatedDTO;
|
||||
import com.appsmith.server.dtos.ResponseDTO;
|
||||
import com.appsmith.server.dtos.SearchResponseDTO;
|
||||
import com.appsmith.server.services.MarketplaceService;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
|
@ -28,7 +16,6 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
|
|
@ -46,78 +33,13 @@ public class MarketplaceController {
|
|||
|
||||
@GetMapping("/search")
|
||||
Mono<ResponseDTO<SearchResponseDTO>> searchAPIOrProviders(@RequestParam String searchKey, @RequestParam(required = false) Integer limit) {
|
||||
SearchResponseDTO searchResponseDTO = new SearchResponseDTO();
|
||||
List<Provider> providers = new ArrayList<>();
|
||||
Provider provider = new Provider();
|
||||
List<String> categories = new ArrayList<>();
|
||||
categories.add("Data");
|
||||
categories.add("Sports");
|
||||
provider.setCategories(categories);
|
||||
provider.setName("New Sports Ltd");
|
||||
provider.setId("RandomSavedId");
|
||||
provider.setDescription("Some description here");
|
||||
provider.setUrl("http://url.com");
|
||||
provider.setImageUrl("http://image.url.com");
|
||||
provider.setDocumentationUrl("http://docu.url.com");
|
||||
Statistics statistics = new Statistics();
|
||||
statistics.setAverageLatency((long) 230);
|
||||
statistics.setImports((long) 1000);
|
||||
statistics.setSuccessRate(99.7);
|
||||
provider.setStatistics(statistics);
|
||||
provider.setCredentialSteps("Credential steps here");
|
||||
|
||||
providers.add(provider);
|
||||
searchResponseDTO.setProviders(providers);
|
||||
|
||||
List<ApiTemplate> apiTemplates = new ArrayList<>();
|
||||
ApiTemplate apiTemplate = new ApiTemplate();
|
||||
apiTemplate.setId("Id");
|
||||
apiTemplate.setPackageName("restapi-plugin");
|
||||
DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration();
|
||||
datasourceConfiguration.setUrl("http://google.com");
|
||||
apiTemplate.setDatasourceConfiguration(datasourceConfiguration);
|
||||
ActionConfiguration actionConfiguration = new ActionConfiguration();
|
||||
actionConfiguration.setPath("/viewSomething");
|
||||
actionConfiguration.setHttpMethod(HttpMethod.GET);
|
||||
List<Property> headers = new ArrayList<>();
|
||||
Property header = new Property();
|
||||
header.setKey("key");
|
||||
header.setValue("value");
|
||||
headers.add(header);
|
||||
actionConfiguration.setHeaders(headers);
|
||||
apiTemplate.setActionConfiguration(actionConfiguration);
|
||||
|
||||
ApiTemplateConfiguration apiTemplateConfiguration = new ApiTemplateConfiguration();
|
||||
apiTemplateConfiguration.setDocumentation("documentation");
|
||||
apiTemplateConfiguration.setDocumentationUrl("http://url.com");
|
||||
ActionExecutionResult actionExecutionResult = new ActionExecutionResult();
|
||||
actionExecutionResult.setBody("body");
|
||||
actionExecutionResult.setStatusCode("200");
|
||||
String headersInString = "{\\r\\n \\\"Date\\\": [\\r\\n \\\"Thu, 06 Feb 2020 13:09:27 GMT\\\"\\r\\n ],\\r\\n \\\"Content-Type\\\": [\\r\\n \\\"application\\/json; charset=utf-8\\\"\\r\\n ],\\r\\n \\\"Connection\\\": [\\r\\n \\\"keep-alive\\\"\\r\\n ],\\r\\n \\\"Set-Cookie\\\": [\\r\\n \\\"__cfduid=dcdfc69e4b24eed1dbde13490b60fa2931580994567; expires=Sat, 07-Mar-20 13:09:27 GMT; path=\\/; domain=.pokeapi.co; HttpOnly; SameSite=Lax; Secure\\\"\\r\\n ],\\r\\n \\\"access-control-allow-origin\\\": [\\r\\n \\\"*\\\"\\r\\n ],\\r\\n \\\"cache-control\\\": [\\r\\n \\\"public, max-age=86400, s-maxage=86400\\\"\\r\\n ],\\r\\n \\\"etag\\\": [\\r\\n \\\"W\\/\\\\\\\"5bc-hMqibo\\/v586SwY5Pw+N9QHVbp1M\\\\\\\"\\\"\\r\\n ],\\r\\n \\\"function-execution-id\\\": [\\r\\n \\\"aclq7ln3lowb\\\"\\r\\n ],\\r\\n \\\"x-powered-by\\\": [\\r\\n \\\"Express\\\"\\r\\n ],\\r\\n \\\"x-cloud-trace-context\\\": [\\r\\n \\\"f838c06278fff2cc715ed3f4cc8c27f6\\\"\\r\\n ],\\r\\n \\\"X-Served-By\\\": [\\r\\n \\\"cache-sin18040-SIN\\\"\\r\\n ],\\r\\n \\\"X-Cache\\\": [\\r\\n \\\"HIT\\\"\\r\\n ],\\r\\n \\\"X-Cache-Hits\\\": [\\r\\n \\\"1\\\"\\r\\n ],\\r\\n \\\"X-Timer\\\": [\\r\\n \\\"S1564191065.116552,VS0,VE1\\\"\\r\\n ],\\r\\n \\\"Vary\\\": [\\r\\n \\\"accept-encoding, x-fh-requested-host, cookie, authorization\\\"\\r\\n ],\\r\\n \\\"CF-Cache-Status\\\": [\\r\\n \\\"REVALIDATED\\\"\\r\\n ],\\r\\n \\\"Accept-Ranges\\\": [\\r\\n \\\"bytes\\\"\\r\\n ],\\r\\n \\\"Expect-CT\\\": [\\r\\n \\\"max-age=604800, report-uri=\\\\\\\"https:\\/\\/report-uri.cloudflare.com\\/cdn-cgi\\/beacon\\/expect-ct\\\\\\\"\\\"\\r\\n ],\\r\\n \\\"Server\\\": [\\r\\n \\\"cloudflare\\\"\\r\\n ],\\r\\n \\\"CF-RAY\\\": [\\r\\n \\\"560d5bce6a17d9e0-SIN\\\"\\r\\n ],\\r\\n \\\"transfer-encoding\\\": [\\r\\n \\\"chunked\\\"\\r\\n ]\\r\\n }";
|
||||
try {
|
||||
JsonNode headersNode = objectMapper.readTree(headersInString);
|
||||
actionExecutionResult.setHeaders(headersNode);
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
apiTemplateConfiguration.setSampleResponse(actionExecutionResult);
|
||||
apiTemplate.setApiTemplateConfiguration(apiTemplateConfiguration);
|
||||
apiTemplates.add(apiTemplate);
|
||||
searchResponseDTO.setApiTemplates(apiTemplates);
|
||||
|
||||
List<Action> actions = new ArrayList<>();
|
||||
Action action = new Action();
|
||||
action.setName("ResultActionAPI");
|
||||
Datasource datasource = new Datasource();
|
||||
datasource.setDatasourceConfiguration(datasourceConfiguration);
|
||||
action.setActionConfiguration(actionConfiguration);
|
||||
action.setDatasource(datasource);
|
||||
actions.add(action);
|
||||
searchResponseDTO.setActions(actions);
|
||||
|
||||
return Mono.just(searchResponseDTO)
|
||||
.map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null));
|
||||
|
||||
return marketplaceService.searchProviderByName(searchKey)
|
||||
.map(result -> {
|
||||
SearchResponseDTO searchResponseDTO = new SearchResponseDTO();
|
||||
searchResponseDTO.setProviders(result);
|
||||
return new ResponseDTO<>(HttpStatus.OK.value(), searchResponseDTO, null);
|
||||
});
|
||||
}
|
||||
|
||||
@GetMapping("/templates")
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
|
@ -51,4 +52,10 @@ public class PluginController extends BaseController<PluginService, Plugin, Stri
|
|||
return service.get(params).collectList()
|
||||
.map(resources -> new ResponseDTO<>(HttpStatus.OK.value(), resources, null));
|
||||
}
|
||||
|
||||
@GetMapping("/{pluginId}/form")
|
||||
public Mono<ResponseDTO<Object>> getDatasourceForm(@PathVariable String pluginId) {
|
||||
return service.getFormConfig(pluginId)
|
||||
.map(form -> new ResponseDTO<>(HttpStatus.OK.value(), form, null));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ public enum AppsmithError {
|
|||
PLUGIN_INSTALLATION_FAILED_DOWNLOAD_ERROR(500, 5002, "Plugin installation failed due to an error while downloading it. Check the jar location & try again."),
|
||||
PLUGIN_RUN_FAILED(500, 5003, "Plugin execution failed with error {0}"),
|
||||
PLUGIN_EXECUTION_TIMEOUT(504, 5040, "Plugin Execution exceeded the maximum allowed time. Please increase the timeout in your action settings or check your backend action endpoint"),
|
||||
MARKETPLACE_TIMEOUT(504, 5041, "Marketplace is responding too slowly. Please check the internet connection");
|
||||
PLUGIN_LOAD_FORM_JSON_FAIL(500, 5004, "Unable to load datasource form configuration. Details: {0}."),
|
||||
MARKETPLACE_TIMEOUT(504, 5041, "Marketplace is responding too slowly. Please try again later");
|
||||
|
||||
|
||||
private Integer httpErrorCode;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import net.minidev.json.parser.JSONParser;
|
|||
import net.minidev.json.parser.ParseException;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
|
@ -434,8 +435,11 @@ public class LayoutActionServiceImpl implements LayoutActionService {
|
|||
for (int i = 0; i < children.size(); i++) {
|
||||
Map data = (Map) children.get(i);
|
||||
JSONObject object = new JSONObject();
|
||||
object.putAll(data);
|
||||
extractAllWidgetNamesFromDSL(object, widgetNames);
|
||||
// If the children tag exists and there are entries within it
|
||||
if (!CollectionUtils.isEmpty(data)) {
|
||||
object.putAll(data);
|
||||
extractAllWidgetNamesFromDSL(object, widgetNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import reactor.core.publisher.Mono;
|
|||
import java.util.List;
|
||||
|
||||
public interface MarketplaceService {
|
||||
|
||||
Mono<ProviderPaginatedDTO> getProviders(MultiValueMap<String, String> params);
|
||||
|
||||
Mono<List<ApiTemplate>> getTemplates(MultiValueMap<String, String> params);
|
||||
|
|
@ -16,6 +17,8 @@ public interface MarketplaceService {
|
|||
Mono<List<String>> getCategories();
|
||||
|
||||
Mono<Boolean> subscribeAndUpdateStatisticsOfProvider(String providerId);
|
||||
|
||||
|
||||
Mono<Provider> getProviderById(String id);
|
||||
|
||||
Mono<List<Provider>> searchProviderByName(String id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -164,6 +164,33 @@ public class MarketplaceServiceImpl implements MarketplaceService {
|
|||
.doOnError(error -> Mono.error(new AppsmithException(AppsmithError.MARKETPLACE_TIMEOUT)));
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* This function searches for providers and returns the providers with exact match in name.
|
||||
* In the future the search should support 'like' for providers and search could expand to include
|
||||
* the actions used in the organization (across all applications) and templates as well.
|
||||
*/
|
||||
public Mono<List<Provider>> searchProviderByName(String name) {
|
||||
URI uri = buildFullURI(null, PROVIDER_PATH + "/name/" + name);
|
||||
|
||||
return webClient
|
||||
.get()
|
||||
.uri(uri)
|
||||
.retrieve()
|
||||
.bodyToMono(String.class)
|
||||
.flatMap(stringBody -> {
|
||||
List<Provider> providers = null;
|
||||
try {
|
||||
providers = objectMapper.readValue(stringBody, ArrayList.class);
|
||||
} catch (JsonProcessingException e) {
|
||||
return Mono.error(new AppsmithException(AppsmithError.JSON_PROCESSING_ERROR, e));
|
||||
}
|
||||
return Mono.just(providers);
|
||||
})
|
||||
.timeout(Duration.ofMillis(timeoutInMillis))
|
||||
.doOnError(error -> Mono.error(new AppsmithException(AppsmithError.MARKETPLACE_TIMEOUT)));
|
||||
}
|
||||
|
||||
private URI buildFullURI(MultiValueMap<String, String> params, String path) {
|
||||
UriComponentsBuilder uriBuilder = UriComponentsBuilder.newInstance();
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import com.appsmith.server.dtos.PluginOrgDTO;
|
|||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
public interface PluginService extends CrudService<Plugin, String> {
|
||||
|
||||
Flux<Plugin> getDefaultPlugins();
|
||||
|
|
@ -22,4 +24,8 @@ public interface PluginService extends CrudService<Plugin, String> {
|
|||
Mono<Plugin> findById(String id);
|
||||
|
||||
Plugin redisInstallPlugin(InstallPluginRedisDTO installPluginRedisDTO);
|
||||
|
||||
Mono<Object> getFormConfig(String pluginId);
|
||||
|
||||
Mono<InputStream> getPluginResource(String pluginId, String resourcePath);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,10 +33,13 @@ import reactor.core.scheduler.Scheduler;
|
|||
|
||||
import javax.validation.Validator;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
|
|
@ -278,4 +281,33 @@ public class PluginServiceImpl extends BaseService<PluginRepository, Plugin, Str
|
|||
|
||||
return Mono.just(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> getFormConfig(String pluginId) {
|
||||
return getPluginResource(pluginId, "form.json")
|
||||
.flatMap(jsonStream -> {
|
||||
try {
|
||||
return Mono.just(new ObjectMapper().readValue(jsonStream, Map.class));
|
||||
} catch (IOException e) {
|
||||
return Mono.error(new AppsmithException(AppsmithError.PLUGIN_LOAD_FORM_JSON_FAIL, e.getMessage()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<InputStream> getPluginResource(String pluginId, String resourcePath) {
|
||||
return findById(pluginId)
|
||||
.flatMap(plugin -> {
|
||||
InputStream formResourceStream = pluginManager
|
||||
.getPlugin(plugin.getPackageName())
|
||||
.getPluginClassLoader()
|
||||
.getResourceAsStream(resourcePath);
|
||||
|
||||
if (formResourceStream == null) {
|
||||
return Mono.error(new AppsmithException(AppsmithError.PLUGIN_LOAD_FORM_JSON_FAIL, "Resource not found"));
|
||||
}
|
||||
|
||||
return Mono.just(formResourceStream);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user