Skip to content

Commit

Permalink
feat(Move): Added service for retrieving a list of moves by their URI…
Browse files Browse the repository at this point in the history
…s [Merge from HOTFIX - 1.3.2]

OpenSILEX/opensilex-dev!1269
  • Loading branch information
Crejak committed Aug 5, 2024
1 parent 8cc4034 commit 50879b2
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

import com.apicatalog.jsonld.StringUtils;
import io.swagger.annotations.*;
import org.apache.commons.collections4.CollectionUtils;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.opensilex.core.csv.api.CSVValidationDTO;
Expand Down Expand Up @@ -530,6 +529,50 @@ public Response getMoveEvent(
return new SingleObjectResponse<>(dto).getResponse();
}

@GET
@Path(MOVE_PATH_PREFIX + "/by_uris")
@ApiOperation("Get a list of moves with all positional information")
@ApiProtected
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Moves retrieved", response = MoveDetailsDTO.class, responseContainer = "List"),
@ApiResponse(code = 404, message = "Move URI not found", response = ErrorResponse.class)
})
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response getMoveEventByUris(
@ApiParam(value = "Move URIs", required = true) @QueryParam("uris") @NotNull List<URI> uris
) throws Exception {
var logic = new MoveLogic(sparql, nosql, currentUser);
var accountDao = new AccountDAO(sparql);
//@todo This map is used to fetch all accounts at once and fill them on the DTOs. This should be generalized
// to all services that need it.
var publisherMap = new HashMap<URI, AccountModel>();
var dtoList = new ArrayList<MoveDetailsDTO>(uris.size());

for (var model : logic.getList(uris)) {
var dto = new MoveDetailsDTO(model);
if (model.getPublisher() != null) {
publisherMap.put(model.getPublisher(), null);
dto.setPublisher(new UserGetDTO());
dto.getPublisher().setUri(model.getPublisher());
}
dtoList.add(dto);
}

//@todo Find a better way to fetch accounts for a list of model. Propagate this to all APIs.
for (var publisher : accountDao.getList((publisherMap.keySet()))) {
publisherMap.put(publisher.getUri(), publisher);
}

for (var dto : dtoList) {
if (dto.getPublisher() != null) {
dto.setPublisher(UserGetDTO.fromModel(publisherMap.get(dto.getPublisher().getUri())));
}
}

return new PaginatedListResponse<>(dtoList).getResponse();
}

