Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@
import static org.entando.entando.aps.system.services.tenants.ITenantManager.PRIMARY_CODE;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.WeakHashMap;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.entando.entando.aps.system.services.tenants.TenantConfig;
import org.entando.entando.ent.exception.EntRuntimeException;
Expand All @@ -45,6 +48,7 @@
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
import org.entando.entando.aps.system.services.storage.CdsActive;

Expand All @@ -60,7 +64,7 @@ public class CdsRemoteCaller {
private final RestTemplate restTemplateWithRedirect;
private final CdsConfiguration configuration;

// used weak map to skip excessive growth
// used weak map to skip excessive growthclear
private Map<String, String> tenantsToken = new WeakHashMap<>();

@Autowired
Expand All @@ -76,8 +80,8 @@ public CdsCreateResponseDto executePostCall(URI url,
boolean isProtectedResource,
Optional<InputStream> fileInputStream ,
Optional<TenantConfig> config,
boolean forceTokenRetrieve) {

boolean forceTokenRetrieve,
int cdsRetry) {
try {
logger.debug("Trying to call POST on url:'{}' with isProtectedResource:'{}' forceTokenRetrieve:'{}' isFile:'{}' and is config tenant empty:'{}'",
url,
Expand All @@ -90,6 +94,7 @@ public CdsCreateResponseDto executePostCall(URI url,
headers.setContentType(MediaType.MULTIPART_FORM_DATA);

MultiValueMap<String, Object> body = fileInputStream
.map(this::cloneInputStream)
.map(is -> buildFileBodyRequest(subPath, isProtectedResource, is))
.orElseGet(() -> buildDirectoryBodyRequest(subPath, isProtectedResource));

Expand All @@ -98,7 +103,8 @@ public CdsCreateResponseDto executePostCall(URI url,
ResponseEntity<List<CdsCreateRowResponseDto>> fullResponse = restTemplate.exchange(url,
HttpMethod.POST,
entity,
new ParameterizedTypeReference<List<CdsCreateRowResponseDto>>(){});
new ParameterizedTypeReference<>() {
});

List<CdsCreateRowResponseDto> responseList = Optional
.ofNullable(fullResponse.getBody())
Expand All @@ -115,15 +121,28 @@ public CdsCreateResponseDto executePostCall(URI url,
return response;
} catch (HttpClientErrorException e) {
if (!forceTokenRetrieve && (e.getStatusCode().equals(HttpStatus.UNAUTHORIZED))) {
return this.executePostCall(url, subPath, isProtectedResource, fileInputStream, config, true);
return this.executePostCall(url, subPath, isProtectedResource, fileInputStream, config, true,
cdsRetry + 1);
} else {
throw buildExceptionWithMessage("POST", e.getStatusCode() , url.toString());
throw buildExceptionWithMessage("POST", e.getStatusCode(), url.toString());
}
} catch(Exception ex){
if (ex instanceof ResourceAccessException && ex.getCause() instanceof SocketTimeoutException
&& cdsRetry < 2) {
return this.executePostCall(url, subPath, isProtectedResource, fileInputStream, config, true,
cdsRetry + 1);
}
throw new EntRuntimeException(String.format(GENERIC_REST_ERROR_MSG, url.toString()), ex);
}
}

private InputStream cloneInputStream(InputStream is) {
try {
return IOUtils.toBufferedInputStream(is);
} catch (IOException e) {
throw new EntRuntimeException("Error in cloning input stream", e);
}
}
private MultiValueMap<String, Object> buildDirectoryBodyRequest(String subPath, boolean isProtectedResource){
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("path", subPath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,14 @@ private void create(String subPath, boolean isProtectedResource, Optional<InputS
this.validateAndReturnResourcePath(config, subPath, isProtectedResource);

URI apiUrl = CdsUrlUtils.buildCdsInternalApiUrl(config, configuration, "/upload/");
int cdsRetry = 0;
CdsCreateResponseDto response = caller.executePostCall(apiUrl,
subPath,
isProtectedResource,
fileInputStream,
config,
false);
false,
cdsRetry);

if (!response.isStatusOk()) {
throw new EntRuntimeException("Invalid status - Response " + response.isStatusOk());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,20 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;

import com.agiletec.aps.util.ApsTenantApplicationUtils;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand All @@ -39,14 +45,17 @@
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;

@ExtendWith(MockitoExtension.class)
Expand Down Expand Up @@ -93,7 +102,8 @@ void shouldCreateDirectoryForTenant() throws Exception {
false,
Optional.empty(),
Optional.ofNullable(tc),
false);
false,
0);

Assertions.assertTrue(ret.isStatusOk());

Expand All @@ -102,7 +112,8 @@ void shouldCreateDirectoryForTenant() throws Exception {
false,
Optional.empty(),
Optional.ofNullable(tc),
false);
false,
0);

Assertions.assertTrue(ret.isStatusOk());

Expand Down Expand Up @@ -147,7 +158,8 @@ void shouldManageErrorInExecutePostCall() throws Exception {
false,
Optional.empty(),
Optional.ofNullable(tc),
false)
false,
0)
);
assertEquals("Generic error in a rest call for url:'http://cds-kube-service:8081/mytenant/api/v1/upload/'", ex.getMessage());

Expand All @@ -166,7 +178,8 @@ void shouldManageErrorInExecutePostCall() throws Exception {
false,
Optional.empty(),
Optional.ofNullable(tc),
false)
false,
0)
);
Assertions.assertEquals(
"Invalid operation 'POST', response status:'502 BAD_GATEWAY' for url:'http://cds-kube-service:8081/mytenant/api/v1/upload/'",
Expand All @@ -187,14 +200,14 @@ void shouldManageErrorInExecutePostCall() throws Exception {
false,
Optional.empty(),
Optional.ofNullable(tc),
false)
false,
0)
);
Assertions.assertEquals(
"Invalid operation 'POST', response status:'401 UNAUTHORIZED' for url:'http://cds-kube-service:8081/mytenant/api/v1/upload/'",
ex.getMessage());

}

