fix: curl import tries to guess the content-type from the body if content-type header is not specified (#11295)
* fix: content-type is empty when it's not specified. * feat: try to guess the content type json or form-urlencoded * fix: broken test for curl with `--data-urlencode` parameter * fix: fix guessing urlEncodedPattern * fix: fix broken tests * fix: Fixed and improved the code formatting
This commit is contained in:
parent
cc3f98c0f1
commit
450950d68f
|
|
@ -2,6 +2,7 @@ package com.appsmith.server.services;
|
|||
|
||||
import com.appsmith.server.helpers.ResponseUtils;
|
||||
import com.appsmith.server.services.ce.CurlImporterServiceCEImpl;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
|
@ -9,11 +10,13 @@ import org.springframework.stereotype.Service;
|
|||
@Service
|
||||
public class CurlImporterServiceImpl extends CurlImporterServiceCEImpl implements CurlImporterService {
|
||||
|
||||
public CurlImporterServiceImpl(PluginService pluginService,
|
||||
LayoutActionService layoutActionService,
|
||||
NewPageService newPageService,
|
||||
ResponseUtils responseUtils) {
|
||||
|
||||
super(pluginService, layoutActionService, newPageService, responseUtils);
|
||||
public CurlImporterServiceImpl(
|
||||
PluginService pluginService,
|
||||
LayoutActionService layoutActionService,
|
||||
NewPageService newPageService,
|
||||
ResponseUtils responseUtils,
|
||||
ObjectMapper objectMapper
|
||||
) {
|
||||
super(pluginService, layoutActionService, newPageService, responseUtils, objectMapper);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ import com.appsmith.server.services.BaseApiImporter;
|
|||
import com.appsmith.server.services.LayoutActionService;
|
||||
import com.appsmith.server.services.NewPageService;
|
||||
import com.appsmith.server.services.PluginService;
|
||||
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.apache.commons.lang.StringUtils;
|
||||
import org.apache.http.NameValuePair;
|
||||
|
|
@ -32,6 +35,7 @@ import java.nio.charset.StandardCharsets;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
|
||||
|
||||
|
|
@ -52,15 +56,20 @@ public class CurlImporterServiceCEImpl extends BaseApiImporter implements CurlIm
|
|||
private final LayoutActionService layoutActionService;
|
||||
private final ResponseUtils responseUtils;
|
||||
private final NewPageService newPageService;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public CurlImporterServiceCEImpl(PluginService pluginService,
|
||||
LayoutActionService layoutActionService,
|
||||
NewPageService newPageService,
|
||||
ResponseUtils responseUtils) {
|
||||
public CurlImporterServiceCEImpl(
|
||||
PluginService pluginService,
|
||||
LayoutActionService layoutActionService,
|
||||
NewPageService newPageService,
|
||||
ResponseUtils responseUtils,
|
||||
ObjectMapper objectMapper
|
||||
) {
|
||||
this.pluginService = pluginService;
|
||||
this.layoutActionService = layoutActionService;
|
||||
this.newPageService = newPageService;
|
||||
this.responseUtils = responseUtils;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -329,7 +338,12 @@ public class CurlImporterServiceCEImpl extends BaseApiImporter implements CurlIm
|
|||
|
||||
} else if ("--data-urlencode".equals(state)) {
|
||||
// The `token` is next to `--data-urlencode`.
|
||||
dataParts.add(token);
|
||||
// ignore the '=' at the start as the curl document says https://curl.se/docs/manpage.html#--data-urlencode
|
||||
if (token.startsWith("=")) {
|
||||
dataParts.add(token.substring(1));
|
||||
} else {
|
||||
dataParts.add(token);
|
||||
}
|
||||
|
||||
} else if (ARG_FORM.equals(state)) {
|
||||
// The token is next to --form
|
||||
|
|
@ -371,13 +385,11 @@ public class CurlImporterServiceCEImpl extends BaseApiImporter implements CurlIm
|
|||
|
||||
}
|
||||
|
||||
if (contentType == null && !dataParts.isEmpty()) {
|
||||
contentType = MediaType.APPLICATION_FORM_URLENCODED_VALUE;
|
||||
headers.add(new Property(HttpHeaders.CONTENT_TYPE, contentType));
|
||||
|
||||
} else if (contentType == null && !formParts.isEmpty()) {
|
||||
contentType = MediaType.MULTIPART_FORM_DATA_VALUE;
|
||||
headers.add(new Property(HttpHeaders.CONTENT_TYPE, contentType));
|
||||
if (contentType == null) {
|
||||
contentType = guessTheContentType(dataParts, formParts);
|
||||
if (contentType != null) {
|
||||
headers.add(new Property(HttpHeaders.CONTENT_TYPE, contentType));
|
||||
}
|
||||
}
|
||||
|
||||
if (!headers.isEmpty()) {
|
||||
|
|
@ -423,6 +435,28 @@ public class CurlImporterServiceCEImpl extends BaseApiImporter implements CurlIm
|
|||
return action;
|
||||
}
|
||||
|
||||
private String guessTheContentType(List<String> dataParts, List<String> formParts) {
|
||||
if (!dataParts.isEmpty()) {
|
||||
final String data = dataParts.get(0);
|
||||
final Pattern urlEncodedPattern = Pattern.compile("([A-Za-z0-9%._\\-/]+=[^\\s]+)");
|
||||
// if it's form url encoded?
|
||||
if (urlEncodedPattern.matcher(data).matches()) {
|
||||
return MediaType.APPLICATION_FORM_URLENCODED_VALUE;
|
||||
} else {
|
||||
// or if it's JSON
|
||||
try {
|
||||
objectMapper.readTree(data);
|
||||
return MediaType.APPLICATION_JSON_VALUE;
|
||||
} catch (JsonProcessingException e) {
|
||||
// ignore exception it's not JSON
|
||||
}
|
||||
}
|
||||
} else if (!formParts.isEmpty()) {
|
||||
return MediaType.MULTIPART_FORM_DATA_VALUE;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void trySaveURL(ActionDTO action, String token) throws MalformedURLException, URISyntaxException {
|
||||
// If the URL appears to not have a protocol set, prepend the `https` protocol.
|
||||
if (!token.matches("\\w+://.*")) {
|
||||
|
|
|
|||
|
|
@ -200,12 +200,12 @@ public class CurlImporterServiceTest {
|
|||
|
||||
Mono<NewPage> branchedPageMono = defaultPageMono
|
||||
.flatMap(defaultPage ->
|
||||
newPageService.findById(branchedPageId, AclPermission.MANAGE_PAGES)
|
||||
.flatMap(newPage -> {
|
||||
newPage.setDefaultResources(defaultPage.getDefaultResources());
|
||||
newPage.getDefaultResources().setBranchName("testBranch");
|
||||
return newPageService.save(newPage);
|
||||
})
|
||||
newPageService.findById(branchedPageId, AclPermission.MANAGE_PAGES)
|
||||
.flatMap(newPage -> {
|
||||
newPage.setDefaultResources(defaultPage.getDefaultResources());
|
||||
newPage.getDefaultResources().setBranchName("testBranch");
|
||||
return newPageService.save(newPage);
|
||||
})
|
||||
)
|
||||
.cache();
|
||||
|
||||
|
|
@ -311,33 +311,24 @@ public class CurlImporterServiceTest {
|
|||
);
|
||||
assertMethod(action, HttpMethod.POST);
|
||||
assertUrl(action, "http://loc");
|
||||
assertEmptyBody(action);
|
||||
assertBodyFormData(
|
||||
action,
|
||||
new Property("", "all of this exactly, but url encoded ")
|
||||
);
|
||||
assertBody(action, "all of this exactly, but url encoded ");
|
||||
assertEmptyBodyFormData(action);
|
||||
|
||||
action = curlImporterService.curlToAction(
|
||||
"curl --data-urlencode 'spaced name=all of this exactly, but url encoded' http://loc"
|
||||
);
|
||||
assertMethod(action, HttpMethod.POST);
|
||||
assertUrl(action, "http://loc");
|
||||
assertEmptyBody(action);
|
||||
assertBodyFormData(
|
||||
action,
|
||||
new Property("spaced name", "all of this exactly, but url encoded")
|
||||
);
|
||||
assertBody(action, "spaced name=all of this exactly, but url encoded");
|
||||
assertEmptyBodyFormData(action);
|
||||
|
||||
action = curlImporterService.curlToAction(
|
||||
"curl --data-urlencode 'awesome=details, all of this exactly, but url encoded' http://loc"
|
||||
);
|
||||
assertMethod(action, HttpMethod.POST);
|
||||
assertUrl(action, "http://loc");
|
||||
assertEmptyBody(action);
|
||||
assertBodyFormData(
|
||||
action,
|
||||
new Property("awesome", "details, all of this exactly, but url encoded")
|
||||
);
|
||||
assertBody(action, "awesome=details, all of this exactly, but url encoded");
|
||||
assertEmptyBodyFormData(action);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -616,7 +607,7 @@ public class CurlImporterServiceTest {
|
|||
assertMethod(action, HttpMethod.POST);
|
||||
assertUrl(action, "https://api.sloths.com");
|
||||
assertEmptyPath(action);
|
||||
assertHeaders(action, new Property("Content-Type", "application/x-www-form-urlencoded"));
|
||||
assertHeaders(action, new Property("Content-Type", "application/x-www-form-urlencoded"));
|
||||
assertEmptyBody(action);
|
||||
assertBodyFormData(
|
||||
action,
|
||||
|
|
@ -702,12 +693,9 @@ public class CurlImporterServiceTest {
|
|||
assertMethod(action, HttpMethod.POST);
|
||||
assertUrl(action, "http://dummy.restapiexample.com");
|
||||
assertPath(action, "/api/v1/create");
|
||||
assertHeaders(action, new Property("Content-Type", "application/x-www-form-urlencoded"));
|
||||
assertEmptyBody(action);
|
||||
assertBodyFormData(
|
||||
action,
|
||||
new Property("{\"name\":\"test\",\"salary\":\"123\",\"age\":\"23\"}", "")
|
||||
);
|
||||
assertHeaders(action, new Property("Content-Type", "application/json"));
|
||||
assertBody(action, "{\"name\":\"test\",\"salary\":\"123\",\"age\":\"23\"}");
|
||||
assertEmptyBodyFormData(action);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -778,12 +766,8 @@ public class CurlImporterServiceTest {
|
|||
assertMethod(action, HttpMethod.POST);
|
||||
assertUrl(action, "http://httpbin.org");
|
||||
assertPath(action, "/post");
|
||||
assertHeaders(action, new Property("Content-Type", "application/x-www-form-urlencoded"));
|
||||
assertEmptyBody(action);
|
||||
assertBodyFormData(
|
||||
action,
|
||||
new Property("a\\n", "")
|
||||
);
|
||||
assertBody(action, "a\\n");
|
||||
assertEmptyBodyFormData(action);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -848,6 +832,9 @@ public class CurlImporterServiceTest {
|
|||
private static void assertEmptyBody(ActionDTO action) {
|
||||
assertThat(action.getActionConfiguration().getBody()).isNullOrEmpty();
|
||||
}
|
||||
private static void assertEmptyBodyFormData(ActionDTO action) {
|
||||
assertThat(action.getActionConfiguration().getBodyFormData()).isNullOrEmpty();
|
||||
}
|
||||
|
||||
private static void assertBody(ActionDTO action, String body) {
|
||||
assertThat(action.getActionConfiguration().getBody()).isEqualTo(body);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user