@DELETE
@Path(MOVE_PATH_PREFIX + "/{uri}")
@ApiOperation("Delete a move event")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ public class MoveDetailsDTO extends MoveGetDTO{

private List<RDFObjectRelationDTO> relations;

/**
* Public constructor necessary for deserialization (e.g. for deserializing responses in automatic API tests)
*/
public MoveDetailsDTO() {

}

public MoveDetailsDTO(MoveModel model) throws URISyntaxException, JsonProcessingException {
super(model);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.opensilex.server.exceptions.BadRequestException;
import org.opensilex.server.exceptions.NotFoundURIException;
import org.opensilex.sparql.deserializer.SPARQLDeserializerNotFoundException;
import org.opensilex.sparql.deserializer.SPARQLDeserializers;
import org.opensilex.sparql.deserializer.URIDeserializer;
import org.opensilex.sparql.exceptions.SPARQLException;
import org.opensilex.sparql.service.SPARQLResult;
Expand Down Expand Up @@ -88,7 +89,7 @@ public void delete(URI uri) throws Exception{

@Override
public MoveModel get(URI uri) throws Exception {
MoveModel model = (MoveModel)(dao.get(uri, currentUser));
MoveModel model = dao.get(uri, currentUser);
if (model == null) {
throw new NotFoundURIException(uri);
}
Expand All @@ -100,6 +101,18 @@ public MoveModel get(URI uri) throws Exception {
return model;
}

public List<MoveModel> getList(List<URI> uriList) throws Exception {
var modelList = dao.getList(uriList, currentUser);
var noSqlModelMap = noSqlDao.getMoveEventNoSqlModelMap(uriList);
for (var model : modelList) {
var noSqlModel = noSqlModelMap.get(SPARQLDeserializers.formatURI(model.getUri()));
if (noSqlModel != null) {
model.setNoSqlModel(noSqlModel);
}
}
return modelList;
}

@Override
public MoveModel create(MoveModel model) throws Exception {
return wrapWithTransaction(session -> createNoTransaction(model, session));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,9 @@ public T get(URI uri, AccountModel user) throws Exception {
return sparql.loadByURI(graph, clazz, uri, user.getLanguage());
}


public List<T> getList(List<URI> uris, AccountModel user) throws Exception {
return sparql.getListByURIs(clazz, uris, user.getLanguage());
}

/**
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ public MoveModel get(URI eventUri, AccountModel user) throws Exception {
return sparql.getByURI(MoveModel.class, eventUri, user.getLanguage());
}

@Override
public List<MoveModel> getList(List<URI> uris, AccountModel user) throws Exception {
return sparql.getListByURIs(MoveModel.class, uris, user.getLanguage());
}

@Override
protected MoveModel fromResult(SPARQLResult result, String lang, MoveModel model) throws Exception {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
import org.opensilex.nosql.mongodb.MongoModel;
import org.opensilex.nosql.mongodb.dao.MongoReadWriteDao;
import org.opensilex.nosql.mongodb.service.v2.MongoDBServiceV2;
import org.opensilex.sparql.deserializer.SPARQLDeserializers;

import java.net.URI;
import java.util.HashMap;
import java.util.List;

import static com.mongodb.client.model.Filters.and;
import java.util.Map;

public class MoveEventNoSqlDao extends MongoReadWriteDao<MoveNosqlModel, MoveNoSqlSearchFilter> {
public static final String COLLECTION_NAME = "move";
Expand All @@ -33,14 +35,34 @@ public MoveEventNoSqlDao(MongoDBServiceV2 mongodb) {
@Override
public List<Bson> getBsonFilters(MoveNoSqlSearchFilter filter) {
List<Bson> result = super.getBsonFilters(filter);
if(filter.geometry != null){
if (filter.geometry != null) {
result.add(Filters.exists(MoveNosqlModel.COORDINATES_FIELD, true));
result.add(Filters.geoWithin(MoveNosqlModel.COORDINATES_FIELD, filter.geometry));
}

return result;
}

/**
* Fetches a list of move NoSQL models by their URIs. The result is represented as a Map to facilitate the
* integration with, for example, a list of {@link MoveModel}s. See for example
* {@link org.opensilex.core.event.bll.MoveLogic#getList(List)}.
*
* @param uris The list of move URIs
* @return A map associating the move URIs to their respective NoSQL models. Note that the map size can be smaller
* than the size of the URI list as moves do not always have an associated NoSQL model. The URI is formatted
* using {@link SPARQLDeserializers#formatURI(URI)}, so you SHOULD query this map using
* <code>map.get(SPARQLDeserializers.formatURI(uri))</code>.
*/
public Map<URI, MoveNosqlModel> getMoveEventNoSqlModelMap(List<URI> uris) {
var iterableResult = collection.find(Filters.in(MoveNosqlModel.ID_FIELD, uris.stream().map(uri -> URI.create(SPARQLDeserializers.getShortURI(uri)))::iterator));
var resultMap = new HashMap<URI, MoveNosqlModel>();
for (var result : iterableResult) {
resultMap.put(SPARQLDeserializers.formatURI(result.getUri()), result);
}
return resultMap;
}

/**
* Override this to be id field as Moves only have an id which is equal to a uri, unlike other places in the application where there is a uri field.
* This will allow get(URI) to work.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@
import org.junit.Test;
import org.opensilex.core.AbstractMongoIntegrationTest;
import org.opensilex.core.event.api.move.MoveCreationDTO;
import org.opensilex.core.event.api.move.MoveDetailsDTO;
import org.opensilex.core.event.api.move.MoveGetDTO;
import org.opensilex.core.event.dal.EventModel;
import org.opensilex.core.event.dal.move.MoveEventNoSqlDao;
import org.opensilex.core.organisation.dal.facility.FacilityModel;
import org.opensilex.core.position.api.*;
import org.opensilex.core.scientificObject.dal.ScientificObjectModel;
import org.opensilex.integration.test.ServiceDescription;
import org.opensilex.security.SecurityModule;
import org.opensilex.server.response.PaginatedListResponse;
import org.opensilex.server.response.SingleObjectResponse;
import org.opensilex.sparql.deserializer.SPARQLDeserializers;
Expand All @@ -35,16 +38,17 @@

public class MoveEventApiTest extends AbstractMongoIntegrationTest {

public String path = EventAPI.MOVE_PATH;
public static String path = EventAPI.MOVE_PATH;

public String getByUriPath = path + "/{uri}";
public String createPath = path;
public String updatePath = path ;
public String deletePath = path+ "/{uri}";
public static String getByUriPath = path + "/{uri}";
public static String getByUriListPath = path + "/by_uris";
public static String createPath = path;
public static String updatePath = path ;
public static String deletePath = path+ "/{uri}";

public String positionPath = PositionAPI.PATH;
public String getPositionPath = positionPath + "/{uri}";
public String getPositionHistoryPath = positionPath + "/history";
public static String positionPath = PositionAPI.PATH;
public static String getPositionPath = positionPath + "/{uri}";
public static String getPositionHistoryPath = positionPath + "/history";

private FacilityModel facilityA;
private FacilityModel facilityB;
Expand All @@ -59,6 +63,24 @@ public class MoveEventApiTest extends AbstractMongoIntegrationTest {
private List<FacilityModel> toFacilities;
private final static int nbMoveMax = 10;

private static final ServiceDescription create;
private static final ServiceDescription getByUriList;

static {
try {
create = new ServiceDescription(
EventAPI.class.getMethod("createMoves", List.class),
path
);
getByUriList = new ServiceDescription(
EventAPI.class.getMethod("getMoveEventByUris", List.class),
getByUriListPath
);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}

@Before
public void createFacilities() throws Exception {

Expand Down Expand Up @@ -122,9 +144,7 @@ public void createScientificObjects() throws Exception{

}


private MoveCreationDTO getCreationDto() {

private MoveCreationDTO getCreationDtoWithoutPosition() {
MoveCreationDTO dto = new MoveCreationDTO();
dto.setDescription("A test event");
dto.setIsInstant(false);
Expand All @@ -141,6 +161,12 @@ private MoveCreationDTO getCreationDto() {
dto.setFrom(facilityA.getUri());
dto.setTo(facilityB.getUri());

return dto;
}

private MoveCreationDTO getCreationDto() {
var dto = getCreationDtoWithoutPosition();

// first position
PositionCreationDTO k2Summit = new PositionCreationDTO();
k2Summit.setDescription("Not far from sky");
Expand Down Expand Up @@ -284,7 +310,7 @@ public void testUpdateWithNullNoSqlModel() throws Exception {
assertNull(dtoFromDb.getTargetsPositions());
}

// @Test
@Test
public void testGetByUri() throws Exception {

MoveCreationDTO creationDTO = getCreationDto();
Expand All @@ -303,6 +329,55 @@ public void testGetByUri() throws Exception {
testEquals(dtoFromDb,creationDTO);
}

@Test
public void testGetListByUri() throws Exception {
// This move MUST NOT be returned by the service
var creationDto1 = getCreationDto();
// This move MUST be returned by the service with its position information
var creationDto2 = getCreationDto();
// This move MUST be returned by the service even if its position information is non existant
var creationDto3 = getCreationDtoWithoutPosition();

var uriList = new UserCallBuilder(create)
.setBody(List.of(creationDto1, creationDto2, creationDto3))
.buildAdmin()
.executeCallAndDeserialize(new TypeReference<PaginatedListResponse<URI>>() {})
.getDeserializedResponse();

var uri1 = uriList.getResult().get(0);
var uri2 = uriList.getResult().get(1);
var uri3 = uriList.getResult().get(2);

// Retrieve the moves 2 and 3
var result23 = new UserCallBuilder(getByUriList)
.addParam("uris", List.of(uri2, uri3))
.buildAdmin()
.executeCallAndDeserialize(new TypeReference<PaginatedListResponse<MoveDetailsDTO>>() {})
.getDeserializedResponse();

// The service MUST return the moves 2 and 3
assertEquals(2, result23.getResult().size());
assertTrue(result23.getResult().stream().anyMatch(dto -> SPARQLDeserializers.compareURIs(dto.getUri(), uri2)));
assertTrue(result23.getResult().stream().anyMatch(dto -> SPARQLDeserializers.compareURIs(dto.getUri(), uri3)));

var result2 = result23.getResult().stream().filter(dto -> SPARQLDeserializers.compareURIs(dto.getUri(), uri2)).findFirst()
.orElseThrow();
var result3 = result23.getResult().stream().filter(dto -> SPARQLDeserializers.compareURIs(dto.getUri(), uri3)).findFirst()
.orElseThrow();

// Move 2 MUST have its position information. Move 3 MUST NOT have any position information.
assertNotNull(result2.getTargetsPositions());
assertEquals(2, result2.getTargetsPositions().size());
assertEquals("A", result2.getTargetsPositions().get(0).getPosition().getX());
assertNull(result3.getTargetsPositions());

// Both moves MUST have their metadata
assertNotNull(result2.getPublisher());
assertEquals(SecurityModule.DEFAULT_SUPER_ADMIN_EMAIL, result2.getPublisher().getEmail());
assertNotNull(result3.getPublisher());
assertEquals(SecurityModule.DEFAULT_SUPER_ADMIN_EMAIL, result3.getPublisher().getEmail());
}

private void testEquals(MoveGetDTO dtoFromDb, MoveCreationDTO creationDTO){
assertEquals(creationDTO.getDescription(), dtoFromDb.getDescription());
assertEquals(creationDTO.getStart(), dtoFromDb.getStart());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public AccountModel get(URI uri) throws Exception {
}


public List<AccountModel> getList(List<URI> uri) throws Exception {
public List<AccountModel> getList(Collection<URI> uri) throws Exception {
return sparql.getListByURIs(AccountModel.class, uri, null);
}

Expand Down

0 comments on commit 50879b2

Please sign in to comment.