@Test
void shouldCreateFileForPrimary() throws Exception {
Map<String,String> configMap = Map.of("cdsPublicUrl","http://my-server/tenant1/cms-resources",
Expand Down Expand Up @@ -233,7 +246,8 @@ void shouldCreateFileForPrimary() throws Exception {
false,
Optional.ofNullable(is),
Optional.empty(),
false);
false,
0);

Assertions.assertTrue(ret.isStatusOk());

Expand Down Expand Up @@ -545,4 +559,103 @@ void shouldRetrieveFileAttribute() {
eq(new ParameterizedTypeReference<Map<String,Object>>(){}));
}

@Test
void shouldThrowExceptionIfAnErrorOccurInCloningFileInputStream() throws Exception {
// set the config map for tenant
Map<String, String> configMap = Map.of("cdsPublicUrl", "http://my-server/tenant1/cms-resources",
"cdsPrivateUrl", "http://cds-kube-service:8081/",
"cdsPath", "/mytenant/api/v1",
"kcAuthUrl", "http://tenant1.server.com/auth",
"kcRealm", "tenant1",
"kcClientId", "id",
"kcClientSecret", "secret",
"tenantCode", "my-tenant1");
TenantConfig tc = new TenantConfig(configMap);
Optional<TenantConfig> tenantConfig = Optional.of(tc);
// set tenant
ApsTenantApplicationUtils.setTenant("my-tenant");

// the input stream used to execute the post call
Optional<InputStream> fileInputStream = Optional.of(
new ByteArrayInputStream("fake".getBytes(StandardCharsets.UTF_8)));

Mockito.when(restTemplate.exchange(anyString(),
eq(HttpMethod.POST),
any(),
eq(new ParameterizedTypeReference<Map<String, Object>>() {
}))).thenReturn(
ResponseEntity.status(HttpStatus.OK).body(Map.of(OAuth2AccessToken.ACCESS_TOKEN, "entando")));

try (MockedStatic<IOUtils> utilities = Mockito.mockStatic(IOUtils.class)) {
utilities.when(() -> IOUtils.toBufferedInputStream(any()))
.thenThrow(new IOException());

URI url = URI.create("http://cds-kube-service:8081/mytenant/api/v1/upload/");
Exception ex = assertThrows(EntRuntimeException.class,
() -> cdsRemoteCaller.executePostCall(
url,
"/sub-path-testy",
false,
fileInputStream,
tenantConfig,
false,
0)
);
assertEquals("Generic error in a rest call for url:'http://cds-kube-service:8081/mytenant/api/v1/upload/'",
ex.getMessage());
assertEquals("Error in cloning input stream", ex.getCause().getMessage());

}
}

