Skip to content
Snippets Groups Projects
Commit 044b1b76 authored by Sara Bertocco's avatar Sara Bertocco
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
Showing
with 703 additions and 0 deletions
target/**
nbactions.xml
# VOSpace XML
## Generating beans from XML schema
cd xsd
xjc vospace.xsd
### Changes to the online XSD files
It seems that xjc does something wrong when retriving the imported XSD from the web, so the dependent files have been downloaded and relative path has been specified in `schemaLocation` attribute.
In vospace.xsd:
<xsd:import namespace="http://www.ivoa.net/xml/UWS/v1.0" schemaLocation="./uws.xsd"/>
In uws.xsd:
<xs:import namespace="http://www.w3.org/1999/xlink" schemaLocation="./xlink.xsd"/>
### Changes to the generated classes
In package-info.java the following element has been added to serialize the XML keeping the namespace.
xmlns = {
@javax.xml.bind.annotation.XmlNs(
namespaceURI = "http://www.ivoa.net/xml/VOSpace/v2.0",
prefix = "vos"
)
}
Some issues emerged in handling inheritance and namespaces in a way compatible both to JSON and XML formats.
In Node.java type has been added:
@XmlAttribute(name = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
protected String type;
public String getType() {
return type;
}
For JSON compatibility the following has been added to Node.java (annotation on class):
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, property = "type", include = JsonTypeInfo.As.EXISTING_PROPERTY)
@JsonTypeIdResolver(NodeTypeJsonResolver.class)
The `@JsonTypeInfo` tells to Jackson that the field type is used to handle inheritance. A custom type id resolver has been created to handle the `vos:` prefix.
2 annotations have been added to each node subtype:
@XmlRootElement(name = "node")
@JsonDeserialize(converter = NodeTypeSetter.UnstructuredDataNode.class)
`@XmlRootElement` is necessary to parse single nodes. The value `"node"` has been specified because by default the bean would be serialized as `<unstructuredDataNode>`.
The `NodeTypeSetter` class fills the `type` field during JSON serialization and deserialization.
FROM library/postgres:11
#ENV POSTGRES_USER vospaceusr
#ENV POSTGRES_PASSWORD Peper0ne
ENV POSTGRES_DB vospacedb
ENV POSTGRES_HOST_AUTH_METHOD=trust
COPY src/main/resources/db_init.sql /docker-entrypoint-initdb.d/
COPY database/user.sql /docker-entrypoint-initdb.d/
POSTGRES_USER=vospaceusr
POSTGRES_PASSWORD=Peper0ne
POSTGRES_DB=vospacedb
CREATE ROLE vospaceusr WITH LOGIN PASSWORD 'Peper0ne';
GRANT USAGE ON SCHEMA public TO vospaceusr;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO vospaceusr;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO vospaceusr;
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<netbeans.hint.jdkPlatform>JDK_15</netbeans.hint.jdkPlatform>
</properties>
</project-shared-configuration>
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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>it.inaf.oats</groupId>
<artifactId>vospace-oats</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>vospace-oats</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JAXB dependency -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
<!-- Jackson-JAXB compatibility -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>it.oats.inaf</groupId>
<artifactId>vospace-datamodel</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>it.inaf.ia2</groupId>
<artifactId>auth-lib</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>it.inaf.ia2</groupId>
<artifactId>rap-client</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>it.inaf.ia2</groupId>
<artifactId>gms-client</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>ia2-snapshots</id>
<name>your custom repo</name>
<url>http://repo.ia2.inaf.it/maven/repository/snapshots</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>
</project>
package it.inaf.oats.vospace;
import net.ivoa.xml.vospace.v2.Node;
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.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import it.inaf.oats.vospace.persistence.NodeDAO;
import net.ivoa.xml.vospace.v2.Property;
import java.util.List;
@RestController
public class CreateNodeController {
@Autowired
NodeDAO node_dao;
@PostMapping(value = "/{path}",
consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
public Node createNode(@PathVariable("path") String path, @RequestBody Node node) {
System.out.println("In createNodeController");
node_dao.createNode(node);
return node;
}
private class RequestWrapper {
List<Property> nodeProperty;
String nodeId;
String nodeType;
}
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package it.inaf.oats.vospace;
import java.util.List;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import net.ivoa.xml.vospace.v2.Node;
import it.inaf.oats.vospace.persistence.NodeDAO;
@RestController
public class ListNodeController {
@Autowired
private NodeDAO nodeDAO;
@GetMapping(value="/{nodeName}")
public ResponseEntity<List<Node>>listNodes(@PathVariable("nodeName")String node_name) {
// dal nome del nodo devo ricavarmi l'ivo_id
String node_ivo_id = "";
return ResponseEntity.ok(nodeDAO.listNode(node_ivo_id));
}
}
package it.inaf.oats.vospace;
import javax.servlet.http.HttpServletRequest;
import net.ivoa.xml.vospace.v2.Node;
import org.springframework.http.MediaType;
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.RestController;
import it.inaf.ia2.aa.data.User;
@RestController
public class PrivateController {
@GetMapping(value = "/private",
produces = {MediaType.APPLICATION_JSON_VALUE})
public User getUser(HttpServletRequest request) {
User user = (User)request.getUserPrincipal();
return user;
}
}
package it.inaf.oats.vospace;
import net.ivoa.xml.vospace.v2.Transfer;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TransferController {
@PostMapping(value = "/transfer",
consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
public Transfer postTransfer(@RequestBody Transfer transfer) {
return transfer;
}
}
\ No newline at end of file
package it.inaf.oats.vospace;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import it.inaf.ia2.aa.LoginFilter;
@SpringBootApplication
public class VospaceApplication {
public static void main(String[] args) {
SpringApplication.run(VospaceApplication.class, args);
}
@Bean
public FilterRegistrationBean loginFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new LoginFilter());
registration.addUrlPatterns("/private/*");
return registration;
}
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package it.inaf.oats.vospace.persistence;
import net.ivoa.xml.vospace.v2.Node;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.http.ResponseEntity;
/**
*
* @author bertocco
*/
@Repository
public class NodeDAO {
private final JdbcTemplate jdbcTemplate;
@Autowired
public NodeDAO(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
public Node createNode(Node myNode) {
StringBuilder sb = new StringBuilder();
sb.append("INSERT INTO ");
sb.append("NodeProperty");
sb.append(" (nodeID,propertyURI,propertyValue) SELECT ?, ?, ?");
sb.append(" WHERE NOT EXISTS (SELECT * FROM NodeProperty");
sb.append(" WHERE nodeID=? and propertyURI=?)");
String sqlQuery = sb.toString();
return myNode;
}
public List<Node> listNode(String nodeIvoId) {
String sql = "SELECT * FROM Node WHERE ivo_id=?";
return jdbcTemplate.query(conn -> {
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, nodeIvoId);
return ps;
}, (row, index) -> {
Node newNode = new Node();
newNode.setUri(row.getString("ivo_id"));
return newNode;
});
}
}
# To change this license header, choose License Headers in Project Properties.
# To change this template file, choose Tools | Templates
# and open the template in the editor.
# show sql statements issued by JPA
#spring.jpa.show-sql=true
# enable debug logging for spring boot and hibernate classes
# this is equivalent to passing '--debug' as command line argument
#logging.level.org.springframework.boot=DEBUG
#logging.level.org.hibernate.SQL=DEBUG
# log to file (absolute/relative path of log file)
#logging.file=path/to/log/file.log
server.port=8083
server.servlet.context-path=/vospace
#spring.profiles.active=@spring.profiles.active@
# For development only:
spring.profiles.active=dev
spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/vospacedb
spring.datasource.username=vospaceusr
spring.datasource.password=Peper0ne
# enable debug logging
# this is equivalent to passing '--debug' as command line argument
logging.level.it.inaf=TRACE
logging.level.org.springframework.security=DEBUG
logging.level.org.springframework.jdbc=TRACE
logging.level.org.springframework.web=TRACE
#logging.level.org.springframework.boot=DEBUG
# log to file (absolute/relative path of log file)
#logging.file=path/to/log/file.log
rap_uri=https://sso.ia2.inaf.it/rap-ia2
gms_uri=https://sso.ia2.inaf.it/gms/
groups_autoload=true
client_id=vospace_test
client_secret=peperone
\ No newline at end of file
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/**
* Author: bertocco
* Created: Sep 17, 2020
*/
CREATE EXTENSION IF NOT EXISTS ltree;
CREATE TABLE node (
id BIGSERIAL PRIMARY KEY,
ivo_id text NOT NULL,
name text NOT NULL,
type text NOT NULL,
format text NOT NULL,
/* format e` per distinguere unstuctured (format=NULL da structured che hanno
un formato noto)
*/
asyncTrans boolean NOT NULL,
/* asyncTransf serve per indicare se il nodo e` ospitato da un cold storage e
deve essere necessariamente trasferito con un trasferimento asincrono */
busyState CHAR(1) NOT NULL,
ownerID INT NOT NULL,
creatorID INT NOT NULL,
groupRead text default NULL,
groupWrite text default NULL,
isPublic CHAR(1) NOT NULL default 'Y',
delta BIGINT default NULL,
/* potrebbe essere un delta di dati trasferito durante un trasferimento asincrono.
Dovrebbe stare sul servizio che fa il trasferimento (es. redis) */
contentType VARCHAR(100) default NULL,
contentEncoding VARCHAR(50) default NULL,
contentLength BIGINT default NULL,
contentMD5 VARCHAR(256) default NULL,
creationDate timestamp NOT NULL DEFAULT NOW(),
lastModification timestamp NOT NULL DEFAULT NOW(),
/*
link TEXT default NULL,
*/
acceptViews TEXT[],
provideViews TEXT[],
/*
storageID VARCHAR(256),
serve per mappare il nome del servizio di storage da interrogare per accedere al contenuto di questo nodo */
protocols TEXT[],
path ltree NOT NULL
/*
is_leaf boolean,
*/
CONSTRAINT ivo_id_unique UNIQUE (ivo_id)
);
# To change this license header, choose License Headers in Project Properties.
# To change this template file, choose Tools | Templates
# and open the template in the editor.
ivo://example.org/vospace
\ No newline at end of file
package it.inaf.oats.vospace;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import net.ivoa.xml.vospace.v2.Property;
import net.ivoa.xml.vospace.v2.UnstructuredDataNode;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import org.mockito.InjectMocks;
import static org.mockito.Mockito.verify;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.junit.jupiter.api.Disabled;
@ExtendWith(MockitoExtension.class)
public class CreateNodeControllerTest {
@InjectMocks
@Spy
private CreateNodeController controller;
private MockMvc mockMvc;
@BeforeEach
public void init() {
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
@Test
@Disabled
public void testFromJsonToXml() throws Exception {
String requestBody = getResourceFileContent("create-unstructured-data-node.json");
mockMvc.perform(post("/mypath")
.content(requestBody)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_XML))
.andDo(print())
.andExpect(status().isOk());
verifyArguments();
}
@Test
@Disabled
public void testFromXmlToJson() throws Exception {
String requestBody = getResourceFileContent("create-unstructured-data-node.xml");
mockMvc.perform(post("/mypath")
.content(requestBody)
.contentType(MediaType.APPLICATION_XML)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk());
verifyArguments();
}
@Test
@Disabled
public void testFromXmlToXml() throws Exception {
String requestBody = getResourceFileContent("create-unstructured-data-node.xml");
mockMvc.perform(post("/mypath")
.content(requestBody)
.contentType(MediaType.APPLICATION_XML)
.accept(MediaType.APPLICATION_XML))
.andDo(print())
.andExpect(status().isOk());
verifyArguments();
}
@Test
@Disabled
public void testFromJsonToJson() throws Exception {
String requestBody = getResourceFileContent("create-unstructured-data-node.json");
mockMvc.perform(post("/mypath")
.content(requestBody)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk());
verifyArguments();
}
private void verifyArguments() {
verify(controller).createNode(eq("mypath"),
argThat(node -> {
UnstructuredDataNode udn = (UnstructuredDataNode) node;
Property property = udn.getProperties().getProperty().get(0);
return "vos:UnstructuredDataNode".equals(udn.getType())
&& "test value".equals(property.getValue())
&& "ivo://ivoa.net/vospace/core#description".equals(property.getUri());
}));
}
protected static String getResourceFileContent(String fileName) throws Exception {
try ( InputStream in = CreateNodeControllerTest.class.getClassLoader().getResourceAsStream(fileName)) {
return new String(in.readAllBytes(), StandardCharsets.UTF_8);
}
}
}
{
"properties": {
"property": [
{
"value": "test value",
"uri": "ivo://ivoa.net/vospace/core#description",
"readOnly": false
}
]
},
"type": "vos:UnstructuredDataNode",
"uri": "vos://example.com!vospace/mydata1",
"accepts": {
"view": []
},
"provides": {
"view": []
},
"capabilities": {
"capability": []
},
"busy": false
}
\ No newline at end of file
<vos:node xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:vos="http://www.ivoa.net/xml/VOSpace/v2.0" xsi:type="vos:UnstructuredDataNode" uri="vos://example.com!vospace/mydata1">
<vos:properties>
<vos:property uri="ivo://ivoa.net/vospace/core#description">test value</vos:property>
</vos:properties>
<vos:accepts/>
<vos:provides/>
<vos:capabilities/>
</vos:node>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment