Commit 33b371f6 authored by Matija Obreza's avatar Matija Obreza
Browse files

Improved API documentation, added Requests API section

parent 545724b3
[[chApiRequests]]
== Managing Requests
Institutes can allow users to request for material through Genesys. Genesys will
validate the user's email address (by waiting for them to click the confirmation
link sent to their address) and also check if the user is registered with ITPGRFA's
Easy-SMTA database.
Genesys maintains a database of requests received, confirmed and dispatched to
genebanks holding the requested materials. This API allows genebanks to retrieve
the request data from Genesys for automated integration into their existing information
systems.
[NOTE]
.Requests and sub-requests
=====================================================================
A client may request for material from several different genebanks in
one session. Genesys splits the request and dispatches sub-requests to
email addresses registered with individual genebanks.
=====================================================================
Managing crop data in Genesys is done through methods available in */api/v0/requests*
namespace.
=== Listing requests
To list all requests for the INSTCODE, you must have the `ADMINISTRATOR` permission
on the institute record. Issue a GET request to */api/v0/requests/{instCode}* endpoint:
include::{snippets}/requests-inst-list/curl-request.adoc[]
==== Path parameters
include::{snippets}/requests-inst-list/path-parameters.adoc[]
==== Request parameters
include::{snippets}/requests-inst-list/request-parameters.adoc[]
==== Server response
The object returned by Genesys contains pagination information and basic request data
in the `content` element:
[source,json,linenums]
----
{
"content": [{
"uuid": "4422e87e-52fe-38ef-b04a-079e663dd3da",
"version": 0,
"instCode": "XXX001",
"instEmail": "institute@localhost",
"state": 0,
"createdDate": null,
"lastModifiedDate": null,
"lastReminderDate": null
}], <1>
"last": true,
"totalElements": 1, <2>
"totalPages": 1, <3>
"size": 10, <4>
"number": 0,
"sort": [{
"direction": "DESC",
"property": "createdDate",
"ignoreCase": false,
"nullHandling": "NATIVE",
"ascending": false
}],
"first": true,
"numberOfElements": 1 <5>
}
----
<1> Array containing request information
<2> Number of elements in the Genesys database
<3> Number of pages
<4> Page size
<5> Number of elements in the `content` array
include::{snippets}/requests-inst-list/response-fields.adoc[]
=== Request details
To retrieve request details from Genesys:
include::{snippets}/requests-inst-details/curl-request.adoc[]
==== Path parameters
include::{snippets}/requests-inst-details/path-parameters.adoc[]
==== Server response
[source,json,linenums]
----
{
"uuid": "7ecb3c6e-63d8-3869-b2c7-28571c9e2864",
"version": 0,
"instCode": "XXX001",
"instEmail": "institute@localhost",
"state": 0,
"createdDate": null,
"lastModifiedDate": null,
"lastReminderDate": null
}
----
include::{snippets}/requests-inst-details/response-fields.adoc[]
......@@ -18,9 +18,10 @@ through https://sandbox.genesys-pgr.org for testing purposes. All data is sent a
In this manual, all URLs are pointing to the Genesys sandbox environment at https://sandbox.genesys-pgr.org.
include::sections/security.adoc[]
include::sections/api-accession.adoc[]
include::sections/api-crop.adoc[]
include::api/security.adoc[]
include::api/accession.adoc[]
include::api/crop.adoc[]
include::api/requests.adoc[]
== Acknowledgements
......
......@@ -71,7 +71,8 @@ include::sections/recovery.adoc[]
include::sections/wiews.adoc[]
include::sections/mcpd.adoc[]
include::sections/security.adoc[]
include::sections/api-accession.adoc[]
include::sections/api-crop.adoc[]
include::api/security.adoc[]
include::api/accession.adoc[]
include::api/crop.adoc[]
include::api/requests.adoc[]
......@@ -16,8 +16,19 @@
package org.genesys2.server.service.impl;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
......@@ -30,10 +41,15 @@ import org.genesys2.server.model.impl.FaoInstituteSetting;
import org.genesys2.server.model.impl.VerificationToken;
import org.genesys2.server.persistence.domain.MaterialRequestRepository;
import org.genesys2.server.persistence.domain.MaterialSubRequestRepository;
import org.genesys2.server.service.*;
import org.genesys2.server.service.ContentService;
import org.genesys2.server.service.EMailService;
import org.genesys2.server.service.EasySMTA;
import org.genesys2.server.service.GenesysService;
import org.genesys2.server.service.InstituteService;
import org.genesys2.server.service.RequestService;
import org.genesys2.server.service.TokenVerificationService;
import org.genesys2.server.service.TokenVerificationService.NoSuchVerificationTokenException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.data.domain.Page;
......@@ -43,9 +59,6 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.util.*;
@Service
@Transactional(readOnly = true)
public class RequestServiceImpl implements RequestService {
......
......@@ -55,6 +55,7 @@ public class RequestsController extends RestController {
*
* @return
*/
@PreAuthorize("hasRole('ADMINISTRATOR')")
@RequestMapping(value = "/requests", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody
Object listRequests(@RequestParam(value = "page", required = false, defaultValue = "0") int page) {
......
......@@ -141,7 +141,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy(@ContextConfiguration(name = "this", classes = { ElasticsearchConfig.class, AbstractRestTest.Config.class}, initializers = PropertyPlacholderInitializer.class))
@ContextHierarchy(@ContextConfiguration(name = "this", classes = { ElasticsearchConfig.class, AbstractRestTest.Config.class }, initializers = PropertyPlacholderInitializer.class))
@ActiveProfiles("dev")
@WebAppConfiguration
public abstract class AbstractRestTest extends SpringTest {
......@@ -180,7 +180,7 @@ public abstract class AbstractRestTest extends SpringTest {
public UserService userService() {
return new UserServiceImpl();
}
@Bean
public PasswordPolicy passwordPolicy() {
return new SimplePasswordPolicy();
......@@ -453,173 +453,172 @@ public abstract class AbstractRestTest extends SpringTest {
}
@Autowired
AccessionHistoricRepository accessionHistoricRepository;
protected AccessionHistoricRepository accessionHistoricRepository;
@Autowired
CropRuleRepository cropRuleRepository;
protected CropRuleRepository cropRuleRepository;
@Autowired
TaxonomyService taxonomyService;
protected TaxonomyService taxonomyService;
@Autowired
Taxonomy2Repository taxonomy2Repository;
protected Taxonomy2Repository taxonomy2Repository;
@Autowired
MetadataMethodRepository metadataMethodRepository;
protected MetadataMethodRepository metadataMethodRepository;
@Autowired
MetadataRepository metadataRepository;
protected MetadataRepository metadataRepository;
@Autowired
DatasetService datasetService;
protected DatasetService datasetService;
@Autowired
ObservationRepository observationRepository;
protected ObservationRepository observationRepository;
@Autowired
KPIParameterRepository kpiParameterRepository;
protected KPIParameterRepository kpiParameterRepository;
@Autowired
KPIService kpiService;
// @Autowired
protected KPIService kpiService;
// @Autowired protected
// OAuthManagementController oAuthManagementController;
@Autowired
OAuthAccessTokenPersistence accessTokenPersistence;
protected OAuthAccessTokenPersistence accessTokenPersistence;
@Autowired
OAuthRefreshTokenPersistence refreshTokenPersistence;
protected OAuthRefreshTokenPersistence refreshTokenPersistence;
@Autowired
OAuthClientDetailsPersistence clientDetailsPersistence;
protected OAuthClientDetailsPersistence clientDetailsPersistence;
@Autowired
OAuth2ClientDetailsService clientDetailsService;
protected OAuth2ClientDetailsService clientDetailsService;
@Autowired
TokenController tokenController;
protected TokenController tokenController;
@Autowired
ConsumerTokenServices tokenServices;
protected ConsumerTokenServices tokenServices;
@Autowired
TokenStore tokenStore;
protected TokenStore tokenStore;
@Autowired
ConsumerTokenServices consumerTokenServices;
protected ConsumerTokenServices consumerTokenServices;
@Autowired
EasySMTA easySMTAConnector;
protected EasySMTA easySMTAConnector;
@Autowired
JavaMailSender mailSender;
protected JavaMailSender mailSender;
@Autowired
AccessionRepository accessionRepository;
protected AccessionRepository accessionRepository;
@Autowired
AccessionCustomRepository accessionCustomRepository;
protected AccessionCustomRepository accessionCustomRepository;
@Autowired
PermissionController permissionController;
protected PermissionController permissionController;
@Autowired
GeoService geoService;
protected GeoService geoService;
@Autowired
CountryRepository countryRepository;
protected CountryRepository countryRepository;
@Autowired
FaoInstituteRepository instituteRepository;
protected FaoInstituteRepository instituteRepository;
@Autowired
FaoInstituteSettingRepository instituteSettingRepository;
protected FaoInstituteSettingRepository instituteSettingRepository;
@Autowired
MethodRepository methodRepository;
protected MethodRepository methodRepository;
@Autowired
LookupController lookupController;
protected LookupController lookupController;
@Autowired
TraitsController traitsController;
protected TraitsController traitsController;
@Autowired
@Qualifier("traitServiceMock")
TraitService traitService;
protected @Qualifier("traitServiceMock") TraitService traitService;
@Autowired
ArticleRepository articleRepository;
protected ArticleRepository articleRepository;
@Autowired
ContentService contentService;
protected ContentService contentService;
@Autowired
JavaMailSender javaMailSender;
protected JavaMailSender javaMailSender;
@Autowired
UsersController restUsersController;
protected UsersController restUsersController;
@Autowired
UserService userService;
protected UserService userService;
@Autowired
UserController userController;
protected UserController userController;
@Autowired
UserPersistence userPersistence;
protected UserPersistence userPersistence;
@Autowired
InstituteService instituteService;
protected InstituteService instituteService;
@Autowired
TeamRepository teamRepository;
protected TeamRepository teamRepository;
@Autowired
AclService aclService;
protected AclService aclService;
@Autowired
AclSidPersistence aclSidPersistence;
protected AclSidPersistence aclSidPersistence;
@Autowired
AclEntryPersistence aclEntryPersistence;
protected AclEntryPersistence aclEntryPersistence;
@Autowired
AclObjectIdentityPersistence aclObjectIdentityPersistence;
protected AclObjectIdentityPersistence aclObjectIdentityPersistence;
@Autowired
AclClassPersistence aclClassPersistence;
protected AclClassPersistence aclClassPersistence;
@Autowired
TeamService teamService;
protected TeamService teamService;
@Autowired
GenesysService genesysService;
protected GenesysService genesysService;
@Autowired
CropService cropService;
protected CropService cropService;
@Autowired
CropRepository cropRepository;
protected CropRepository cropRepository;
@Autowired
ParameterRepository parameterRepository;
protected ParameterRepository parameterRepository;
@Autowired
ParameterCategoryRepository parameterCategoryRepository;
protected ParameterCategoryRepository parameterCategoryRepository;
@Autowired
OrganizationService organizationService;
protected OrganizationService organizationService;
@Autowired
OrganizationRepository organizationRepository;
protected OrganizationRepository organizationRepository;
@Autowired
RequestService requestService;
protected RequestService requestService;
@Autowired
MaterialRequestRepository materialRequestRepository;
protected MaterialRequestRepository materialRequestRepository;
@Autowired
MaterialSubRequestRepository materialSubRequestRepository;
protected MaterialSubRequestRepository materialSubRequestRepository;
}
package org.genesys2.tests.resttests;
package org.genesys2.tests.resttests.docs;
import static org.hamcrest.Matchers.*;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*;
......@@ -14,6 +14,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.genesys2.server.model.impl.Crop;
import org.genesys2.server.model.impl.CropRule;
import org.genesys2.tests.resttests.AbstractRestTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
......@@ -248,7 +249,7 @@ public class ApiDocumentation extends AbstractRestTest {
}
// FIXME permissions
@Ignore
// @Ignore
@Test
public void updateCropRulesTest() throws Exception {
LOG.info("Start test-method updateCropRulesTest");
......
package org.genesys2.tests.resttests.docs;
import static org.hamcrest.Matchers.*;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
import static org.springframework.restdocs.request.RequestDocumentation.*;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.UUID;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.genesys2.server.model.genesys.MaterialRequest;
import org.genesys2.server.model.genesys.MaterialSubRequest;
import org.genesys2.server.model.impl.FaoInstitute;
import org.genesys2.tests.resttests.AbstractRestTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.restdocs.RestDocumentation;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
@Transactional(transactionManager = "transactionManager")
public class ApiRequestsDoc extends AbstractRestTest {
private static final String TEST_INSTCODE = "XXX001";
private static final Log LOG = LogFactory.getLog(ApiRequestsDoc.class);
@Rule
public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets");
@Autowired
WebApplicationContext webApplicationContext;
MockMvc mockMvc;
private static final ObjectMapper objectMapper;
private FaoInstitute institute;
private UUID requestUuid;
static {
objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
objectMapper.configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false);
objectMapper.setSerializationInclusion(Include.NON_EMPTY);
}
@Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(documentationConfiguration(this.restDocumentation).uris().withScheme("https").withHost("sandbox.genesys-pgr.org").withPort(443)).build();
Collection<FaoInstitute> institutes = new ArrayList<>();
institute = new FaoInstitute();
institute.setCode(ApiRequestsDoc.TEST_INSTCODE);
institutes.add(institute);
instituteService.update(institutes);
MaterialRequest request = new MaterialRequest();
request.setEmail("user@localhost");
request.setPid("PID");
request.setState(MaterialRequest.VALIDATED);
materialRequestRepository.save(request);
MaterialSubRequest subrequest = new MaterialSubRequest();
subrequest.setInstCode(ApiRequestsDoc.TEST_INSTCODE);
subrequest.setInstEmail("institute@localhost");
subrequest.setSourceRequest(request);
subrequest.setState(MaterialSubRequest.NOTCONFIRMED);
materialSubRequestRepository.save(subrequest);
requestUuid = UUID.fromString(subrequest.getUuid());
}
@After
public void tearDown() {
instituteRepository.deleteAll();
materialSubRequestRepository.deleteAll();
materialRequestRepository.deleteAll();
}
@Test
public void listRequestsTest() throws Exception {
mockMvc.perform(get("/api/v0/requests/{instCode}", TEST_INSTCODE).param("page", "0").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andDo(r -> {
System.err.println(r.getResponse().getContentAsString());
}).andExpect(jsonPath("$.size", is(10))).andExpect(jsonPath("$.first", is(true)))
.andDo(document("requests-inst-list", pathParameters(parameterWithName("instCode").description("Institute WIEWS code (e.g. NGA039)")),
requestParameters(parameterWithName("page").description("Page to request from the server")),
responseFields(
fieldWithPath("content").description("Array containing request headers"),
fieldWithPath("numberOfElements").description("Number of records in the content array"),
fieldWithPath("totalElements").description("Total number of requests in Genesys belonging to this institute"),
fieldWithPath("size").description("Page size"),
fieldWithPath("number").description("Current page number"),
fieldWithPath("totalPages").description("Page count"),
fieldWithPath("sort").description("Arrray sorting details"),
fieldWithPath("first").ignored(),
fieldWithPath("last").ignored()
)));
}
// /requests/{instCode}/r/{uuid:.{36}}
@Test
public void getRequestInfoTest() throws Exception {
mockMvc.perform(get("/api/v0/requests//{instCode}/r/{uuid}", TEST_INSTCODE, requestUuid.toString()).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andDo(r -> {
System.err.println(r.getResponse().getContentAsString());
})
// .andExpect(jsonPath("$", hasSize(1)))
.andExpect(jsonPath("$.uuid", is(requestUuid.toString()))).andExpect(jsonPath("$.instCode", is(TEST_INSTCODE)))
.andDo(document("requests-inst-details",
pathParameters(parameterWithName("instCode").description("Institute WIEWS code (e.g. NGA039)"),
parameterWithName("uuid").description("UUID of the request")),
responseFields(fieldWithPath("uuid").description("The request UUID"), fieldWithPath("version").description("Record version indicator"),