@Test
void shouldRetryOneTimeIfPostTimeOutOccurs() throws Exception {
// set the config map for tenant
Map<String, String> configMap = Map.of("cdsPublicUrl", "http://my-server/tenant1/cms-resources",
"cdsPrivateUrl", "http://cds-kube-service:8081/",
"cdsPath", "/mytenant/api/v1",
"kcAuthUrl", "http://tenant1.server.com/auth",
"kcRealm", "tenant1",
"kcClientId", "id",
"kcClientSecret", "secret",
"tenantCode", "my-tenant1");
TenantConfig tc = new TenantConfig(configMap);
Optional<TenantConfig> tenantConfig = Optional.of(tc);
// set tenant
ApsTenantApplicationUtils.setTenant("my-tenant");
// the input stream used to execute the post call
Optional<InputStream> fileInputStream = Optional.of(
new ByteArrayInputStream("fake".getBytes(StandardCharsets.UTF_8)));

CdsCreateRowResponseDto resp = new CdsCreateRowResponseDto();
resp.setStatus("OK");
List<CdsCreateRowResponseDto> list = new ArrayList<>();
list.add(resp);
URI url = URI.create("http://cds-kube-service:8081/mytenant/api/v1/upload/");
Mockito.when(restTemplate.exchange(eq(url), eq(HttpMethod.POST), any(),
eq(new ParameterizedTypeReference<List<CdsCreateRowResponseDto>>() {
})))

.thenThrow(new HttpClientErrorException(HttpStatus.UNAUTHORIZED))
.thenThrow(new ResourceAccessException("", new SocketTimeoutException("")))
.thenReturn(ResponseEntity.ok(list));

URI auth = URI.create("http://tenant1.server.com/auth/realms/tenant1/protocol/openid-connect/token");
Mockito.when(restTemplate.exchange(eq(auth.toString()),
eq(HttpMethod.POST),
any(),
eq(new ParameterizedTypeReference<Map<String, Object>>() {
}))).thenReturn(
ResponseEntity.status(HttpStatus.OK).body(Map.of("access_token", "xxxxxx")));

CdsCreateResponseDto responseDto = cdsRemoteCaller.executePostCall(
url,
"/sub-path-testy",
false,
fileInputStream,
tenantConfig,
false,
0);
assertTrue(responseDto.isStatusOk());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ void shouldCreateDirectory() throws Exception {
eq(false),
any(),
any(),
eq(false))).thenReturn(ret);
eq(false),
eq(0))).thenReturn(ret);

ApsTenantApplicationUtils.setTenant("my-tenant");
cdsStorageManager.createDirectory("/sub-path-testy",false);
Expand All @@ -96,7 +97,8 @@ void shouldCreateDirectory() throws Exception {
eq(false),
any(),
any(),
eq(false));
eq(false),
eq(0));

}

Expand Down Expand Up @@ -126,7 +128,8 @@ void shouldNotCreateDirectory() throws Exception {
eq(false),
any(),
any(),
eq(false))).thenReturn(ret);
eq(false),
eq(0))).thenReturn(ret);

ApsTenantApplicationUtils.setTenant("my-tenant");
Assertions.assertThatThrownBy(
Expand Down Expand Up @@ -157,7 +160,8 @@ void shouldCreateFile() throws Exception {
eq(false),
any(),
any(),
eq(false))).thenReturn(ret);
eq(false),
eq(0))).thenReturn(ret);

ApsTenantApplicationUtils.setTenant("my-tenant");
InputStream is = new ByteArrayInputStream("testo a casos".getBytes(StandardCharsets.UTF_8));
Expand All @@ -169,7 +173,8 @@ void shouldCreateFile() throws Exception {
eq(false),
any(),
any(),
eq(false));
eq(false),
eq(0));

}

Expand Down Expand Up @@ -533,7 +538,8 @@ void shouldEditFile() throws Exception {
eq(false),
any(),
any(),
eq(false))).thenReturn(ret);
eq(false),
eq(0))).thenReturn(ret);

ApsTenantApplicationUtils.setTenant("my-tenant");
InputStream is = new ByteArrayInputStream("testo a casos".getBytes(StandardCharsets.UTF_8));
Expand All @@ -552,7 +558,8 @@ void shouldEditFile() throws Exception {
eq(false),
any(),
any(),
eq(false));
eq(false),
eq(0));

}

Expand Down