Skip to content

Commit

Permalink
Merge pull request #70 from dvsa/threadLocalMap
Browse files Browse the repository at this point in the history
feat:threadlocal & concurrency hashmaps
  • Loading branch information
sr4850 authored Aug 8, 2024
2 parents 483b7a9 + 8e46c53 commit f2defbf
Show file tree
Hide file tree
Showing 16 changed files with 148 additions and 115 deletions.
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -124,5 +124,10 @@
<artifactId>ion-java</artifactId>
<version>1.11.9</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
</dependencies>
</project>
59 changes: 50 additions & 9 deletions src/main/java/apiCalls/Utils/generic/BaseAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,89 @@

import activesupport.http.RestUtils;
import activesupport.system.Properties;
import activesupport.aws.s3.SecretsManager;
import apiCalls.actions.Token;
import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import io.restassured.response.ValidatableResponse;
import org.apache.hc.core5.http.HttpException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dvsa.testing.lib.url.api.URL;
import org.dvsa.testing.lib.url.utils.EnvironmentType;

import java.util.Date;
import java.util.HashMap;
import java.util.UUID;

public class BaseAPI extends Token {
protected static final Logger LOGGER = LogManager.getLogger(BaseAPI.class);

protected static EnvironmentType env = EnvironmentType.getEnum(Properties.get("env", true));

static Headers headers = new Headers();

public synchronized String adminJWT() throws HttpException {
String adminUser =SecretsManager.getSecret("adminUser");
String adminPassword = SecretsManager.getSecret("adminPassword");
if(getAdminToken() == null){
String adminUser = SecretsManager.getSecretValue("adminUser");
String adminPassword = SecretsManager.getSecretValue("adminPassword");

if (getAdminToken() == null || isTokenExpired(getAdminToken())) {
LOGGER.info("Generating new admin token");
generateAdminToken(adminUser, adminPassword);
} else {
LOGGER.info("Using existing admin token");
}

return getAdminToken();
}

private boolean isTokenExpired(String token) {
try {
DecodedJWT decodedJWT = JWT.decode(token);
return decodedJWT.getExpiresAt().before(new Date());
} catch (JWTDecodeException e) {
LOGGER.error("Error decoding token: " + e.getMessage());
return true;
}
}

protected void logApiCall(String requestId, String methodName, String message) {
LOGGER.info("RequestID: {}, Method: {}, Message: {}", requestId, methodName, message);
}

public synchronized HashMap<String, String> header(String requestId) throws HttpException {
headers.getApiHeader().put("Authorization", "Bearer " + adminJWT());
logApiCall(requestId, "header", "Authorization header set.");
return (HashMap<String, String>) headers.getApiHeader();
}

public synchronized String fetchApplicationInformation(String applicationNumber, String jsonPath, String defaultReturn) throws HttpException {
String requestId = UUID.randomUUID().toString();
String url = URL.build(env, String.format("application/%s/overview/", applicationNumber)).toString();
return retrieveAPIData(url, jsonPath, defaultReturn);
return retrieveAPIData(url, jsonPath, defaultReturn, requestId);
}

public synchronized String fetchTMApplicationInformation(String applicationNumber, String jsonPath, String defaultReturn) throws HttpException {
String requestId = UUID.randomUUID().toString();
String url = URL.build(env, String.format("transport-manager-application/%s", applicationNumber)).toString();
return retrieveAPIData(url, jsonPath, defaultReturn);
return retrieveAPIData(url, jsonPath, defaultReturn, requestId);
}

public synchronized String fetchInternalUserInformation(String userId, String jsonPath, String defaultReturn) throws HttpException {
String requestId = UUID.randomUUID().toString();
String url = URL.build(env, String.format("user/internal/%s", userId)).toString();
return retrieveAPIData(url, jsonPath, defaultReturn);
return retrieveAPIData(url, jsonPath, defaultReturn, requestId);
}

public synchronized String retrieveAPIData(String url, String jsonPath, String defaultReturn) throws HttpException {
headers.apiHeader.put("Authorization", "Bearer " + adminJWT());
public synchronized String retrieveAPIData(String url, String jsonPath, String defaultReturn, String requestId) throws HttpException {
headers.getApiHeader().put("Authorization", "Bearer " + adminJWT());
ValidatableResponse response = RestUtils.get(url, headers.getApiHeader());

try {
return response.extract().response().jsonPath().getString(jsonPath);
} catch (NullPointerException ne) {
LOGGER.error("RequestID: {}, NullPointerException when extracting JSON path: {}", requestId, jsonPath, ne);
return defaultReturn;
}
}
Expand Down
13 changes: 9 additions & 4 deletions src/main/java/apiCalls/Utils/generic/Headers.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package apiCalls.Utils.generic;

import java.util.HashMap;
import java.util.Map;

public class Headers {

private static String API_HEADER;
private static final ThreadLocal<Map<String, String>> apiHeaderThreadLocal = ThreadLocal.withInitial(HashMap::new);

public HashMap<String, String> apiHeader = new HashMap<>();
private static String API_HEADER;

public HashMap<String, String> getApiHeader() {
return apiHeader;
public Map<String, String> getApiHeader() {
return apiHeaderThreadLocal.get();
}

public static String getAPI_HEADER() {
Expand All @@ -19,4 +20,8 @@ public static String getAPI_HEADER() {
public static void setAPI_HEADER(String api_header) {
API_HEADER = api_header;
}

public static void clear() {
apiHeaderThreadLocal.remove();
}
}
55 changes: 0 additions & 55 deletions src/main/java/apiCalls/Utils/generic/SecretsManager.java

This file was deleted.

29 changes: 20 additions & 9 deletions src/main/java/apiCalls/actions/CreateApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -1083,16 +1083,27 @@ public CreateApplication(RegisterUser registerUser, UserDetails getUserDetails)

public synchronized ValidatableResponse startApplication() throws HttpException {
String createApplicationResource = URL.build(env, "application").toString();
apiHeaders.apiHeader.put("Authorization", "Bearer " + getUserDetails().getJwtToken());
ApplicationBuilder applicationBuilder = new ApplicationBuilder().withOperatorType(getOperatorType())
.withLicenceType(getLicenceType()).withNiFlag(getNiFlag()).withOrganisation(getUserDetails().getOrganisationId())
.withLgvDeclarationConfirmation("0");
if (operatorType.equals(OperatorType.GOODS.asString()) && licenceType.equals(LicenceType.STANDARD_INTERNATIONAL.asString())) {
applicationBuilder.withVehicleType(getVehicleType());
if (VehicleType.LGV_ONLY_FLEET.asString().equals(getVehicleType()))
applicationBuilder.withLgvDeclarationConfirmation("1");

synchronized (this) {
apiHeaders.getApiHeader().put("Authorization", "Bearer " + getUserDetails().getJwtToken());

ApplicationBuilder applicationBuilder = new ApplicationBuilder()
.withOperatorType(getOperatorType())
.withLicenceType(getLicenceType())
.withNiFlag(getNiFlag())
.withOrganisation(getUserDetails().getOrganisationId())
.withLgvDeclarationConfirmation("0");

if (operatorType.equals(OperatorType.GOODS.asString()) && licenceType.equals(LicenceType.STANDARD_INTERNATIONAL.asString())) {
applicationBuilder.withVehicleType(getVehicleType());
if (VehicleType.LGV_ONLY_FLEET.asString().equals(getVehicleType())) {
applicationBuilder.withLgvDeclarationConfirmation("1");
}
}

apiResponse = RestUtils.post(applicationBuilder, createApplicationResource, apiHeaders.getApiHeader());
}
apiResponse = RestUtils.post(applicationBuilder, createApplicationResource, apiHeaders.getApiHeader());

setApplicationId(apiResponse.extract().jsonPath().getString("id.application"));
setLicenceId(apiResponse.extract().jsonPath().getString("id.licence"));

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/apiCalls/actions/GrantLicence.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public GrantLicence(CreateApplication application) {

public HashMap<String, String> header() throws HttpException {
apiHeaders.getApiHeader().put("Authorization", "Bearer " + adminJWT());
return apiHeaders.apiHeader;
return (HashMap<String, String>) apiHeaders.getApiHeader();
}

public synchronized ValidatableResponse grantLicence() throws HttpException {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/apiCalls/actions/InternalDetails.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class InternalDetails extends BaseAPI {
EnvironmentType env = EnvironmentType.getEnum(Properties.get("env", true));
public HashMap<String, String> header() throws HttpException {
apiHeaders.getApiHeader().put("Authorization", "Bearer " + adminJWT());
return apiHeaders.apiHeader;
return (HashMap<String, String>) apiHeaders.getApiHeader();
}

public synchronized ValidatableResponse getFinancialStandingRates () throws HttpException {
Expand Down
58 changes: 40 additions & 18 deletions src/main/java/apiCalls/actions/Token.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,44 +11,66 @@
import org.dvsa.testing.lib.url.api.URL;
import org.dvsa.testing.lib.url.utils.EnvironmentType;

import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

public class Token {
private static final Logger LOGGER = Logger.getLogger(Token.class.getName());
private String adminToken;

EnvironmentType env = EnvironmentType.getEnum(Properties.get("env", true));
HashMap<String, String> header = new HashMap<>();
TokenRequestBuilder tokenBody = new TokenRequestBuilder();

private final EnvironmentType env;
private final ConcurrentHashMap<String, String> header;
private final TokenRequestBuilder tokenBody;

public Token() {
this.env = EnvironmentType.getEnum(Properties.get("env", true));
this.header = new ConcurrentHashMap<>();
this.tokenBody = new TokenRequestBuilder();
}

public synchronized String generateAdminToken(String adminUser, String adminPassword) throws HttpException {
String adminToken = null;
if (getAdminToken() == null) {
if (adminToken == null) {
LOGGER.info("Admin token is null, generating a new token.");
adminToken = getToken(adminUser, adminPassword, UserType.INTERNAL.asString());
setToken(adminToken);
} else {
LOGGER.info("Admin token already exists, returning the existing token.");
}
return adminToken;
}

public synchronized String getToken(String username, String password, String realm) throws HttpException {
String jwtTokenResource;
ValidatableResponse tokenResponse;
jwtTokenResource = URL.build(env).toString().concat("auth/login");
String jwtTokenResource = URL.build(env).toString().concat("auth/login");
tokenBody.withUsername(username).withPassword(password).withRealm(realm);
tokenResponse = RestUtils.post(tokenBody, jwtTokenResource, header);
try {
Utils.checkHTTPStatusCode(tokenResponse, HttpStatus.SC_CREATED);
} catch (Exception e) {
tokenResponse = RestUtils.post(tokenBody, jwtTokenResource, header);

LOGGER.info("Requesting token from URL: " + jwtTokenResource);

int retryCount = 0;
int maxRetries = 3;
while (retryCount < maxRetries) {
try {
ValidatableResponse tokenResponse = RestUtils.post(tokenBody, jwtTokenResource, header);
Utils.checkHTTPStatusCode(tokenResponse, HttpStatus.SC_CREATED);
LOGGER.info("Token successfully created.");
return tokenResponse.extract().body().jsonPath().getString("flags.identity.Token.access_token");
} catch (Exception e) {
LOGGER.warning("Token creation failed, retrying... " + e.getMessage());
retryCount++;
try {
Thread.sleep((long) Math.pow(2, retryCount) * 1000);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
}
return tokenResponse.extract().body().jsonPath().getString("flags.identity.Token.access_token");
throw new HttpException("Failed to create token after " + maxRetries + " retries.");
}

public String getAdminToken() {
return adminToken;
}

public void setToken(String adminToken) {
private synchronized void setToken(String adminToken) {
this.adminToken = adminToken;
LOGGER.info("Admin token set.");
}
}
2 changes: 1 addition & 1 deletion src/main/java/apiCalls/actions/UpdateLicence.java
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,7 @@ public UpdateLicence(CreateApplication application) {

public synchronized HashMap<String, String> header() throws HttpException {
apiHeaders.getApiHeader().put("Authorization", "Bearer " + adminJWT());
return apiHeaders.apiHeader;
return (HashMap<String, String>) apiHeaders.getApiHeader();
}

public synchronized void createVariation() throws HttpException {
Expand Down
12 changes: 7 additions & 5 deletions src/main/java/apiCalls/actions/UserDetails.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,23 @@
import org.dvsa.testing.lib.url.api.URL;
import org.dvsa.testing.lib.url.utils.EnvironmentType;

import java.util.concurrent.ConcurrentHashMap;

public class UserDetails extends BaseAPI {
private Headers apiHeaders = new Headers();
private String jwtToken;

private String organisationId;

EnvironmentType env = EnvironmentType.getEnum(Properties.get("env", true));

private ValidatableResponse apiResponse;

private EnvironmentType env = EnvironmentType.getEnum(Properties.get("env", true));

private static final ConcurrentHashMap<String, String> userOrgMap = new ConcurrentHashMap<>();

public synchronized ValidatableResponse getUserDetails(String userType, String userId, String username, String password) throws HttpException {
String userDetailsResource;
apiHeaders
.apiHeader
.put("Authorization", "Bearer " + adminJWT());
apiHeaders.getApiHeader().put("Authorization", "Bearer " + adminJWT());

if (userType.equals(UserType.EXTERNAL.asString())) {
String orgId;
Expand All @@ -40,6 +40,8 @@ public synchronized ValidatableResponse getUserDetails(String userType, String u
setJwtToken(getToken(username, password, Realm.SELF_SERVE.asString()));
orgId = apiResponse.extract().jsonPath().prettyPeek().getString("organisationUsers.organisation.id");
setOrganisationId(orgId);
userOrgMap.put(userId, orgId);

} else if (userType.equals(UserType.INTERNAL.asString())) {
userDetailsResource = URL.build(env, String.format("user/%s/%s", userType, userId)).toString();
apiResponse = RestUtils.get(userDetailsResource, apiHeaders.getApiHeader());
Expand Down
Loading

0 comments on commit f2defbf

Please sign in to comment.