Commit bd4cc563 authored by Matija Obreza's avatar Matija Obreza

Import Catalog code

parent aef7c1b1
......@@ -517,6 +517,11 @@
<artifactId>file-repository-ftpserver</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
......@@ -548,6 +553,16 @@
<artifactId>bsh</artifactId>
<version>2.0b6</version>
</dependency>
<dependency>
<groupId>com.vladsch.flexmark</groupId>
<artifactId>flexmark-all</artifactId>
<version>0.28.34</version>
</dependency>
<dependency>
<groupId>com.vdurmont</groupId>
<artifactId>semver4j</artifactId>
<version>2.2.0</version>
</dependency>
</dependencies>
<build>
......@@ -566,6 +581,8 @@
<!-- List packages to be processed -->
<include>org.genesys2.server.model</include>
<include>org.genesys2.server.model.*</include>
<include>org.genesys.catalog.server.model</include>
<include>org.genesys.catalog.server.model.*</include>
</includes>
<outputDirectory>target/generated-sources/querydsl</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
......
/*
* Copyright 2017 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.server.annotations;
import org.genesys.catalog.server.service.PublishValidationInterface;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
/**
* Fields with this annotation are required fields, they have to be filled
* during publication process. You should implement
* {@link PublishValidationInterface} for parent class. That give you use
* {@link PublishValidationInterface#validation()} for validate before publish.
* And if you want use {@link PublishValidation#innerCheck()} you should
* implement {@link PublishValidationInterface} for child class and override
* {@link PublishValidationInterface#validation()} method.
*
* @author Andrey Lugovskoy.
*/
@Documented
@Target({ FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface PublishValidation {
/**
* If field is object and innerCheck=true than we have to check inner fields.
*
* @return boolean value
*/
boolean innerCheck() default false;
/**
* You should set {@link Boolean#TRUE} for this field to avoid recursive invoke
* {@link PublishValidationInterface#validation()} method if child class has
* link to parent class.
*
* @return boolean value
*/
boolean ignoreValidation() default false;
}
/*
* 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.server.controller;
import com.vladsch.flexmark.ast.Node;
import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension;
import com.vladsch.flexmark.ext.tables.TablesExtension;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.KeepType;
import com.vladsch.flexmark.util.options.MutableDataHolder;
import com.vladsch.flexmark.util.options.MutableDataSet;
import org.apache.commons.lang3.StringUtils;
import org.genesys.catalog.server.model.traits.DescriptorList;
import org.genesys.catalog.server.service.FreemarkerTemplating;
import org.genesys.catalog.server.service.FreemarkerTemplating.FreemarkerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* Export Descriptor list.
*/
@Component
public class DescriptorListExporter {
@Autowired
private FreemarkerTemplating freemarker;
/** The Constant OPTIONS. */
/*@formatter:off*/
static final MutableDataHolder OPTIONS = new MutableDataSet()
.set(Parser.REFERENCES_KEEP, KeepType.LAST)
.set(Parser.HTML_BLOCK_PARSER, false)
// .set(Parser.HTML_BLOCK_DEEP_PARSER, true)
// .set(Parser.HTML_BLOCK_START_ONLY_ON_BLOCK_TAGS, true)
.set(HtmlRenderer.INDENT_SIZE, 2)
.set(HtmlRenderer.PERCENT_ENCODE_URLS, true)
// .set(HtmlRenderer.ESCAPE_HTML, false)
// for full GFM table compatibility add the following table extension options:
.set(TablesExtension.COLUMN_SPANS, false)
.set(TablesExtension.APPEND_MISSING_COLUMNS, true)
.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true)
.set(TablesExtension.HEADER_SEPARATOR_COLUMN_MATCH, true)
.set(Parser.EXTENSIONS, Arrays.asList(
TablesExtension.create(),
StrikethroughExtension.create()
));
/*@formatter:on*/
/**
* Markdown descriptor list.
*
* @param descriptorList the descriptor list
* @return the string
* @throws FreemarkerException the freemarker exception
*/
public String markdownDescriptorList(final DescriptorList descriptorList) throws FreemarkerException {
final Map<String, Object> root = new HashMap<>();
root.put("descriptorList", descriptorList);
return freemarker.processTemplateResource("descriptorlist/booklet.ftl", root);
}
/**
* Html descriptor list.
*
* @param descriptorList the descriptor list
* @return the string
* @throws FreemarkerException the freemarker exception
*/
public String htmlDescriptorList(final DescriptorList descriptorList) throws FreemarkerException {
final Map<String, Object> root = new HashMap<>();
root.put("descriptorList", descriptorList);
final String markdown = freemarker.processTemplateResource("descriptorlist/booklet.ftl", root);
// System.err.println(markdown);
final Parser parser = Parser.builder(OPTIONS).build();
final Node document = parser.parse(markdown);
final HtmlRenderer renderer = HtmlRenderer.builder(OPTIONS).build();
final String html = renderer.render(document);
// System.err.println(html);
// HTML wrapper
root.clear();
root.put("title", descriptorList.getTitle());
root.put("author", StringUtils.defaultIfEmpty(descriptorList.getPublisher(), descriptorList.getOwner().getName()));
root.put("html", html);
return freemarker.processTemplateResource("html.ftl", root);
}
}
/*
* 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.server.controller.api;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import org.genesys.blocks.model.filters.BasicModelFilter;
import org.springframework.data.domain.Page;
import java.io.Serializable;
/**
* The Class FilteredPage.
*
* @param <T> the generic type
*/
public class FilteredPage<T> implements Serializable {
private static final long serialVersionUID = 6965069448240229428L;
/** The data is serialized on the base JSON level. */
@JsonUnwrapped
public Page<T> page;
/** The filter. */
public BasicModelFilter filter;
/** The filter code. */
public String filterCode;
/**
* Instantiates a new filtered page.
*
* @param filterCode the filter code
* @param filter the filter
* @param data the data
*/
public FilteredPage(final String filterCode, final BasicModelFilter filter, final Page<T> data) {
this.filterCode = filterCode;
this.filter = filter;
this.page = data;
}
}
/*
* 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.server.controller.api.v0;
import com.fasterxml.jackson.annotation.JsonView;
import org.genesys.blocks.model.JsonViews;
/**
* The Class ApiError.
*
* @param <T> the generic type
*/
public class ApiError<T extends Throwable> {
@JsonView(JsonViews.Public.class)
private final String error;
@JsonView(JsonViews.Public.class)
private final String localizedError;
/**
* Instantiates a new api error.
*
* @param ex the ex
*/
public ApiError(final T ex) {
this.error = ex.getMessage();
this.localizedError = ex.getLocalizedMessage();
}
/**
* Gets the error.
*
* @return the error
*/
public String getError() {
return error;
}
/**
* Gets the localized error.
*
* @return the localized error
*/
public String getLocalizedError() {
return localizedError;
}
}
/*
* 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.server.controller.api.v0;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.genesys.catalog.server.exceptions.InvalidApiUsageException;
import org.genesys.catalog.server.exceptions.NotFoundElement;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.web.bind.annotation.ControllerAdvice;
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;
/**
* API exception handler returns errors in {@link ApiError}.
*
* @author Matija Obreza
*/
@ControllerAdvice(basePackages = { "org.genesys.catalog.server.controller.api.v0" })
public class ApiExceptionHandler {
/** The log. */
protected final Log LOG = LogFactory.getLog(getClass());
// @ResponseStatus(code = HttpStatus.NOT_FOUND)
// @ExceptionHandler(NoSuchAccessionException.class)
// @ResponseBody
// public ApiError<Exception> handleMissingAccession(NoSuchAccessionException
// ex, WebRequest request) throws JsonProcessingException {
// LOG.warn("Returning BrAPI error: " + ex.getMessage());
// return new ApiError<>(ex);
// }
/**
* Handle missing credentials.
*
* @param e the e
* @param request the request
* @return the api error
*/
@ResponseStatus(code = HttpStatus.FORBIDDEN)
@ExceptionHandler({ AuthenticationCredentialsNotFoundException.class })
@ResponseBody
public ApiError<Exception> handleMissingCredentials(final Exception e, final WebRequest request) {
LOG.warn("Authentication is required.", e);
return new ApiError<>(e);
}
/**
* Handle access denied.
*
* @param e the e
* @param request the request
* @return the api error
*/
@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
@ExceptionHandler({ AccessDeniedException.class })
@ResponseBody
public ApiError<Exception> handleAccessDenied(final Exception e, final WebRequest request) {
LOG.warn("Authentication is required.", 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(HttpMessageNotReadableException.class)
@ResponseBody
public ApiError<Exception> handleConverterError(final Exception e, final WebRequest request) {
LOG.warn("Invalid payload provided.", e);
return new ApiError<>(e);
}
/**
* Handle invalid api usage.
*
* @param e the e
* @param request the request
* @return the api error
*/
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
@ExceptionHandler({ InvalidApiUsageException.class, ConcurrencyFailureException.class })
@ResponseBody
public ApiError<Exception> handleInvalidApiUsage(final Exception e, final WebRequest request) {
LOG.warn("Invalid payload provided.", e);
return new ApiError<>(e);
}
/**
* Handle not found.
*
* @param e the e
* @param request the request
* @return the api error
*/
@ResponseStatus(code = HttpStatus.NOT_FOUND)
@ExceptionHandler(NotFoundElement.class)
@ResponseBody
public ApiError<Exception> handleNotFound(final Exception e, final WebRequest request) {
LOG.warn("Element not found", e);
return new ApiError<>(e);
}
/**
* Handle server error.
*
* @param e the e
* @param request the request
* @return the api error
*/
@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Throwable.class)
@ResponseBody
public ApiError<Exception> handleServerError(final Exception e, final WebRequest request) {
LOG.error("Wow! Such! Exception!", e);
return new ApiError<>(e);
}
}
/*
* 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.server.controller.api.v0;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.net.UnknownHostException;
/**
* The Class ApiInfoController.
*
* @author Andrey Lugovskoy.
*/
@RestController
@RequestMapping(ApiInfoController.API_BASE)
@PreAuthorize("isAuthenticated()")
public class ApiInfoController implements InitializingBean {
private static final Logger LOG = LoggerFactory.getLogger(ApiInfoController.class);
/** The Constant API_BASE. */
public static final String API_BASE = "/api/v0/info";
@Value("${build.version}")
private String version;
@Value("${build.artifactId}")
private String artifactId;
@Value("${build.revision}")
private String revision;
private ApiInfo apiInfo;
@Override
public void afterPropertiesSet() throws Exception {
String hN;
try {
hN = java.net.InetAddress.getLocalHost().getHostName();
} catch (final UnknownHostException e) {
LOG.error("Error getting Host Name");
hN = "";
}
apiInfo = new ApiInfo(artifactId, version, revision, hN);
LOG.info("API Info: art=" + artifactId + " ver=" + version + " rev=" + revision);
}
/**
* Api info.
*
* @return the api info
*/
@RequestMapping(value = "/version", method = RequestMethod.GET)
public ApiInfo apiInfo() {
return apiInfo;
}
/**
* The Class ApiInfo.
*/
public static class ApiInfo {
private final String artifactId;
private final String version;
private final String revision;
private final String hostName;
/**
* Instantiates a new api info.
*
* @param artifactId the artifact id
* @param version the version
* @param revision the revision
* @param hostName the host name
*/
public ApiInfo(final String artifactId, final String version, final String revision, final String hostName) {
this.artifactId = artifactId;
this.version = version;
this.revision = revision;
this.hostName = hostName;
}
/**
* Gets the host name.
*