diff --git a/README.md b/README.md index 4067c48..63febb3 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Go to your project's pom.xml file and add as dependency. com.liveperson.faas functions-client - 1.1.6 + 1.2.2 ``` @@ -35,10 +35,9 @@ FaasClient.Builder builder = new FaasWebClient.Builder(accountId); Furthermore you have to choose a method of authorization. Either you provide a client secret and client Id, as we use OAuth 2.0 with client credentials by default, -or you alternatively pass your own implementation of the `AuthSignatureBilder`. - -* [More information on Client Credentials](https://developers.liveperson.com/liveperson-functions-external-invocations-client-credentials.html) +or you alternatively pass your own implementation of the `AuthSignatureBuilder` or `AuthDpopSignatureBuilder`. +* [More information on Client Credentials](https://developers.liveperson.com/liveperson-functions-foundations-external-invocation.html#authentication) ```java String clientId = "clientId"; @@ -48,6 +47,9 @@ builder.withClientSecret(clientSecret); or AuthSignatureBuilder authSignatureBuilder = new YourAuthSignatureBuilder(); builder.withAuthSignatureBuilder(authSignatureBuider); +or // Oauth2 + DPoP (only for internal usage) +AuthDPoPSignatureBuilder authDPoPSignatureBuilder = new YourAuthDPoPSignatureBuilder(); +builder.withAuthDPoPSignatureBuilder(authDPoPSignatureBuilder); ``` This is the bare minimum that has to be provided to build the client. @@ -56,6 +58,10 @@ This is the bare minimum that has to be provided to build the client. FaasClient faasClient = builder.build(); ``` +### DPoP authorization + +The client supports Oauth2+DPoP authorization ONLY FOR INTERNAL USE in service-to-service. You must provide your implementation of the `AuthDPoPSignatureBuilder` during the initialization. + ### Optional fields for builder Additionally you can pass your own implementations of specific fields to the builder before building the client. @@ -139,7 +145,7 @@ optionalParams.setRequestId("requestId"); ### Fetching lambdas -**You have to use your own authentication method when fetching lambdas as it still relies on OAuth 1.0.** +**You have to use your own authentication method when fetching lambdas as it relies on OAuth 1.0. / Oauth 2.0 + DPoP**
Fetching lambdas of account diff --git a/src/main/java/com/liveperson/faas/client/FaaSWebClient.java b/src/main/java/com/liveperson/faas/client/FaaSWebClient.java index 15fa866..86d5175 100644 --- a/src/main/java/com/liveperson/faas/client/FaaSWebClient.java +++ b/src/main/java/com/liveperson/faas/client/FaaSWebClient.java @@ -15,11 +15,13 @@ import com.liveperson.faas.metriccollector.MetricCollector; import com.liveperson.faas.metriccollector.NullMetricCollector; import com.liveperson.faas.response.lambda.LambdaResponse; +import com.liveperson.faas.security.AuthDPoPSignatureBuilder; import com.liveperson.faas.security.AuthSignatureBuilder; import com.liveperson.faas.security.JwtSignatureBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.util.CollectionUtils; import org.springframework.util.StopWatch; import org.springframework.web.util.UriComponents; @@ -74,6 +76,7 @@ public class FaaSWebClient implements FaaSClient { private CsdsClient csdsClient; private RestClient restClient; private AuthSignatureBuilder authSignatureBuilder; + private AuthDPoPSignatureBuilder authDPoPSignatureBuilder; private MetricCollector metricCollector; private String accountId; private IsImplementedCache isImplementedCache; @@ -82,7 +85,7 @@ private FaaSWebClient() { } private static void updateQueryParams(String key, Map newQueryParamsMap, - UriComponentsBuilder uriComponentsBuilder) { + UriComponentsBuilder uriComponentsBuilder) { String value = newQueryParamsMap.get(key); if (value != null) { uriComponentsBuilder.queryParam(key, value); @@ -91,14 +94,14 @@ private static void updateQueryParams(String key, Map newQueryPa @Override public T invokeByUUID(String externalSystem, String lambdaUUID, FaaSInvocation data, Class responseType, - OptionalParams optionalParams) throws FaaSException { + OptionalParams optionalParams) throws FaaSException { String invokeUri = String.format(FaaSWebClient.INVOKE_UUID_URI, accountId, lambdaUUID); return invokeWithUri(externalSystem, data, responseType, invokeUri, optionalParams); } @Override public void invokeByUUID(String externalSystem, String lambdaUUID, FaaSInvocation data, - OptionalParams optionalParams) throws FaaSException { + OptionalParams optionalParams) throws FaaSException { String invokeUri = String.format(FaaSWebClient.INVOKE_UUID_URI, accountId, lambdaUUID); invokeWithUriNoResponse(externalSystem, data, invokeUri, optionalParams); @@ -106,45 +109,48 @@ public void invokeByUUID(String externalSystem, String lambdaUUID, FaaSInvocatio @Override public T invokeByEvent(String externalSystem, FaaSEvent event, FaaSInvocation data, Class responseType, - OptionalParams optionalParams) throws FaaSException { + OptionalParams optionalParams) throws FaaSException { String invokeUri = String.format(FaaSWebClient.INVOKE_EVENT_URI, accountId, event); return invokeWithUri(externalSystem, data, responseType, invokeUri, optionalParams); } @Override public T invokeByEvent(String externalSystem, String event, FaaSInvocation data, Class responseType, - OptionalParams optionalParams) throws FaaSException { + OptionalParams optionalParams) throws FaaSException { String invokeUri = String.format(FaaSWebClient.INVOKE_EVENT_URI, accountId, event); return invokeWithUri(externalSystem, data, responseType, invokeUri, optionalParams); } @Override public void invokeByEvent(String externalSystem, FaaSEvent event, FaaSInvocation data, - OptionalParams optionalParams) throws FaaSException { + OptionalParams optionalParams) throws FaaSException { String invokeUri = String.format(FaaSWebClient.INVOKE_EVENT_URI, accountId, event); invokeWithUriNoResponse(externalSystem, data, invokeUri, optionalParams); } @Override public void invokeByEvent(String externalSystem, String event, FaaSInvocation data, - OptionalParams optionalParams) throws FaaSException { + OptionalParams optionalParams) throws FaaSException { String invokeUri = String.format(FaaSWebClient.INVOKE_EVENT_URI, accountId, event); invokeWithUriNoResponse(externalSystem, data, invokeUri, optionalParams); } - public boolean isImplemented(String externalSystem, FaaSEvent event, OptionalParams optionalParams) throws FaaSException { + public boolean isImplemented(String externalSystem, FaaSEvent event, OptionalParams optionalParams) + throws FaaSException { String eventId = event.toString(); return isEventImplemented(externalSystem, eventId, optionalParams); } @Override - public boolean isImplemented(String externalSystem, String event, OptionalParams optionalParams) throws FaaSException { + public boolean isImplemented(String externalSystem, String event, OptionalParams optionalParams) + throws FaaSException { return isEventImplemented(externalSystem, event, optionalParams); } - private boolean isEventImplemented(String externalSystem, String event, OptionalParams optionalParams) throws FaaSException { - String requestId = optionalParams.getRequestId().equals("") ? UUID.randomUUID().toString() : - optionalParams.getRequestId(); + private boolean isEventImplemented(String externalSystem, String event, OptionalParams optionalParams) + throws FaaSException { + String requestId = optionalParams.getRequestId().equals("") ? UUID.randomUUID().toString() + : optionalParams.getRequestId(); int timeOutInMs = optionalParams.getTimeOutInMs(); StopWatch stopWatch = new StopWatch(); stopWatch.start(); @@ -157,8 +163,11 @@ private boolean isEventImplemented(String externalSystem, String event, Optional String invokeUri = String.format(IS_IMPLEMENTED_URI, accountId, event); url = buildGWDomainUrl(externalSystem, invokeUri); logger.info(String.format(REQUEST_LOG_IS_IMPLEMENTED, requestId, accountId, url)); - String authHeader = authSignatureBuilder.getAuthHeader(); - String response = restClient.get(url, this.setHeaders(authHeader, requestId), timeOutInMs); + + Map headers = generateRequestHeaders(this.getGWDomain(), url, requestId, + HttpMethod.GET.name()); + + String response = restClient.get(url, headers, timeOutInMs); boolean isImplemented = objectMapper.readValue(response, com.liveperson.faas.dto.FaaSEventImplemented.class).getImplemented(); @@ -184,16 +193,17 @@ private boolean isEventImplemented(String externalSystem, String event, Optional } private void collectMetricsIsImplementedFails(String externalSystem, String eventId, StopWatch stopWatch, - Exception e, int statusCode) { - if (stopWatch.isRunning()) stopWatch.stop(); + Exception e, int statusCode) { + if (stopWatch.isRunning()) + stopWatch.stop(); metricCollector.onIsImplementedFailure(externalSystem, stopWatch.getTotalTimeSeconds(), eventId, accountId, statusCode, e); } public List getLambdas(String userId, Map optionalQueryParams, - OptionalParams optionalParams) throws FaaSException { - String requestId = optionalParams.getRequestId().equals("") ? UUID.randomUUID().toString() : - optionalParams.getRequestId(); + OptionalParams optionalParams) throws FaaSException { + String requestId = optionalParams.getRequestId().equals("") ? UUID.randomUUID().toString() + : optionalParams.getRequestId(); int timeOutInMs = optionalParams.getTimeOutInMs(); StopWatch stopWatch = new StopWatch(); stopWatch.start(); @@ -201,10 +211,12 @@ public List getLambdas(String userId, Map option try { String getLambdasUri = String.format(GET_LAMBDAS_URI, accountId); url = buildUIDomainUrl(userId, optionalQueryParams, getLambdasUri); - // TODO: Needs Oauth1 still! - String authHeader = authSignatureBuilder.getAuthHeader(); + + Map headers = generateRequestHeaders(this.getUIDomain(), url, requestId, + HttpMethod.GET.name()); + logger.info(String.format(REQUEST_LOG_GET_LAMBDAS, requestId, accountId, url)); - String response = restClient.get(url, setHeaders(authHeader, requestId), timeOutInMs); + String response = restClient.get(url, headers, timeOutInMs); stopWatch.stop(); metricCollector.onGetLambdasSuccess(userId, stopWatch.getTotalTimeSeconds(), accountId); return objectMapper.readValue(response, new TypeReference>() { @@ -224,14 +236,15 @@ public List getLambdas(String userId, Map option } private void collectMetricsGetLambdasFails(String userId, StopWatch stopWatch, int statusCode, Exception e) { - if (stopWatch.isRunning()) stopWatch.stop(); + if (stopWatch.isRunning()) + stopWatch.stop(); metricCollector.onGetLambdasFailure(userId, stopWatch.getTotalTimeSeconds(), accountId, statusCode, e); } private T invokeWithUri(String externalSystem, FaaSInvocation data, - Class responseType, String invokeUri, OptionalParams optionalParams) throws FaaSException { - String requestId = optionalParams.getRequestId().equals("") ? UUID.randomUUID().toString() : - optionalParams.getRequestId(); + Class responseType, String invokeUri, OptionalParams optionalParams) throws FaaSException { + String requestId = optionalParams.getRequestId().equals("") ? UUID.randomUUID().toString() + : optionalParams.getRequestId(); int timeOutInMs = optionalParams.getTimeOutInMs(); StopWatch stopWatch = new StopWatch(); stopWatch.start(); @@ -242,9 +255,12 @@ private T invokeWithUri(String externalSystem, FaaSInvocation data, isLambda = invokeUri.contains("lambdas"); lambdaOrEventName = extractLambdaOrEventName(invokeUri); url = buildGWDomainUrl(externalSystem, invokeUri); - String authHeader = authSignatureBuilder.getAuthHeader(); + + Map headers = generateRequestHeaders(this.getGWDomain(), url, requestId, + HttpMethod.POST.name()); + logger.info(String.format(REQUEST_LOG_INVOKE, requestId, accountId, url, data)); - String response = restClient.post(url, this.setHeaders(authHeader, requestId), data.toString(), + String response = restClient.post(url, headers, data.toString(), timeOutInMs); collectMetricsForSuccessfulInvocation(externalSystem, stopWatch, isLambda, lambdaOrEventName); @@ -269,10 +285,10 @@ private String extractLambdaOrEventName(String invokeUri) { return lambdaOrEventName; } - private void invokeWithUriNoResponse(String externalSystem, FaaSInvocation data - , String invokeUri, OptionalParams optionalParams) throws FaaSException { - String requestId = optionalParams.getRequestId().equals("") ? UUID.randomUUID().toString() : - optionalParams.getRequestId(); + private void invokeWithUriNoResponse(String externalSystem, FaaSInvocation data, String invokeUri, + OptionalParams optionalParams) throws FaaSException { + String requestId = optionalParams.getRequestId().equals("") ? UUID.randomUUID().toString() + : optionalParams.getRequestId(); int timeOutInMs = optionalParams.getTimeOutInMs(); StopWatch stopWatch = new StopWatch(); stopWatch.start(); @@ -283,9 +299,12 @@ private void invokeWithUriNoResponse(String externalSystem, FaaSInvocation data isLambda = invokeUri.contains("lambdas"); lambdaOrEventName = extractLambdaOrEventName(invokeUri); url = buildGWDomainUrl(externalSystem, invokeUri); - String authHeader = authSignatureBuilder.getAuthHeader(); + + Map headers = generateRequestHeaders(this.getGWDomain(), url, requestId, + HttpMethod.POST.name()); + logger.info(String.format(REQUEST_LOG_INVOKE, requestId, accountId, url, data)); - restClient.post(url, this.setHeaders(authHeader, requestId), data.toString(), timeOutInMs); + restClient.post(url, headers, data.toString(), timeOutInMs); collectMetricsForSuccessfulInvocation(externalSystem, stopWatch, isLambda, lambdaOrEventName); } catch (RestException e) { logger.error(String.format(REQUEST_REST_EXCEPTION_LOG, url, requestId, accountId, e.getStatusCode(), @@ -302,8 +321,9 @@ private void invokeWithUriNoResponse(String externalSystem, FaaSInvocation data } private void collectMetricsForSuccessfulInvocation(String externalSystem, StopWatch stopWatch, boolean isLambda, - String lambdaOrEventName) { - if (stopWatch.isRunning()) stopWatch.stop(); + String lambdaOrEventName) { + if (stopWatch.isRunning()) + stopWatch.stop(); if (isLambda) metricCollector.onInvokeByUUIDSuccess(externalSystem, stopWatch.getTotalTimeSeconds(), lambdaOrEventName, accountId); @@ -313,8 +333,9 @@ private void collectMetricsForSuccessfulInvocation(String externalSystem, StopWa } private void collectMetricsForFailedInvocation(String externalSystem, StopWatch stopWatch, boolean isLambda, - String lambdaOrEventName, Exception e, int statusCode) { - if (stopWatch.isRunning()) stopWatch.stop(); + String lambdaOrEventName, Exception e, int statusCode) { + if (stopWatch.isRunning()) + stopWatch.stop(); if (isLambda) metricCollector.onInvokeByUUIDFailure(externalSystem, stopWatch.getTotalTimeSeconds(), lambdaOrEventName, accountId, statusCode, e); @@ -324,7 +345,7 @@ private void collectMetricsForFailedInvocation(String externalSystem, StopWatch } private String buildUIDomainUrl(String userId, Map optionalQueryParams, - String getLambdasUri) throws CsdsRetrievalException { + String getLambdasUri) throws CsdsRetrievalException { UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.newInstance() .scheme(PROTOCOL) .host(getUIDomain()) @@ -354,7 +375,8 @@ private String buildGWDomainUrl(String externalSystem, String invokeUri) throws private FaaSException handleFaaSInvocationException(RestException e) throws FaaSException { FaaSError faaSError = this.getFaaSError(e); - if (FaaSLambdaErrorCodes.contains(faaSError.getErrorCode())) throw new FaaSLambdaException(faaSError, e); + if (FaaSLambdaErrorCodes.contains(faaSError.getErrorCode())) + throw new FaaSLambdaException(faaSError, e); throw new FaaSDetailedException(faaSError, e); } @@ -367,19 +389,60 @@ private FaaSError getFaaSError(RestException e) throws FaaSException { } } + /** + * Generates the request headers based on the authentication method + * + * @param domain the request domain + * @param url the request full url including domain and path parameters + * @param requestId the request identification + * @param method the request method + * @return the request required headers + * @throws TokenGenerationException + * @throws DPoPJwtGenerationException + */ + private Map generateRequestHeaders(String domain, String url, String requestId, String method) + throws TokenGenerationException, DPoPJwtGenerationException { + if (this.authDPoPSignatureBuilder != null) { + String domainUrl = PROTOCOL + "://" + domain; + String accessToken = authDPoPSignatureBuilder.getAccessTokenInternal(domainUrl); + String dpopJwt = authDPoPSignatureBuilder.getDpopHeaderInternal(url, method, accessToken); + return this.getHeaders(accessToken, requestId, dpopJwt); + } else { + String authHeader = authSignatureBuilder.getAuthHeader(); + return this.getHeaders(authHeader, requestId); + } + } + /** * Set headers for the RESTful call * * @param authorizationHeader the authorization header + * @param requestId identifies the request * @return the relevant headers */ - private Map setHeaders(String authorizationHeader, String requestId) { + private Map getHeaders(String authorizationHeader, String requestId) { Map headers = new HashMap(); headers.put("X-REQUEST-ID", requestId); headers.put(HttpHeaders.AUTHORIZATION, authorizationHeader); return headers; } + /** + * Set headers for the RESTful call + * + * @param accessToken the Oauth2 + DPoP access token + * @param requestId identifies the request + * @param dpopJwt the DPoP header + * @return the relevant headers + */ + private Map getHeaders(String accessToken, String requestId, String dpopJwt) { + Map headers = new HashMap(); + headers.put("X-REQUEST-ID", requestId); + headers.put(HttpHeaders.AUTHORIZATION, "DPoP " + accessToken); + headers.put("DPoP", dpopJwt); + return headers; + } + /** * Determines the faas UI domain for the RESTful call. * This domain is used for managing & deploying functions. @@ -407,6 +470,7 @@ public static class Builder { private Map csdsMap; private RestClient restClient; private AuthSignatureBuilder authSignatureBuilder; + private AuthDPoPSignatureBuilder authDPoPSignatureBuilder; private MetricCollector metricCollector; private String accountId; private String clientSecret; @@ -442,6 +506,11 @@ public Builder withAuthSignatureBuilder(AuthSignatureBuilder authSignatureBuilde return this; } + public Builder withAuthDPoPSignatureBuilder(AuthDPoPSignatureBuilder authDPoPSignatureBuilder) { + this.authDPoPSignatureBuilder = authDPoPSignatureBuilder; + return this; + } + public Builder withMetricCollector(MetricCollector metricCollector) { this.metricCollector = metricCollector; return this; @@ -464,8 +533,7 @@ private boolean isInitalized(Object o) { public FaaSWebClient build() { FaaSWebClient client = new FaaSWebClient(); client.accountId = this.accountId; - client.restClient = isInitalized(this.restClient) ? this.restClient : - new DefaultRestClient(); + client.restClient = isInitalized(this.restClient) ? this.restClient : new DefaultRestClient(); if (isInitalized(this.metricCollector)) { client.metricCollector = this.metricCollector; } else { @@ -483,11 +551,15 @@ public FaaSWebClient build() { if (isInitalized(this.authSignatureBuilder)) { client.authSignatureBuilder = this.authSignatureBuilder; } else if (isInitalized(this.clientSecret) && isInitalized(this.clientId)) { - client.authSignatureBuilder = new JwtSignatureBuilder(client.restClient, client.csdsClient, accountId - , this.clientId, this.clientSecret); + client.authSignatureBuilder = new JwtSignatureBuilder(client.restClient, client.csdsClient, accountId, + this.clientId, this.clientSecret); + } else if (isInitalized(this.authDPoPSignatureBuilder)) { + client.authDPoPSignatureBuilder = this.authDPoPSignatureBuilder; } else { - throw new IllegalStateException("Neither AuthSignatureBuilder instance, nor clientId and clientSecret" + - " were provided, thus impossible to use any authentication method"); + throw new IllegalStateException( + "Neither AuthSignatureBuilder instance, nor clientId and clientSecret, nor AuthDpopSignatureBuilder" + + + " were provided, thus impossible to use any authentication method"); } if (isInitalized(this.isImplementedCache)) client.isImplementedCache = this.isImplementedCache; diff --git a/src/main/java/com/liveperson/faas/exception/DPoPJwtGenerationException.java b/src/main/java/com/liveperson/faas/exception/DPoPJwtGenerationException.java new file mode 100644 index 0000000..31adb43 --- /dev/null +++ b/src/main/java/com/liveperson/faas/exception/DPoPJwtGenerationException.java @@ -0,0 +1,12 @@ +package com.liveperson.faas.exception; + +public class DPoPJwtGenerationException extends Exception { + + public DPoPJwtGenerationException(String message) { + super(message); + } + + public DPoPJwtGenerationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/liveperson/faas/security/AuthDPoPSignatureBuilder.java b/src/main/java/com/liveperson/faas/security/AuthDPoPSignatureBuilder.java new file mode 100644 index 0000000..1d0ea33 --- /dev/null +++ b/src/main/java/com/liveperson/faas/security/AuthDPoPSignatureBuilder.java @@ -0,0 +1,35 @@ +package com.liveperson.faas.security; + +import com.liveperson.faas.exception.TokenGenerationException; +import com.liveperson.faas.exception.DPoPJwtGenerationException; + +/** + * Generates a Oauth2 + DPoP for authentication purposes + * OAuth2+DPoP authentication is only available INTERNALLY for service-to-service. + * + * @author eplazaso + */ +public interface AuthDPoPSignatureBuilder { + + /** + * Generate Oauth2 access token string. Called whenever the faas-client needs to authenticate to send a request. + * Its return value is used in the 'Authorization'-header of the request. + * + * @param domainUrl Protocol (HTTPS) + domain of the API registered in the authentication server required to get the access token. E.g., https://va.faasgw.liveperson.net + * @throws TokenGenerationException when token generation fails + * @return a string containing the access token that will be included in the 'Authorization' header + */ + String getAccessTokenInternal(String domainUrl) throws TokenGenerationException; + + /** + * Generate the DPoP header. Called whenever the faas-client needs to authenticate to send a request. + * + * @param url Request 'url' including protocol domain and path + * @param method 'http-method' of the request + * @param accessToken A string containing the access token that was returned by 'getAccessTokenInternal' method + * @return The the 'DPoP' header of the request. + * @throws DPoPJwtGenerationException when DPoP header generation fails + */ + String getDpopHeaderInternal(String url, String method, String accessToken) throws DPoPJwtGenerationException; + +} diff --git a/src/test/java/com/liveperson/faas/client/FaaSClientTest.java b/src/test/java/com/liveperson/faas/client/FaaSClientTest.java index 680f539..6080bce 100644 --- a/src/test/java/com/liveperson/faas/client/FaaSClientTest.java +++ b/src/test/java/com/liveperson/faas/client/FaaSClientTest.java @@ -11,6 +11,7 @@ import com.liveperson.faas.http.RestClient; import com.liveperson.faas.metriccollector.MetricCollector; import com.liveperson.faas.response.lambda.LambdaResponse; +import com.liveperson.faas.security.AuthDPoPSignatureBuilder; import com.liveperson.faas.security.AuthSignatureBuilder; import com.liveperson.faas.util.EventResponse; import com.liveperson.faas.util.UUIDResponse; @@ -31,807 +32,1033 @@ import java.util.List; import java.util.Map; import java.util.TimeZone; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyFloat; +import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyMap; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) public class FaaSClientTest { + private final ObjectMapper objectMapper = new ObjectMapper(); + private final SimpleDateFormat mockDateFormat = new SimpleDateFormat("yyyy-MM-dd"); + @InjectMocks + private FaaSWebClient client; + @InjectMocks + private FaaSWebClient clientWithDPoP; + @Mock + private RestClient restClientMock; + @Mock + private CsdsClient csdsClientMock; + @Captor + private ArgumentCaptor> httpHeaderCaptor; + @Captor + private ArgumentCaptor urlCaptor; + @Captor + private ArgumentCaptor httpBodyCaptor; + private String authHeader = "Bearer authenticate"; + @Mock + private AuthSignatureBuilder authSignatureBuilder; + @Mock + private AuthDPoPSignatureBuilder authDPoPSignatureBuilder; + @Mock + private MetricCollector metricCollectorMock; + @Mock + private DefaultIsImplementedCache defaultIsImplementedCacheMock; + private String accountId = "11111111"; + private String apiVersion = "1"; + private String externalSystem = "test_system"; + private String userId = "0051393312"; + private FaaSEvent event = FaaSEvent.ChatPostSurveyEmailTranscript; + private String lambdaUUID = "81ec57ed-b353-4c71-8543-423364db169d"; + private String faasGWUrl = "faasGW.com"; + private String faasUIUrl = "faasUI.com"; + private String requestId = "requestId"; + private int defaultTimeOut = 15000; + // Oauth2 + DPOP + private String accessToken = "some_access_token"; + private String dpopHeader = "dpopJWT"; + + private OptionalParams optionalParams; + + @Before + public void before() throws Exception { + client = getFaaSClient(); + clientWithDPoP = getFaaSClientWithDpopAuth(); + optionalParams = new OptionalParams(); + mockDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + optionalParams.setTimeOutInMs(defaultTimeOut); + optionalParams.setRequestId(requestId); + when(csdsClientMock.getDomain(eq(FaaSWebClient.CSDS_GW_SERVICE_NAME))).thenReturn(faasGWUrl); + when(csdsClientMock.getDomain(eq(FaaSWebClient.CSDS_UI_SERVICE_NAME))).thenReturn(faasUIUrl); + when(authSignatureBuilder.getAuthHeader()).thenReturn(authHeader); + // Oauth2 + DPoP + when(authDPoPSignatureBuilder.getAccessTokenInternal(anyString())).thenReturn(accessToken); + when(authDPoPSignatureBuilder.getDpopHeaderInternal(anyString(), anyString(), anyString())) + .thenReturn(dpopHeader); + } + + @Test + public void invokeViaUUIDWithRequestId() throws Exception { + String payload = "request_data"; + long timestamp = System.currentTimeMillis(); + FaaSInvocation invocationData = new FaaSInvocation(null, payload); + invocationData.setTimestamp(timestamp); + optionalParams.setRequestId(requestId); + + when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn("\"lambda_result\""); + String response = client.invokeByUUID(externalSystem, lambdaUUID, invocationData, String.class, + optionalParams); + + verify(metricCollectorMock, times(1)).onInvokeByUUIDSuccess(eq(externalSystem), anyFloat(), + eq(lambdaUUID), + eq(accountId)); + assertEquals("Lambda invocation with the wrong body", + getExpectedRequestBody(timestamp, "[]", "\"request_data\""), httpBodyCaptor.getValue()); + assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( + "Authorization").contains("Bearer")); + assertTrue(httpHeaderCaptor.getValue().get( + "X-REQUEST-ID").contains(requestId)); + assertEquals("Lambda invocation result does not match expected value", + "lambda_result", response); + } + + @Test + public void invokeViaUUIDWithDPoP() throws Exception { + String payload = "request_data"; + long timestamp = System.currentTimeMillis(); + FaaSInvocation invocationData = new FaaSInvocation(null, payload); + invocationData.setTimestamp(timestamp); + + when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn("\"lambda_result\""); + String response = clientWithDPoP.invokeByUUID(externalSystem, lambdaUUID, invocationData, String.class, + optionalParams); + + verify(metricCollectorMock, times(1)).onInvokeByUUIDSuccess(eq(externalSystem), anyFloat(), + eq(lambdaUUID), + eq(accountId)); + verify(authDPoPSignatureBuilder, times(1)).getAccessTokenInternal(eq("https://" + faasGWUrl)); + verify(authDPoPSignatureBuilder, times(1)).getDpopHeaderInternal( + eq(getExpectedInvokeUUIDUrl()), + eq("POST"), eq(accessToken)); + assertEquals("Lambda invocation with the wrong body", + getExpectedRequestBody(timestamp, "[]", "\"request_data\""), httpBodyCaptor.getValue()); + assertTrue("Lambda invocation with wrong authorization header", + httpHeaderCaptor.getValue().get("Authorization").equals("DPoP " + accessToken)); + assertTrue("Lambda invocation with wrong DPoP header", + httpHeaderCaptor.getValue().get("DPoP").equals(dpopHeader)); + assertEquals("Lambda invocation result does not match expected value", + "lambda_result", response); + } + + @Test + public void invokeViaUUIDWithStringPayload() throws Exception { + String payload = "request_data"; + long timestamp = System.currentTimeMillis(); + FaaSInvocation invocationData = new FaaSInvocation(null, payload); + invocationData.setTimestamp(timestamp); + + when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn("\"lambda_result\""); + String response = client.invokeByUUID(externalSystem, lambdaUUID, invocationData, String.class, + optionalParams); + + verify(metricCollectorMock, times(1)).onInvokeByUUIDSuccess(eq(externalSystem), anyFloat(), + eq(lambdaUUID), + eq(accountId)); + assertEquals("Lambda invocation with the wrong body", + getExpectedRequestBody(timestamp, "[]", "\"request_data\""), httpBodyCaptor.getValue()); + assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( + "Authorization").contains("Bearer")); + assertEquals("Lambda invocation result does not match expected value", + "lambda_result", response); + } + + @Test + public void invokeViaUUIDWithoutPayload() throws Exception { + long timestamp = System.currentTimeMillis(); + FaaSInvocation invocationData = new FaaSInvocation(null, null); + invocationData.setTimestamp(timestamp); + + when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn("\"lambda_result\""); + String response = client.invokeByUUID(externalSystem, lambdaUUID, invocationData, String.class, + optionalParams); + + verify(metricCollectorMock, times(1)).onInvokeByUUIDSuccess(eq(externalSystem), anyFloat(), + eq(lambdaUUID), + eq(accountId)); + + assertEquals("Lambda invocation with the wrong body", + getExpectedRequestBody(timestamp, "[]", "{}"), httpBodyCaptor.getValue()); + assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( + "Authorization").contains("Bearer")); + assertEquals("Lambda invocation result does not match expected value", + "lambda_result", response); + } + + @Test + public void invokeViaUUIDWithUUIDResponsePayload() throws Exception { + UUIDResponse payload = new UUIDResponse(); + payload.key = "requestKey"; + payload.value = "requestValue"; + long timestamp = System.currentTimeMillis(); + Map headers = getTestHeaders(); + FaaSInvocation invocationData = getUUIDResponseFaaSInvocation(payload, timestamp, + headers); + UUIDResponse expectedResponse = new UUIDResponse(); + expectedResponse.key = "responseKey"; + expectedResponse.value = "responseValue"; + + when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn("{\"key\":\"responseKey\"," + + "\"value\":\"responseValue\"}"); + UUIDResponse response = client.invokeByUUID(externalSystem, lambdaUUID, invocationData, + UUIDResponse.class, + optionalParams); + + verify(metricCollectorMock, times(1)).onInvokeByUUIDSuccess(eq(externalSystem), anyFloat(), + eq(lambdaUUID), + eq(accountId)); + assertEquals("Lambda invocation with the wrong body", + getExpectedRequestBody(timestamp, + "[{\"key\":\"testHeader\",\"value\":\"testHeaderValue\"}]", "{\"key" + + "\":\"requestKey\",\"value\":\"requestValue\"}"), + httpBodyCaptor.getValue()); + assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( + "Authorization").contains("Bearer")); + assertEquals("Lambda invocation result does not match expected value", expectedResponse.toString(), + response.toString()); + } + + @Test(expected = FaaSDetailedException.class) + public void invokeViaUUIDThrowsFaasDetailedException() throws IOException, FaaSException { + try { + FaaSError faaSError = new FaaSError("faas.error.code", "My custom error."); + RestException exception = new RestException("Error during rest call.", + objectMapper.writeValueAsString(faaSError), + 500); + long timestamp = System.currentTimeMillis(); + FaaSInvocation invocationData = new FaaSInvocation(null, null); + invocationData.setTimestamp(timestamp); + + when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))) + .thenThrow(exception); + client.invokeByUUID(externalSystem, lambdaUUID, invocationData, String.class, optionalParams); + } catch (Exception ex) { + verify(metricCollectorMock, times(1)).onInvokeByUUIDFailure(eq(externalSystem), anyFloat(), + eq(lambdaUUID), + eq(accountId), eq(500), any()); + throw ex; + } + } + + @Test(expected = FaaSLambdaException.class) + public void invokeViaUUIDThrowFaaSLambdaException() throws IOException, FaaSException { + try { + long timestamp = System.currentTimeMillis(); + FaaSInvocation invocationData = new FaaSInvocation(null, null); + invocationData.setTimestamp(timestamp); + FaaSError faaSError = new FaaSError(FaaSLambdaErrorCodes.CUSTOM_FAILURE.getCode(), + "My custom error."); + + when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))) + .thenThrow(new RestException("Error during rest call.", + objectMapper.writeValueAsString(faaSError), + 500)); + client.invokeByUUID(externalSystem, lambdaUUID, invocationData, String.class, optionalParams); + } catch (Exception ex) { + verify(metricCollectorMock, times(1)).onInvokeByUUIDFailure(eq(externalSystem), anyFloat(), + eq(lambdaUUID), + eq(accountId), eq(500), any()); + throw ex; + } + } + + @Test(expected = FaaSException.class) + public void invokeViaUUIDThrowsFaaSException() throws IOException, FaaSException { + try { + long timestamp = System.currentTimeMillis(); + FaaSInvocation invocationData = new FaaSInvocation(null, null); + invocationData.setTimestamp(timestamp); + when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))) + .thenThrow(NullPointerException.class); + client.invokeByUUID(externalSystem, lambdaUUID, invocationData, String.class, optionalParams); + } catch (Exception ex) { + verify(metricCollectorMock, times(1)).onInvokeByUUIDFailure(eq(externalSystem), anyFloat(), + eq(lambdaUUID), + eq(accountId), eq(-1), any()); + throw ex; + + } + } + + @Test + public void invokeViaEventType() throws Exception { + UUIDResponse payload = new UUIDResponse(); + payload.key = "requestKey"; + payload.value = "requestValue"; + long timestamp = System.currentTimeMillis(); + String mockResponse = "[\n" + + " {\n" + + " \"uuid\": \"" + lambdaUUID + "\",\n" + + " \"timestamp\": \"2017-07-09\",\n" + + " \"result\": {\n" + + " \"key\": \"responseKey\",\n" + + " \"value\": \"responseValue\"\n" + + " }\n" + + " }\n" + + "]"; + Map headers = getTestHeaders(); + FaaSInvocation invocationData = getUUIDResponseFaaSInvocation(payload, timestamp, + headers); + EventResponse expectedResponse = getExpectedResponse(); + + when(restClientMock.post(eq(getExpectedInvokeEventUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn(mockResponse); + EventResponse[] response = client.invokeByEvent(externalSystem, + FaaSEvent.ChatPostSurveyEmailTranscript, + invocationData, EventResponse[].class, optionalParams); + + verify(metricCollectorMock, times(1)).onInvokeByEventSuccess(eq(externalSystem), anyInt(), + eq(event.toString()), + eq(accountId)); + assertEquals("Lambda invocation with the wrong body", + getExpectedRequestBody(timestamp, + "[{\"key\":\"testHeader\",\"value\":\"testHeaderValue\"}]", "{\"key" + + "\":\"requestKey\",\"value\":\"requestValue\"}"), + httpBodyCaptor.getValue()); + assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( + "Authorization").contains("Bearer")); + assertEquals("Lambda invocation result does not match expected value", expectedResponse.toString(), + response[0].toString()); + } + + @Test + public void invokeViaEventTypeWithAuthDPoP() throws Exception { + UUIDResponse payload = new UUIDResponse(); + payload.key = "requestKey"; + payload.value = "requestValue"; + long timestamp = System.currentTimeMillis(); + String mockResponse = "[\n" + + " {\n" + + " \"uuid\": \"" + lambdaUUID + "\",\n" + + " \"timestamp\": \"2017-07-09\",\n" + + " \"result\": {\n" + + " \"key\": \"responseKey\",\n" + + " \"value\": \"responseValue\"\n" + + " }\n" + + " }\n" + + "]"; + Map headers = getTestHeaders(); + FaaSInvocation invocationData = getUUIDResponseFaaSInvocation(payload, timestamp, + headers); + EventResponse expectedResponse = getExpectedResponse(); + + when(restClientMock.post(eq(getExpectedInvokeEventUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn(mockResponse); + EventResponse[] response = clientWithDPoP.invokeByEvent(externalSystem, + FaaSEvent.ChatPostSurveyEmailTranscript, + invocationData, EventResponse[].class, optionalParams); + + verify(metricCollectorMock, times(1)).onInvokeByEventSuccess(eq(externalSystem), anyInt(), + eq(event.toString()), + eq(accountId)); + verify(authDPoPSignatureBuilder, times(1)).getAccessTokenInternal(eq("https://" + faasGWUrl)); + verify(authDPoPSignatureBuilder, times(1)).getDpopHeaderInternal( + eq(getExpectedInvokeEventUrl()), + eq("POST"), eq(accessToken)); + assertEquals("Lambda invocation with the wrong body", + getExpectedRequestBody(timestamp, + "[{\"key\":\"testHeader\",\"value\":\"testHeaderValue\"}]", "{\"key" + + "\":\"requestKey\",\"value\":\"requestValue\"}"), + httpBodyCaptor.getValue()); + assertTrue("Lambda invocation with wrong authorization header", + httpHeaderCaptor.getValue().get("Authorization").equals("DPoP " + accessToken)); + assertTrue("Lambda invocation with wrong DPoP header", + httpHeaderCaptor.getValue().get("DPoP").equals(dpopHeader)); + assertEquals("Lambda invocation result does not match expected value", expectedResponse.toString(), + response[0].toString()); + } + + @Test + public void invokeViaEventTypeWithEventString() throws Exception { + UUIDResponse payload = new UUIDResponse(); + payload.key = "requestKey"; + payload.value = "requestValue"; + long timestamp = System.currentTimeMillis(); + String mockResponse = "[\n" + + " {\n" + + " \"uuid\": \"" + lambdaUUID + "\",\n" + + " \"timestamp\": \"2017-07-09\",\n" + + " \"result\": {\n" + + " \"key\": \"responseKey\",\n" + + " \"value\": \"responseValue\"\n" + + " }\n" + + " }\n" + + "]"; + Map headers = getTestHeaders(); + FaaSInvocation invocationData = getUUIDResponseFaaSInvocation(payload, timestamp, + headers); + EventResponse expectedResponse = getExpectedResponse(); + + when(restClientMock.post(eq(getExpectedInvokeEventUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn(mockResponse); + EventResponse[] response = client.invokeByEvent(externalSystem, + FaaSEvent.ChatPostSurveyEmailTranscript.toString(), + invocationData, EventResponse[].class, optionalParams); + + verify(metricCollectorMock, times(1)).onInvokeByEventSuccess(eq(externalSystem), anyInt(), + eq(event.toString()), + eq(accountId)); + assertEquals("Lambda invocation with the wrong body", + getExpectedRequestBody(timestamp, + "[{\"key\":\"testHeader\",\"value\":\"testHeaderValue\"}]", "{\"key" + + "\":\"requestKey\",\"value\":\"requestValue\"}"), + httpBodyCaptor.getValue()); + assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( + "Authorization").contains("Bearer")); + assertEquals("Lambda invocation result does not match expected value", expectedResponse.toString(), + response[0].toString()); + } + + @Test + public void invokeViaEventTypeWithEventStringWithRequestId() throws Exception { + UUIDResponse payload = new UUIDResponse(); + payload.key = "requestKey"; + payload.value = "requestValue"; + long timestamp = System.currentTimeMillis(); + String mockResponse = "[\n" + + " {\n" + + " \"uuid\": \"" + lambdaUUID + "\",\n" + + " \"timestamp\": \"2017-07-09\",\n" + + " \"result\": {\n" + + " \"key\": \"responseKey\",\n" + + " \"value\": \"responseValue\"\n" + + " }\n" + + " }\n" + + "]"; + Map headers = getTestHeaders(); + FaaSInvocation invocationData = getUUIDResponseFaaSInvocation(payload, timestamp, + headers); + EventResponse expectedResponse = getExpectedResponse(); + optionalParams.setRequestId(requestId); + + when(restClientMock.post(eq(getExpectedInvokeEventUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn(mockResponse); + EventResponse[] response = client.invokeByEvent(externalSystem, + FaaSEvent.ChatPostSurveyEmailTranscript.toString(), + invocationData, EventResponse[].class, optionalParams); + + verify(metricCollectorMock, times(1)).onInvokeByEventSuccess(eq(externalSystem), anyInt(), + eq(event.toString()), + eq(accountId)); + assertEquals("Lambda invocation with the wrong body", + getExpectedRequestBody(timestamp, + "[{\"key\":\"testHeader\",\"value\":\"testHeaderValue\"}]", "{\"key" + + "\":\"requestKey\",\"value\":\"requestValue\"}"), + httpBodyCaptor.getValue()); + assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( + "Authorization").contains("Bearer")); + assertEquals("Lambda invocation result does not match expected value", expectedResponse.toString(), + response[0].toString()); + } + + @Test + public void invokeViaEventTypeWithRequestId() throws Exception { + UUIDResponse payload = new UUIDResponse(); + payload.key = "requestKey"; + payload.value = "requestValue"; + long timestamp = System.currentTimeMillis(); + String mockResponse = "[\n" + + " {\n" + + " \"uuid\": \"" + lambdaUUID + "\",\n" + + " \"timestamp\": \"2017-07-09\",\n" + + " \"result\": {\n" + + " \"key\": \"responseKey\",\n" + + " \"value\": \"responseValue\"\n" + + " }\n" + + " }\n" + + "]"; + Map headers = getTestHeaders(); + FaaSInvocation invocationData = getUUIDResponseFaaSInvocation(payload, timestamp, + headers); + EventResponse expectedResponse = getExpectedResponse(); + optionalParams.setRequestId(requestId); + + when(restClientMock.post(eq(getExpectedInvokeEventUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn(mockResponse); + EventResponse[] response = client.invokeByEvent(externalSystem, + FaaSEvent.ChatPostSurveyEmailTranscript, + invocationData, EventResponse[].class, optionalParams); + + verify(metricCollectorMock, times(1)).onInvokeByEventSuccess(eq(externalSystem), anyFloat(), + eq(event.toString()), eq(accountId)); + assertEquals("Lambda invocation with the wrong body", + getExpectedRequestBody(timestamp, + "[{\"key\":\"testHeader\",\"value\":\"testHeaderValue\"}]", "{\"key" + + "\":\"requestKey\",\"value\":\"requestValue\"}"), + httpBodyCaptor.getValue()); + assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( + "Authorization").contains("Bearer")); + assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( + "X-REQUEST-ID").contains(requestId)); + assertEquals("Lambda invocation result does not match expected value", expectedResponse.toString(), + response[0].toString()); + } + + @Test(expected = FaaSException.class) + public void invokeViaEventTypeThrowFaaSException() throws IOException, FaaSException { + try { + long timestamp = System.currentTimeMillis(); + FaaSInvocation invocationData = new FaaSInvocation(null, null); + invocationData.setTimestamp(timestamp); + + when(restClientMock.post(eq(getExpectedInvokeEventUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))) + .thenThrow(new IOException("Error during rest call.")); + client.invokeByEvent(externalSystem, event, invocationData, EventResponse[].class, + optionalParams); + } catch (Exception ex) { + verify(metricCollectorMock, times(1)).onInvokeByEventFailure(eq(externalSystem), anyFloat(), + eq(event.toString()), eq(accountId), eq(-1), any()); + throw ex; + } + } + + @Test(expected = FaaSLambdaException.class) + public void invokeViaEventTypeThrowsFaaSLambdaException() throws IOException, FaaSException { + try { + long timestamp = System.currentTimeMillis(); + FaaSInvocation invocationData = new FaaSInvocation(null, null); + invocationData.setTimestamp(timestamp); + FaaSError faaSError = new FaaSError(FaaSLambdaErrorCodes.CUSTOM_FAILURE.getCode(), + "My custom error."); + + when(restClientMock.post(eq(getExpectedInvokeEventUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))) + .thenThrow(new RestException("Error during rest call.", + objectMapper.writeValueAsString(faaSError), + 500)); + client.invokeByEvent(externalSystem, event, invocationData, EventResponse[].class, + optionalParams); + } catch (Exception ex) { + verify(metricCollectorMock, times(1)).onInvokeByEventFailure(eq(externalSystem), anyFloat(), + eq(event.toString()), eq(accountId), eq(500), any()); + throw ex; + } + } + + @Test + public void invokeWithUUIDWithoutResponse() throws IOException, FaaSException { + Map headers = getHeaders(); + FaaSInvocation faaSInvocation = getStringFaaSInvocation(headers); + + client.invokeByUUID(externalSystem, lambdaUUID, faaSInvocation, optionalParams); + + verify(metricCollectorMock, times(1)).onInvokeByUUIDSuccess(eq(externalSystem), anyFloat(), + eq(lambdaUUID), + eq(accountId)); + verify(restClientMock, times(1)).post(getExpectedInvokeUUIDUrl(), headers, + faaSInvocation.toString(), optionalParams.getTimeOutInMs()); + } + + @Test + public void invokeWithUUIDWithoutResponseWithDPoP() throws Exception { + Map headers = getHeaders(); + FaaSInvocation faaSInvocation = getStringFaaSInvocation(headers); + + clientWithDPoP.invokeByUUID(externalSystem, lambdaUUID, faaSInvocation, optionalParams); + + verify(metricCollectorMock, times(1)).onInvokeByUUIDSuccess(eq(externalSystem), anyFloat(), + eq(lambdaUUID), + eq(accountId)); + + verify(authDPoPSignatureBuilder, times(1)).getAccessTokenInternal(eq("https://" + faasGWUrl)); + verify(authDPoPSignatureBuilder, times(1)).getDpopHeaderInternal( + eq(getExpectedInvokeUUIDUrl()), + eq("POST"), eq(accessToken)); + + Map expectedHeaders = getHeaders(); + expectedHeaders.put("Authorization", "DPoP " + accessToken); + expectedHeaders.put("DPoP", dpopHeader); + verify(restClientMock, times(1)).post(getExpectedInvokeUUIDUrl(), expectedHeaders, + faaSInvocation.toString(), optionalParams.getTimeOutInMs()); + } + + @Test + public void invokeWithUUIDWithRequestIdWithoutResponse() throws IOException, FaaSException { + Map headers = getCompleteHeaders(); + FaaSInvocation faaSInvocation = getStringFaaSInvocation(headers); + optionalParams.setRequestId(requestId); + + client.invokeByUUID(externalSystem, lambdaUUID, faaSInvocation, optionalParams); + + verify(metricCollectorMock, times(1)).onInvokeByUUIDSuccess(eq(externalSystem), anyFloat(), + eq(lambdaUUID), + eq(accountId)); + verify(restClientMock, times(1)).post(getExpectedInvokeUUIDUrl(), headers, + faaSInvocation.toString(), optionalParams.getTimeOutInMs()); + } + + @Test + public void invokeWithFaaSEventWithoutResponse() throws IOException, FaaSException { + Map headers = getHeaders(); + FaaSInvocation faaSInvocation = getStringFaaSInvocation(headers); + + client.invokeByEvent(externalSystem, event, faaSInvocation, optionalParams); + + verify(metricCollectorMock, times(1)).onInvokeByEventSuccess(eq(externalSystem), anyFloat(), + eq(event.toString()), eq(accountId)); + verify(restClientMock, times(1)).post(getExpectedInvokeEventUrl(), headers, + faaSInvocation.toString(), optionalParams.getTimeOutInMs()); + } + + @Test + public void invokeWithFaaSEventWithoutResponseWithDPoP() throws Exception { + Map headers = getHeaders(); + FaaSInvocation faaSInvocation = getStringFaaSInvocation(headers); + + clientWithDPoP.invokeByEvent(externalSystem, event, faaSInvocation, optionalParams); + + verify(authDPoPSignatureBuilder, times(1)).getAccessTokenInternal(eq("https://" + faasGWUrl)); + verify(authDPoPSignatureBuilder, times(1)).getDpopHeaderInternal( + eq(getExpectedInvokeEventUrl()), + eq("POST"), eq(accessToken)); + + Map expectedHeaders = getHeaders(); + expectedHeaders.put("Authorization", "DPoP " + accessToken); + expectedHeaders.put("DPoP", dpopHeader); + verify(restClientMock, times(1)).post(getExpectedInvokeEventUrl(), expectedHeaders, + faaSInvocation.toString(), optionalParams.getTimeOutInMs()); + } + + @Test + public void invokeWithFaaSEventWithEventStringWithoutResponse() throws IOException, FaaSException { + Map headers = getHeaders(); + FaaSInvocation faaSInvocation = getStringFaaSInvocation(headers); + + client.invokeByEvent(externalSystem, event.toString(), faaSInvocation, optionalParams); + + verify(metricCollectorMock, times(1)).onInvokeByEventSuccess(eq(externalSystem), anyFloat(), + eq(event.toString()), eq(accountId)); + verify(restClientMock, times(1)).post(getExpectedInvokeEventUrl(), headers, + faaSInvocation.toString(), optionalParams.getTimeOutInMs()); + } + + @Test + public void invokeWithFaaSEventWithRequestIdWithoutResponse() throws IOException, FaaSException { + Map headers = getCompleteHeaders(); + FaaSInvocation faaSInvocation = getStringFaaSInvocation(headers); + optionalParams.setRequestId(requestId); + + client.invokeByEvent(externalSystem, event, faaSInvocation, optionalParams); + + verify(metricCollectorMock, times(1)).onInvokeByEventSuccess(eq(externalSystem), anyFloat(), + eq(event.toString()), eq(accountId)); + verify(restClientMock, times(1)).post(getExpectedInvokeEventUrl(), headers, + faaSInvocation.toString(), optionalParams.getTimeOutInMs()); + } + + @Test + public void invokeWithFaaSEventWithRequestIdAndEventStringWithoutResponse() throws IOException, FaaSException { + Map headers = getCompleteHeaders(); + FaaSInvocation faaSInvocation = getStringFaaSInvocation(headers); + optionalParams.setRequestId(requestId); - private final ObjectMapper objectMapper = new ObjectMapper(); - private final SimpleDateFormat mockDateFormat = new SimpleDateFormat("yyyy-MM-dd"); - @InjectMocks - private FaaSWebClient client; - @Mock - private RestClient restClientMock; - @Mock - private CsdsClient csdsClientMock; - @Captor - private ArgumentCaptor> httpHeaderCaptor; - @Captor - private ArgumentCaptor urlCaptor; - @Captor - private ArgumentCaptor httpBodyCaptor; - private String authHeader = "Bearer authenticate"; - @Mock - private AuthSignatureBuilder authSignatureBuilder; - @Mock - private MetricCollector metricCollectorMock; - @Mock - private DefaultIsImplementedCache defaultIsImplementedCacheMock; - private String accountId = "11111111"; - private String apiVersion = "1"; - private String externalSystem = "test_system"; - private String userId = "0051393312"; - private FaaSEvent event = FaaSEvent.ChatPostSurveyEmailTranscript; - private String lambdaUUID = "81ec57ed-b353-4c71-8543-423364db169d"; - private String faasGWUrl = "faasGW.com"; - private String faasUIUrl = "faasUI.com"; - private String requestId = "requestId"; - private int defaultTimeOut = 15000; - - private OptionalParams optionalParams; - - @Before - public void before() throws Exception { - client = getFaaSClient(); - optionalParams = new OptionalParams(); - mockDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - optionalParams.setTimeOutInMs(defaultTimeOut); - optionalParams.setRequestId(requestId); - when(csdsClientMock.getDomain(eq(FaaSWebClient.CSDS_GW_SERVICE_NAME))).thenReturn(faasGWUrl); - when(csdsClientMock.getDomain(eq(FaaSWebClient.CSDS_UI_SERVICE_NAME))).thenReturn(faasUIUrl); - when(authSignatureBuilder.getAuthHeader()).thenReturn(authHeader); - } - - @Test - public void invokeViaUUIDWithRequestId() throws Exception { - String payload = "request_data"; - long timestamp = System.currentTimeMillis(); - FaaSInvocation invocationData = new FaaSInvocation(null, payload); - invocationData.setTimestamp(timestamp); - optionalParams.setRequestId(requestId); - - when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), - httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn("\"lambda_result\""); - String response = client.invokeByUUID(externalSystem, lambdaUUID, invocationData, String.class, optionalParams - ); - - verify(metricCollectorMock, times(1)).onInvokeByUUIDSuccess(eq(externalSystem), anyFloat(), eq(lambdaUUID), - eq(accountId)); - assertEquals("Lambda invocation with the wrong body", - getExpectedRequestBody(timestamp, "[]", "\"request_data\""), httpBodyCaptor.getValue()); - assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( - "Authorization").contains("Bearer")); - assertTrue(httpHeaderCaptor.getValue().get( - "X-REQUEST-ID").contains(requestId)); - assertEquals("Lambda invocation result does not match expected value", - "lambda_result", response); - - } - - @Test - public void invokeViaUUIDWithStringPayload() throws Exception { - String payload = "request_data"; - long timestamp = System.currentTimeMillis(); - FaaSInvocation invocationData = new FaaSInvocation(null, payload); - invocationData.setTimestamp(timestamp); - - when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), - httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn("\"lambda_result\""); - String response = client.invokeByUUID(externalSystem, lambdaUUID, invocationData, String.class, optionalParams); - - verify(metricCollectorMock, times(1)).onInvokeByUUIDSuccess(eq(externalSystem), anyFloat(), eq(lambdaUUID), - eq(accountId)); - assertEquals("Lambda invocation with the wrong body", - getExpectedRequestBody(timestamp, "[]", "\"request_data\""), httpBodyCaptor.getValue()); - assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( - "Authorization").contains("Bearer")); - assertEquals("Lambda invocation result does not match expected value", - "lambda_result", response); - } - - @Test - public void invokeViaUUIDWithoutPayload() throws Exception { - long timestamp = System.currentTimeMillis(); - FaaSInvocation invocationData = new FaaSInvocation(null, null); - invocationData.setTimestamp(timestamp); - - when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), - httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn("\"lambda_result\""); - String response = client.invokeByUUID(externalSystem, lambdaUUID, invocationData, String.class, optionalParams); - - verify(metricCollectorMock, times(1)).onInvokeByUUIDSuccess(eq(externalSystem), anyFloat(), eq(lambdaUUID), - eq(accountId)); - - assertEquals("Lambda invocation with the wrong body", - getExpectedRequestBody(timestamp, "[]", "{}"), httpBodyCaptor.getValue()); - assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( - "Authorization").contains("Bearer")); - assertEquals("Lambda invocation result does not match expected value", - "lambda_result", response); - } - - @Test - public void invokeViaUUIDWithUUIDResponsePayload() throws Exception { - UUIDResponse payload = new UUIDResponse(); - payload.key = "requestKey"; - payload.value = "requestValue"; - long timestamp = System.currentTimeMillis(); - Map headers = getTestHeaders(); - FaaSInvocation invocationData = getUUIDResponseFaaSInvocation(payload, timestamp, headers); - UUIDResponse expectedResponse = new UUIDResponse(); - expectedResponse.key = "responseKey"; - expectedResponse.value = "responseValue"; - - when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), - httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn("{\"key\":\"responseKey\"," + - "\"value\":\"responseValue\"}"); - UUIDResponse response = client.invokeByUUID(externalSystem, lambdaUUID, invocationData, UUIDResponse.class, - optionalParams); - - verify(metricCollectorMock, times(1)).onInvokeByUUIDSuccess(eq(externalSystem), anyFloat(), eq(lambdaUUID), - eq(accountId)); - assertEquals("Lambda invocation with the wrong body", - getExpectedRequestBody(timestamp, "[{\"key\":\"testHeader\",\"value\":\"testHeaderValue\"}]", "{\"key" + - "\":\"requestKey\",\"value\":\"requestValue\"}"), - httpBodyCaptor.getValue()); - assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( - "Authorization").contains("Bearer")); - assertEquals("Lambda invocation result does not match expected value", expectedResponse.toString(), - response.toString()); - } - - @Test(expected = FaaSDetailedException.class) - public void invokeViaUUIDThrowsFaasDetailedException() throws IOException, FaaSException { - try { - FaaSError faaSError = new FaaSError("faas.error.code", "My custom error."); - RestException exception = new RestException("Error during rest call.", - objectMapper.writeValueAsString(faaSError), - 500); - long timestamp = System.currentTimeMillis(); - FaaSInvocation invocationData = new FaaSInvocation(null, null); - invocationData.setTimestamp(timestamp); - - when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), - httpBodyCaptor.capture(), eq(defaultTimeOut))) - .thenThrow(exception); - client.invokeByUUID(externalSystem, lambdaUUID, invocationData, String.class, optionalParams); - } catch (Exception ex) { - verify(metricCollectorMock, times(1)).onInvokeByUUIDFailure(eq(externalSystem), anyFloat(), eq(lambdaUUID), - eq(accountId), eq(500), any()); - throw ex; - } - } - - @Test(expected = FaaSLambdaException.class) - public void invokeViaUUIDThrowFaaSLambdaException() throws IOException, FaaSException { - try { - long timestamp = System.currentTimeMillis(); - FaaSInvocation invocationData = new FaaSInvocation(null, null); - invocationData.setTimestamp(timestamp); - FaaSError faaSError = new FaaSError(FaaSLambdaErrorCodes.CUSTOM_FAILURE.getCode(), "My custom error."); - - when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), - httpBodyCaptor.capture(), eq(defaultTimeOut))) - .thenThrow(new RestException("Error during rest call.", objectMapper.writeValueAsString(faaSError), - 500)); - client.invokeByUUID(externalSystem, lambdaUUID, invocationData, String.class, optionalParams); - } catch (Exception ex) { - verify(metricCollectorMock, times(1)).onInvokeByUUIDFailure(eq(externalSystem), anyFloat(), eq(lambdaUUID), - eq(accountId), eq(500), any()); - throw ex; - } - } - - @Test(expected = FaaSException.class) - public void invokeViaUUIDThrowsFaaSException() throws IOException, FaaSException { - try { - long timestamp = System.currentTimeMillis(); - FaaSInvocation invocationData = new FaaSInvocation(null, null); - invocationData.setTimestamp(timestamp); - when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), - httpBodyCaptor.capture(), eq(defaultTimeOut))) - .thenThrow(NullPointerException.class); - client.invokeByUUID(externalSystem, lambdaUUID, invocationData, String.class, optionalParams); - } catch (Exception ex) { - verify(metricCollectorMock, times(1)).onInvokeByUUIDFailure(eq(externalSystem), anyFloat(), eq(lambdaUUID), - eq(accountId), eq(-1), any()); - throw ex; - - } - } - - @Test - public void invokeViaEventType() throws Exception { - UUIDResponse payload = new UUIDResponse(); - payload.key = "requestKey"; - payload.value = "requestValue"; - long timestamp = System.currentTimeMillis(); - String mockResponse = "[\n" + - " {\n" + - " \"uuid\": \"" + lambdaUUID + "\",\n" + - " \"timestamp\": \"2017-07-09\",\n" + - " \"result\": {\n" + - " \"key\": \"responseKey\",\n" + - " \"value\": \"responseValue\"\n" + - " }\n" + - " }\n" + - "]"; - Map headers = getTestHeaders(); - FaaSInvocation invocationData = getUUIDResponseFaaSInvocation(payload, timestamp, headers); - EventResponse expectedResponse = getExpectedResponse(); - - when(restClientMock.post(eq(getExpectedInvokeEventUrl()), httpHeaderCaptor.capture(), - httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn(mockResponse); - EventResponse[] response = client.invokeByEvent(externalSystem, - FaaSEvent.ChatPostSurveyEmailTranscript, - invocationData, EventResponse[].class, optionalParams); - - verify(metricCollectorMock, times(1)).onInvokeByEventSuccess(eq(externalSystem), anyInt(), eq(event.toString()), - eq(accountId)); - assertEquals("Lambda invocation with the wrong body", - getExpectedRequestBody(timestamp, "[{\"key\":\"testHeader\",\"value\":\"testHeaderValue\"}]", "{\"key" + - "\":\"requestKey\",\"value\":\"requestValue\"}"), - httpBodyCaptor.getValue()); - assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( - "Authorization").contains("Bearer")); - assertEquals("Lambda invocation result does not match expected value", expectedResponse.toString(), - response[0].toString()); - } - - @Test - public void invokeViaEventTypeWithEventString() throws Exception { - UUIDResponse payload = new UUIDResponse(); - payload.key = "requestKey"; - payload.value = "requestValue"; - long timestamp = System.currentTimeMillis(); - String mockResponse = "[\n" + - " {\n" + - " \"uuid\": \"" + lambdaUUID + "\",\n" + - " \"timestamp\": \"2017-07-09\",\n" + - " \"result\": {\n" + - " \"key\": \"responseKey\",\n" + - " \"value\": \"responseValue\"\n" + - " }\n" + - " }\n" + - "]"; - Map headers = getTestHeaders(); - FaaSInvocation invocationData = getUUIDResponseFaaSInvocation(payload, timestamp, headers); - EventResponse expectedResponse = getExpectedResponse(); - - when(restClientMock.post(eq(getExpectedInvokeEventUrl()), httpHeaderCaptor.capture(), - httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn(mockResponse); - EventResponse[] response = client.invokeByEvent(externalSystem, - FaaSEvent.ChatPostSurveyEmailTranscript.toString(), - invocationData, EventResponse[].class, optionalParams); - - verify(metricCollectorMock, times(1)).onInvokeByEventSuccess(eq(externalSystem), anyInt(), eq(event.toString()), - eq(accountId)); - assertEquals("Lambda invocation with the wrong body", - getExpectedRequestBody(timestamp, "[{\"key\":\"testHeader\",\"value\":\"testHeaderValue\"}]", "{\"key" + - "\":\"requestKey\",\"value\":\"requestValue\"}"), - httpBodyCaptor.getValue()); - assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( - "Authorization").contains("Bearer")); - assertEquals("Lambda invocation result does not match expected value", expectedResponse.toString(), - response[0].toString()); - } - - @Test - public void invokeViaEventTypeWithEventStringWithRequestId() throws Exception { - UUIDResponse payload = new UUIDResponse(); - payload.key = "requestKey"; - payload.value = "requestValue"; - long timestamp = System.currentTimeMillis(); - String mockResponse = "[\n" + - " {\n" + - " \"uuid\": \"" + lambdaUUID + "\",\n" + - " \"timestamp\": \"2017-07-09\",\n" + - " \"result\": {\n" + - " \"key\": \"responseKey\",\n" + - " \"value\": \"responseValue\"\n" + - " }\n" + - " }\n" + - "]"; - Map headers = getTestHeaders(); - FaaSInvocation invocationData = getUUIDResponseFaaSInvocation(payload, timestamp, headers); - EventResponse expectedResponse = getExpectedResponse(); - optionalParams.setRequestId(requestId); - - when(restClientMock.post(eq(getExpectedInvokeEventUrl()), httpHeaderCaptor.capture(), - httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn(mockResponse); - EventResponse[] response = client.invokeByEvent(externalSystem, - FaaSEvent.ChatPostSurveyEmailTranscript.toString(), - invocationData, EventResponse[].class, optionalParams); - - verify(metricCollectorMock, times(1)).onInvokeByEventSuccess(eq(externalSystem), anyInt(), eq(event.toString()), - eq(accountId)); - assertEquals("Lambda invocation with the wrong body", - getExpectedRequestBody(timestamp, "[{\"key\":\"testHeader\",\"value\":\"testHeaderValue\"}]", "{\"key" + - "\":\"requestKey\",\"value\":\"requestValue\"}"), - httpBodyCaptor.getValue()); - assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( - "Authorization").contains("Bearer")); - assertEquals("Lambda invocation result does not match expected value", expectedResponse.toString(), - response[0].toString()); - } - - @Test - public void invokeViaEventTypeWithRequestId() throws Exception { - UUIDResponse payload = new UUIDResponse(); - payload.key = "requestKey"; - payload.value = "requestValue"; - long timestamp = System.currentTimeMillis(); - String mockResponse = "[\n" + - " {\n" + - " \"uuid\": \"" + lambdaUUID + "\",\n" + - " \"timestamp\": \"2017-07-09\",\n" + - " \"result\": {\n" + - " \"key\": \"responseKey\",\n" + - " \"value\": \"responseValue\"\n" + - " }\n" + - " }\n" + - "]"; - Map headers = getTestHeaders(); - FaaSInvocation invocationData = getUUIDResponseFaaSInvocation(payload, timestamp, headers); - EventResponse expectedResponse = getExpectedResponse(); - optionalParams.setRequestId(requestId); - - when(restClientMock.post(eq(getExpectedInvokeEventUrl()), httpHeaderCaptor.capture(), - httpBodyCaptor.capture(), eq(defaultTimeOut))).thenReturn(mockResponse); - EventResponse[] response = client.invokeByEvent(externalSystem, - FaaSEvent.ChatPostSurveyEmailTranscript, - invocationData, EventResponse[].class, optionalParams); - - verify(metricCollectorMock, times(1)).onInvokeByEventSuccess(eq(externalSystem), anyFloat(), - eq(event.toString()), eq(accountId)); - assertEquals("Lambda invocation with the wrong body", - getExpectedRequestBody(timestamp, "[{\"key\":\"testHeader\",\"value\":\"testHeaderValue\"}]", "{\"key" + - "\":\"requestKey\",\"value\":\"requestValue\"}"), - httpBodyCaptor.getValue()); - assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( - "Authorization").contains("Bearer")); - assertTrue("Lambda invocation with wrong authorization header", httpHeaderCaptor.getValue().get( - "X-REQUEST-ID").contains(requestId)); - assertEquals("Lambda invocation result does not match expected value", expectedResponse.toString(), - response[0].toString()); - } - - @Test(expected = FaaSException.class) - public void invokeViaEventTypeThrowFaaSException() throws IOException, FaaSException { - try { - long timestamp = System.currentTimeMillis(); - FaaSInvocation invocationData = new FaaSInvocation(null, null); - invocationData.setTimestamp(timestamp); - - when(restClientMock.post(eq(getExpectedInvokeEventUrl()), httpHeaderCaptor.capture(), - httpBodyCaptor.capture(), eq(defaultTimeOut))) - .thenThrow(new IOException("Error during rest call.")); - client.invokeByEvent(externalSystem, event, invocationData, EventResponse[].class, optionalParams); - } catch (Exception ex) { - verify(metricCollectorMock, times(1)).onInvokeByEventFailure(eq(externalSystem), anyFloat(), - eq(event.toString()), eq(accountId), eq(-1), any()); - throw ex; - } - } - - @Test(expected = FaaSLambdaException.class) - public void invokeViaEventTypeThrowsFaaSLambdaException() throws IOException, FaaSException { - try { - long timestamp = System.currentTimeMillis(); - FaaSInvocation invocationData = new FaaSInvocation(null, null); - invocationData.setTimestamp(timestamp); - FaaSError faaSError = new FaaSError(FaaSLambdaErrorCodes.CUSTOM_FAILURE.getCode(), "My custom error."); - - when(restClientMock.post(eq(getExpectedInvokeEventUrl()), httpHeaderCaptor.capture(), - httpBodyCaptor.capture(), eq(defaultTimeOut))) - .thenThrow(new RestException("Error during rest call.", objectMapper.writeValueAsString(faaSError), - 500)); - client.invokeByEvent(externalSystem, event, invocationData, EventResponse[].class, optionalParams); - } catch (Exception ex) { - verify(metricCollectorMock, times(1)).onInvokeByEventFailure(eq(externalSystem), anyFloat(), - eq(event.toString()), eq(accountId), eq(500), any()); - throw ex; - } - } - - @Test - public void invokeWithUUIDWithoutResponse() throws IOException, FaaSException { - Map headers = getHeaders(); - FaaSInvocation faaSInvocation = getStringFaaSInvocation(headers); - - client.invokeByUUID(externalSystem, lambdaUUID, faaSInvocation, optionalParams); - - verify(metricCollectorMock, times(1)).onInvokeByUUIDSuccess(eq(externalSystem), anyFloat(), eq(lambdaUUID), - eq(accountId)); - verify(restClientMock, times(1)).post(getExpectedInvokeUUIDUrl(), headers, - faaSInvocation.toString(), optionalParams.getTimeOutInMs()); - } - - @Test - public void invokeWithUUIDWithRequestIdWithoutResponse() throws IOException, FaaSException { - Map headers = getCompleteHeaders(); - FaaSInvocation faaSInvocation = getStringFaaSInvocation(headers); - optionalParams.setRequestId(requestId); - - client.invokeByUUID(externalSystem, lambdaUUID, faaSInvocation, optionalParams); - - verify(metricCollectorMock, times(1)).onInvokeByUUIDSuccess(eq(externalSystem), anyFloat(), eq(lambdaUUID), - eq(accountId)); - verify(restClientMock, times(1)).post(getExpectedInvokeUUIDUrl(), headers, - faaSInvocation.toString(), optionalParams.getTimeOutInMs()); - } - - @Test - public void invokeWithFaaSEventWithoutResponse() throws IOException, FaaSException { - Map headers = getHeaders(); - FaaSInvocation faaSInvocation = getStringFaaSInvocation(headers); - - client.invokeByEvent(externalSystem, event, faaSInvocation, optionalParams); - - verify(metricCollectorMock, times(1)).onInvokeByEventSuccess(eq(externalSystem), anyFloat(), - eq(event.toString()), eq(accountId)); - verify(restClientMock, times(1)).post(getExpectedInvokeEventUrl(), headers, - faaSInvocation.toString(), optionalParams.getTimeOutInMs()); - } - - @Test - public void invokeWithFaaSEventWithEventStringWithoutResponse() throws IOException, FaaSException { - Map headers = getHeaders(); - FaaSInvocation faaSInvocation = getStringFaaSInvocation(headers); - - client.invokeByEvent(externalSystem, event.toString(), faaSInvocation, optionalParams); - - verify(metricCollectorMock, times(1)).onInvokeByEventSuccess(eq(externalSystem), anyFloat(), - eq(event.toString()), eq(accountId)); - verify(restClientMock, times(1)).post(getExpectedInvokeEventUrl(), headers, - faaSInvocation.toString(), optionalParams.getTimeOutInMs()); - } - - @Test - public void invokeWithFaaSEventWithRequestIdWithoutResponse() throws IOException, FaaSException { - Map headers = getCompleteHeaders(); - FaaSInvocation faaSInvocation = getStringFaaSInvocation(headers); - optionalParams.setRequestId(requestId); - - client.invokeByEvent(externalSystem, event, faaSInvocation, optionalParams); - - verify(metricCollectorMock, times(1)).onInvokeByEventSuccess(eq(externalSystem), anyFloat(), - eq(event.toString()), eq(accountId)); - verify(restClientMock, times(1)).post(getExpectedInvokeEventUrl(), headers, - faaSInvocation.toString(), optionalParams.getTimeOutInMs()); - } - - @Test - public void invokeWithFaaSEventWithRequestIdAndEventStringWithoutResponse() throws IOException, FaaSException { - Map headers = getCompleteHeaders(); - FaaSInvocation faaSInvocation = getStringFaaSInvocation(headers); - optionalParams.setRequestId(requestId); - - client.invokeByEvent(externalSystem, event.toString(), faaSInvocation, optionalParams); - - verify(metricCollectorMock, times(1)).onInvokeByEventSuccess(eq(externalSystem), anyFloat(), - eq(event.toString()), eq(accountId)); - verify(restClientMock, times(1)).post(getExpectedInvokeEventUrl(), headers, - faaSInvocation.toString(), optionalParams.getTimeOutInMs()); - } - - @Test(expected = FaaSDetailedException.class) - public void invokeViaUUIDNoResponseThrowsFaasDetailedException() throws Exception { - long timestamp = System.currentTimeMillis(); - FaaSInvocation invocationData = new FaaSInvocation(null, null); - invocationData.setTimestamp(timestamp); - FaaSError faaSError = new FaaSError("faas.error.code", "My custom error."); - - when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), httpBodyCaptor.capture() - , eq(defaultTimeOut))) - .thenThrow(new RestException("Error during rest call.", objectMapper.writeValueAsString(faaSError), - 500)); - client.invokeByUUID(externalSystem, lambdaUUID, invocationData, optionalParams); - } - - @Test(expected = FaaSLambdaException.class) - public void invokeViaUUIDNoResponseThrowFaaSLambdaException() throws Exception { - long timestamp = System.currentTimeMillis(); - FaaSInvocation invocationData = new FaaSInvocation(null, null); - invocationData.setTimestamp(timestamp); - FaaSError faaSError = new FaaSError(FaaSLambdaErrorCodes.CUSTOM_FAILURE.getCode(), "My custom error."); - - when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), httpBodyCaptor.capture() - , eq(defaultTimeOut))) - .thenThrow(new RestException("Error during rest call.", objectMapper.writeValueAsString(faaSError), - 500)); - client.invokeByUUID(externalSystem, lambdaUUID, invocationData, optionalParams); - } - - @Test(expected = FaaSException.class) - public void invokeViaUUIDNoResponseThrowsFaaSException() throws IOException, FaaSException { - try { - long timestamp = System.currentTimeMillis(); - FaaSInvocation invocationData = new FaaSInvocation(null, null); - invocationData.setTimestamp(timestamp); - - when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), - httpBodyCaptor.capture(), eq(defaultTimeOut))) - .thenThrow(NullPointerException.class); - client.invokeByUUID(externalSystem, lambdaUUID, invocationData, optionalParams); - } catch (Exception ex) { - verify(metricCollectorMock, times(1)).onInvokeByUUIDFailure(eq(externalSystem), anyFloat(), eq(lambdaUUID), - eq(accountId), eq(-1), any()); - throw ex; - } - } - - @Test - public void getLambdas() throws Exception { - String mockResponse = getMockResponse(); - - when(restClientMock.get(eq(getExpectedLambdasOfAnAccountUrl()), httpHeaderCaptor.capture(), eq(defaultTimeOut))) - .thenReturn(mockResponse); - List actualResponse = client.getLambdas(userId, new HashMap(), optionalParams); - List expectedResponse = objectMapper.readValue(mockResponse, - new TypeReference>() { - }); - - verify(metricCollectorMock, times(1)).onGetLambdasSuccess(eq(userId), anyFloat(), eq(accountId)); - assertEquals(expectedResponse.get(0), actualResponse.get(0)); - } - - @Test - public void getLambdasWithOptionalQueryParameters() throws IOException { - Map headers = getHeaders(); - LambdaResponse lambdaResponse = new LambdaResponse(); - LambdaResponse[] responses = new LambdaResponse[2]; - responses[0] = lambdaResponse; - String myResponse = objectMapper.writeValueAsString(responses); - HashMap filterMap = new HashMap(); - filterMap.put("state", "Productive"); - filterMap.put("eventId", "eventId"); - filterMap.put("name", "myFilter"); - String expectedUrl = String.format("https://faasUI.com/api/account/%s/lambdas?v=1&userId=%s" + - "&state=Productive&eventId=eventId&name=myFilter", accountId, userId); - - when(restClientMock.get(urlCaptor.capture(), any(), anyInt())).thenReturn(myResponse); - try { - client.getLambdas(userId, filterMap, optionalParams); - } catch (FaaSException e) { - } - - verify(metricCollectorMock, times(1)).onGetLambdasSuccess(eq(userId), anyFloat(), eq(accountId)); - assertEquals(expectedUrl, urlCaptor.getValue()); - - } - - @Test(expected = FaaSDetailedException.class) - public void getLambdasThrowFaaSDetailedException() throws Exception { - try { - FaaSError faaSError = new FaaSError("faas.error.code", "My custom error."); - - when(restClientMock.get(eq(getExpectedLambdasOfAnAccountUrl()), anyMap(), eq(defaultTimeOut))) - .thenThrow(new RestException("Error during call.", objectMapper.writeValueAsString(faaSError), - 500)); - - client.getLambdas(userId, new HashMap(), optionalParams); - } catch (Exception ex) { - verify(metricCollectorMock, times(1)).onGetLambdasFailure(eq(userId), anyFloat(), eq(accountId), eq(500), - any()); - throw ex; - } - } - - @Test(expected = FaaSException.class) - public void getLambdasThrowFaaSExceptionIfResponseNotParsable() throws IOException, FaaSException { - try { - when(restClientMock.get(eq(getExpectedLambdasOfAnAccountUrl()), httpHeaderCaptor.capture(), - eq(defaultTimeOut))) - .thenThrow(new RestException("Error during call.", "This is an unexpected error response.", 500)); - client.getLambdas(userId, new HashMap(), optionalParams); - } catch (Exception ex) { - verify(metricCollectorMock, times(1)).onGetLambdasFailure(eq(userId), anyFloat(), eq(accountId), eq(500), - any()); - throw ex; - } - } - - @Test(expected = FaaSException.class) - public void getLambdasThrowFaaSExceptionWhenRuntimeExceptionOccurs() throws IOException, FaaSException { - try { - when(restClientMock.get(eq(getExpectedLambdasOfAnAccountUrl()), httpHeaderCaptor.capture(), - eq(defaultTimeOut))) - .thenThrow(new NullPointerException()); - client.getLambdas(userId, new HashMap(), optionalParams); - } catch (Exception ex) { - verify(metricCollectorMock, times(1)).onGetLambdasFailure(eq(userId), anyFloat(), eq(accountId), eq(-1), - any()); - throw ex; - } - } - - @Test - public void isImplementedEventRetrievedFromCache() throws Exception { - FaaSEventImplementedExpiry eventExpiry = new FaaSEventImplementedExpiry(); - eventExpiry.setImplemented(true); - eventExpiry.setExpirationDate(LocalDateTime.now().plusMinutes(2)); - - when(restClientMock.get(eq(getExpectedIsImplementedUrl()), httpHeaderCaptor.capture(), eq(defaultTimeOut))).thenReturn( - "{\"implemented\": true}"); - when(defaultIsImplementedCacheMock.getIfCachedAndValid(eq(event.toString()))).thenReturn(eventExpiry); - - boolean isImplemented = client.isImplemented(externalSystem, event, optionalParams); - - verify(metricCollectorMock, times(0)).onIsImplementedSuccess(eq(externalSystem), anyFloat(), - eq(event.toString()), eq(accountId)); - assertTrue("Lambda should be implemented", isImplemented); - verify(restClientMock, times(0)).get(any(), any(), eq(optionalParams.getTimeOutInMs())); - } - - @Test(expected = FaaSDetailedException.class) - public void isImplementedThrowsFaaSDetailedException() throws IOException, FaaSException { - try { - FaaSError faaSError = new FaaSError("faas.error.code", "My custom error."); - - when(restClientMock.get(eq(getExpectedIsImplementedUrl()), httpHeaderCaptor.capture(), eq(defaultTimeOut))) - .thenThrow(new RestException("Error during rest call.", objectMapper.writeValueAsString(faaSError), - 500)); - - client.isImplemented(externalSystem, event, optionalParams); - } catch (Exception ex) { - verify(metricCollectorMock, times(1)).onIsImplementedFailure(eq(externalSystem), anyFloat(), - eq(event.toString()), eq(accountId), eq(500), any()); - throw ex; - } - } - - @Test(expected = FaaSException.class) - public void isImplementedThrowFaaSException() throws FaaSException, TokenGenerationException { - try { - when(authSignatureBuilder.getAuthHeader()).thenThrow(new TokenGenerationException("could not generate " + - "token")); - client.isImplemented(externalSystem, event, optionalParams); - } catch (Exception ex) { - verify(metricCollectorMock, times(1)).onIsImplementedFailure(eq(externalSystem), anyFloat(), - eq(event.toString()), eq(accountId), eq(-1), any()); - throw ex; - } - - } - - private FaaSWebClient getFaaSClient() { - return new FaaSWebClient.Builder(accountId).withCsdsClient(csdsClientMock) - .withRestClient(restClientMock) - .withAuthSignatureBuilder(authSignatureBuilder) - .withMetricCollector(metricCollectorMock) - .withIsImplementedCache(defaultIsImplementedCacheMock) - .build(); - } - - private ConcurrentMap setUpCache() { - ConcurrentMap implementationCache = new ConcurrentHashMap<>(); - LocalDateTime futureExpirationDate = LocalDateTime.now().plusMinutes(5); - FaaSEventImplementedExpiry expiry = new FaaSEventImplementedExpiry(); - expiry.setImplemented(true); - expiry.setExpirationDate(futureExpirationDate); - implementationCache.put(event.toString(), expiry); - return implementationCache; - } - - private Map getHeaders() { - Map headers = new HashMap<>(); - headers.put("Authorization", authHeader); - headers.put("X-REQUEST-ID", requestId); - return headers; - } - - private Map getCompleteHeaders() { - Map headers = new HashMap<>(); - headers.put("Authorization", authHeader); - headers.put("X-REQUEST-ID", requestId); - return headers; - } - - private Map getTestHeaders() { - Map headers = new HashMap(); - headers.put("testHeader", "testHeaderValue"); - return headers; - } - - private FaaSInvocation getUUIDResponseFaaSInvocation(UUIDResponse payload, long timestamp, - Map headers) { - FaaSInvocation invocationData = new FaaSInvocation(); - invocationData.setHeaders(headers); - invocationData.setPayload(payload); - invocationData.setTimestamp(timestamp); - return invocationData; - } - - private FaaSInvocation getStringFaaSInvocation(Map headers) { - FaaSInvocation faaSInvocation = new FaaSInvocation(); - faaSInvocation.setTimestamp(100); - faaSInvocation.setHeaders(headers); - faaSInvocation.setPayload("payload"); - return faaSInvocation; - } - - private String getExpectedInvokeUUIDUrl() { - String expectedUrl = "https://%s/api/account/%s/lambdas/%s/invoke?externalSystem=%s&v=%s"; - return String.format(expectedUrl, faasGWUrl, accountId, lambdaUUID, externalSystem, apiVersion); - } - - private String getExpectedInvokeEventUrl() { - String expectedUrl = "https://%s/api/account/%s/events/%s/invoke?externalSystem=%s&v=%s"; - return String.format(expectedUrl, faasGWUrl, accountId, event, externalSystem, apiVersion); - } - - private String getExpectedIsImplementedUrl() { - String expectedUrl = "https://%s/api/account/%s/events/%s/isImplemented?externalSystem=%s&v=%s"; - return String.format(expectedUrl, faasGWUrl, accountId, event, externalSystem, apiVersion); - } - - private String getExpectedLambdasOfAnAccountUrl() { - String expectedUrl = "https://%s/api/account/%s/lambdas?v=%s&userId=%s"; - String faasUIUrl = "faasUI.com"; - return String.format(expectedUrl, faasUIUrl, accountId, apiVersion, userId); - } - - private String getExpectedRequestBody(long timestamp, String headers, String payload) throws Exception { - String expectedBody = "{\"timestamp\":%d,\"headers\":%s,\"payload\":%s}"; - return String.format(expectedBody, timestamp, headers, payload); - } - - private String getMockResponse() { - return "[\n" + - " {\n" + - " \"uuid\": \"6d0372f3-524c-4fe9-a9a4-bf0157c9deac\",\n" + - " \"version\": 1,\n" + - " \"name\": \"Sergey_Test\",\n" + - " \"description\": \"Test\",\n" + - " \"samplePayload\": {\n" + - " \"headers\": [],\n" + - " \"payload\": {}\n" + - " },\n" + - " \"state\": \"Productive\",\n" + - " \"runtime\": {\n" + - " \"uuid\": \"57732DA8-24F3-486B-9582-2F2F8C2AF43D\",\n" + - " \"name\": \"Node.js 10\",\n" + - " \"baseImageName\": \"lp-building-block_snapshot/lp-openfaas-lambda-node-base-image:latest\"\n" + - " },\n" + - " \"createdBy\": \"2851393312\",\n" + - " \"updatedBy\": \"2851393312\",\n" + - " \"createdAt\": \"2019-07-22T20:51:28.000Z\",\n" + - " \"updatedAt\": \"2019-07-22T21:02:33.000Z\",\n" + - " \"lastDeployment\": {\n" + - " \"uuid\": \"31B65DAA-A5E7-4B73-BC9F-C30F1380571D\",\n" + - " \"name\": \"stoic_hopper6\",\n" + - " \"lambdaUUID\": \"6d0372f3-524c-4fe9-a9a4-bf0157c9deac\",\n" + - " \"lambdaVersion\": 1,\n" + - " \"createdAt\": \"2019-07-22T20:51:28.000Z\",\n" + - " \"deployedAt\": \"2019-07-22T21:02:33.000Z\",\n" + - " \"createdBy\": \"2851393312\",\n" + - " \"imageName\": \"lpcr.int.liveperson.net/faas/lp-tlv-6d0372f3-524c-4fe9-a9a4-bf0157c9deac:1\"," + - "\n" + - " \"deploymentState\": \"Deploy Finish\"\n" + - " },\n" + - " \"implementation\": {\n" + - " \"code\": \"function lambda(input, callback) {\\n callback(null, `Hello World`);\\n}\",\n" + - " \"dependencies\": [],\n" + - " \"environmentVariables\": []\n" + - " }\n" + - " }\n" + - "]"; - } - - private EventResponse getExpectedResponse() throws ParseException { - EventResponse expectedResponse = new EventResponse(); - expectedResponse.uuid = lambdaUUID; - expectedResponse.timestamp = mockDateFormat.parse("2017-07-09"); - expectedResponse.result = new UUIDResponse(); - expectedResponse.result.key = "responseKey"; - expectedResponse.result.value = "responseValue"; - return expectedResponse; - } + client.invokeByEvent(externalSystem, event.toString(), faaSInvocation, optionalParams); + + verify(metricCollectorMock, times(1)).onInvokeByEventSuccess(eq(externalSystem), anyFloat(), + eq(event.toString()), eq(accountId)); + verify(restClientMock, times(1)).post(getExpectedInvokeEventUrl(), headers, + faaSInvocation.toString(), optionalParams.getTimeOutInMs()); + } + + @Test(expected = FaaSDetailedException.class) + public void invokeViaUUIDNoResponseThrowsFaasDetailedException() throws Exception { + long timestamp = System.currentTimeMillis(); + FaaSInvocation invocationData = new FaaSInvocation(null, null); + invocationData.setTimestamp(timestamp); + FaaSError faaSError = new FaaSError("faas.error.code", "My custom error."); + + when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))) + .thenThrow(new RestException("Error during rest call.", + objectMapper.writeValueAsString(faaSError), + 500)); + client.invokeByUUID(externalSystem, lambdaUUID, invocationData, optionalParams); + } + + @Test(expected = FaaSLambdaException.class) + public void invokeViaUUIDNoResponseThrowFaaSLambdaException() throws Exception { + long timestamp = System.currentTimeMillis(); + FaaSInvocation invocationData = new FaaSInvocation(null, null); + invocationData.setTimestamp(timestamp); + FaaSError faaSError = new FaaSError(FaaSLambdaErrorCodes.CUSTOM_FAILURE.getCode(), "My custom error."); + + when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))) + .thenThrow(new RestException("Error during rest call.", + objectMapper.writeValueAsString(faaSError), + 500)); + client.invokeByUUID(externalSystem, lambdaUUID, invocationData, optionalParams); + } + + @Test(expected = FaaSException.class) + public void invokeViaUUIDNoResponseThrowsFaaSException() throws IOException, FaaSException { + try { + long timestamp = System.currentTimeMillis(); + FaaSInvocation invocationData = new FaaSInvocation(null, null); + invocationData.setTimestamp(timestamp); + + when(restClientMock.post(eq(getExpectedInvokeUUIDUrl()), httpHeaderCaptor.capture(), + httpBodyCaptor.capture(), eq(defaultTimeOut))) + .thenThrow(NullPointerException.class); + client.invokeByUUID(externalSystem, lambdaUUID, invocationData, optionalParams); + } catch (Exception ex) { + verify(metricCollectorMock, times(1)).onInvokeByUUIDFailure(eq(externalSystem), anyFloat(), + eq(lambdaUUID), + eq(accountId), eq(-1), any()); + throw ex; + } + } + + @Test + public void getLambdas() throws Exception { + String mockResponse = getMockResponse(); + + when(restClientMock.get(eq(getExpectedLambdasOfAnAccountUrl()), httpHeaderCaptor.capture(), + eq(defaultTimeOut))) + .thenReturn(mockResponse); + List actualResponse = client.getLambdas(userId, new HashMap(), + optionalParams); + List expectedResponse = objectMapper.readValue(mockResponse, + new TypeReference>() { + }); + + verify(metricCollectorMock, times(1)).onGetLambdasSuccess(eq(userId), anyFloat(), eq(accountId)); + assertEquals(expectedResponse.get(0), actualResponse.get(0)); + } + + @Test + public void getLambdasWithAuthDPoP() throws Exception { + String mockResponse = getMockResponse(); + + when(restClientMock.get(eq(getExpectedLambdasOfAnAccountUrl()), httpHeaderCaptor.capture(), + eq(defaultTimeOut))) + .thenReturn(mockResponse); + List actualResponse = clientWithDPoP.getLambdas(userId, new HashMap(), + optionalParams); + List expectedResponse = objectMapper.readValue(mockResponse, + new TypeReference>() { + }); + + verify(metricCollectorMock, times(1)).onGetLambdasSuccess(eq(userId), anyFloat(), eq(accountId)); + verify(authDPoPSignatureBuilder, times(1)).getAccessTokenInternal(eq("https://" + faasUIUrl)); + verify(authDPoPSignatureBuilder, times(1)).getDpopHeaderInternal( + eq(getExpectedLambdasOfAnAccountUrl()), + eq("GET"), eq(accessToken)); + assertTrue("Lambda invocation with wrong authorization header", + httpHeaderCaptor.getValue().get("Authorization").equals("DPoP " + accessToken)); + assertTrue("Lambda invocation with wrong DPoP header", + httpHeaderCaptor.getValue().get("DPoP").equals(dpopHeader)); + assertEquals(expectedResponse.get(0), actualResponse.get(0)); + } + + @Test + public void getLambdasWithOptionalQueryParameters() throws IOException { + Map headers = getHeaders(); + LambdaResponse lambdaResponse = new LambdaResponse(); + LambdaResponse[] responses = new LambdaResponse[2]; + responses[0] = lambdaResponse; + String myResponse = objectMapper.writeValueAsString(responses); + HashMap filterMap = new HashMap(); + filterMap.put("state", "Productive"); + filterMap.put("eventId", "eventId"); + filterMap.put("name", "myFilter"); + String expectedUrl = String.format("https://faasUI.com/api/account/%s/lambdas?v=1&userId=%s" + + "&state=Productive&eventId=eventId&name=myFilter", accountId, userId); + + when(restClientMock.get(urlCaptor.capture(), any(), anyInt())).thenReturn(myResponse); + try { + client.getLambdas(userId, filterMap, optionalParams); + } catch (FaaSException e) { + } + + verify(metricCollectorMock, times(1)).onGetLambdasSuccess(eq(userId), anyFloat(), eq(accountId)); + assertEquals(expectedUrl, urlCaptor.getValue()); + + } + + @Test(expected = FaaSDetailedException.class) + public void getLambdasThrowFaaSDetailedException() throws Exception { + try { + FaaSError faaSError = new FaaSError("faas.error.code", "My custom error."); + + when(restClientMock.get(eq(getExpectedLambdasOfAnAccountUrl()), anyMap(), eq(defaultTimeOut))) + .thenThrow(new RestException("Error during call.", + objectMapper.writeValueAsString(faaSError), + 500)); + + client.getLambdas(userId, new HashMap(), optionalParams); + } catch (Exception ex) { + verify(metricCollectorMock, times(1)).onGetLambdasFailure(eq(userId), anyFloat(), eq(accountId), + eq(500), + any()); + throw ex; + } + } + + @Test(expected = FaaSException.class) + public void getLambdasThrowFaaSExceptionIfResponseNotParsable() throws IOException, FaaSException { + try { + when(restClientMock.get(eq(getExpectedLambdasOfAnAccountUrl()), httpHeaderCaptor.capture(), + eq(defaultTimeOut))) + .thenThrow(new RestException("Error during call.", + "This is an unexpected error response.", 500)); + client.getLambdas(userId, new HashMap(), optionalParams); + } catch (Exception ex) { + verify(metricCollectorMock, times(1)).onGetLambdasFailure(eq(userId), anyFloat(), eq(accountId), + eq(500), + any()); + throw ex; + } + } + + @Test(expected = FaaSException.class) + public void getLambdasThrowFaaSExceptionWhenRuntimeExceptionOccurs() throws IOException, FaaSException { + try { + when(restClientMock.get(eq(getExpectedLambdasOfAnAccountUrl()), httpHeaderCaptor.capture(), + eq(defaultTimeOut))) + .thenThrow(new NullPointerException()); + client.getLambdas(userId, new HashMap(), optionalParams); + } catch (Exception ex) { + verify(metricCollectorMock, times(1)).onGetLambdasFailure(eq(userId), anyFloat(), eq(accountId), + eq(-1), + any()); + throw ex; + } + } + + @Test + public void isImplementedEventRetrievedFromCache() throws Exception { + FaaSEventImplementedExpiry eventExpiry = new FaaSEventImplementedExpiry(); + eventExpiry.setImplemented(true); + eventExpiry.setExpirationDate(LocalDateTime.now().plusMinutes(2)); + + when(restClientMock.get(eq(getExpectedIsImplementedUrl()), httpHeaderCaptor.capture(), + eq(defaultTimeOut))).thenReturn( + "{\"implemented\": true}"); + when(defaultIsImplementedCacheMock.getIfCachedAndValid(eq(event.toString()))).thenReturn(eventExpiry); + + boolean isImplemented = client.isImplemented(externalSystem, event, optionalParams); + + verify(metricCollectorMock, times(0)).onIsImplementedSuccess(eq(externalSystem), anyFloat(), + eq(event.toString()), eq(accountId)); + assertTrue("Lambda should be implemented", isImplemented); + verify(restClientMock, times(0)).get(any(), any(), eq(optionalParams.getTimeOutInMs())); + } + + @Test + public void isImplementedEventNoCacheWithDPoP() throws Exception { + when(restClientMock.get(eq(getExpectedIsImplementedUrl()), httpHeaderCaptor.capture(), + eq(defaultTimeOut))).thenReturn( + "{\"implemented\": true}"); + when(defaultIsImplementedCacheMock.getIfCachedAndValid(eq(event.toString()))).thenReturn(null); + + boolean isImplemented = clientWithDPoP.isImplemented(externalSystem, event, optionalParams); + + assertTrue("Lambda should be implemented", isImplemented); + // verify(restClientMock, times(0)).get(any(), any(), + // eq(optionalParams.getTimeOutInMs())); + verify(authDPoPSignatureBuilder, times(1)).getAccessTokenInternal(eq("https://" + faasGWUrl)); + verify(authDPoPSignatureBuilder, times(1)).getDpopHeaderInternal( + eq(getExpectedIsImplementedUrl()), + eq("GET"), eq(accessToken)); + assertTrue("Lambda invocation with wrong authorization header", + httpHeaderCaptor.getValue().get("Authorization").equals("DPoP " + accessToken)); + assertTrue("Lambda invocation with wrong DPoP header", + httpHeaderCaptor.getValue().get("DPoP").equals(dpopHeader)); + + } + + @Test(expected = FaaSDetailedException.class) + public void isImplementedThrowsFaaSDetailedException() throws IOException, FaaSException { + try { + FaaSError faaSError = new FaaSError("faas.error.code", "My custom error."); + + when(restClientMock.get(eq(getExpectedIsImplementedUrl()), httpHeaderCaptor.capture(), + eq(defaultTimeOut))) + .thenThrow(new RestException("Error during rest call.", + objectMapper.writeValueAsString(faaSError), + 500)); + + client.isImplemented(externalSystem, event, optionalParams); + } catch (Exception ex) { + verify(metricCollectorMock, times(1)).onIsImplementedFailure(eq(externalSystem), anyFloat(), + eq(event.toString()), eq(accountId), eq(500), any()); + throw ex; + } + } + + @Test(expected = FaaSException.class) + public void isImplementedThrowFaaSException() throws FaaSException, TokenGenerationException { + try { + when(authSignatureBuilder.getAuthHeader()) + .thenThrow(new TokenGenerationException("could not generate " + + "token")); + client.isImplemented(externalSystem, event, optionalParams); + } catch (Exception ex) { + verify(metricCollectorMock, times(1)).onIsImplementedFailure(eq(externalSystem), anyFloat(), + eq(event.toString()), eq(accountId), eq(-1), any()); + throw ex; + } + + } + + private FaaSWebClient getFaaSClient() { + return new FaaSWebClient.Builder(accountId).withCsdsClient(csdsClientMock) + .withRestClient(restClientMock) + .withAuthSignatureBuilder(authSignatureBuilder) + .withMetricCollector(metricCollectorMock) + .withIsImplementedCache(defaultIsImplementedCacheMock) + .build(); + } + + private FaaSWebClient getFaaSClientWithDpopAuth() { + return new FaaSWebClient.Builder(accountId).withCsdsClient(csdsClientMock) + .withRestClient(restClientMock) + .withAuthDPoPSignatureBuilder(this.authDPoPSignatureBuilder) + .withMetricCollector(metricCollectorMock) + .withIsImplementedCache(defaultIsImplementedCacheMock) + .build(); + } + + private Map getHeaders() { + Map headers = new HashMap<>(); + headers.put("Authorization", authHeader); + headers.put("X-REQUEST-ID", requestId); + return headers; + } + + private Map getCompleteHeaders() { + Map headers = new HashMap<>(); + headers.put("Authorization", authHeader); + headers.put("X-REQUEST-ID", requestId); + return headers; + } + + private Map getTestHeaders() { + Map headers = new HashMap(); + headers.put("testHeader", "testHeaderValue"); + return headers; + } + + private FaaSInvocation getUUIDResponseFaaSInvocation(UUIDResponse payload, long timestamp, + Map headers) { + FaaSInvocation invocationData = new FaaSInvocation(); + invocationData.setHeaders(headers); + invocationData.setPayload(payload); + invocationData.setTimestamp(timestamp); + return invocationData; + } + + private FaaSInvocation getStringFaaSInvocation(Map headers) { + FaaSInvocation faaSInvocation = new FaaSInvocation(); + faaSInvocation.setTimestamp(100); + faaSInvocation.setHeaders(headers); + faaSInvocation.setPayload("payload"); + return faaSInvocation; + } + + private String getExpectedInvokeUUIDUrl() { + String expectedUrl = "https://%s/api/account/%s/lambdas/%s/invoke?externalSystem=%s&v=%s"; + return String.format(expectedUrl, faasGWUrl, accountId, lambdaUUID, externalSystem, apiVersion); + } + + private String getExpectedInvokeEventUrl() { + String expectedUrl = "https://%s/api/account/%s/events/%s/invoke?externalSystem=%s&v=%s"; + return String.format(expectedUrl, faasGWUrl, accountId, event, externalSystem, apiVersion); + } + + private String getExpectedIsImplementedUrl() { + String expectedUrl = "https://%s/api/account/%s/events/%s/isImplemented?externalSystem=%s&v=%s"; + return String.format(expectedUrl, faasGWUrl, accountId, event, externalSystem, apiVersion); + } + + private String getExpectedLambdasOfAnAccountUrl() { + String expectedUrl = "https://%s/api/account/%s/lambdas?v=%s&userId=%s"; + String faasUIUrl = "faasUI.com"; + return String.format(expectedUrl, faasUIUrl, accountId, apiVersion, userId); + } + + private String getExpectedRequestBody(long timestamp, String headers, String payload) throws Exception { + String expectedBody = "{\"timestamp\":%d,\"headers\":%s,\"payload\":%s}"; + return String.format(expectedBody, timestamp, headers, payload); + } + + private String getMockResponse() { + return "[\n" + + " {\n" + + " \"uuid\": \"6d0372f3-524c-4fe9-a9a4-bf0157c9deac\",\n" + + " \"version\": 1,\n" + + " \"name\": \"Sergey_Test\",\n" + + " \"description\": \"Test\",\n" + + " \"samplePayload\": {\n" + + " \"headers\": [],\n" + + " \"payload\": {}\n" + + " },\n" + + " \"state\": \"Productive\",\n" + + " \"runtime\": {\n" + + " \"uuid\": \"57732DA8-24F3-486B-9582-2F2F8C2AF43D\",\n" + + " \"name\": \"Node.js 10\",\n" + + " \"baseImageName\": \"lp-building-block_snapshot/lp-openfaas-lambda-node-base-image:latest\"\n" + + + " },\n" + + " \"createdBy\": \"2851393312\",\n" + + " \"updatedBy\": \"2851393312\",\n" + + " \"createdAt\": \"2019-07-22T20:51:28.000Z\",\n" + + " \"updatedAt\": \"2019-07-22T21:02:33.000Z\",\n" + + " \"lastDeployment\": {\n" + + " \"uuid\": \"31B65DAA-A5E7-4B73-BC9F-C30F1380571D\",\n" + + " \"name\": \"stoic_hopper6\",\n" + + " \"lambdaUUID\": \"6d0372f3-524c-4fe9-a9a4-bf0157c9deac\",\n" + + " \"lambdaVersion\": 1,\n" + + " \"createdAt\": \"2019-07-22T20:51:28.000Z\",\n" + + " \"deployedAt\": \"2019-07-22T21:02:33.000Z\",\n" + + " \"createdBy\": \"2851393312\",\n" + + " \"imageName\": \"lpcr.int.liveperson.net/faas/lp-tlv-6d0372f3-524c-4fe9-a9a4-bf0157c9deac:1\"," + + + "\n" + + " \"deploymentState\": \"Deploy Finish\"\n" + + " },\n" + + " \"implementation\": {\n" + + " \"code\": \"function lambda(input, callback) {\\n callback(null, `Hello World`);\\n}\",\n" + + + " \"dependencies\": [],\n" + + " \"environmentVariables\": []\n" + + " }\n" + + " }\n" + + "]"; + } + + private EventResponse getExpectedResponse() throws ParseException { + EventResponse expectedResponse = new EventResponse(); + expectedResponse.uuid = lambdaUUID; + expectedResponse.timestamp = mockDateFormat.parse("2017-07-09"); + expectedResponse.result = new UUIDResponse(); + expectedResponse.result.key = "responseKey"; + expectedResponse.result.value = "responseValue"; + return expectedResponse; + } }