Skip to content

Commit

Permalink
Merge pull request #73 from europeana/EA-2560_EntityMigrationEndpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
ikattey authored May 21, 2021
2 parents acaaa6a + 8a5cf87 commit 34cfc7d
Show file tree
Hide file tree
Showing 10 changed files with 336 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ public class EntityManagementConfiguration {
@Value("${auth.enabled: true}")
private boolean authEnabled;

@Value("${enrichmentMigrationPassword}")
private String enrichmentsMigrationPassword;


public EntityManagementConfiguration() {
LOG.info("Initializing EntityManagementConfiguration bean as: configuration");
Expand Down Expand Up @@ -167,4 +170,8 @@ public boolean shouldComputeMetrics() {
public boolean isAuthEnabled() {
return authEnabled;
}

public String getEnrichmentsMigrationPassword() {
return enrichmentsMigrationPassword;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public class BaseMvcTestUtils {
public static final String CONCEPT_DATA_RECONCELIATION_XML = "/metis-deref/concept-data-reconceliation.xml";
public static final String CONCEPT_METIS_BATHTUB = "/metis-deref/concept-metis-bathtub.xml";
public static final String CONCEPT_XML = "/metis-deref/concept.xml";
public static final String VALID_MIGRATION_ID = "http://www.wikidata.org/entity/testing";
public static final String INVALID_MIGRATION_ID = "http://www.testing.org/entity/testing";

public static String getEntityIdentifier(String result) throws JSONException {
String id = getEntityId(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import eu.europeana.entitymanagement.common.config.AppConfigConstants;
import eu.europeana.entitymanagement.definitions.model.Concept;
import eu.europeana.entitymanagement.definitions.model.EntityRecord;
import eu.europeana.entitymanagement.vocabulary.EntityTypes;
import eu.europeana.entitymanagement.web.service.EntityRecordService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -17,6 +18,7 @@
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

Expand All @@ -25,7 +27,11 @@

import static eu.europeana.entitymanagement.testutils.BaseMvcTestUtils.*;
import static eu.europeana.entitymanagement.testutils.BaseMvcTestUtils.BASE_SERVICE_URL;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.hasSize;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

/**
Expand Down Expand Up @@ -79,12 +85,76 @@ void permanentDeletionShouldBeSuccessful() throws Exception {

String requestPath = getEntityRequestPath(record.getEntityId());

mockMvc.perform(post(BASE_SERVICE_URL + "/" + requestPath + BASE_ADMIN_URL)
mockMvc.perform(delete(BASE_SERVICE_URL + "/" + requestPath + BASE_ADMIN_URL)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isNoContent());

// check that record is deleted
Optional<EntityRecord> dbRecordOptional = entityRecordService.retrieveEntityRecordByUri(record.getEntityId());
Assertions.assertTrue(dbRecordOptional.isEmpty());
}

@Test
void migrationExistingEntityShouldBeSuccessful() throws Exception {
String requestBody = "{\"id\" : \"" + BaseMvcTestUtils.VALID_MIGRATION_ID + "\"}";
String entityId = EntityRecordUtils.buildEntityIdUri("concept", "1");
ResultActions results = mockMvc.perform(post(BASE_SERVICE_URL + "/{type}/{identifier}" + BASE_ADMIN_URL, "concept", "1")
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody))
.andExpect(status().isAccepted());

results.andExpect(jsonPath("$.id", any(String.class)))
.andExpect(jsonPath("$.type", is(EntityTypes.Concept.name())))
.andExpect(jsonPath("$.isAggregatedBy").isNotEmpty())
.andExpect(jsonPath("$.isAggregatedBy.aggregates", hasSize(2)))
// should have Europeana and Datasource proxies
.andExpect(jsonPath("$.proxies", hasSize(2)));

// check that record is present
Optional<EntityRecord> dbRecordOptional = entityRecordService.retrieveEntityRecordByUri(entityId);
Assertions.assertFalse(dbRecordOptional.isEmpty());

entityRecordService.delete(entityId);
}

@Test
void migrationExistingEntityInvalidEntityType() throws Exception {
String requestBody = "{\"id\" : \"" + BaseMvcTestUtils.VALID_MIGRATION_ID + "\"}";
mockMvc.perform(post(BASE_SERVICE_URL + "/{type}/{identifier}" + BASE_ADMIN_URL, "testing", "1")
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody))
.andExpect(status().isInternalServerError());
}

@Test
void migrationExistingEntityInvalidDataSource() throws Exception {
String requestBody = "{\"id\" : \"" + BaseMvcTestUtils.INVALID_MIGRATION_ID + "\"}";
mockMvc.perform(post(BASE_SERVICE_URL + "/{type}/{identifier}" + BASE_ADMIN_URL, "concept", "1")
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody))
.andExpect(status().isBadRequest());
}

@Test
void migrationExistingEntityAlreadyExist() throws Exception {
String requestBody = "{\"id\" : \"" + BaseMvcTestUtils.VALID_MIGRATION_ID + "\"}";
// create entity already
Concept concept = objectMapper.readValue(loadFile(CONCEPT_JSON), Concept.class);
EntityRecord entityRecord = new EntityRecord();
entityRecord.setEntity(concept);
entityRecord.setEntityId(concept.getEntityId());
EntityRecord record = entityRecordService.saveEntityRecord(entityRecord);

System.out.println(record.getEntityId());
String requestPath = getEntityRequestPath(record.getEntityId());
System.out.println(requestPath);

mockMvc.perform(post(BASE_SERVICE_URL + requestPath + BASE_ADMIN_URL)
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody))
.andExpect(status().isBadRequest());

entityRecordService.delete(record.getEntityId());

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package eu.europeana.entitymanagement.exception;


import eu.europeana.api.commons.error.EuropeanaApiException;
import org.springframework.http.HttpStatus;

/**
* Exception thrown when a entity already exists
*/
public class EntityAlreadyExistsException extends EuropeanaApiException {

private static final long serialVersionUID = -2506967519765835153L;

public EntityAlreadyExistsException(String entityUri) {
super("Entity already exists for '" + entityUri + "' found");
}

@Override
public boolean doLog() {
return false;
}


@Override
public HttpStatus getResponseStatus() {
return HttpStatus.BAD_REQUEST;
}


@Override
public boolean doLogStacktrace() {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package eu.europeana.entitymanagement.exception;

import eu.europeana.api.commons.error.EuropeanaApiException;
import org.springframework.http.HttpStatus;

/**
* Exception thrown for all errors that occur while creating or saving an entity to the database
Expand All @@ -18,4 +19,21 @@ public EntityCreationException(String msg) {
public EntityCreationException(String msg, Throwable th) {
super(msg);
}

/**
* @return boolean indicating whether this type of exception should be logged or not
*/
@Override
public boolean doLog() {
return true;
}

public boolean logStacktrace() {
return false;
}

@Override
public HttpStatus getResponseStatus() {
return HttpStatus.BAD_REQUEST;
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package eu.europeana.entitymanagement.web;

import eu.europeana.api.commons.web.http.HttpHeaders;
import eu.europeana.entitymanagement.definitions.model.Aggregation;
import eu.europeana.entitymanagement.exception.EntityCreationException;
import eu.europeana.entitymanagement.web.service.EntityObjectFactory;
import eu.europeana.entitymanagement.web.xml.model.RdfBaseWrapper;
import eu.europeana.entitymanagement.web.xml.model.XmlBaseEntityImpl;
import javax.annotation.Resource;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

Expand All @@ -19,6 +22,10 @@
import eu.europeana.entitymanagement.serialization.EntityXmlSerializer;
import eu.europeana.entitymanagement.vocabulary.FormatTypes;
import eu.europeana.entitymanagement.web.service.AuthorizationService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import java.util.Date;


public abstract class BaseRest extends BaseRestController {
Expand Down Expand Up @@ -83,4 +90,51 @@ protected String serialize(EntityRecord entityRecord, FormatTypes format, String
return responseBody;
}

/**
* Generates serialised EntityRecord Response entity along with
* Http status and headers
*
* @param profile
* @param outFormat
* @param contentType
* @param entityRecord
* @param status
* @return
* @throws EntityCreationException
*/
public ResponseEntity<String> generateResponseEntity(String profile, FormatTypes outFormat,
String contentType, EntityRecord entityRecord, HttpStatus status)
throws EntityCreationException {

Aggregation isAggregatedBy = entityRecord.getEntity().getIsAggregatedBy();

long timestamp = isAggregatedBy != null ?
isAggregatedBy.getModified().getTime() :
0L;

String etag = computeEtag(timestamp, outFormat.name(), getApiVersion());

org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
headers.add(HttpHeaders.ALLOW, HttpHeaders.ALLOW_GET);
if (!outFormat.equals(FormatTypes.schema)) {
headers.add(HttpHeaders.VARY, HttpHeaders.ACCEPT);
headers.add(HttpHeaders.LINK, HttpHeaders.VALUE_LDP_RESOURCE);
}
if (contentType != null && !contentType.isEmpty())
headers.add(HttpHeaders.CONTENT_TYPE, contentType);

String body = serialize(entityRecord, outFormat, profile);
return ResponseEntity.status(status).headers(headers).eTag(etag).body(body);
}


/**
* Generates a unique hex string based on the input params
* TODO: move logic to {@link eu.europeana.api.commons.web.controller.BaseRestController#generateETag(Date, String, String)}
*/
public String computeEtag(long timestamp, String format, String version){
return DigestUtils.md5Hex(String.format("%s:%s:%s", timestamp, format, version));
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -289,31 +289,6 @@ private ResponseEntity<String> createResponse(String profile, String type, Strin
return generateResponseEntity(profile, outFormat, contentType, entityRecord, HttpStatus.OK);
}

private ResponseEntity<String> generateResponseEntity(String profile, FormatTypes outFormat,
String contentType, EntityRecord entityRecord, HttpStatus status)
throws EntityCreationException {

Aggregation isAggregatedBy = entityRecord.getEntity().getIsAggregatedBy();

long timestamp = isAggregatedBy != null ?
isAggregatedBy.getModified().getTime() :
0L;

String etag = computeEtag(timestamp, outFormat.name(), getApiVersion());

org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
headers.add(HttpHeaders.ALLOW, HttpHeaders.ALLOW_GET);
if (!outFormat.equals(FormatTypes.schema)) {
headers.add(HttpHeaders.VARY, HttpHeaders.ACCEPT);
headers.add(HttpHeaders.LINK, HttpHeaders.VALUE_LDP_RESOURCE);
}
if (contentType != null && !contentType.isEmpty())
headers.add(HttpHeaders.CONTENT_TYPE, contentType);

String body = serialize(entityRecord, outFormat, profile);
return ResponseEntity.status(status).headers(headers).eTag(etag).body(body);
}

private ResponseEntity<String> launchTaskAndRetrieveEntity(String type, String identifier,
EntityRecord entityRecord, String profile) throws Exception {
// launch synchronous update, then retrieve entity from DB afterwards
Expand Down Expand Up @@ -360,12 +335,4 @@ private String getDatabaseIdentifier(String entityId) {
//entity id is "http://data.europeana.eu/{type}/{identifier}"
return entityId.substring(entityId.lastIndexOf("/") + 1);
}

/**
* Generates a unique hex string based on the input params
* TODO: move logic to {@link eu.europeana.api.commons.web.controller.BaseRestController#generateETag(Date, String, String)}
*/
private String computeEtag(long timestamp, String format, String version){
return DigestUtils.md5Hex(String.format("%s:%s:%s", timestamp, format, version));
}
}
Loading

0 comments on commit 34cfc7d

Please sign in to comment.