diff --git a/src/main/java/org/genesys/catalog/api/ApiExceptionHandler.java b/src/main/java/org/genesys/catalog/api/ApiExceptionHandler.java index f89b8cc9109810027eaaa23024380316ae2a00e8..a59aa873f56c3ce855f3d39b01990e3dea23b20b 100644 --- a/src/main/java/org/genesys/catalog/api/ApiExceptionHandler.java +++ b/src/main/java/org/genesys/catalog/api/ApiExceptionHandler.java @@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.context.request.WebRequest; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; /** * API exception handler returns errors in {@link ApiError}. @@ -93,11 +94,26 @@ public class ApiExceptionHandler { @ResponseStatus(code = HttpStatus.BAD_REQUEST) @ExceptionHandler(HttpMessageNotReadableException.class) @ResponseBody - public ApiError handleConverterError(final Exception e, final WebRequest request) { - LOG.warn("Invalid payload provided.", e); + public ApiError handleConverterError(final HttpMessageNotReadableException e, final HttpServletRequest request) { + LOG.warn("Invalid payload provided {} {}", request.getMethod(), request.getRequestURL(), e); return new ApiError<>(e); } + /** + * Handle converter error. + * + * @param e the e + * @param request the request + * @return the api error + */ + @ResponseStatus(code = HttpStatus.BAD_REQUEST) + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + @ResponseBody + public ApiError handleConverterArgumentError(final MethodArgumentTypeMismatchException e, final HttpServletRequest request) { + LOG.warn("Invalid argument {} for {} provided {} {}", e.getName(), e.getParameter(), request.getMethod(), request.getRequestURL(), e); + return new ApiError<>(e); + } + /** * Handle invalid api usage. * @@ -108,8 +124,8 @@ public class ApiExceptionHandler { @ResponseStatus(code = HttpStatus.BAD_REQUEST) @ExceptionHandler({ InvalidApiUsageException.class, ConcurrencyFailureException.class }) @ResponseBody - public ApiError handleInvalidApiUsage(final Exception e, final WebRequest request) { - LOG.warn("Invalid payload provided.", e); + public ApiError handleInvalidApiUsage(final Exception e, final HttpServletRequest request) { + LOG.warn("{} for {} {}", e.getMessage(), request.getMethod(), request.getRequestURL(), e); return new ApiError<>(e); } @@ -123,8 +139,8 @@ public class ApiExceptionHandler { @ResponseStatus(code = HttpStatus.NOT_FOUND) @ExceptionHandler(NotFoundElement.class) @ResponseBody - public ApiError handleNotFound(final Exception e, final WebRequest request) { - LOG.warn("Element not found", e); + public ApiError handleNotFound(final Exception e, final HttpServletRequest request) { + LOG.warn("Element not found {} {}", request.getMethod(), request.getRequestURL(), e); return new ApiError<>(e); } diff --git a/src/main/java/org/genesys/catalog/api/v0/DescriptorListController.java b/src/main/java/org/genesys/catalog/api/v0/DescriptorListController.java index b6b47cd4cab4d70564da792727944bb649bb3592..485f666f17273669473af4252525842e988d9503 100644 --- a/src/main/java/org/genesys/catalog/api/v0/DescriptorListController.java +++ b/src/main/java/org/genesys/catalog/api/v0/DescriptorListController.java @@ -15,6 +15,8 @@ */ package org.genesys.catalog.api.v0; +import org.apache.commons.lang3.StringUtils; +import org.genesys.blocks.model.JsonViews; import org.genesys.catalog.api.FilteredPage; import org.genesys.catalog.model.filters.DescriptorListFilter; import org.genesys.catalog.model.traits.Descriptor; @@ -27,10 +29,13 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; +import com.fasterxml.jackson.annotation.JsonView; + import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; @@ -163,16 +168,24 @@ public class DescriptorListController { * @param pageSize the page size * @param direction the direction * @param sort the sort + * @param filterCode short filter code -- overrides filter in body * @param filter the descriptor list filter * @return the page */ @PostMapping(value = "/list") public FilteredPage listDescriptorLists(@RequestParam(name = "p", required = false, defaultValue = "0") final int page, - @RequestParam(name = "l", required = false, defaultValue = "50") final int pageSize, - @RequestParam(name = "d", required = false, defaultValue = "ASC") final Sort.Direction direction, - @RequestParam(name = "s", required = false, defaultValue = "id") final String[] sort, @RequestBody final DescriptorListFilter filter) { + @RequestParam(name = "l", required = false, defaultValue = "50") final int pageSize, + @RequestParam(name = "d", required = false, defaultValue = "ASC") final Sort.Direction direction, + @RequestParam(name = "s", required = false, defaultValue = "id") final String[] sort, + @RequestParam(name = "f", required = false) String filterCode, + @RequestBody DescriptorListFilter filter) throws IOException { - return new FilteredPage<>(shortFilterService.getCode(filter), filter, descriptorListService.listDescriptorLists(filter, new PageRequest(page, Integer.min(pageSize, 100), direction, sort))); + if (StringUtils.isNotBlank(filterCode)) { + filter = shortFilterService.filterByCode(filterCode, DescriptorListFilter.class); + } else { + filterCode = shortFilterService.getCode(filter); + } + return new FilteredPage<>(filterCode, filter, descriptorListService.listDescriptorLists(filter, new PageRequest(page, Integer.min(pageSize, 100), direction, sort))); } /** @@ -241,4 +254,16 @@ public class DescriptorListController { return descriptorListService.removeDescriptors(descriptorList, descriptors.toArray(new Descriptor[] {})); } + /** + * Autocomplete. + * + * @param text the text + * @return the list + */ + @JsonView(JsonViews.Minimal.class) + @GetMapping(value = "/autocomplete", produces = MediaType.APPLICATION_JSON_VALUE) + public List autocomplete(@RequestParam("d") final String text) { + return descriptorListService.autocompleteDescriptorLists(text); + } + } diff --git a/src/main/java/org/genesys/catalog/api/v0/GeoController.java b/src/main/java/org/genesys/catalog/api/v0/GeoController.java new file mode 100644 index 0000000000000000000000000000000000000000..3f88cc7ee25f7eadf46b84cc79b65977755ba570 --- /dev/null +++ b/src/main/java/org/genesys/catalog/api/v0/GeoController.java @@ -0,0 +1,104 @@ +/* + * 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.genesys.catalog.api.v0; + +import java.io.IOException; +import java.util.UUID; + +import org.genesys.catalog.exceptions.NotFoundElement; +import org.genesys.catalog.model.vocab.VocabularyTerm; +import org.genesys.catalog.service.VocabularyService; +import org.genesys.catalog.service.worker.ISO3166VocabularyUpdater; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * The Class GeoController. + * + * @author Maxym Borodenko + */ +@RestController("cataloGeoController") +@RequestMapping(GeoController.API_BASE) +@PreAuthorize("isAuthenticated()") +public class GeoController { + + /** The Constant API_BASE. */ + protected static final String API_BASE = "/api/v0/geo"; + + /** The Constant ISO3166_2ALPHA. */ + public static final UUID ISO3166_2ALPHA = ISO3166VocabularyUpdater.ISO3166_2ALPHA; + + /** The Constant ISO3166_3ALPHA. */ + public static final UUID ISO3166_3ALPHA = ISO3166VocabularyUpdater.ISO3166_3ALPHA; + + /** The Constant ISO3166_NUMERIC. */ + public static final UUID ISO3166_NUMERIC = ISO3166VocabularyUpdater.ISO3166_NUMERIC; + + private static final Logger LOG = LoggerFactory.getLogger(GeoController.class); + + @Autowired + private ISO3166VocabularyUpdater iso3166VocabularyUpdater; + + @Autowired + private VocabularyService vocabularyService; + + /** + * Update countries codes. + * + * @return the string + * @throws IOException Signals that an I/O exception has occurred. + */ + @PreAuthorize("hasRole('ADMINISTRATOR')") + @PostMapping(value = "/update") + public @ResponseBody String updateCountriesCodes() throws IOException { + LOG.info("Updating ISO country codes"); + vocabularyService.autoUpdateOrCreateVocabulary(ISO3166_2ALPHA, iso3166VocabularyUpdater.getISO3166Alpha2Vocabulary()); + vocabularyService.autoUpdateOrCreateVocabulary(ISO3166_3ALPHA, iso3166VocabularyUpdater.getISO3166Alpha3Vocabulary()); + vocabularyService.autoUpdateOrCreateVocabulary(ISO3166_NUMERIC, iso3166VocabularyUpdater.getISO3166NumericVocabulary()); + return "OK"; + } + + /** + * Gets the. + * + * @param code the code + * @return the vocabulary term + */ + @GetMapping(value = "/iso3166/{code}", produces = MediaType.APPLICATION_JSON_VALUE) + public VocabularyTerm get(@PathVariable("code") final String code) { + final boolean isNumeric = code.chars().allMatch(Character::isDigit); + + if (isNumeric) { + return vocabularyService.getVocabularyTerm(ISO3166_NUMERIC, code); + } else if (code.length() == 2) { + return vocabularyService.getVocabularyTerm(ISO3166_2ALPHA, code); + } else if (code.length() == 3) { + return vocabularyService.getVocabularyTerm(ISO3166_3ALPHA, code); + } + + throw new NotFoundElement("Code is not in valid format: 3-letter | 2-letter | numeric"); + } + +} diff --git a/src/main/java/org/genesys/catalog/api/v0/VocabularyController.java b/src/main/java/org/genesys/catalog/api/v0/VocabularyController.java new file mode 100644 index 0000000000000000000000000000000000000000..be25d72f18c79d6a866f4620a1049802ff73ca63 --- /dev/null +++ b/src/main/java/org/genesys/catalog/api/v0/VocabularyController.java @@ -0,0 +1,193 @@ +/* + * 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.genesys.catalog.api.v0; + +import java.io.IOException; +import java.util.List; +import java.util.UUID; + +import org.genesys.catalog.api.FilteredPage; +import org.genesys.catalog.model.filters.ControlledVocabularyFilter; +import org.genesys.catalog.model.vocab.ControlledVocabulary; +import org.genesys.catalog.model.vocab.VocabularyTerm; +import org.genesys.catalog.service.ShortFilterService; +import org.genesys.catalog.service.VocabularyService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +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.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * The Class VocabularyController. + * + * @author Matija Obreza + */ +@RestController +@RequestMapping(VocabularyController.API_BASE) +public class VocabularyController { + + /** The Constant API_BASE. */ + public static final String API_BASE = "/api/v0/vocabulary"; + + @Autowired + private VocabularyService vocabularyService; + + /** The short filter service. */ + @Autowired + protected ShortFilterService shortFilterService; + + /** + * Gets the. + * + * @param uuid the uuid + * @return the controlled vocabulary + */ + @GetMapping(value = "/{UUID}") + public ControlledVocabulary get(@PathVariable("UUID") final UUID uuid) { + return vocabularyService.getVocabulary(uuid); + } + + /** + * Gets the term. + * + * @param uuid the uuid + * @param code the code + * @return the term + */ + @GetMapping(value = "/{UUID}/{code}") + public VocabularyTerm getTerm(@PathVariable("UUID") final UUID uuid, @PathVariable("code") final String code) { + return vocabularyService.getVocabularyTerm(uuid, code); + } + + /** + * Autocomplete term. + * + * @param uuid the uuid + * @param like the like + * @return the list + */ + @PostMapping(value = "/{UUID}/autocomplete") + public List autocompleteTerm(@PathVariable("UUID") final UUID uuid, @RequestParam("q") final String like) { + return vocabularyService.autocompleteTerms(uuid, like); + } + + /** + * List terms. + * + * @param uuid the uuid + * @param page the page + * @param pageSize the page size + * @param direction the direction + * @param sort the sort + * @return the page + */ + @PostMapping(value = "/{UUID}/terms") + public Page listTerms(@PathVariable("UUID") final UUID uuid, @RequestParam(name = "p", required = false, defaultValue = "0") final int page, + @RequestParam(name = "l", required = false, defaultValue = "50") final int pageSize, + @RequestParam(name = "d", required = false, defaultValue = "ASC") final Sort.Direction direction, + @RequestParam(name = "s", required = false, defaultValue = "id") final String[] sort) { + + // LOG.warn("Loading vocab"); + final ControlledVocabulary vocabulary = vocabularyService.getVocabulary(uuid); + // LOG.warn("Loading terms"); + final Page x = vocabularyService.listTerms(vocabulary, new PageRequest(page, Integer.min(pageSize, 100), direction, sort)); + // LOG.warn("Returning terms"); + return x; + } + + /** + * Delete. + * + * @param uuid the uuid + * @param version the version + * @return the controlled vocabulary + */ + @DeleteMapping(value = "/{UUID},{version}") + public ControlledVocabulary delete(@PathVariable("UUID") final UUID uuid, @PathVariable("version") final int version) { + return vocabularyService.deleteVocabulary(vocabularyService.getVocabulary(uuid, version)); + } + + /** + * Creates the. + * + * @param source the source + * @return the controlled vocabulary + */ + @PostMapping(value = "/create") + public ControlledVocabulary create(@RequestBody final ControlledVocabulary source) { + return vocabularyService.createVocabulary(source); + } + + /** + * Update controlled vocabulary. + * + * @param source the source + * @return the controlled vocabulary + */ + @PostMapping(value = "/update") + public ControlledVocabulary updateControlledVocabulary(@RequestBody final ControlledVocabulary source) { + return vocabularyService.updateVocabulary(source); + } + + /** + * List controlled vocabularies. + * + * @param page the page + * @param pageSize the page size + * @param direction the direction + * @param sort the sort + * @param filter the controlled vocabulary filter + * @return the page + */ + @PostMapping(value = "/list") + public FilteredPage list(@RequestParam(name = "p", required = false, defaultValue = "0") final int page, + @RequestParam(name = "l", required = false, defaultValue = "50") final int pageSize, + @RequestParam(name = "d", required = false, defaultValue = "ASC") final Sort.Direction direction, + @RequestParam(name = "s", required = false, defaultValue = "id") final String[] sort, @RequestBody final ControlledVocabularyFilter filter) { + + return new FilteredPage<>(shortFilterService.getCode(filter), filter, vocabularyService.listVocabularies(filter, new PageRequest(page, Integer.min(pageSize, 100), direction, sort))); + } + + /** + * Filter controlled vocabularies by filter code. + * + * @param page the page + * @param pageSize the page size + * @param direction the direction + * @param sort the sort + * @param filterCode the filter code + * @return the filtered page + * @throws IOException Signals that an I/O exception has occurred. + */ + @PostMapping(value = "/list/{filterCode}") + public FilteredPage list(@RequestParam(name = "p", required = false, defaultValue = "0") final int page, + @RequestParam(name = "l", required = false, defaultValue = "50") final int pageSize, + @RequestParam(name = "d", required = false, defaultValue = "ASC") final Sort.Direction direction, + @RequestParam(name = "s", required = false, defaultValue = "id") final String[] sort, @PathVariable("filterCode") final String filterCode) throws IOException { + + final ControlledVocabularyFilter filter = shortFilterService.filterByCode(filterCode, ControlledVocabularyFilter.class); + return new FilteredPage<>(filterCode, filter, vocabularyService.listVocabularies(filter, new PageRequest(page, Integer.min(pageSize, 100), direction, sort))); + } + +}