diff --git a/backend/src/main/java/eu/bbmri_eric/negotiator/form/repository/AccessFormRepository.java b/backend/src/main/java/eu/bbmri_eric/negotiator/form/repository/AccessFormRepository.java index 2d599b8ae..6fb590950 100644 --- a/backend/src/main/java/eu/bbmri_eric/negotiator/form/repository/AccessFormRepository.java +++ b/backend/src/main/java/eu/bbmri_eric/negotiator/form/repository/AccessFormRepository.java @@ -42,4 +42,16 @@ public interface AccessFormRepository extends JpaRepository { + ")", nativeQuery = true) boolean isElementPartOfSectionOfAccessForm(Long accessFormId, Long sectionId, Long elementId); + + @Query( + value = + "SELECT a.* " + + "FROM resource r " + + " INNER JOIN access_form a on a.id = r.access_form_id " + + "WHERE r.organization_id = :organizationId " + + "GROUP BY a.id " + + "ORDER BY COUNT(r.id) DESC " + + "LIMIT 1", + nativeQuery = true) + Optional findFirstMostCommonAccessFormByOrganization(Long organizationId); } diff --git a/backend/src/main/java/eu/bbmri_eric/negotiator/governance/resource/ResourceServiceImpl.java b/backend/src/main/java/eu/bbmri_eric/negotiator/governance/resource/ResourceServiceImpl.java index 24265722c..6cf15a63e 100644 --- a/backend/src/main/java/eu/bbmri_eric/negotiator/governance/resource/ResourceServiceImpl.java +++ b/backend/src/main/java/eu/bbmri_eric/negotiator/governance/resource/ResourceServiceImpl.java @@ -59,6 +59,8 @@ public class ResourceServiceImpl implements ResourceService { private final OrganizationRepository organizationRepository; private final NegotiationAccessManager negotiationAccessManager; + private static final long DEFAULT_ACCESS_FORM_ID = 1L; + public ResourceServiceImpl( NetworkRepository networkRepository, ResourceRepository repository, @@ -222,10 +224,7 @@ public List addResources(List resource .findById(resDTO.getDiscoveryServiceId()) .orElseThrow( () -> new DiscoveryServiceNotFoundException(resDTO.getDiscoveryServiceId())); - AccessForm accessForm = - accessFormRepository - .findById(resDTO.getAccessFormId()) - .orElseThrow(() -> new AccessFormNotFoundException(resDTO.getAccessFormId())); + AccessForm accessForm = getAccessForm(resDTO.getAccessFormId(), resDTO.getOrganizationId()); Organization organization = organizationRepository .findById(resDTO.getOrganizationId()) @@ -261,4 +260,19 @@ private Negotiation fetchNegotiationFromDB(String negotiationId) { .findById(negotiationId) .orElseThrow(() -> new EntityNotFoundException(negotiationId)); } + + private AccessForm getAccessForm(Long accessFormId, Long organizationId) { + if (accessFormId == null) { + return accessFormRepository + .findFirstMostCommonAccessFormByOrganization(organizationId) + .orElseGet( + () -> + accessFormRepository + .findById(DEFAULT_ACCESS_FORM_ID) + .orElseThrow(() -> new AccessFormNotFoundException(DEFAULT_ACCESS_FORM_ID))); + } + return accessFormRepository + .findById(accessFormId) + .orElseThrow(() -> new AccessFormNotFoundException(accessFormId)); + } } diff --git a/backend/src/main/java/eu/bbmri_eric/negotiator/governance/resource/dto/ResourceCreateDTO.java b/backend/src/main/java/eu/bbmri_eric/negotiator/governance/resource/dto/ResourceCreateDTO.java index 9595fb6f0..629c8e754 100644 --- a/backend/src/main/java/eu/bbmri_eric/negotiator/governance/resource/dto/ResourceCreateDTO.java +++ b/backend/src/main/java/eu/bbmri_eric/negotiator/governance/resource/dto/ResourceCreateDTO.java @@ -32,8 +32,7 @@ public class ResourceCreateDTO { @Schema(description = "ID of the associated organization", example = "1") private Long organizationId; - @NotNull - @Schema(description = "ID of the access form related to the resource", example = "42") + @Schema(description = "Optional ID of the access form related to the resource", example = "42") private Long accessFormId; @NotNull diff --git a/backend/src/main/java/eu/bbmri_eric/negotiator/governance/resource/dto/ResourceWithRepsDTO.java b/backend/src/main/java/eu/bbmri_eric/negotiator/governance/resource/dto/ResourceWithRepsDTO.java index c50cd85ad..84b9e3ebc 100644 --- a/backend/src/main/java/eu/bbmri_eric/negotiator/governance/resource/dto/ResourceWithRepsDTO.java +++ b/backend/src/main/java/eu/bbmri_eric/negotiator/governance/resource/dto/ResourceWithRepsDTO.java @@ -1,5 +1,6 @@ package eu.bbmri_eric.negotiator.governance.resource.dto; +import eu.bbmri_eric.negotiator.form.dto.AccessFormDTO; import eu.bbmri_eric.negotiator.user.UserDTO; import io.swagger.v3.oas.annotations.media.Schema; import java.util.Set; @@ -14,4 +15,7 @@ public class ResourceWithRepsDTO extends ResourceResponseModel { example = "[{\"id\": \"123\", \"name\": \"Sarah Rep\", \"email\": \"sarah@example.com\"}, {\"id\": \"456\", \"name\": \"Adam Rep\", \"email\": \"adam@example.com\"}]") private Set representatives; + + @Schema(description = "Access form associated with this resource") + private AccessFormDTO accessForm; } diff --git a/backend/src/test/java/eu/bbmri_eric/negotiator/governance/resource/ResourceControllerTests.java b/backend/src/test/java/eu/bbmri_eric/negotiator/governance/resource/ResourceControllerTests.java index c682b5061..7e8a05683 100644 --- a/backend/src/test/java/eu/bbmri_eric/negotiator/governance/resource/ResourceControllerTests.java +++ b/backend/src/test/java/eu/bbmri_eric/negotiator/governance/resource/ResourceControllerTests.java @@ -2,6 +2,7 @@ import static org.hamcrest.core.Is.is; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -55,6 +56,10 @@ public class ResourceControllerTests { private static final String RESOURCES_ENDPOINT = "/v3/resources"; + private static final long DEFAULT_ACCESS_FORM_ID = 1L; + + private static final long DEFAULT_DISCOVERY_SERVICE_ID = 1L; + @Autowired private WebApplicationContext context; @Autowired private ResourceRepository repository; @Autowired private OrganizationRepository organizationRepository; @@ -71,6 +76,36 @@ public void before() { mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build(); } + private Organization createOrganization(String suffix) { + return Organization.builder() + .name("Organization " + suffix) + .description("Organization " + suffix) + .externalId("test_organization_" + suffix) + .build(); + } + + private ResourceCreateDTO createResource( + String name, String sourceId, Long organizationId, Long accessFormId) { + return ResourceCreateDTO.builder() + .name(name) + .description(name) + .contactEmail(name.toLowerCase().replace(" ", "") + "@test.org") + .uri("https://" + name.toLowerCase().replace(" ", "") + ".test.org") + .sourceId(sourceId) + .organizationId(organizationId) + .accessFormId(accessFormId) + .discoveryServiceId(DEFAULT_DISCOVERY_SERVICE_ID) + .build(); + } + + private DiscoveryService createDiscoveryService(long id) { + return DiscoveryService.builder() + .name("test_discovery_service") + .id(id) + .url("http://discoveryservice.net") + .build(); + } + @Test @WithMockUser("researcher") void getResourceById_validId_ok() throws Exception { @@ -427,6 +462,133 @@ void addResource_batchOfResources_ok() throws Exception { assertEquals(resourceDTO2.getUri(), resource2.get().getUri()); } + @Test + @WithUserDetails("admin") + void addResource_noAccessFormSelected_ok() throws Exception { + Organization org1 = createOrganization("10"); + organizationRepository.save(org1); + Long org1Id = organizationRepository.findByExternalId("test_organization_10").get().getId(); + + ResourceCreateDTO resourceDTO1 = createResource("Resource 10", "resource_10", org1Id, null); + + discoveryServiceRepository + .findById(DEFAULT_DISCOVERY_SERVICE_ID) + .orElseGet( + () -> + discoveryServiceRepository.save( + createDiscoveryService(DEFAULT_DISCOVERY_SERVICE_ID))); + + String requestBody = TestUtils.jsonFromRequest(Arrays.asList(resourceDTO1)); + MvcResult result = + mockMvc + .perform( + MockMvcRequestBuilders.post(RESOURCES_ENDPOINT) + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody)) + .andExpect(status().isCreated()) + .andExpect(content().contentType("application/hal+json")) + .andExpect(jsonPath("$._embedded.resources[0].name", is(resourceDTO1.getName()))) + .andExpect( + jsonPath("$._embedded.resources[0].sourceId", is(resourceDTO1.getSourceId()))) + .andExpect( + jsonPath("$._embedded.resources[0].description", is(resourceDTO1.getDescription()))) + .andExpect( + jsonPath( + "$._embedded.resources[0].contactEmail", is(resourceDTO1.getContactEmail()))) + .andExpect(jsonPath("$._embedded.resources[0].uri", is(resourceDTO1.getUri()))) + .andReturn(); + + String id1 = + JsonPath.parse(result.getResponse().getContentAsString()) + .read("$._embedded.resources[0].sourceId"); + Optional resource1 = repository.findBySourceId(id1); + assertTrue(resource1.isPresent()); + assertEquals(resourceDTO1.getName(), resource1.get().getName()); + assertEquals(resourceDTO1.getDescription(), resource1.get().getDescription()); + assertEquals(resourceDTO1.getContactEmail(), resource1.get().getContactEmail()); + assertEquals(resourceDTO1.getUri(), resource1.get().getUri()); + assertEquals(DEFAULT_ACCESS_FORM_ID, resource1.get().getAccessForm().getId()); + } + + @Test + @WithUserDetails("admin") + void addResource_useOrganizationDefaultAccessForm_ok() throws Exception { + Organization org1 = createOrganization("11"); + organizationRepository.save(org1); + Long org1Id = organizationRepository.findByExternalId("test_organization_11").get().getId(); + + ResourceCreateDTO resourceDTO1 = createResource("Resource 11", "resource_11", org1Id, 2L); + ResourceCreateDTO resourceDTO2 = createResource("Resource 12", "resource_12", org1Id, null); + + discoveryServiceRepository + .findById(DEFAULT_DISCOVERY_SERVICE_ID) + .orElseGet( + () -> + discoveryServiceRepository.save( + createDiscoveryService(DEFAULT_DISCOVERY_SERVICE_ID))); + + String requestBody1 = TestUtils.jsonFromRequest(Arrays.asList(resourceDTO1)); + MvcResult result1 = + mockMvc + .perform( + MockMvcRequestBuilders.post(RESOURCES_ENDPOINT) + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody1)) + .andExpect(status().isCreated()) + .andExpect(content().contentType("application/hal+json")) + .andExpect(jsonPath("$._embedded.resources[0].name", is(resourceDTO1.getName()))) + .andExpect( + jsonPath("$._embedded.resources[0].sourceId", is(resourceDTO1.getSourceId()))) + .andExpect( + jsonPath("$._embedded.resources[0].description", is(resourceDTO1.getDescription()))) + .andExpect( + jsonPath( + "$._embedded.resources[0].contactEmail", is(resourceDTO1.getContactEmail()))) + .andExpect(jsonPath("$._embedded.resources[0].uri", is(resourceDTO1.getUri()))) + .andReturn(); + + String requestBody2 = TestUtils.jsonFromRequest(Arrays.asList(resourceDTO2)); + MvcResult result2 = + mockMvc + .perform( + MockMvcRequestBuilders.post(RESOURCES_ENDPOINT) + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody2)) + .andExpect(status().isCreated()) + .andExpect(content().contentType("application/hal+json")) + .andExpect(jsonPath("$._embedded.resources[0].name", is(resourceDTO2.getName()))) + .andExpect( + jsonPath("$._embedded.resources[0].sourceId", is(resourceDTO2.getSourceId()))) + .andExpect( + jsonPath("$._embedded.resources[0].description", is(resourceDTO2.getDescription()))) + .andExpect( + jsonPath( + "$._embedded.resources[0].contactEmail", is(resourceDTO2.getContactEmail()))) + .andExpect(jsonPath("$._embedded.resources[0].uri", is(resourceDTO2.getUri()))) + .andReturn(); + + String id1 = + JsonPath.parse(result1.getResponse().getContentAsString()) + .read("$._embedded.resources[0].sourceId"); + Optional resource1 = repository.findBySourceId(id1); + assertTrue(resource1.isPresent()); + assertEquals(resourceDTO1.getName(), resource1.get().getName()); + assertEquals(resourceDTO1.getDescription(), resource1.get().getDescription()); + assertEquals(resourceDTO1.getContactEmail(), resource1.get().getContactEmail()); + assertEquals(resourceDTO1.getUri(), resource1.get().getUri()); + + Long id2 = + JsonPath.parse(result2.getResponse().getContentAsString()) + .read("$._embedded.resources[0].id", Long.class); + Optional resource2 = repository.findById(id2); + assertTrue(resource2.isPresent()); + assertEquals(resourceDTO2.getName(), resource2.get().getName()); + assertEquals(resourceDTO2.getDescription(), resource2.get().getDescription()); + assertEquals(resourceDTO2.getContactEmail(), resource2.get().getContactEmail()); + assertEquals(resourceDTO2.getUri(), resource2.get().getUri()); + assertEquals(2L, resource2.get().getAccessForm().getId()); + } + @Test @WithUserDetails("admin") void updateResource_singleResource_ok() throws Exception { diff --git a/frontend/src/components/OrganizationListItem.vue b/frontend/src/components/OrganizationListItem.vue index d021ad4cf..97cb18471 100644 --- a/frontend/src/components/OrganizationListItem.vue +++ b/frontend/src/components/OrganizationListItem.vue @@ -180,6 +180,19 @@ + + +
+ + Access Form: + + {{ resource.accessForm.name }} + + + None assigned + + +