Skip to content
Snippets Groups Projects
Commit 6a4ca17d authored by Benjamin's avatar Benjamin
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
Showing
with 919 additions and 0 deletions
# IDEA files
*.iml
.idea
# Target
target
# Fichiers temporaires
*~
\ No newline at end of file
LGPL
ore-si-ng
=========
POC d'implantation sur une base de l'utilisation de Postgresql sans
Hibernate.
pom.xml 0 → 100644
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>fr.inra</groupId>
<artifactId>si-ore-ng</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<name>si-ore-ng</name>
<description>SI ORE NG</description>
<url>http://gitlab.codelutin.com</url>
<inceptionYear>2018</inceptionYear>
<licenses>
<license>
<name>Lesser General Public License version 3</name>
<url>http://www.gnu.org/licenses/lgpl.html</url>
</license>
</licenses>
<developers>
<developer>
<id>jcouteau</id>
<name>Jean Couteau</name>
<email>couteau@codelutin.com</email>
<organization>CodeLutin</organization>
<organizationUrl>http://www.codelutin.com/</organizationUrl>
<timezone>Europe/Paris</timezone>
<roles>
<role>developer</role>
</roles>
</developer>
<developer>
<id>bpoussin</id>
<name>Benjamin POUSSIN</name>
<email>poussin@codelutin.com</email>
<organization>CodeLutin</organization>
<organizationUrl>http://www.codelutin.com/</organizationUrl>
<timezone>Europe/Paris</timezone>
<roles>
<role>developer</role>
</roles>
</developer>
</developers>
<scm>
<connection>scm:git:git@gitlab.codelutin.com:inra/si-ore-ng.git</connection>
<developerConnection>git@gitlab.codelutin.com:inra/si-ore-ng.git</developerConnection>
<url>git@gitlab.codelutin.com:inra/si-ore-ng.git</url>
</scm>
<properties>
<projectId>si-ore-ng</projectId>
<!-- license to use -->
<license.licenseName>lgpl_v3</license.licenseName>
<!-- Java Version -->
<java.version>11</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<flyway-spring-test.version>5.2.1</flyway-spring-test.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb.flyway-test-extensions</groupId>
<artifactId>flyway-spring-test</artifactId>
<version>${flyway-spring-test.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.7.1</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<sourceDocumentName>index.adoc</sourceDocumentName>
<backend>html</backend>
<attributes>
<snippets>${project.build.directory}/rest-api</snippets>
</attributes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
= Getting Started With ore-si-ng REST Api
All examplee output for a service running at http://localhost:8080:
.request
include::{snippets}/home/http-request.adoc[]
.response
include::{snippets}/home/http-response.adoc[]
package fr.inra.oresing;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication(scanBasePackages = {"fr.inra.oresing"})
public class OreSiNg {
public static void main(String[] args) {
SpringApplication.run(OreSiNg.class, args);
}
/**
* Mapper json pour la persistence (dialogue avec la base de données)
*/
@Bean
public ObjectMapper sqlJsonMapper() {
ObjectMapper mapper = new ObjectMapper();
// there is no case in SQL, but in java we love camelCase :p
mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)
.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.registerModule(new JavaTimeModule())
// .setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE)
;
return mapper;
}
}
package fr.inra.oresing;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class OreSiUtils {
private static Pattern patternField = Pattern.compile("^(?:get|set|is)?(.)(.*)");
public static Map<String, Object> mapOf(SerializableSupplier... getter) {
return Stream.of(getter)
.collect(Collectors.toMap(OreSiUtils::fieldOf, SerializableSupplier::get));
}
public static <T> String[] fieldsOf(SerializableSupplier<T> ...getters) {
return Stream.of(getters)
.map(getter -> extractFieldName(getter.method().getName()))
.toArray(String[]::new);
}
public static <T> String fieldOf(SerializableSupplier<T> getter) {
return extractFieldName(getter.method().getName());
}
public static <T> String fieldOf(SerializableConsumer<T> setter) {
return extractFieldName(setter.method().getName());
}
private static String extractFieldName(String methodName) {
String result = methodName;
Matcher matcher = patternField.matcher(methodName);
if (matcher.find()) {
result = matcher.group(1).toLowerCase() + matcher.group(2);
}
return result;
}
public interface SerializableConsumer<T> extends Consumer<T>, Serializable, MethodReferenceReflection {}
public interface SerializableSupplier<T> extends Supplier<T>, Serializable, MethodReferenceReflection {}
interface MethodReferenceReflection {
//inspired by: http://benjiweber.co.uk/blog/2015/08/17/lambda-parameter-names-with-reflection/
default SerializedLambda serialized() {
try {
Method replaceMethod = getClass().getDeclaredMethod("writeReplace");
replaceMethod.setAccessible(true);
return (SerializedLambda) replaceMethod.invoke(this);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
default Class getContainingClass() {
try {
String className = serialized().getImplClass().replaceAll("/", ".");
return Class.forName(className);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
default Method method() {
SerializedLambda lambda = serialized();
Class containingClass = getContainingClass();
return Arrays.stream(containingClass.getDeclaredMethods())
.filter(method -> Objects.equals(method.getName(), lambda.getImplMethodName()))
.findFirst()
.orElseThrow(UnableToGuessMethodException::new);
}
class UnableToGuessMethodException extends RuntimeException {}
}
}
package fr.inra.oresing.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.UUID;
@Getter
@Setter
@ToString(callSuper = true)
public class Application extends OreSiEntity {
private String name;
private UUID config; // lien vers un BinaryFile
}
package fr.inra.oresing.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.Date;
import java.util.List;
@Getter
@Setter
@ToString(callSuper = true)
public class BinaryFile extends OreSiEntity {
private String name;
private long size;
private byte[] data;
}
package fr.inra.oresing.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.List;
import java.util.UUID;
@Getter
@Setter
@ToString(callSuper = true)
public class Data extends OreSiEntity {
private UUID binaryFile;
private List<UUID> refs;
private String jsonData;
}
package fr.inra.oresing.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.springframework.hateoas.Identifiable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@Accessors(chain = true)
@Getter
@Setter
@ToString
public abstract class OreSiEntity implements Identifiable<UUID> {
private UUID id = UUID.randomUUID();
private Date creationDate;
private Date updateDate;
}
package fr.inra.oresing.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.UUID;
@Getter
@Setter
@ToString(callSuper = true)
public class ReferenceType extends OreSiEntity {
private UUID application;
private String description;
private UUID binaryFile;
}
package fr.inra.oresing.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.UUID;
@Getter
@Setter
@ToString(callSuper = true)
public class ReferenceValue extends OreSiEntity {
private UUID referenceType;
private String label;
}
package fr.inra.oresing.persistence;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
@Component
public class JsonRowMapper<T> implements RowMapper<T> {
@Autowired
@Qualifier("sqlJsonMapper")
private ObjectMapper jsonMapper;
@Override
public T mapRow(ResultSet rs, int rowNum) throws SQLException {
try {
Class<T> type = (Class<T>)Class.forName(rs.getString("@class"));
String json = rs.getString("json");
T result = jsonMapper.readValue(json, type);
return result;
} catch (ClassNotFoundException | IOException eee) {
throw new SQLException("Can't convert result from database to object", eee);
}
}
}
package fr.inra.oresing.persistence;
import fr.inra.oresing.OreSiUtils;
import fr.inra.oresing.model.Application;
import fr.inra.oresing.model.BinaryFile;
import fr.inra.oresing.model.Data;
import fr.inra.oresing.model.OreSiEntity;
import fr.inra.oresing.model.ReferenceType;
import fr.inra.oresing.model.ReferenceValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
@Component
public class OreSiRepository {
private static final String BINARYFILE_UPSERT =
"INSERT INTO BinaryFile (id, name, size, data) values (:id, :name, :size, :data) "
+ " ON CONFLICT (id) DO UPDATE SET updateDate=current_timestamp, name=EXCLUDED.name, size=EXCLUDED.size, data=EXCLUDED.data"
+ " RETURNING id";
private static final String APPLICATION_UPSERT =
"INSERT INTO Application (id, name, config) values (:id, :name, :config) "
+ " ON CONFLICT (id) DO UPDATE SET updateDate=current_timestamp, name=EXCLUDED.name, config=EXCLUDED.config"
+ " RETURNING id";
private static final String REFERENCETYPE_UPSERT =
"INSERT INTO ReferenceType (id, application, description, file) values (:id, :application, :description, :binaryFile) "
+ " ON CONFLICT (id) DO UPDATE SET updateDate=current_timestamp, application=EXCLUDED.application, description=EXCLUDED.description, binaryFile=EXCLUDED.binaryFile"
+ " RETURNING id";
private static final String REFERENCEVALUE_UPSERT =
"INSERT INTO ReferenceValue (id, referenceType, label) values (:id, :referenceType, :label) "
+ " ON CONFLICT (id) DO UPDATE SET updateDate=current_timestamp, referenceType=EXCLUDED.referenceType, label=EXCLUDED.label"
+ " RETURNING id";
private static final String DATA_UPSERT =
"INSERT INTO Application (id, refs, jsonData) values (:id, :binaryFile, :refs, :jsonData) "
+ " ON CONFLICT (id) DO UPDATE SET updateDate=current_timestamp, binaryFile=EXCLUDED.binaryFile, refs=EXCLUDED.refs, jsonData=EXCLUDED.jsonData"
+ " RETURNING id";
private static final String TEMPLATE_SELECT_ALL = "SELECT '%s' as \"@class\", to_jsonb(t) as json FROM %s t";
private static final String TEMPLATE_SELECT_BY_ID = TEMPLATE_SELECT_ALL + " WHERE id=:id";
@Autowired
protected JsonRowMapper<OreSiEntity> jsonRowMapper;
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
protected UUID storeEntity(OreSiEntity e, String query, Map<String, Object> paramMap) {
if (e.getId() == null) {
e.setId(UUID.randomUUID());
}
UUID result = namedParameterJdbcTemplate.queryForObject(
query, paramMap, UUID.class);
return result;
}
public UUID store(BinaryFile e) {
Map<String, Object> paramMap = OreSiUtils.mapOf(e::getId, e::getName, e::getSize, e::getData);
return storeEntity(e, BINARYFILE_UPSERT, paramMap);
}
public UUID store(Application e) {
Map<String, Object> paramMap = OreSiUtils.mapOf(e::getId, e::getName, e::getConfig);
return storeEntity(e, APPLICATION_UPSERT, paramMap);
}
public UUID store(ReferenceType e) {
Map<String, Object> paramMap = OreSiUtils.mapOf(e::getId, e::getApplication, e::getDescription, e::getBinaryFile);
return storeEntity(e, REFERENCETYPE_UPSERT, paramMap);
}
public UUID store(ReferenceValue e) {
Map<String, Object> paramMap = OreSiUtils.mapOf(e::getId, e::getReferenceType, e::getLabel);
return storeEntity(e, REFERENCEVALUE_UPSERT, paramMap);
}
public UUID store(Data e) {
Map<String, Object> paramMap = OreSiUtils.mapOf(e::getId, e::getBinaryFile, e::getRefs, e::getJsonData);
return storeEntity(e, DATA_UPSERT, paramMap);
}
public <E extends OreSiEntity> List<E> findAll(Class<E> entityType) {
String query = String.format(TEMPLATE_SELECT_ALL, entityType.getName(), entityType.getSimpleName());
List<OreSiEntity> result = namedParameterJdbcTemplate.query(query, jsonRowMapper);
return (List<E>) result;
}
public <E extends OreSiEntity> Optional<E> findById(Class<E> entityType, UUID id) {
String query = String.format(TEMPLATE_SELECT_BY_ID, entityType.getName(), entityType.getSimpleName());
Optional<OreSiEntity> result = namedParameterJdbcTemplate.query(query, Map.of("id", id), jsonRowMapper).stream().findFirst();
return (Optional<E>)result;
}
}
package fr.inra.oresing.rest;
import fr.inra.oresing.model.Application;
import fr.inra.oresing.model.BinaryFile;
import fr.inra.oresing.model.ReferenceType;
import fr.inra.oresing.model.ReferenceValue;
import fr.inra.oresing.persistence.OreSiRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.Mapping;
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;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
@RestController
public class OreSiResources {
@Autowired
private OreSiRepository repo;
@RequestMapping("/")
public String home() {
return "Hello World!";
}
@PostMapping(value="/files", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Map<String, Object>> createFile( @RequestParam("data") MultipartFile file) throws IOException {
if (!file.isEmpty()) {
BinaryFile binaryFile = new BinaryFile();
binaryFile.setName(file.getOriginalFilename());
binaryFile.setSize(file.getSize());
binaryFile.setData(file.getBytes());
UUID result = repo.store(binaryFile);
return ResponseEntity.created(URI.create("/files/" + result)).body(Map.of("id", result.toString()));
} else {
return ResponseEntity.badRequest().build();
}
}
@GetMapping(value = "/files/{id}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<byte[]> getFile(@PathVariable("id") UUID id) {
Optional<BinaryFile> optionalBinaryFile = repo.findById(BinaryFile.class, id);
if (optionalBinaryFile.isPresent()) {
BinaryFile binaryFile = optionalBinaryFile.get();
HttpHeaders headers = new HttpHeaders();
headers.setContentLength(binaryFile.getSize());
headers.set("Content-disposition", "attachment;filename=" + binaryFile.getName());
return new ResponseEntity(binaryFile.getData(), headers, HttpStatus.OK);
} else {
return ResponseEntity.notFound().build();
}
}
@PostMapping(value="/applications", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Map<String, Object>> createApplication(@RequestBody Application e) {
UUID result = repo.store(e);
return ResponseEntity.created(URI.create("/applications/" + result)).body(Map.of("id", result.toString()));
}
@GetMapping(value = "/applications/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Application> getApplication(@PathVariable("id") UUID id) {
Optional<Application> opt = repo.findById(Application.class, id);
return opt.map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());
}
@PostMapping(value="/applications/{id}/references", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Map<String, Object>> createReference(@PathVariable("id") UUID id, @RequestParam("data") MultipartFile file) throws IOException {
if (!file.isEmpty()) {
BinaryFile binaryFile = new BinaryFile();
binaryFile.setName(file.getOriginalFilename());
binaryFile.setSize(file.getSize());
binaryFile.setData(file.getBytes());
UUID result = repo.store(binaryFile);
return ResponseEntity.created(URI.create(String.format("/applications/%s/references/%s", id, result))).body(Map.of("id", result.toString()));
} else {
return ResponseEntity.badRequest().build();
}
}
@PostMapping(value="/referencetypes", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Map<String, Object>> createReferenceType(@RequestBody ReferenceType e) {
UUID result = repo.store(e);
return ResponseEntity.created(URI.create("/referencetypes/" + result)).body(Map.of("id", result.toString()));
}
@GetMapping(value = "/referencetypes/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ReferenceType> getReferenceType(@PathVariable("id") UUID id) {
Optional<ReferenceType> opt = repo.findById(ReferenceType.class, id);
return opt.map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());
}
@PostMapping(value="/referencevalue", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Map<String, Object>> createReferenceValue(@RequestBody ReferenceValue e) {
UUID result = repo.store(e);
return ResponseEntity.created(URI.create("/referencevalue/" + result)).body(Map.of("id", result.toString()));
}
@GetMapping(value = "/referencevalue/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ReferenceValue> getReferenceValue(@PathVariable("id") UUID id) {
Optional<ReferenceValue> opt = repo.findById(ReferenceValue.class, id);
return opt.map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());
}
}
\ No newline at end of file
#Configuration for development mode
#Need to be overridden in final bundle
server.port = 8081
server.servlet-path=/services
spring.datasource.url=jdbc:postgresql://localhost:5432/ore-si
spring.datasource.username=dbuser
spring.datasource.password=xxxxxxxx
spring.datasource.driver-class-name=org.postgresql.Driver
flyway.enabled=true
flyway.baseline-on-migrate=true
flyway.baseline-version=0
flyway.encoding=UTF-8
flyway.locations=classpath:db/migration
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
-- check les foreign key pour le colonne references de la table data
CREATE OR REPLACE FUNCTION fk_check(targetTable TEXT, uid UUID[])
RETURNS BOOLEAN AS $$
DECLARE
result TEXT;
BEGIN
-- TODO
EXECUTE format('select count(id) > 0 from %s where id = $1;', targetTable) INTO result USING uid;
RETURN result;
END;
$$ language 'plpgsql';
create domain EntityId as uuid NOT NULL DEFAULT gen_random_uuid();
create domain EntityRef as uuid NOT NULL;
create domain ListEntityRef as uuid[] NOT NULL;
create domain DateOrNow as timestamp DEFAULT current_timestamp;
create table BinaryFile (
id EntityId PRIMARY KEY,
creationDate DateOrNow,
updateDate DateOrNow,
name Text,
size INT,
data bytea
);
create table Application (
id EntityId PRIMARY KEY,
creationDate DateOrNow,
updateDate DateOrNow,
name Text,
config EntityRef REFERENCES BinaryFile(id)
);
create table ReferenceType (
id EntityId PRIMARY KEY,
creationDate DateOrNow,
updateDate DateOrNow,
application EntityRef REFERENCES Application(id),
description jsonb,
binaryFile EntityRef REFERENCES BinaryFile(id)
);
create table ReferenceValue (
id EntityId PRIMARY KEY,
creationDate DateOrNow,
updateDate DateOrNow,
referenceType EntityRef REFERENCES ReferenceType(id),
label Text
);
create table Data (
id EntityId PRIMARY KEY,
creationDate DateOrNow,
updateDate DateOrNow,
binaryFile EntityRef REFERENCES BinaryFile(id),
refs ListEntityRef CHECK(fk_check('ReferenceValue', refs)),
jsonData jsonb[]
);
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<!-- Reads request input using UTF-8 encoding -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Handles all requests into the application -->
<servlet>
<servlet-name>spring-mvc-webapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:ore-si-rest-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-mvc-webapp</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
\ No newline at end of file
package fr.inra.oresing.rest;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jayway.jsonpath.JsonPath;
import fr.inra.oresing.OreSiNg;
import fr.inra.oresing.OreSiUtils;
import fr.inra.oresing.model.Application;
import fr.inra.oresing.model.BinaryFile;
import fr.inra.oresing.model.ReferenceValue;
import org.flywaydb.test.FlywayTestExecutionListener;
import org.flywaydb.test.annotation.FlywayTest;
import org.hamcrest.core.Is;
import org.hamcrest.core.IsNull;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Stream;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = OreSiNg.class)
@AutoConfigureWebMvc
@AutoConfigureMockMvc
@TestExecutionListeners({SpringBootDependencyInjectionTestExecutionListener.class,
FlywayTestExecutionListener.class})
@Rollback
@FlywayTest
public class OreSiResourcesTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Test
public void addApplication() throws Exception {
MockMultipartFile firstFile = new MockMultipartFile("data", "filename.txt", "text/plain", "some csv".getBytes());
String response = mockMvc.perform(MockMvcRequestBuilders.multipart("/files")
.file(firstFile))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id", IsNull.notNullValue()))
.andReturn().getResponse().getContentAsString();
String fileId = JsonPath.parse(response).read("$.id");
Application app = new Application();
app.setName("monsore");
app.setConfig(UUID.fromString(fileId));
response = mockMvc.perform(post("/applications")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(app)))
.andExpect(status().isCreated())
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
// id
.andExpect(jsonPath("$.id", IsNull.notNullValue()))
.andReturn().getResponse().getContentAsString();
String appId = JsonPath.parse(response).read("$.id");
response = mockMvc.perform(get("/applications/" + appId)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
// id
.andExpect(jsonPath("$.id", Is.is(appId)))
.andReturn().getResponse().getContentAsString();
Application app2 = objectMapper.readValue(response, Application.class);
Date now = new Date();
Assert.assertNotNull(app2.getCreationDate());
Assert.assertNotNull(app2.getUpdateDate());
Assert.assertTrue(deepFieldEquals(app, app2, OreSiUtils.fieldsOf(app::getCreationDate, app::getUpdateDate)));
}
boolean deepFieldEquals(Object o1, Object o2, String ... excludes) {
Map<String, Object> map1 = objectMapper.convertValue(o1, new TypeReference<Map>() { });
Map<String, Object> map2 = objectMapper.convertValue(o2, new TypeReference<Map>() { });
Stream.of(excludes)
.peek(map1::remove)
.forEach(map2::remove);
return map1.equals(map2);
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment