fix: S3 plugin: provide support for MinIO (#11766)

* update option label

* add minIO as a separate datasource option
add datasource connection changes to support minIO without SSL and when using CA signed certificates

* fix cypress tests
This commit is contained in:
Sumit Kumar 2022-03-24 18:28:00 +05:30 committed by GitHub
parent 8eea57358c
commit 40f4e3c755
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 78 additions and 9 deletions

View File

@ -120,7 +120,7 @@ describe("Validate CRUD queries for Amazon S3 along with UI flow verifications",
"File content is not base64 encoded.",
);
});
cy.validateNSelectDropdown("File Data Type", "Base64", "Text / Binary");
cy.validateNSelectDropdown("File Data Type", "Base64", "Text");
cy.onlyQueryRun();
cy.wait("@postExecute").then(({ response }) => {
@ -249,7 +249,7 @@ describe("Validate CRUD queries for Amazon S3 along with UI flow verifications",
cy.typeValueNValidate("AutoFile", "File Path");
//Commenting below since below dropdown is removed from Read
//cy.validateNSelectDropdown("File Data Type", "Base64", "Text / Binary");
//cy.validateNSelectDropdown("File Data Type", "Base64", "Text");
cy.onlyQueryRun();
cy.wait("@postExecute").then(({ response }) => {
@ -343,7 +343,7 @@ describe("Validate CRUD queries for Amazon S3 along with UI flow verifications",
);
cy.typeValueNValidate("assets-test.appsmith.com", "Bucket Name");
cy.typeValueNValidate("CRUDNewPageFile", "File Path");
cy.validateNSelectDropdown("File Data Type", "Base64", "Text / Binary");
cy.validateNSelectDropdown("File Data Type", "Base64", "Text");
cy.typeValueNValidate(
'{"data": "Hi, this is Automation script adding file for S3 CRUD New Page validation!"}',
"Content",

View File

@ -6,6 +6,7 @@ import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException
import com.appsmith.external.models.Condition;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.Endpoint;
import com.appsmith.external.models.Property;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
@ -288,4 +289,22 @@ public class PluginUtils {
public static List<String> parseList(String arrayString) throws IOException {
return objectMapper.readValue(arrayString, ArrayList.class);
}
public static <T> T getValueSafelyFromPropertyList(List<Property> properties, int index, Class<T> type,
T defaultValue) {
if (CollectionUtils.isEmpty(properties) || index > properties.size() - 1 || properties.get(index) == null
|| properties.get(index).getValue() == null) {
return defaultValue;
}
return (T) properties.get(index).getValue();
}
public static <T> T getValueSafelyFromPropertyList(List<Property> properties, int index, Class<T> type) {
return getValueSafelyFromPropertyList(properties, index, type, null);
}
public static Object getValueSafelyFromPropertyList(List<Property> properties, int index) {
return getValueSafelyFromPropertyList(properties, index, Object.class);
}
}

View File

@ -1,8 +1,10 @@
package com.external.utils;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
@ -17,6 +19,7 @@ import java.util.regex.Pattern;
import static com.amazonaws.regions.Regions.DEFAULT_REGION;
import static com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError.PLUGIN_ERROR;
import static com.appsmith.external.helpers.PluginUtils.getValueSafelyFromPropertyList;
import static com.external.plugins.AmazonS3Plugin.CUSTOM_ENDPOINT_INDEX;
import static com.external.plugins.AmazonS3Plugin.CUSTOM_ENDPOINT_REGION_PROPERTY_INDEX;
import static com.external.plugins.AmazonS3Plugin.S3_SERVICE_PROVIDER_PROPERTY_INDEX;
@ -59,6 +62,7 @@ public class DatasourceUtils {
WASABI ("wasabi"),
DIGITAL_OCEAN_SPACES ("digital-ocean-spaces"),
DREAM_OBJECTS ("dream-objects"),
MINIO ("minio"),
OTHER ("other");
private String name;
@ -183,6 +187,31 @@ public class DatasourceUtils {
region = getRegionFromEndpointPattern(endpoint, DREAM_OBJECTS_URL_ENDPOINT_PATTERN,
DREAM_OBJECTS_REGION_GROUP_INDEX);
break;
case MINIO:
region = getUserProvidedRegion(properties);
if (StringUtils.isBlank(region)) {
/**
* Set a default region in case user has not provided a region.
* Minio server can be configured to work both ways - with or without region attribute. Hence,
* it is upto the user to know whether the Minio server they want to connect to has been
* configured with a region or not.
* As per my experimentation, in case the Minio server has not been configured with a region
* attribute, then any placeholder value will work. However, I am going with US_EAST_1 here
* since this the value that Minio documentation uses to show example applications.
* Ref: https://docs.min.io/docs/how-to-use-aws-sdk-for-java-with-minio-server.html
*/
region = Regions.US_EAST_1.getName();
}
ClientConfiguration clientConfiguration = new ClientConfiguration();
clientConfiguration.setSignerOverride("AWSS3V4SignerType");
/* Ref: https://docs.min.io/docs/how-to-use-aws-sdk-for-java-with-minio-server.html */
s3ClientBuilder = s3ClientBuilder
.withPathStyleAccessEnabled(true)
.withClientConfiguration(clientConfiguration);
break;
default:
region = (String) properties.get(CUSTOM_ENDPOINT_REGION_PROPERTY_INDEX).getValue();
@ -195,6 +224,10 @@ public class DatasourceUtils {
return s3ClientBuilder;
}
private static String getUserProvidedRegion(List<Property> properties) {
return getValueSafelyFromPropertyList(properties, CUSTOM_ENDPOINT_REGION_PROPERTY_INDEX, String.class);
}
/**
* This method checks if the S3 endpoint URL has correct format and extracts region information from it.
*

View File

@ -41,7 +41,7 @@
"value": "YES"
},
{
"label": "Text / Binary",
"label": "Text",
"value": "NO"
}
]

View File

@ -38,6 +38,10 @@
"label": "DreamObjects",
"value": "dream-objects"
},
{
"label": "MinIO",
"value": "minio"
},
{
"label": "Other",
"value": "other"
@ -48,6 +52,7 @@
"label": "Access Key",
"configProperty": "datasourceConfiguration.authentication.username",
"controlType": "INPUT_TEXT",
"isRequired": true,
"initialValue": ""
},
{
@ -56,14 +61,16 @@
"controlType": "INPUT_TEXT",
"dataType": "PASSWORD",
"initialValue": "",
"isRequired": true,
"encrypted": true
},
{
"label": "Endpoint URL",
"label": "Endpoint URL (with or without protocol and port no)",
"configProperty": "datasourceConfiguration.endpoints[0].host",
"controlType": "INPUT_TEXT",
"initialValue": "",
"placeholderText": "user-storage.de-fra1.upcloudobjects.com",
"isRequired": true,
"placeholderText": "https://user-storage.de-fra1.upcloudobjects.com",
"hidden": {
"path": "datasourceConfiguration.properties[1].value",
"comparison": "EQUALS",
@ -84,9 +91,19 @@
"initialValue": "",
"placeholderText": "de-fra1",
"hidden": {
"path": "datasourceConfiguration.properties[1].value",
"comparison": "NOT_EQUALS",
"value": "other"
"conditionType": "AND",
"conditions": [
{
"path": "datasourceConfiguration.properties[1].value",
"comparison": "NOT_EQUALS",
"value": "other"
},
{
"path": "datasourceConfiguration.properties[1].value",
"comparison": "NOT_EQUALS",
"value": "minio"
}
]
}
}
]