Skip to content

Commit

Permalink
Added: Bridgehead Operation
Browse files Browse the repository at this point in the history
  • Loading branch information
djuarezgf committed Dec 26, 2023
1 parent 2fc3dd5 commit 590a00c
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 29 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [0.0.1 - 2023-12-13]
## [0.0.1 - 2023-12-26]
### Added
- First version of the project
- Spring Application
Expand Down Expand Up @@ -41,3 +41,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Github CI
- Exporter Service implementation
- Bridgehead configuration with explorer and exporter mapping
- Bridgehead Operation
7 changes: 7 additions & 0 deletions src/main/java/de/samply/annotations/Bridgehead.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
package de.samply.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Bridgehead {
}
21 changes: 6 additions & 15 deletions src/main/java/de/samply/app/ProjectManagerController.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public ResponseEntity<String> fetchActions(
@Bridgehead @RequestParam(name = ProjectManagerConst.BRIDGEHEAD, required = false) String bridgehead,
@RequestParam(name = ProjectManagerConst.SITE) String site
) {
return convertToResponseEntity(
return convertToResponseEntity(() ->
this.frontendService.fetchModuleActionPackage(site, Optional.ofNullable(projectCode), Optional.ofNullable(bridgehead)));
}

Expand Down Expand Up @@ -241,7 +241,7 @@ public ResponseEntity<String> createProjectQuery(
@NotEmpty @RequestParam(name = ProjectManagerConst.OUTPUT_FORMAT) OutputFormat outputFormat,
@NotEmpty @RequestParam(name = ProjectManagerConst.TEMPLATE_ID) String templateId
) {
return convertToResponseEntity(this.queryService.createQuery(query, queryFormat, label, description, outputFormat, templateId));
return convertToResponseEntity(() -> this.queryService.createQuery(query, queryFormat, label, description, outputFormat, templateId));
}

@RoleConstraints(organisationRoles = {OrganisationRole.RESEARCHER})
Expand All @@ -254,7 +254,7 @@ public ResponseEntity<String> createProjectCqlDataQuery(
@NotEmpty @RequestParam(name = ProjectManagerConst.TEMPLATE_ID) String templateId

) {
return convertToResponseEntity(this.queryService.createQuery(query, QueryFormat.CQL_DATA, label, description, outputFormat, templateId));
return convertToResponseEntity(() -> this.queryService.createQuery(query, QueryFormat.CQL_DATA, label, description, outputFormat, templateId));
}

@RoleConstraints(projectRoles = {ProjectRole.CREATOR, ProjectRole.DEVELOPER, ProjectRole.BRIDGEHEAD_ADMIN, ProjectRole.PROJECT_MANAGER_ADMIN})
Expand Down Expand Up @@ -335,7 +335,7 @@ private ByteArrayResource fetchResource(Path filePath) throws DocumentServiceExc
@PostMapping(value = ProjectManagerConst.SAVE_QUERY_IN_BRIDGEHEAD)
public ResponseEntity<String> saveQueryInBridgehead(
@ProjectCode @RequestParam(name = ProjectManagerConst.PROJECT_CODE) String projectCode,
@Bridgehead @RequestParam(name = ProjectManagerConst.BRIDGEHEAD, required = false) String bridgehead
@Bridgehead @RequestParam(name = ProjectManagerConst.BRIDGEHEAD) String bridgehead
) {
return convertToResponseEntity(() -> this.exporterService.sendQueryToBridgehead(projectCode, bridgehead));
}
Expand All @@ -347,7 +347,7 @@ public ResponseEntity<String> saveQueryInBridgehead(
@PostMapping(value = ProjectManagerConst.SAVE_AND_EXECUTE_QUERY_IN_BRIDGEHEAD)
public ResponseEntity<String> saveAndExecuteQueryInBridgehead(
@ProjectCode @RequestParam(name = ProjectManagerConst.PROJECT_CODE) String projectCode,
@Bridgehead @RequestParam(name = ProjectManagerConst.BRIDGEHEAD, required = false) String bridgehead
@Bridgehead @RequestParam(name = ProjectManagerConst.BRIDGEHEAD) String bridgehead
) {
return convertToResponseEntity(() -> this.exporterService.sendQueryToBridgeheadAndExecute(projectCode, bridgehead));
}
Expand All @@ -361,7 +361,7 @@ public ResponseEntity<String> fetchTokenScript(
@ProjectCode @RequestParam(name = ProjectManagerConst.PROJECT_CODE) String projectCode,
@Bridgehead @RequestParam(name = ProjectManagerConst.BRIDGEHEAD) String bridgehead
) {
return convertToResponseEntity(this.tokenManagerService.fetchAuthenticationScript(projectCode, bridgehead));
return convertToResponseEntity(() -> this.tokenManagerService.fetchAuthenticationScript(projectCode, bridgehead));
}


Expand All @@ -374,15 +374,6 @@ private ResponseEntity convertToResponseEntity(RunnableWithException runnable) {
}
}

private <T> ResponseEntity convertToResponseEntity(T result) {
try {
return (result != null) ? ResponseEntity.ok(objectMapper.writeValueAsString(result))
: ResponseEntity.notFound().build();
} catch (Exception e) {
return createInternalServerError(e);
}
}

private <T> ResponseEntity convertToResponseEntity(SupplierWithException<T> supplier) {
try {
T result = supplier.get();
Expand Down
14 changes: 12 additions & 2 deletions src/main/java/de/samply/bridgehead/BridgeheadConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
public class BridgeheadConfiguration {

private Map<String, BridgeheadConfig> config = new HashMap<>();
private Map<String, String> explorerCodeBridgeheadMap = new HashMap<>();

@Data
public static class BridgeheadConfig {
Expand All @@ -36,13 +37,22 @@ public String getExporterApiKey(String bridgehead) {
}

public Optional<String> getBridgehead(String explorerCode) {
String bridgehead = explorerCodeBridgeheadMap.get(explorerCode);
if (bridgehead == null) {
bridgehead = fetchBridgehead(explorerCode);
explorerCodeBridgeheadMap.put(explorerCode, bridgehead);
}
return Optional.ofNullable(bridgehead);
}

private String fetchBridgehead(String explorerCode) {
for (String bridgehead : config.keySet()) {
BridgeheadConfig bridgeheadConfig = config.get(bridgehead);
if (bridgeheadConfig.explorerCode.equals(explorerCode)) {
return Optional.of(bridgehead);
return bridgehead;
}
}
return Optional.empty();
return null;
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package de.samply.bridgehead;

public enum BridgeheadOperationType {
SEND_QUERY_TO_BRIDGEHEAD_AND_EXECUTE,
SEND_QUERY_TO_BRIDGEHEAD,
CREATE_DATASHIELD_TOKEN

}
48 changes: 48 additions & 0 deletions src/main/java/de/samply/db/model/BridgeheadOperation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package de.samply.db.model;

import de.samply.bridgehead.BridgeheadOperationType;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.http.HttpStatus;

import java.time.LocalDate;

@Entity
@Table(name = "bridgehead_operation", schema = "samply")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BridgeheadOperation {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;

@Column(name = "bridgehead", nullable = false)
private String bridgehead;

@Column(name = "user_email", nullable = false)
private String userEmail;

@Column(name = "timestamp", nullable = false)
private LocalDate timestamp;

@Column(name = "http_status", nullable = false)
@Enumerated(EnumType.STRING)
private HttpStatus httpStatus;

@Column(name = "error")
private String error;

@Column(name = "type", nullable = false)
@Enumerated(EnumType.STRING)
private BridgeheadOperationType type;

@ManyToOne
@JoinColumn(name = "project_id", nullable = false)
private Project project;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package de.samply.db.repository;

import de.samply.db.model.BridgeheadOperation;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface BridgeheadOperationRepository extends JpaRepository<BridgeheadOperation, Long> {

}
48 changes: 37 additions & 11 deletions src/main/java/de/samply/exporter/ExporterService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import de.samply.app.ProjectManagerConst;
import de.samply.bridgehead.BridgeheadConfiguration;
import de.samply.bridgehead.BridgeheadOperationType;
import de.samply.db.model.BridgeheadOperation;
import de.samply.db.model.Project;
import de.samply.db.model.Query;
import de.samply.db.repository.BridgeheadOperationRepository;
import de.samply.db.repository.ProjectRepository;
import de.samply.security.SessionUser;
import de.samply.utils.Base64Utils;
Expand Down Expand Up @@ -51,6 +54,7 @@ public class ExporterService {
private final int webClientTcpKeepConnetionNumberOfTries;
private final SessionUser sessionUser;
private final ProjectRepository projectRepository;
private final BridgeheadOperationRepository bridgeheadOperationRepository;
private ObjectMapper objectMapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT)
.registerModule(new JavaTimeModule()).configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

Expand All @@ -64,8 +68,8 @@ public ExporterService(
@Value(ProjectManagerConst.WEBCLIENT_TIME_IN_SECONDS_AFTER_RETRY_WITH_FAILURE_SV) Integer webClientTimeInSecondsAfterRetryWithFailure,
BridgeheadConfiguration bridgeheadConfiguration,
ProjectRepository projectRepository,
SessionUser sessionUser
) {
SessionUser sessionUser,
BridgeheadOperationRepository bridgeheadOperationRepository) {
this.bridgeheadConfiguration = bridgeheadConfiguration;
this.webClientMaxNumberOfRetries = webClientMaxNumberOfRetries;
this.webClientTimeInSecondsAfterRetryWithFailure = webClientTimeInSecondsAfterRetryWithFailure;
Expand All @@ -76,6 +80,7 @@ public ExporterService(
this.webClientTcpKeepConnetionNumberOfTries = webClientTcpKeepConnetionNumberOfTries;
this.sessionUser = sessionUser;
this.projectRepository = projectRepository;
this.bridgeheadOperationRepository = bridgeheadOperationRepository;
}

private WebClient createWebClient(String exporterUrl) {
Expand All @@ -93,17 +98,18 @@ private WebClient createWebClient(String exporterUrl) {
}

public void sendQueryToBridgehead(@NotNull String projectCode, @NotNull String bridgehead) throws ExporterServiceException {
log.info("Sending query of project" + projectCode + " to bridgehead" + bridgehead + "...");
postRequest(bridgehead, ProjectManagerConst.EXPORTER_REQUEST, generateBodyConfigurationForExportRequest(projectCode));
log.info("Sending query of project " + projectCode + " to bridgehead " + bridgehead + " ...");
postRequest(bridgehead, projectCode, ProjectManagerConst.EXPORTER_CREATE_QUERY, generateBodyConfigurationForExportRequest(projectCode));
}

public void sendQueryToBridgeheadAndExecute(@NotNull String projectCode, @NotNull String bridgehead) throws ExporterServiceException {
log.info("Sending query of project " + projectCode + " to bridgehead " + bridgehead + " to be executed...");
postRequest(bridgehead, ProjectManagerConst.EXPORTER_CREATE_QUERY, generateBodyConfigurationForExportCreateQuery(projectCode));
postRequest(bridgehead, projectCode, ProjectManagerConst.EXPORTER_REQUEST, generateBodyConfigurationForExportCreateQuery(projectCode));
}

private void postRequest(String bridgehead, String restService, Map<String, String> bodyParameters) {
private void postRequest(String bridgehead, String projectCode, String restService, Map<String, String> bodyParameters) {
AtomicInteger retryCount = new AtomicInteger(0);
String email = sessionUser.getEmail();
fetchWebClient(bridgehead).post().uri(uriBuilder -> uriBuilder.path(restService).build())
.header(ProjectManagerConst.HTTP_HEADER_API_KEY, bridgeheadConfiguration.getExporterApiKey(bridgehead))
.contentType(MediaType.APPLICATION_JSON)
Expand All @@ -117,7 +123,8 @@ private void postRequest(String bridgehead, String restService, Map<String, Stri
)
.doOnError(WebClientResponseException.class, ex -> {
HttpStatusCode statusCode = ex.getStatusCode();
log.error(ExceptionUtils.getStackTrace(ex));
String error = ExceptionUtils.getStackTrace(ex);
log.error(error);
if (statusCode.equals(HttpStatus.BAD_REQUEST)) {
log.error("Received 400 Bad Request");
} else if (statusCode.is5xxServerError()) {
Expand All @@ -126,12 +133,31 @@ private void postRequest(String bridgehead, String restService, Map<String, Stri
log.error("Received HTTP Status Code: " + statusCode);
}
if (retryCount.get() >= webClientMaxNumberOfRetries) {
//TODO
createBridgeheadOperation(restService, (HttpStatus) ex.getStatusCode(), error, bridgehead, projectCode, email);
}
})
.subscribe(result -> {
//TODO
});
.subscribe(result -> createBridgeheadOperation(restService, HttpStatus.OK, null, bridgehead, projectCode, email));
}

private void createBridgeheadOperation(
String restService, HttpStatus status, String error, String bridgehead, String projectCode, String email) {
BridgeheadOperation bridgeheadOperation = new BridgeheadOperation();
bridgeheadOperation.setBridgehead(bridgehead);
bridgeheadOperation.setProject(projectRepository.findByCode(projectCode).get());
bridgeheadOperation.setTimestamp(LocalDate.now());
bridgeheadOperation.setType(fetchBridgeheadOperationType(restService));
bridgeheadOperation.setHttpStatus(status);
bridgeheadOperation.setError(error);
bridgeheadOperation.setUserEmail(email);
bridgeheadOperationRepository.save(bridgeheadOperation);
}

private BridgeheadOperationType fetchBridgeheadOperationType(String restService) {
return switch (restService) {
case ProjectManagerConst.EXPORTER_REQUEST -> BridgeheadOperationType.SEND_QUERY_TO_BRIDGEHEAD_AND_EXECUTE;
case ProjectManagerConst.EXPORTER_CREATE_QUERY -> BridgeheadOperationType.SEND_QUERY_TO_BRIDGEHEAD;
default -> throw new IllegalStateException("Unexpected value: " + restService);
};
}

private WebClient fetchWebClient(String bridgehead) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,18 @@ CREATE TABLE samply.project_document
type TEXT NOT NULL,
creator_email TEXT NOT NULL,
label TEXT
);

CREATE TABLE samply.bridgehead_operation
(
id SERIAL PRIMARY KEY,
bridgehead TEXT NOT NULL,
user_email TEXT NOT NULL,
timestamp TIMESTAMP NOT NULL,
http_status TEXT NOT NULL,
error TEXT,
type TEXT NOT NULL,
project_id BIGINT NOT NULL
);

ALTER TABLE samply.project
Expand All @@ -94,7 +105,13 @@ ALTER TABLE samply.project_document
FOREIGN KEY (project_id)
REFERENCES samply.project (id);

ALTER TABLE samply.bridgehead_operation
ADD CONSTRAINT fk_project_id
FOREIGN KEY (project_id)
REFERENCES samply.project (id);

CREATE INDEX idx_project_bridgehead_project_id ON samply.project_bridgehead (project_id);
CREATE INDEX idx_project_bridgehead_user_project_bridgehead_id ON samply.project_bridgehead_user (project_bridgehead_id);
CREATE INDEX idx_project_document_project_id ON samply.project_document (project_id);
CREATE INDEX idx_project_query_id ON samply.project (query_id);
CREATE INDEX idx_project_id ON samply.bridgehead_operation (project_id);

0 comments on commit 590a00c

Please sign in to comment.