Skip to content

Commit

Permalink
Merge pull request #285 from RADAR-base/update-source-attributes
Browse files Browse the repository at this point in the history
Update source attributes
  • Loading branch information
nivemaham authored Jul 10, 2018
2 parents 6da13a4 + f5ce9d6 commit 67acfd7
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 49 deletions.
23 changes: 23 additions & 0 deletions src/main/java/org/radarcns/management/service/SourceService.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package org.radarcns.management.service;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import org.radarcns.management.domain.Source;
import org.radarcns.management.repository.SourceRepository;
import org.radarcns.management.service.dto.MinimalSourceDetailsDTO;
Expand Down Expand Up @@ -131,4 +134,24 @@ public List<MinimalSourceDetailsDTO> findAllMinimalSourceDetailsByProjectAndAssi
.map(sourceMapper::sourceToMinimalSourceDetailsDTO)
.collect(Collectors.toList());
}

/**
* This method does a safe update of source assigned to a subject. It will allow updates of
* attributes only.
* @param sourceToUpdate source fetched from database
* @param attributes value to update
* @return Updated {@link MinimalSourceDetailsDTO} of source
*/
public MinimalSourceDetailsDTO safeUpdate(Source sourceToUpdate,
Map<String, String> attributes) {

// update source attributes
Map<String, String> updatedAttributes = new HashMap<>();
updatedAttributes.putAll(sourceToUpdate.getAttributes());
updatedAttributes.putAll(attributes);

sourceToUpdate.setAttributes(updatedAttributes);
// rest of the properties should not be updated from this request.
return sourceMapper.sourceToMinimalSourceDetailsDTO(sourceRepository.save(sourceToUpdate));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public ResponseEntity<List<SourceDTO>> getAllSources(@ApiParam Pageable pageable
log.debug("REST request to get all Sources");
Page<SourceDTO> page = sourceService.findAll(pageable);
HttpHeaders headers = PaginationUtil
.generatePaginationHttpHeaders(page, "/api/source-types");
.generatePaginationHttpHeaders(page, "/api/sources");
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.radarcns.auth.authorization.AuthoritiesConstants.INACTIVE_PARTICIPANT;
import static org.radarcns.auth.authorization.AuthoritiesConstants.PARTICIPANT;
import static org.radarcns.auth.authorization.Permission.SOURCE_UPDATE;
import static org.radarcns.auth.authorization.Permission.SUBJECT_CREATE;
import static org.radarcns.auth.authorization.Permission.SUBJECT_DELETE;
import static org.radarcns.auth.authorization.Permission.SUBJECT_READ;
Expand All @@ -11,36 +12,43 @@
import static org.radarcns.auth.authorization.RadarAuthorization.checkPermissionOnSubject;
import static org.radarcns.management.security.SecurityUtils.getJWT;

import com.codahale.metrics.annotation.Timed;
import io.github.jhipster.web.util.ResponseUtil;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;

import com.codahale.metrics.annotation.Timed;
import io.github.jhipster.web.util.ResponseUtil;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.radarcns.auth.config.Constants;
import org.radarcns.auth.exception.NotAuthorizedException;
import org.radarcns.management.domain.Role;
import org.radarcns.management.domain.Source;
import org.radarcns.management.domain.SourceType;
import org.radarcns.management.domain.Subject;
import org.radarcns.management.repository.ProjectRepository;
import org.radarcns.management.repository.SubjectRepository;
import org.radarcns.management.security.SecurityUtils;
import org.radarcns.management.service.SourceService;
import org.radarcns.management.service.SourceTypeService;
import org.radarcns.management.service.SubjectService;
import org.radarcns.management.service.dto.MinimalSourceDetailsDTO;
import org.radarcns.management.service.dto.SourceTypeDTO;
import org.radarcns.management.service.dto.SubjectDTO;
import org.radarcns.management.service.mapper.SubjectMapper;
import org.radarcns.management.web.rest.errors.CustomNotFoundException;
import org.radarcns.management.web.rest.errors.CustomParameterizedException;
import org.radarcns.management.web.rest.errors.ErrorConstants;
import org.radarcns.management.web.rest.util.HeaderUtil;
import org.radarcns.management.web.rest.util.PaginationUtil;
import org.slf4j.Logger;
Expand Down Expand Up @@ -95,6 +103,11 @@ public class SubjectResource {
@Autowired
private AuditEventRepository eventRepository;

@Autowired
private SourceService sourceService;



/**
* POST /subjects : Create a new subject.
*
Expand Down Expand Up @@ -447,4 +460,61 @@ public ResponseEntity<List<MinimalSourceDetailsDTO>> getSubjectSources(

return ResponseEntity.ok().body(subjectService.getSources(subject.get()));
}


/**
* POST /subjects/:login/sources/:sourceName Update source attributes and source-name.
*
* <p>The request body is a {@link Map} of strings. This request allows
* update of attributes only. Attributes will be merged and if a new value is
* provided for an existing key, the new value will be updated. The request will be validated
* for SOURCE.UPDATE permission and
* </p>
*
* @param attributes The {@link Map} specification
* @return The {@link MinimalSourceDetailsDTO} completed with all identifying fields.
* @throws CustomNotFoundException if the subject or the source not found using given ids.
*/
@PostMapping("/subjects/{login:" + Constants.ENTITY_ID_REGEX + "}/sources/{sourceName:"
+ Constants.ENTITY_ID_REGEX + "}")
@ApiResponses({
@ApiResponse(code = 200, message = "An existing source was updated"),
@ApiResponse(code = 400, message = "You must supply existing sourceId)"),
@ApiResponse(code = 404, message = "Either the subject or the source was not found.")
})
@Timed
public ResponseEntity<MinimalSourceDetailsDTO> updateSubjectSource(@PathVariable String login,
@PathVariable String sourceName, @RequestBody Map<String, String> attributes)
throws CustomNotFoundException, NotAuthorizedException,
URISyntaxException {
// check the subject id
Optional<Subject> subject = subjectRepository.findOneWithEagerBySubjectLogin(login);
if (!subject.isPresent()) {
Map<String, String> errorParams = new HashMap<>();
errorParams.put("message", "Subject ID not found");
errorParams.put("subjectLogin", login);
throw new CustomNotFoundException(ErrorConstants.ERR_SUBJECT_NOT_FOUND, errorParams);
}
// check the permission to update source
SubjectDTO subjectDto = subjectMapper.subjectToSubjectDTO(subject.get());
checkPermissionOnSubject(getJWT(servletRequest), SOURCE_UPDATE,
subjectDto.getProject().getProjectName(), subjectDto.getLogin());

// find source under subject
List<Source> sources = subject.get().getSources().stream()
.filter(s -> s.getSourceName().equals(sourceName))
.collect(Collectors.toList());

// exception if source is not found under subject
if (sources.isEmpty()) {
Map<String, String> errorParams = new HashMap<>();
errorParams.put("message", "Source not found under assigned sources of subject");
errorParams.put("subjectLogin", login);
errorParams.put("sourceName", sourceName);
throw new CustomNotFoundException(ErrorConstants.ERR_SUBJECT_NOT_FOUND, errorParams);
}

// there should be only one source under a source-name.
return ResponseEntity.ok().body(sourceService.safeUpdate(sources.get(0), attributes));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.servlet.ServletException;
Expand All @@ -26,6 +28,7 @@
import org.radarcns.management.repository.ProjectRepository;
import org.radarcns.management.repository.SubjectRepository;
import org.radarcns.management.security.JwtAuthenticationFilter;
import org.radarcns.management.service.SourceService;
import org.radarcns.management.service.SourceTypeService;
import org.radarcns.management.service.SubjectService;
import org.radarcns.management.service.dto.MinimalSourceDetailsDTO;
Expand All @@ -44,6 +47,7 @@
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -102,6 +106,9 @@ public class SubjectResourceIntTest {
@Autowired
private HttpServletRequest servletRequest;

@Autowired
private SourceService sourceService;

private MockMvc restSubjectMockMvc;

@Before
Expand All @@ -114,6 +121,7 @@ public void setUp() throws ServletException {
ReflectionTestUtils.setField(subjectResource, "projectRepository" , projectRepository);
ReflectionTestUtils.setField(subjectResource, "sourceTypeService", sourceTypeService);
ReflectionTestUtils.setField(subjectResource, "servletRequest", servletRequest);
ReflectionTestUtils.setField(subjectResource, "sourceService", sourceService);

JwtAuthenticationFilter filter = OAuthHelper.createAuthenticationFilter();
filter.init(new MockFilterConfig());
Expand Down Expand Up @@ -374,43 +382,6 @@ public void dynamicSourceRegistrationWithId() throws Exception {
.andExpect(status().is4xxClientError());
}

@Test
@Transactional
public void dynamicSourceRegistrationWithoutId() throws Exception {
final int databaseSizeBeforeCreate = subjectRepository.findAll().size();

// Create the Subject
SubjectDTO subjectDto = createEntityDTO(em);
restSubjectMockMvc.perform(post("/api/subjects")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(subjectDto)))
.andExpect(status().isCreated());

// Validate the Subject in the database
List<Subject> subjectList = subjectRepository.findAll();
assertThat(subjectList).hasSize(databaseSizeBeforeCreate + 1);
Subject testSubject = subjectList.get(subjectList.size() - 1);

String subjectLogin = testSubject.getUser().getLogin();
assertNotNull(subjectLogin);

// Create a source description
MinimalSourceDetailsDTO sourceRegistrationDto = createSourceWithoutDeviceId();

restSubjectMockMvc.perform(post("/api/subjects/{login}/sources", subjectLogin)
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(sourceRegistrationDto)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.sourceId").isNotEmpty());

// A source can not be assigned twice to a subject, so this call must fail
assertThat(sourceRegistrationDto.getSourceId()).isNull();
restSubjectMockMvc.perform(post("/api/subjects/{login}/sources", subjectLogin)
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(sourceRegistrationDto)))
.andExpect(status().is4xxClientError());
}

private MinimalSourceDetailsDTO createSourceWithDeviceId() {
// Create a source description
MinimalSourceDetailsDTO sourceRegistrationDto = new MinimalSourceDetailsDTO();
Expand Down Expand Up @@ -448,4 +419,54 @@ private MinimalSourceDetailsDTO createSourceWithoutDeviceId() {
assertThat(sourceRegistrationDto.getSourceId()).isNull();
return sourceRegistrationDto;
}

@Test
@Transactional
public void testDynamicRegistrationAndUpdateSourceAttributes() throws Exception {
final int databaseSizeBeforeCreate = subjectRepository.findAll().size();

// Create the Subject
SubjectDTO subjectDto = createEntityDTO(em);
restSubjectMockMvc.perform(post("/api/subjects")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(subjectDto)))
.andExpect(status().isCreated());

// Validate the Subject in the database
List<Subject> subjectList = subjectRepository.findAll();
assertThat(subjectList).hasSize(databaseSizeBeforeCreate + 1);
Subject testSubject = subjectList.get(subjectList.size() - 1);

String subjectLogin = testSubject.getUser().getLogin();
assertNotNull(subjectLogin);

// Create a source description
MinimalSourceDetailsDTO sourceRegistrationDto = createSourceWithoutDeviceId();

MvcResult result = restSubjectMockMvc.perform(post("/api/subjects/{login}/sources",
subjectLogin)
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(sourceRegistrationDto)))
.andExpect(status().isOk())
.andReturn();

MinimalSourceDetailsDTO value = (MinimalSourceDetailsDTO)
TestUtil.convertJsonStringToObject(result
.getResponse().getContentAsString() , MinimalSourceDetailsDTO.class);

assertNotNull(value.getSourceName());

Map<String, String> attributes = new HashMap<>();
attributes.put("TEST_KEY" , "Value");
attributes.put("ANDROID_VERSION" , "something");
attributes.put("Other" , "test");

restSubjectMockMvc.perform(post(
"/api/subjects/{login}/sources/{sourceName}", subjectLogin, value.getSourceName())
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(attributes)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.attributes").isNotEmpty());

}
}
27 changes: 21 additions & 6 deletions src/test/java/org/radarcns/management/web/rest/TestUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,27 @@ public class TestUtil {
MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));


private static final JavaTimeModule module = new JavaTimeModule();

private static final ObjectMapper mapper = new ObjectMapper()
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.registerModule(module);


/**
* Convert a JSON String to an object.
*
* @param json JSON String to convert.
* @param objectClass Object class to form.
*
* @return the converted object instance.
*/
public static <T> Object convertJsonStringToObject(String json, Class<T> objectClass)
throws IOException {
return mapper.readValue(json, objectClass);
}

/**
* Convert an object to JSON byte array.
*
Expand All @@ -31,12 +52,6 @@ public class TestUtil {
* @return the JSON byte array
*/
public static byte[] convertObjectToJsonBytes(Object object) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

JavaTimeModule module = new JavaTimeModule();
mapper.registerModule(module);

return mapper.writeValueAsBytes(object);
}

Expand Down

0 comments on commit 67acfd7

Please sign in to comment.