Commit 9bbb4e68 authored by Matija Obreza's avatar Matija Obreza

Updated to BrAPI v1.3

- Added /calls call
parent 8e102206
......@@ -2,8 +2,11 @@
== Authentication
All BrAPI requests can be made anonymously, without providing the OAuth2
*Bearer: <TOKEN>* authentication header.
All BrAPI requests require OAuth2 authentication header in HTTP requests:
----
Authorization: Bearer <TOKEN>
----
=== Login
......
/*
* Copyright 2018 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.genesys2.brapi.model;
import java.util.Arrays;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
/**
* Data pagination request.
*
* @author Matija Obreza
*/
public class BrAPIPage {
private static final int MAX_PAGESIZE = 1000;
/** The default sort properties. */
private final String[] DEFAULT_SORT_PROPERTIES = { "id" };
/** Page (0-based). */
private Integer page;
/** Page size (length). */
private Integer pageSize;
/** Sort direction. */
private Sort.Direction dir;
/** Sort properties. */
private String[] sort;
/**
* Gets the page.
*
* @return the page
*/
public int getPage() {
return page;
}
/**
* Sets the page.
*
* @param page the new page
*/
public void setPage(int p) {
this.page = p;
}
/**
* Gets the pageSize.
*
* @return the pageSize
*/
public int getPageSize() {
return pageSize;
}
/**
* Sets the pageSize.
*
* @param pageSize the new pageSize
*/
public void setPageSize(int l) {
this.pageSize = l;
}
/**
* Gets the d.
*
* @return the d
*/
public Sort.Direction getDir() {
return dir;
}
/**
* Sets the d.
*
* @param d the new d
*/
public void setDir(Sort.Direction d) {
this.dir = d;
}
/**
* Gets the s.
*
* @return the s
*/
public String[] getSort() {
return sort;
}
/**
* Sets the s.
*
* @param s the new s
*/
public void setSort(String[] s) {
this.sort = s;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "BrAPIPage page=" + page + ", pageSize=" + pageSize + ", dir=" + dir + ", sort=" + Arrays.toString(sort);
}
/**
* Get sort direction or {@link Sort.Direction#ASC} if null.
*
* @param defaultDir the default dir
* @return sort direction or {@link Sort.Direction#ASC}
*/
private Direction getDirection(Direction defaultDir) {
return dir == null ? defaultDir : dir;
}
/**
* Gets list of sort properties or provided defaults.
*
* @param defaultSortProps the default sort props
* @return provided properties or defaultSortProps
*/
private String[] getSortProperties(String[] defaultSortProps) {
return sort == null || sort.length == 0 ? defaultSortProps : sort;
}
/**
* To page request using the {@link #DEFAULT_SORT_PROPERTIES} and ASC sort
*
* @return the pageable
*/
public Pageable toPageRequest() {
return new PageRequest(page == null ? 0 : page, Integer.min(pageSize == null ? MAX_PAGESIZE : pageSize, MAX_PAGESIZE), getDirection(Sort.Direction.ASC), getSortProperties(DEFAULT_SORT_PROPERTIES));
}
/**
* To page request.
*
* @param maxPageSize the max page size
* @param defaultDir the default dir
* @param defaultSort the default sort
* @return the pageable
*/
public Pageable toPageRequest(int maxPageSize, Direction defaultDir, String... defaultSort) {
return new PageRequest(page == null ? 0 : page, Integer.min(pageSize == null ? maxPageSize : pageSize, maxPageSize), getDirection(defaultDir), getSortProperties(defaultSort));
}
/**
* To page request.
*
* @param maxPageSize the max page size
* @param sort the sort
* @return the pageable
*/
public static Pageable toPageRequest(int maxPageSize, Sort sort) {
return new PageRequest(0, maxPageSize, sort);
}
}
/*
* Copyright 2019 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.genesys2.brapi.model;
import java.util.Arrays;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* The BrAPI Call.
*/
public class Call {
private static final String BRAPI_CALL_PREFIX = "/brapi/v1/";
/** The call. */
private String call;
/** The data types. */
private String[] dataTypes;
/** The methods. */
private RequestMethod[] methods;
/** The Constant versions. */
private static final String[] versions = { "1.3" };
public Call(String apiPrefix, String endpoint, RequestMethod[] requestMethods, String[] dataTypes) {
this.call = apiPrefix.concat(endpoint);
if (this.call.startsWith(BRAPI_CALL_PREFIX)) {
this.call = this.call.substring(BRAPI_CALL_PREFIX.length());
}
this.methods = requestMethods;
this.dataTypes = dataTypes;
}
/**
* Gets the call.
*
* @return the call
*/
public String getCall() {
return call;
}
/**
* Sets the call.
*
* @param call the new call
*/
public void setCall(String call) {
this.call = call;
}
/**
* Gets the data types.
*
* @return the data types
*/
public String[] getDataTypes() {
return dataTypes;
}
/**
* Sets the data types.
*
* @param dataTypes the new data types
*/
public void setDataTypes(String[] dataTypes) {
this.dataTypes = dataTypes;
}
/**
* Gets the methods.
*
* @return the methods
*/
public RequestMethod[] getMethods() {
return methods;
}
/**
* Sets the methods.
*
* @param methods the new methods
*/
public void setMethods(RequestMethod[] methods) {
this.methods = methods;
}
/**
* Gets the versions.
*
* @return the versions
*/
public static String[] getVersions() {
return versions;
}
@Override
public String toString() {
return "" + call + " " + Arrays.toString(methods) + " dataTypes=" + Arrays.toString(dataTypes);
}
}
package org.genesys2.brapi.model;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
......@@ -71,12 +72,14 @@ public class Germplasm implements Serializable {
/// collection 11) Short term 12) Medium term 13) Long term 20) Field collection 30) In vitro collection 40)
/// Cryopreserved collection 50) DNA collection 99) Other (elaborate in REMARKS field)
@JsonProperty("typeOfGermplasmStorageCode")
private List<Integer> storage;
private Collection<Integer> storage;
/// [MCPD] Genus name for taxon. Initial uppercase letter required.
@JsonProperty("germplasmGenus")
private String genus;
/// [MCPD] Specific epithet portion of the scientific name in lowercase letters.
@JsonProperty("germplasmSpecies")
private String species;
/// [MCPD]
......@@ -286,15 +289,15 @@ public class Germplasm implements Serializable {
/**
* @return the storage
*/
public List<Integer> getStorage() {
public Collection<Integer> getStorage() {
return storage;
}
/**
* @param storage the storage to set
* @param set the storage to set
*/
public void setStorage(List<Integer> storage) {
this.storage = storage;
public void setStorage(Collection<Integer> set) {
this.storage = set;
}
/**
......
......@@ -3,6 +3,7 @@ package org.genesys2.brapi.service;
import java.util.UUID;
import org.genesys2.brapi.model.Germplasm;
import org.genesys2.server.service.filter.AccessionFilter;
import org.genesys2.server.service.impl.NoSuchAccessionException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
......@@ -16,8 +17,10 @@ public interface BrAPIService {
Page<String> getCrops(Pageable pageable);
Germplasm getGermplasmById(String germplasmId) throws NoSuchAccessionException;
Germplasm getGermplasmById(UUID germplasmId) throws NoSuchAccessionException;
Page<Germplasm> searchGermplasm(String germplasmName, String germplasmPUI, UUID germplasmDbId, Pageable pageable);
Page<Germplasm> searchGermplasm(AccessionFilter filter, Pageable pageRequest);
}
......@@ -6,16 +6,12 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.genesys2.brapi.model.Germplasm;
import org.genesys2.brapi.service.BrAPIService;
import org.genesys2.server.model.elastic.AccessionDetails;
import org.genesys2.server.model.elastic.Taxonomy;
import org.genesys2.server.model.genesys.Accession;
import org.genesys2.server.model.genesys.Taxonomy2;
import org.genesys2.server.model.impl.Crop;
import org.genesys2.server.service.AccessionService;
import org.genesys2.server.service.CropService;
import org.genesys2.server.service.FilterConstants;
import org.genesys2.server.service.GenesysFilterService;
import org.genesys2.server.service.GenesysService;
import org.genesys2.server.service.impl.FilterHandler;
import org.genesys2.server.service.impl.FilterHandler.AppliedFilter;
import org.genesys2.server.service.impl.FilterHandler.AppliedFilters;
import org.genesys2.server.service.filter.AccessionFilter;
import org.genesys2.server.service.impl.NoSuchAccessionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
......@@ -34,52 +30,50 @@ import org.springframework.transaction.annotation.Transactional;
public class BrAPIServiceImpl implements BrAPIService {
@Autowired
private GenesysFilterService filterService;
@Autowired
private GenesysService genesysService;
private AccessionService accessionService;
@Autowired
private CropService cropService;
@Override
public Germplasm getGermplasmById(String germplasmId) throws NoSuchAccessionException {
return toGermplasm(genesysService.getAccession(UUID.fromString(germplasmId)));
public Germplasm getGermplasmById(UUID germplasmId) throws NoSuchAccessionException {
return toGermplasm(accessionService.getByUuid(germplasmId));
}
/**
* Convert Accession to Germplasm
*/
private Germplasm toGermplasm(AccessionDetails a) {
private Germplasm toGermplasm(Accession accession) {
Germplasm g = new Germplasm();
g.setUuid(a.getUuid());
g.setAcceNumb(a.getAcceNumb());
g.setDefaultDisplayName(a.getAcceNumb());
g.setAcceName(a.getAcceNumb());
g.setUuid(accession.getUuid());
g.setAcceNumb(accession.getAccessionNumber());
g.setDefaultDisplayName(accession.getAccessionNumber());
g.setAcceName(accession.getAccessionNumber());
if (a.getAliases() != null)
g.setSynonyms(a.getAliases().stream().map(alias -> alias.getName()).collect(Collectors.toList()));
if (accession.getAccessionId().getAliases() != null)
g.setSynonyms(accession.getAccessionId().getAliases().stream().map(alias -> alias.getName()).collect(Collectors.toList()));
g.setPedigree(a.getPedigree());
if (a.getCrops() != null && a.getCrops().size() > 0)
g.setCommonCropName(a.getCrops().get(0));
g.setPedigree(accession.getAncest());
if (accession.getCrop() != null)
g.setCommonCropName(accession.getCrop().getShortName());
else
g.setCommonCropName(a.getCropName());
g.setCommonCropName(accession.getCropName());
g.setInstCode(a.getInstitute().getCode());
g.setInstName(a.getInstitute().getFullName());
g.setInstCode(accession.getInstitute().getCode());
g.setInstName(accession.getInstitute().getFullName());
g.setSampStat(a.getSampStat());
g.setOrigCty(a.getOrgCty().getIso3());
g.setStorage(a.getStorage());
g.setSampStat(accession.getSampStat());
g.setOrigCty(accession.getCountryOfOrigin().getCode3());
g.setStorage(accession.getAccessionId().getStorage());
Taxonomy taxonomy = a.getTaxonomy();
Taxonomy2 taxonomy = accession.getTaxonomy();
g.setGenus(taxonomy.getGenus());
g.setSpecies(taxonomy.getSpecies());
g.setSpAuthor(taxonomy.getSpAuthor());
g.setSubtaxa(taxonomy.getSubtaxa());
g.setSubtAuthor(taxonomy.getSubtAuthor());
g.setAcqDate(a.getAcqDate());
g.setAcqDate(accession.getAcquisitionDate());
return g;
}
......@@ -90,19 +84,29 @@ public class BrAPIServiceImpl implements BrAPIService {
}
@Override
public Page<Germplasm> searchGermplasm(String germplasmName, String germplasmPUI, UUID germplasmDbId, Pageable pageable) {
AppliedFilters filters = new AppliedFilters();
public Page<Germplasm> searchGermplasm(String germplasmName, String germplasmPUI, UUID germplasmDbId, Pageable page) {
AccessionFilter accessionFilter = new AccessionFilter();
if (StringUtils.isNotBlank(germplasmName)) {
filters.add(new AppliedFilter().setFilterName(FilterConstants.ACCENUMB).addFilterValue(new FilterHandler.StartsWithFilter(germplasmName)));
if (StringUtils.isNotBlank(germplasmPUI)) {
accessionFilter.doi().add(germplasmPUI);
}
if (germplasmDbId != null) {
filters.add(new AppliedFilter().setFilterName(FilterConstants.UUID).addFilterValue(new FilterHandler.LiteralValueFilter(germplasmDbId)));
accessionFilter.uuid().add(germplasmDbId);
}
if (StringUtils.isNotBlank(germplasmName)) {
accessionFilter.acceNumb().eq = germplasmName;
}
Page<AccessionDetails> accns = filterService.listAccessionDetails(filters, pageable);
return new PageImpl<>(accns.getContent().stream().map(a -> toGermplasm(a)).collect(Collectors.toList()), pageable, accns.getTotalElements());
Page<Accession> accns = accessionService.list(accessionFilter, page);
return new PageImpl<>(accns.getContent().stream().map(a -> toGermplasm(a)).collect(Collectors.toList()), page, accns.getTotalElements());
}
@Override
public Page<Germplasm> searchGermplasm(AccessionFilter filter, Pageable page) {
Page<Accession> accns = accessionService.list(filter, page);
return new PageImpl<>(accns.getContent().stream().map(a -> toGermplasm(a)).collect(Collectors.toList()), page, accns.getTotalElements());
}
}
package org.genesys2.server.brapi;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.genesys2.brapi.model.BrAPIPage;
import org.genesys2.brapi.model.BrAPIResponse;
import org.genesys2.brapi.model.Call;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@Controller("brapiCalls")
@RequestMapping(value = { "/brapi/v1" })
public class CallsController implements InitializingBean {
private static final RequestMethod[] GET_MAPPING = { RequestMethod.GET };
private static final RequestMethod[] POST_MAPPING = { RequestMethod.POST };
private static final RequestMethod[] PUT_MAPPING = { RequestMethod.PUT };
private static final RequestMethod[] DELETE_MAPPING = { RequestMethod.DELETE };
private List<Call> supportedCalls;
@Override
public void afterPropertiesSet() throws Exception {
// Scan the org.genesys2.server.brapi package for @Controller annotations
supportedCalls = new ArrayList<>();
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(Controller.class));
scanner.addIncludeFilter(new AnnotationTypeFilter(RestController.class));
for (BeanDefinition bd : scanner.findCandidateComponents("org.genesys2.server.brapi")) {
registerBrAPIMethods(bd);
}
supportedCalls.forEach(call -> {
System.err.println("BrAPI v1.3 call: /brapi/v1/" + call);
});
}
private void registerBrAPIMethods(BeanDefinition bd) throws ClassNotFoundException {
Class<?> clazz = Class.forName(bd.getBeanClassName());
String apiPrefix = "";
if (clazz.isAnnotationPresent(RequestMapping.class)) {
RequestMapping mapping = clazz.getAnnotation(RequestMapping.class);
apiPrefix = mapping.value()[0];
} else if (clazz.isAnnotationPresent(RestController.class)) {
RestController mapping = clazz.getAnnotation(RestController.class);
apiPrefix = mapping.value();
}
for (Method m : clazz.getDeclaredMethods()) {
registerBrAPIMethod(apiPrefix, m);
}
}
private void registerBrAPIMethod(String apiPrefix, Method m) {
if (m.isAnnotationPresent(RequestMapping.class)) {
RequestMapping mapping = m.getAnnotation(RequestMapping.class);
supportedCalls.add(new Call(apiPrefix, mapping.value()[0], mapping.method(), mapping.produces()));
} else if (m.isAnnotationPresent(GetMapping.class)) {
GetMapping mapping = m.getAnnotation(GetMapping.class);
supportedCalls.add(new Call(apiPrefix, mapping.value()[0], GET_MAPPING, mapping.produces()));
} else if (m.isAnnotationPresent(PostMapping.class)) {
PostMapping mappng = m.getAnnotation(PostMapping.class);
supportedCalls.add(new Call(apiPrefix, mappng.value()[0], POST_MAPPING, mappng.produces()));
} else if (m.isAnnotationPresent(DeleteMapping.class)) {
DeleteMapping mappng = m.getAnnotation(DeleteMapping.class);
supportedCalls.add(new Call(apiPrefix, mappng.value()[0], DELETE_MAPPING, mappng.produces()));
} else if (m.isAnnotationPresent(DeleteMapping.class)) {
PutMapping mappng = m.getAnnotation(PutMapping.class);
supportedCalls.add(new Call(apiPrefix, mappng.value()[0], PUT_MAPPING, mappng.produces()));
}
}
@RequestMapping(value = "/calls", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody BrAPIResponse<Call> brapiCalls(final BrAPIPage page) {
Pageable pageable = page.toPageRequest();
Page<Call> callsPage = new PageImpl<>(callsPage(pageable), pageable, supportedCalls.size());
return new BrAPIResponse<>(callsPage);
}
private List<Call> callsPage(Pageable page) {
if (page.getOffset() >= supportedCalls.size()) {
return Collections.emptyList();
}
return supportedCalls.subList(page.getOffset(), Math.min(supportedCalls.size(), page.getOffset() + page.getPageSize()));
}
}
......@@ -15,20 +15,16 @@
*/
package org.genesys2.server.brapi;
import org.genesys2.brapi.model.BrAPIPage;
import org.genesys2.brapi.model.BrAPIResponse;
import org.genesys2.brapi.service.BrAPIService;
import org.genesys2.server.service.impl.NonUniqueAccessionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@Controller("brapiCrops")
//@PreAuthorize("isAuthenticated()")
@RestController("brapiCrops")
@RequestMapping(value = { "/brapi/v1" })
public class CropsController extends BaseBrAPIController {
......@@ -43,9 +39,8 @@ public class CropsController extends BaseBrAPIController {
* @return
* @throws NonUniqueAccessionException
*/
@RequestMapping(value = "/crops", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody BrAPIResponse<String> getGermplasmById(@RequestParam(name = "pageSize", required = false, defaultValue = "50") int pageSize,
@RequestParam(name = "page", required = false, defaultValue = "0") int page) {
return new BrAPIResponse<>(brapiService.getCrops(new PageRequest(Math.max(0, page), Math.min(pageSize, 200))));
@GetMapping(value = "/commoncropnames")
public BrAPIResponse<String> commonCropNames(final BrAPIPage page) {
return new BrAPIResponse<>(brapiService.getCrops(page.toPageRequest()));
}
}
/*
* Copyright 2017 Global Crop Diversity Trust
* Copyright 2019 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -17,14 +17,17 @@ package org.genesys2.server.brapi;
import java.util.UUID;
import org.genesys2.brapi.model.BrAPIPage;
import org.genesys2.brapi.model.BrAPIResponse;
import org.genesys2.brapi.model.Germplasm;
import org.genesys2.brapi.service.BrAPIService;
import org.genesys2.server.service.filter.AccessionFilter;
import org.genesys2.server.service.impl.NoSuchAccessionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
......@@ -32,7 +35,6 @@ import org.springframework.web.bind.annotation.ResponseBody;