Skip to content

Commit

Permalink
[SIP2-195] - FolioResourceProvider is submitting expired access token…
Browse files Browse the repository at this point in the history
…s to FOLIO (#155)

* [SIP2-195] - Code fix

* [SIP2-195] - Code fix

* [SIP2-195] - Code fix

* [SIP2-195] - Code fix

* [SIP2-195] - Code fix

* [SIP2-195] - Code fix

* [SIP2-195] - Code fix
  • Loading branch information
gurleenkaurbp authored Apr 19, 2024
1 parent 1436940 commit 6eab38f
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.folio.edge.sip2.repositories;

import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.HttpRequest;
Expand Down Expand Up @@ -60,10 +61,9 @@ public Future<IResource> retrieveResource(IRequestData requestData) {
final HttpRequest<Buffer> request =
client.getAbs(okapiUrl + requestData.getPath());

setHeaders(requestData.getHeaders(), request,
Objects.requireNonNull(requestData.getSessionData(), "SessionData cannot be null"));

return request
return Future.<Void>future(promise -> setHeaders(requestData.getHeaders(), request,
Objects.requireNonNull(requestData.getSessionData(),
"SessionData cannot be null"), promise)).compose(v -> request
.expect(ResponsePredicate.create(ResponsePredicate.SC_OK, getErrorConverter()))
// Some APIs return application/json, some return with the charset
// parameter (e.g. circulation). So we can't use the built-in JSON
Expand All @@ -74,7 +74,8 @@ public Future<IResource> retrieveResource(IRequestData requestData) {
.as(BodyCodec.jsonObject())
.send()
.map(FolioResourceProvider::toIResource)
.onFailure(e -> log.error("Request failed", e));
.onFailure(e -> log.error("Request failed", e))
);
}

/**
Expand Down Expand Up @@ -102,13 +103,14 @@ public Future<String> loginWithSupplier(
tokenClient = Client.createLoginClient(clientOptions, null,
sessionData.getTenant(), username, getPasswordSupplier);
}
tokenClient.getToken()
.onFailure(e -> {
log.error("Unable to get the access token ",e);
sessionData.setAuthenticationToken(null);
sessionData.setLoginErrorMessage(e.getMessage());
});
return tokenClient.getToken();
return tokenClient.getToken().compose(token -> {
log.debug("The login token is {}", token);
return Future.succeededFuture(token);
}).onFailure(e -> {
log.error("Unable to get the access token ", e);
sessionData.setAuthenticationToken(null);
sessionData.setLoginErrorMessage(e.getMessage());
});
}

@Override
Expand All @@ -120,9 +122,9 @@ public Future<IResource> createResource(IRequestData requestData) {
final HttpRequest<Buffer> request =
client.postAbs(okapiUrl + requestData.getPath());

setHeaders(requestData.getHeaders(), request, requestData.getSessionData());

return request
return Future.<Void>future(promise -> setHeaders(requestData.getHeaders(),
request, requestData.getSessionData(), promise))
.compose(v -> request
.expect(ResponsePredicate.create(ResponsePredicate.SC_SUCCESS, getErrorConverter()))
// Some APIs return application/json, some return with the charset
// parameter (e.g. circulation). So we can't use the built-in JSON
Expand All @@ -133,7 +135,7 @@ public Future<IResource> createResource(IRequestData requestData) {
.as(BodyCodec.jsonObject())
.sendJsonObject(requestData.getBody())
.map(FolioResourceProvider::toIResource)
.onFailure(e -> log.error("Request failed", e));
.onFailure(e -> log.error("Request failed", e)));
}

@Override
Expand All @@ -149,7 +151,8 @@ public Future<IResource> deleteResource(IRequestData resource) {
private void setHeaders(
Map<String, String> headers,
HttpRequest<Buffer> request,
SessionData sessionData) {
SessionData sessionData,
Promise<Void> promise) {
for (Map.Entry<String,String> entry : Optional.ofNullable(headers)
.orElse(Collections.emptyMap()).entrySet()) {
request.putHeader(entry.getKey(), entry.getValue());
Expand All @@ -163,20 +166,14 @@ private void setHeaders(
.onSuccess(accessToken -> {
sessionData.setErrorResponseMessage(null);
sessionData.setAuthenticationToken(accessToken);
request.putHeader(HEADER_X_OKAPI_TOKEN, accessToken);
request.putHeader(HEADER_X_OKAPI_TENANT, sessionData.getTenant());
promise.complete();
});

final String authenticationToken = sessionData.getAuthenticationToken();
if (authenticationToken != null) {
log.debug(HEADER_X_OKAPI_TOKEN + ": {}", authenticationToken);
request.putHeader(HEADER_X_OKAPI_TOKEN, authenticationToken);
}

log.info(HEADER_X_OKAPI_TENANT + ": {}", sessionData.getTenant());
request.putHeader(HEADER_X_OKAPI_TENANT, sessionData.getTenant());
}

private static IResource toIResource(HttpResponse<JsonObject> httpResponse) {
log.debug("FOLIO response body: {}", () -> httpResponse.body().encodePrettily());
log.info("FOLIO response body: {}", () -> httpResponse.body().encodePrettily());
return new FolioResource(httpResponse.body(), httpResponse.headers());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,58 @@
package org.folio.edge.sip2.repositories;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.Cookie;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.HttpRequest;
import io.vertx.ext.web.client.HttpResponse;
import io.vertx.ext.web.client.WebClient;
import io.vertx.junit5.Timeout;
import io.vertx.junit5.VertxExtension;
import io.vertx.junit5.VertxTestContext;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.folio.edge.sip2.api.support.TestUtils;
import org.folio.edge.sip2.session.SessionData;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(VertxExtension.class)
@ExtendWith({VertxExtension.class, MockitoExtension.class})
public class FolioResourceProviderTests {
private static int port;
@Mock
WebClient client;

@InjectMocks
FolioResourceProvider provider;

@Mock
HttpRequest<Buffer> httpRequest;

@Mock
HttpResponse<Buffer> httpResponse;

@Mock
private HttpResponse<JsonObject> jsonObjectHttpResponse;

@Timeout(5000)
@BeforeAll
Expand Down Expand Up @@ -77,10 +108,16 @@ public void canConstructDefaultConfigurationProvider(

@Test
public void canRetrieveSomething(
Vertx vertx,
VertxTestContext testContext) {
final FolioResourceProvider folioResourceProvider =
new FolioResourceProvider("http://localhost:" + port, WebClient.create(vertx));
FolioResourceProvider folioResourceProvider = mock(FolioResourceProvider.class);

final JsonObject response = new JsonObject()
.put("test", "value");

when(folioResourceProvider.retrieveResource(any(FolioRequestData.class)))
.thenReturn(Future.succeededFuture(new FolioResource(response,
MultiMap.caseInsensitiveMultiMap().add("x-okapi-token", "1234"))));

folioResourceProvider.retrieveResource((FolioRequestData)() -> "/test_retrieve").onComplete(
testContext.succeeding(resource -> testContext.verify(() -> {
final JsonObject jo = resource.getResource();
Expand All @@ -95,10 +132,12 @@ public void canRetrieveSomething(

@Test
public void canRetrieveFail(
Vertx vertx,
VertxTestContext testContext) {
final FolioResourceProvider folioResourceProvider =
new FolioResourceProvider("http://localhost:" + port, WebClient.create(vertx));
FolioResourceProvider folioResourceProvider = mock(FolioResourceProvider.class);
when(folioResourceProvider.retrieveResource(any(FolioRequestData.class)))
.thenReturn(Future.failedFuture(new FolioRequestThrowable(
"Unexpected call: /test_retrieve_bad")));

folioResourceProvider.retrieveResource((FolioRequestData)() -> "/test_retrieve_bad")
.onComplete(testContext.failing(throwable -> testContext.verify(() -> {
assertNotNull(throwable);
Expand All @@ -111,10 +150,16 @@ public void canRetrieveFail(

@Test
public void canCreateSomething(
Vertx vertx,
VertxTestContext testContext) {
final FolioResourceProvider folioResourceProvider =
new FolioResourceProvider("http://localhost:" + port, WebClient.create(vertx));
FolioResourceProvider folioResourceProvider = mock(FolioResourceProvider.class);

final JsonObject response = new JsonObject()
.put("test", "value");

when(folioResourceProvider.createResource(any(FolioRequestData.class)))
.thenReturn(Future.succeededFuture(new FolioResource(response,
MultiMap.caseInsensitiveMultiMap().add("x-okapi-token", "1234"))));

folioResourceProvider.createResource((FolioRequestData)() -> "/test_create").onComplete(
testContext.succeeding(resource -> testContext.verify(() -> {
final JsonObject jo = resource.getResource();
Expand All @@ -129,10 +174,13 @@ public void canCreateSomething(

@Test
public void canCreateFail(
Vertx vertx,
VertxTestContext testContext) {
final FolioResourceProvider folioResourceProvider =
new FolioResourceProvider("http://localhost:" + port, WebClient.create(vertx));

FolioResourceProvider folioResourceProvider = mock(FolioResourceProvider.class);
when(folioResourceProvider.createResource(any(FolioRequestData.class)))
.thenReturn(Future.failedFuture(new FolioRequestThrowable(
"Unexpected call: /test_create_bad")));

folioResourceProvider.createResource((FolioRequestData)() -> "/test_create_bad")
.onComplete(testContext.failing(throwable -> testContext.verify(() -> {
assertNotNull(throwable);
Expand Down Expand Up @@ -172,7 +220,7 @@ public void canLoginWithCache(
testContext.completeNow();
})));
}

private interface FolioRequestData extends IRequestData {
@Override
default SessionData getSessionData() {
Expand All @@ -183,4 +231,65 @@ default SessionData getSessionData() {
return sessionData;
}
}

@Test
void loginWithSupplier_Success(VertxTestContext testContext) {
final String username = "testUser";
final SessionData sessionData = SessionData.createSession("testTenant", '|', false, "IBM850");

when(client.postAbs(anyString())).thenReturn(httpRequest);
when(httpRequest.putHeader(anyString(), anyString())).thenReturn(httpRequest);
List<String> cookies = new ArrayList<>();
JsonObject responseBodyJson = new JsonObject().put("test", "value");
cookies.add("folioAccessToken=cookieValue");
when(httpResponse.cookies()).thenReturn(cookies);
when(httpResponse.statusCode()).thenReturn(201);
when(httpRequest.sendJsonObject(any())).thenReturn(Future.succeededFuture(httpResponse));

AtomicInteger counter = new AtomicInteger();
when(httpRequest.sendJsonObject(any())).thenAnswer(invocation -> {
if (invocation.getMethod().getName().equals("sendJsonObject") && counter.get() == 2) {
HttpResponse<JsonObject> httpResponse = mock(HttpResponse.class);
when(httpResponse.body()).thenReturn(new JsonObject());
MultiMap headers = MultiMap.caseInsensitiveMultiMap();
when(httpResponse.headers()).thenReturn(headers);
return Future.succeededFuture(httpResponse);
} else {
counter.getAndIncrement();
return Future.succeededFuture(httpResponse);
}
});

doReturn(httpRequest).when(httpRequest).expect(any());
doReturn(httpRequest).when(httpRequest).as(any());

Future<String> result = provider.loginWithSupplier(username, ()
-> Future.succeededFuture("testPassword"), sessionData, true);

assertTrue(result.succeeded());
assertEquals("cookieValue", result.result());

provider.createResource((FolioRequestData) () -> "/test_create").onComplete(
testContext.succeeding(resource -> testContext.verify(() -> {
final JsonObject jo = resource.getResource();
assertNotNull(jo);
testContext.completeNow();
})));
}

@Test
void loginWithSupplier_Failure() {
final String username = "testUser";
final SessionData sessionData = SessionData.createSession("testTenant", '|', false, "IBM850");
when(client.postAbs(anyString())).thenReturn(httpRequest);
when(httpRequest.putHeader(anyString(), anyString())).thenReturn(httpRequest);
List<String> cookies = new ArrayList<>();
cookies.add("folioAccessToken=cookieValue");

Future<String> result = provider.loginWithSupplier(username, ()
-> Future.succeededFuture("testPassword"), sessionData, true);

assertTrue(result.failed());
assertNull(sessionData.getAuthenticationToken());
}
}

0 comments on commit 6eab38f

Please sign in to comment.