Compare commits

...

16 Commits

Author SHA1 Message Date
Biagio Peccerillo 5d4c9c3f5f Removed obsolete 'version' attribute from docker-compose files 2024-12-12 12:45:37 +01:00
Biagio Peccerillo 70a1039df2 Added Sphinx documentation
Details:
- Added index and intro pages
- Added sphinx-maven plugin in pom.xml
2024-12-12 12:43:16 +01:00
Biagio Peccerillo a85c98b639 Version set to 2.0.1-SNAPSHOT 2024-12-12 11:58:37 +01:00
Biagio Peccerillo 4318fef6d6 Improved analysis tools for containerized application
Details:
- Added a file appender for the logs
- Installed vim to easily navigate logs
2024-12-10 12:24:57 +01:00
Biagio Peccerillo 2b54a98760 Improved documentation and removed useless constraint
Details:
- Improved UserManager and GroupManager REST API docs
- removed content-type constraint on group admin DELETE
2024-12-10 11:41:34 +01:00
Biagio Peccerillo 3f45dce6f7 Merge branch 'master' into feature/28427 2024-12-09 16:32:11 +01:00
Biagio Peccerillo 589e38cd55 Improved AuthorizationControl documentation
For methods with Authorization Control limitations, a hint to the users has been added
under the method description indicating what roles are necessary to do the corrispondent
REST API call
2024-12-06 15:25:33 +01:00
Biagio Peccerillo 868dbabcff Completed REST API documentation
Additions:
- method descriptions
- parameter descriptions
- request/response examples
- return value descriptions
- response codes
Affected classes:
- {ACL,Group,Items,Script,Storage,User,Workspace}Manager, ItemsCreator, ItemsSharing
- DocsGenerator and MessageManager ignored altogether
2024-12-06 15:25:33 +01:00
Biagio Peccerillo bf55883511 Added end boundary string to multipart/form-data examples 2024-12-06 15:25:33 +01:00
Biagio Peccerillo a34eef4617 Merged with master 2024-12-06 15:25:20 +01:00
Biagio Peccerillo 0ee16386f0 disabled useless modules in enunciate.xml 2024-12-06 15:23:43 +01:00
Biagio Peccerillo d0fac83c50 ItemsCreator documented
Addition:
- method description
- request example
- response example
- parameter description
- return value description
- response codes
2024-12-06 15:23:40 +01:00
Biagio Peccerillo 1d3cc6c073 Rendered "https://dev.d4science.org/how-to-access-resources" as link in Enunciate docs 2024-12-06 15:23:37 +01:00
Biagio Peccerillo 65b9f611dc Improved Enunciate support
Changes:
- added "enunciate-lombok" module to support Lombok-annotated classes
- added some source-paths to locate external classes
- disabled two useless enunciate modules
2024-12-06 15:23:30 +01:00
Biagio Peccerillo deb1d315de Corrected Dockerfiles
Changed "d4science" in "harbor.d4science.org/gcube" to clarify the source registry
2024-12-06 15:23:20 +01:00
Biagio Peccerillo eedf6ef667 Improved enunciate documentation
Changes:
- switched to enunciate 2.18 (from 2.17)
- improved storagehub description in the index page (still improvable)
- added missing dependencies from storagehub-model
2024-12-06 15:23:05 +01:00
25 changed files with 1499 additions and 208 deletions

View File

@ -1,3 +1,3 @@
FROM d4science/smartgears-distribution:4.0.1-SNAPSHOT-java17-tomcat10.1.19
FROM harbor.d4science.org/gcube/smartgears-distribution:4.0.1-SNAPSHOT-java17-tomcat10.1.19
COPY ./target/storagehub.war /tomcat/webapps/
COPY ./docker/storagehub.xml /tomcat/conf/Catalina/localhost/

View File

@ -1,4 +1,4 @@
FROM d4science/smartgears-distribution:4.0.0-SNAPSHOT-java17-tomcat10.1.19
FROM harbor.d4science.org/gcube/smartgears-distribution:4.0.0-SNAPSHOT-java17-tomcat10.1.19
#install unzip
RUN apt-get update && apt-get install unzip

View File

@ -1,8 +1,9 @@
FROM smartgears-distribution:4.0.0-java17-tomcat10.1.19
FROM harbor.d4science.org/gcube/smartgears-distribution:4.0.0-SNAPSHOT-java17-tomcat10.1.19
COPY ./target/storagehub.war /tomcat/webapps/
COPY ./docker/jackrabbit /app/jackrabbit
COPY ./docker/storagehub.xml /tomcat/conf/Catalina/localhost/
COPY ./docker/logback.xml /etc/
COPY ./docker/container.ini /etc/
RUN mkdir -p /etc/config/storagehub
RUN apt update && apt install -y vim
COPY ./docker/storage-settings.properties /etc/config/storagehub/

View File

@ -1,4 +1,3 @@
version: '3.7'
services:
elb:
image: haproxy

View File

@ -1,4 +1,3 @@
version: '3.7'
services:
postgres:
image: postgres:16.2

View File

@ -1,4 +1,3 @@
version: '3.7'
services:
storagehub:
image: d4science/storagehub:latest

View File

@ -1,25 +1,31 @@
<configuration scan="true" debug="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>Ï
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>storagehub.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.gcube" level="DEBUG" />
<logger name="org.gcube.smartgears" level="TRACE" />
<logger name="org.gcube.smartgears.handlers" level="TRACE"/>
<logger name="org.gcube.smartgears.handlers" level="TRACE" />
<logger name="org.gcube.common.events" level="WARN" />
<logger name="org.gcube.data.publishing" level="ERROR" />
<logger name="org.gcube.documentstore" level="ERROR" />
<logger name="org.gcube.common.core.publisher.is.legacy" level="TRACE" />
<logger name="org.gcube.data.access" level="TRACE" />
<logger name="org.gcube.data.access.storagehub.handlers" level="DEBUG"/>
<logger name="org.gcube.data.access.storagehub.handlers" level="DEBUG" />
<root level="WARN">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
</configuration>

20
docs/Makefile Normal file
View File

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

58
docs/conf.py Normal file
View File

@ -0,0 +1,58 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'StorageHub'
copyright = '2024, Lucio Lelii, Biagio Peccerillo'
author = 'Lucio Lelii, Biagio Peccerillo'
# The full version, including alpha/beta/rc tags
release = '2.0.1'
# -- General configuration ---------------------------------------------------
source_suffix = {
'.rst': 'restructuredtext',
}
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

8
docs/index.rst Normal file
View File

@ -0,0 +1,8 @@
Welcome to StorageHub's documentation!
======================================
.. toctree::
:maxdepth: 2
:caption: Contents:
intro.rst

98
docs/intro.rst Normal file
View File

@ -0,0 +1,98 @@
Introduction
============
StorageHub is a versatile service designed to provide seamless access
to various storage resources, ensuring data persistence and management. It acts
as an intermediary layer that can interface with any underlying storage
solution, such as Amazon S3 or MongoDB, offering a unified and flexible approach
to storage management.
Base URL
--------
In the production environment, its current value is https://api.d4science.org/
Key Features
------------
Flexibility and Integration
~~~~~~~~~~~~~~~~~~~~~~~~~~~
StorageHub is designed to be highly flexible, allowing it to serve as an
intermediate layer for diverse storage solutions. This flexibility ensures that
it can adapt to different storage backends without requiring significant changes
to the applications that rely on it.
RESTful Interface
~~~~~~~~~~~~~~~~~
StorageHub exposes a RESTful API, which allows any application capable of making
HTTP requests to access it. This REST interface provides a standardized way to
interact with the storage resources, enabling easy integration with various
applications and services. See the available REST-API on `StorageHub API docs
<../api-docs/index.html>`_.
Metadata Management
~~~~~~~~~~~~~~~~~~~
StorageHub leverages a JackRabbit-based object store to manage all metadata
associated with the stored data. This ensures that metadata is efficiently
organized and easily retrievable, enhancing the overall data management
capabilities of the service.
Direct Payload Storage
~~~~~~~~~~~~~~~~~~~~~~
While metadata is handled by JackRabbit, the actual data payloads are stored
directly on the underlying storage solutions. This approach optimizes storage
efficiency and performance, ensuring that large data payloads are managed
effectively.
Primary Use Cases
-----------------
Workspace
~~~~~~~~~
The main application that interacts with StorageHub is the Workspace portlet,
which is easily accessible from the Virtual Research Environments (VREs). The
Workspace provides a "standard" HTML interface where users can perform all the
common operations available in a file system, such as creating, reading,
updating, and deleting files and directories.
In addition to these standard file system operations, the Workspace offers
features that are specific to VREs. These include publishing on the Catalogue,
sharing resources with other users, and managing versions of files. These
capabilities make the Workspace a versatile tool for managing data within the
VREs, leveraging the services provided by StorageHub.
Java Client
~~~~~~~~~~~
The methods of the Web Service can be called by writing your own REST client
application or by using already existing REST client plugins.
In case of a Java client, we provide the StorageHub Client Library, which is a
Java library designed to facilitate seamless interaction with StorageHub. It
abstracts the complexities of the REST API, providing a more intuitive and
convenient interface for Java developers.
The StorageHub Client Library allows developers to easily integrate StorageHub's
capabilities into their applications without dealing with the intricacies of
HTTP requests and responses. The library handles all the necessary communication
with StorageHub, allowing developers to focus on their application's core
functionality.
.. tip:: If you're coding in Java, it is recommended that you include the
StorageHub Client Library into your project.
Authorization
-------------
D4Science adopts state-of-the-art industry standards for authentication and
authorization. Specifically, the implementation fully adopts `OIDC (OpenID
Connect) <https://openid.net/connect>`_ for authentication and UMA 2 (User
Managed Authorization) for authorization flows. `JSON Web Token (JWT) Access
token <https://jwt.io/>`_ are used for both authentication and authorization.
Obtain your Bearer token here: https://dev.d4science.org/how-to-access-resources

35
docs/make.bat Normal file
View File

@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

View File

@ -1,24 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<enunciate
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://enunciate.webcohesion.com/schemas/enunciate-2.14.0.xsd">
<api-classes>
<!-- Use patterns to exclude classes... e.g. for URI-Resolver <exclude
pattern="org.gcube.datatransfer.resolver.services.DocsGenerator" /> -->
</api-classes>
xsi:noNamespaceSchemaLocation="http://enunciate.webcohesion.com/schemas/enunciate-2.18.0.xsd">
<description>
<![CDATA[
<h1>StorageHUB</h1>
<p>StorageHUB is the service implementing the gCube Workspace feature.</p>
<p>It provides an intermediate layer between the storage and the services
willing to access it.</p>
]]>
</description>
<code-license>This project is licensed under the EUPL V.1.1 License - see the LICENSE.md file for details.</code-license>
<modules>
<gwt-json-overlay disabled="true" />
<php-json-client disabled="true" />
<ruby-json-client disabled="true" />
<java-json-client disabled="true" />
<javascript-client disabled="true" />
<docs docsDir="${project.build.directory}" docsSubdir="api-docs" />
<java-xml-client disabled="true" />
<jaxb disabled="true" />
<jaxws disabled="true" />
<c-xml-client disabled="true" />
<csharp-xml-client disabled="true" />
<obj-c-xml-client disabled="true" />
<php-xml-client disabled="true" />
<spring-webnt disabled="true" />
<jaxrs groupBy="class" disableExamples="false" path-sort-strategy="depth_first" />
<swagger basePath="/workspace" />
<docs docsDir="${project.build.directory}" docsSubdir="api-docs" />
<docs
freemarkerTemplate="${project.basedir}/src/main/resources/META-INF/enunciate/d4science_docs.fmt">
<additional-css
file="css/d4science_enunciate_custom.css" />
<additional-css file="css/d4science_enunciate_custom.css" />
</docs>
<swagger basePath="/workspace" />
</modules>
</enunciate>

54
pom.xml
View File

@ -10,7 +10,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.gcube.data.access</groupId>
<artifactId>storagehub</artifactId>
<version>2.0.0</version>
<version>2.0.1-SNAPSHOT</version>
<name>storagehub</name>
<scm>
<connection>
@ -32,7 +32,7 @@
<warname>storagehub</warname>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<enunciate.version>2.17.1</enunciate.version>
<enunciate.version>2.18.1</enunciate.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<java_version>17</java_version>
@ -42,7 +42,7 @@
<dependency>
<groupId>org.gcube.distribution</groupId>
<artifactId>gcube-smartgears-bom</artifactId>
<version>4.0.0</version>
<version>4.0.1-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@ -428,7 +428,32 @@
<groupId>com.webcohesion.enunciate</groupId>
<artifactId>enunciate-maven-plugin</artifactId>
<version>${enunciate.version}</version>
<configuration></configuration>
<dependencies>
<dependency>
<groupId>com.webcohesion.enunciate</groupId>
<artifactId>enunciate-lombok</artifactId>
<version>2.9.1</version>
</dependency>
</dependencies>
<configuration>
<sourcepath-includes>
<!-- Include storagehub classes -->
<sourcepath-include>
<groupId>org.gcube.common</groupId>
<artifactId>storagehub</artifactId>
</sourcepath-include>
<!-- Include storagehub-model classes -->
<sourcepath-include>
<groupId>org.gcube.common</groupId>
<artifactId>storagehub-model</artifactId>
</sourcepath-include>
<!-- Include jersey media classes -->
<sourcepath-include>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
</sourcepath-include>
</sourcepath-includes>
</configuration>
<executions>
<execution>
<id>assemble</id>
@ -465,6 +490,27 @@
</execution>
</executions>
</plugin>
<!-- SPHINX PLUGIN triggered at 'compile' -->
<plugin>
<groupId>kr.motd.maven</groupId>
<artifactId>sphinx-maven-plugin</artifactId>
<version>2.10.0</version>
<configuration>
<outputDirectory>
${project.build.directory}/${project.artifactId}/docs</outputDirectory>
<builder>html</builder>
<configDirectory>${basedir}/docs</configDirectory>
<sourceDirectory>${basedir}/docs</sourceDirectory>
</configuration>
<executions>
<execution>
<phase>process-resources</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>

View File

@ -35,14 +35,19 @@ import org.glassfish.jersey.media.multipart.FormDataParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.DocumentationExample;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import com.webcohesion.enunciate.metadata.rs.ResourceMethodSignature;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.servlet.ServletContext;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
@ -53,10 +58,13 @@ import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
/**
* Manage the Access Control List of shared folders
*/
@Path("items")
@ManagedBy(StorageHubApplicationManager.class)
@RequestHeaders({
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
@RequestHeader( name = "Authorization", description = "Bearer token, see <a href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>"),
})
public class ACLManager extends Impersonable {
@ -91,9 +99,13 @@ public class ACLManager extends Impersonable {
/**
* returns the AccessType for all the users in a shared folder
*
* @exception {@link RepositoryException} when a generic jcr error occurs
* @exception {@link UserNotAuthorizedException} when the caller is not authorized to access to the shared folder
* @param id id of the shared folder
*/
@ResourceMethodSignature(output = ACLList.class, pathParams = { @PathParam("id") })
@StatusCodes({
@ResponseCode ( code = 200, condition = "Shared folder found."),
@ResponseCode ( code = 500, condition = "This item does not exist."),
})
@GET
@Path("{id}/acls")
@Produces(MediaType.APPLICATION_JSON)
@ -122,15 +134,26 @@ public class ACLManager extends Impersonable {
/**
* Set a new AccessType for a user in a shared folder or VRE folder
*
*
* @param String user
* @param accessType accessType
*
* @exception {@link RepositoryException} when a generic jcr error occurs
* @exception {@link UserNotAuthorizedException} when the caller is not ADMINISTRATOR of the shared folder
* @exception {@link InvalidCallParameters} when the folder is not shared with the specified user
* @exception {@link InvalidItemException} when the folder is not share
* @param id id of the shared folder
* @param user user id
* @param access access type<br>
* <strong>Possible values:</strong> <code>READ_ONLY</code>, <code>WRITE_OWNER</code>, <code>WRITE_ALL</code>, <code>ADMINISTRATOR</code>
*/
@ResourceMethodSignature(output = void.class, pathParams = { @PathParam("id") }, formParams = {
@FormParam("user"), @FormParam("access") })
@StatusCodes({
@ResponseCode ( code = 204, condition = "Access type updated."),
@ResponseCode ( code = 400, condition = "User does not exist."),
@ResponseCode ( code = 415, condition = "Wrong content type."),
@ResponseCode ( code = 500, condition = "This shared item does not exist or wrong access type."),
})
@DocumentationExample(value = "...\n\n--------boundaryString\n" +
"Content-Disposition: form-data; name=\"user\"\n\n" +
"user2\n" +
"--------boundaryString\n" +
"Content-Disposition: form-data; name=\"accessType\"\n\n" +
"WRITE_OWNER\n" +
"--------boundaryString--")
@PUT
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("{id}/acls")
@ -186,18 +209,17 @@ public class ACLManager extends Impersonable {
}
/**
* remove right for a user only on Shared folder
* Remove a user from the shared folder
*
*
* @param String user
*
*
* @exception {@link RepositoryException} when a generic jcr error occurs
* @exception {@link UserNotAuthorizedException} when the caller is not ADMINISTRATOR of the shared folder
* @exception {@link InvalidCallParameters} when the folder is not shared with the specified user
* @exception {@link InvalidItemException} when the folder is not share
* @param id id of the shared folder
* @param user user id
*/
//TODO: is this method correct? can ACL be removed, is correct that this means an unshare operation?
@ResourceMethodSignature(output = void.class, pathParams = { @PathParam("id"), @PathParam("user") })
@StatusCodes({
@ResponseCode ( code = 204, condition = "User removed."),
@ResponseCode ( code = 415, condition = "Wrong content type."),
@ResponseCode ( code = 500, condition = "This shared item does not exist."),
})
@DELETE
@Consumes(MediaType.TEXT_PLAIN)
@Path("{id}/acls/{user}")
@ -233,7 +255,20 @@ public class ACLManager extends Impersonable {
}
}
/**
* Check if the current user can write on the shared folder
*
* @param id id of the shared folder
* @return true if the current user can write on the shared folder, false otherwise
* @responseExample text/plain true
*/
@ResourceMethodSignature(output = Boolean.class, pathParams = { @PathParam("id") })
@StatusCodes({
@ResponseCode ( code = 200, condition = "Shared folder found."),
@ResponseCode ( code = 406, condition = "This shared folder does not exist."),
})
@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("{id}/acls/write")
public Boolean canWriteInto() {
InnerMethodName.set("canWriteIntoFolder");

View File

@ -14,11 +14,14 @@ import jakarta.ws.rs.core.Response.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.Ignore;
@Path("api-docs")
public class DocsGenerator {
private static Logger logger = LoggerFactory.getLogger(DocsGenerator.class);
@Ignore
@GET
@Path("/{any: .*}")
public InputStream toDoc(@Context HttpServletRequest req) throws WebApplicationException {

View File

@ -28,8 +28,12 @@ import org.glassfish.jersey.media.multipart.FormDataParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.DocumentationExample;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import com.webcohesion.enunciate.metadata.rs.ResourceMethodSignature;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
@ -46,11 +50,14 @@ import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
/**
* Manages groups
*/
@Path("groups")
@Singleton
@ManagedBy(StorageHubApplicationManager.class)
@RequestHeaders({
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
@RequestHeader( name = "Authorization", description = "Bearer token, see <a href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>"),
})
public class GroupManager {
@ -66,6 +73,15 @@ public class GroupManager {
PathUtil pathUtil;
/**
* Get list of the groups for the current user
*
* @return list of groups
* @responseExample text/plain ["group1", "group2", "group3"]
*/
@StatusCodes({
@ResponseCode ( code = 200, condition = "Success."),
})
@GET
@Path("")
@Produces(MediaType.APPLICATION_JSON)
@ -87,9 +103,44 @@ public class GroupManager {
return groups;
}
/**
* Create a new group <br>
* <strong>Only users with <code>Infrastructure-Manager</code> role allowed</strong>
*
* @param group group name
* @param accessType access type<br> <strong>Possible values:</strong> <code>READ_ONLY</code>, <code>WRITE_OWNER</code>, <code>WRITE_ALL</code>, <code>ADMINISTRATOR</code>
* @param folderOwner folder owner
* @param useDefaultStorage use default storage if true<br>
* <strong>Possible values:</strong> <code>true</code>, <code>false</code><br>
* <strong>Optional</strong> default: <code>true</code>
* @return group name
* @responseExample text/plain "group"
*/
@ResourceMethodSignature(output = String.class, formParams = {
@FormParam("group"), @FormParam("accessType"), @FormParam("folderOwner"), @FormParam("useDefaultStorage") })
@StatusCodes({
@ResponseCode ( code = 200, condition = "Success."),
@ResponseCode ( code = 403, condition = "You're not allowed to create groups."),
@ResponseCode ( code = 406, condition = "Error creating group."),
@ResponseCode ( code = 415, condition = "Wrong content type."),
})
@DocumentationExample(value = "...\n\n--------boundaryString\n" +
"Content-Disposition: form-data; name=\"group\"\n\n" +
"my_group\n" +
"--------boundaryString\n" +
"Content-Disposition: form-data; name=\"accessType\"\n\n" +
"ADMINISTRATOR\n" +
"--------boundaryString\n" +
"Content-Disposition: form-data; name=\"folderOwner\"\n\n" +
"user1\n" +
"--------boundaryString\n" +
"Content-Disposition: form-data; name=\"useDefaultStorage\"\n\n" +
"true\n" +
"--------boundaryString--")
@POST
@Path("")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
@AuthorizationControl(allowedRoles={INFRASTRUCTURE_MANAGER_ROLE})
public String createGroup(@FormDataParam("group") String group, @FormDataParam("accessType") AccessType accessType, @FormDataParam("folderOwner") String folderOwner, @FormDataParam("useDefaultStorage") @DefaultValue("true") boolean useDefaultStorage){
@ -112,8 +163,22 @@ public class GroupManager {
return group;
}
/**
* Delete a group <br>
* <strong>Only users with <code>Infrastructure-Manager</code> role allowed</strong>
*
* @param group group name
* @return group name
* @responseExample text/plain "group"
*/
@StatusCodes({
@ResponseCode ( code = 200, condition = "Success."),
@ResponseCode ( code = 403, condition = "You're not allowed to delete groups."),
@ResponseCode ( code = 406, condition = "Error deleting group."),
})
@DELETE
@Path("{group}")
@Produces(MediaType.TEXT_PLAIN)
@AuthorizationControl(allowedRoles={INFRASTRUCTURE_MANAGER_ROLE})
public String deleteGroup(@PathParam("group") String group){
@ -138,6 +203,18 @@ public class GroupManager {
public boolean isVREManager() { return SecretManagerProvider.get().getOwner().getRoles().contains(VREMANAGER_ROLE); }
/**
* Add an administrator to a group
*
* @param id group name
* @param userId user name
*/
@StatusCodes({
@ResponseCode ( code = 204, condition = "Success."),
@ResponseCode ( code = 406, condition = "Error adding an admin."),
@ResponseCode ( code = 415, condition = "Wrong content type."),
})
@ResourceMethodSignature(output = String.class, pathParams = { @PathParam("id")}, formParams = { @FormParam("userId") })
@PUT
@Path("{id}/admins")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@ -175,9 +252,20 @@ public class GroupManager {
}
/**
* Remove an administrator from a group. The removed admin remains in the group as a <em>normal</em> user.
*
* @param id group name
* @param userId user name
*/
@StatusCodes({
@ResponseCode ( code = 204, condition = "Success."),
@ResponseCode ( code = 406, condition = "Error removing an admin."),
@ResponseCode ( code = 415, condition = "Wrong content type."),
})
@ResourceMethodSignature(output = void.class, pathParams = { @PathParam("id")}, formParams = { @FormParam("userId") })
@DELETE
@Path("{id}/admins/{userId}")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void removeAdmin(@PathParam("id") String groupId, @PathParam("userId") String userId){
InnerMethodName.set("removeAdmin");
@ -210,6 +298,16 @@ public class GroupManager {
}
}
/**
* Get the list of administrators of a group
*
* @param groupId group name
* @return list of administrators
*/
@StatusCodes({
@ResponseCode ( code = 200, condition = "Success."),
@ResponseCode ( code = 406, condition = "This group does not exist."),
})
@GET
@Path("{groupId}/admins")
@Produces(MediaType.APPLICATION_JSON)
@ -236,6 +334,23 @@ public class GroupManager {
}
/**
* Add a user to a group <br>
* <strong>Only users with <code>Infrastructure-Manager</code> or <code>VRE-Manager</code> role allowed</strong>
*
* @param id group name
* @param userId user name
* @return true if the user has been added to the group
* @responseExample text/plain true
*/
@StatusCodes({
@ResponseCode ( code = 200, condition = "Success."),
@ResponseCode ( code = 403, condition = "You're not allowed to add users to groups."),
@ResponseCode ( code = 406, condition = "Group or user does not exist."),
@ResponseCode ( code = 415, condition = "Wrong content type."),
})
@ResourceMethodSignature(output = boolean.class, pathParams = { @PathParam("id")}, formParams = { @FormParam("userId") })
@DocumentationExample(value = "...\n\nuserId=user1\n")
@PUT
@Path("{id}/users")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@ -271,6 +386,19 @@ public class GroupManager {
/**
* Remove a user from a group <br>
* <strong>Only users with <code>Infrastructure-Manager</code> or <code>VRE-Manager</code> role allowed</strong>
*
* @param groupId group name
* @param userId user name
* @return true if the user has been removed from the group
*/
@StatusCodes({
@ResponseCode ( code = 200, condition = "Success."),
@ResponseCode ( code = 403, condition = "You're not allowed to add users to groups."),
@ResponseCode ( code = 406, condition = "Group or user does not exist."),
})
@DELETE
@Path("{groupId}/users/{userId}")
@AuthorizationControl(allowedRoles={VREMANAGER_ROLE, INFRASTRUCTURE_MANAGER_ROLE})
@ -304,6 +432,17 @@ public class GroupManager {
return success;
}
/**
* Get the list of users of a group <br>
* <strong>Only users with <code>Infrastructure-Manager</code> or <code>VRE-Manager</code> role allowed</strong>
*
* @param groupId group name
* @return list of users
*/
@StatusCodes({
@ResponseCode ( code = 200, condition = "Success."),
@ResponseCode ( code = 406, condition = "Group does not exist."),
})
@GET
@Path("{groupId}/users")
@Produces(MediaType.APPLICATION_JSON)

View File

@ -42,13 +42,17 @@ import org.glassfish.jersey.media.multipart.FormDataParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.DocumentationExample;
import com.webcohesion.enunciate.metadata.Ignore;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import com.webcohesion.enunciate.metadata.rs.ResourceMethodSignature;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.servlet.ServletContext;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
@ -59,10 +63,12 @@ import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
/**
* Manage item sharing.
*/
@Path("items")
@RequestHeaders({
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
@RequestHeader( name = "Authorization", description = "Bearer token, see <a href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>"),
})
public class ItemSharing extends Impersonable{
@ -92,10 +98,12 @@ public class ItemSharing extends Impersonable{
@Inject Node2ItemConverter node2Item;
@Inject Item2NodeConverter item2Node;
// TODO: Remove this method - not used by anyone
@Ignore
@POST
@SuppressWarnings("unchecked")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@POST
@Produces(MediaType.TEXT_PLAIN)
@Path("{id}/share")
public String shareWithMap(@FormParam("mapUserPermission") String mapUserPermissionString, @FormParam("defaultAccessType") String defaultAccessTypeString){
InnerMethodName.set("shareFolder");
@ -197,9 +205,32 @@ public class ItemSharing extends Impersonable{
/**
* Share an item with some users and set its access type.
*
* @param id id of the item
* @param users set of users
* @param defaultAccessType default access type<br>
* <strong>Possible values:</strong> <code>READ_ONLY</code>, <code>WRITE_OWNER</code>, <code>WRITE_ALL</code>, <code>ADMINISTRATOR</code>
* @return id of the shared item
* @responseExample text/plain 5f4b3b4e-4b3b- ... -4b3b4e4b3b4e
*/
@ResourceMethodSignature(output = String.class, pathParams = { @PathParam("id") }, formParams = {
@FormParam("users"), @FormParam("defaultAccessType") })
@DocumentationExample("...\n\n--------boundaryString\n" +
"Content-Disposition: form-data; name=\"users\"\n\n" +
"user1\n" +
"--------boundaryString\n" +
"Content-Disposition: form-data; name=\"users\"\n\n" +
"user2\n" +
"--------boundaryString\n" +
"Content-Disposition: form-data; name=\"defaultAccessType\"\n\n" +
"READ_ONLY\n" +
"------boundaryString--")
@PUT
@Path("{id}/share")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
public String share(@FormDataParam("users") Set<String> users, @FormDataParam("defaultAccessType") AccessType accessType){
InnerMethodName.set("shareFolder");
Session ses = null;
@ -339,9 +370,24 @@ public class ItemSharing extends Impersonable{
}
/**
* Unshare an item with some users.
*
* @param id id of the item
* @param users set of users
* @return id of the unshared item
* @responseExample text/plain 5f4b3b4e-4b3b- ... -4b3b4e4b3b4e
*/
@ResourceMethodSignature(output = String.class, pathParams = { @PathParam("id") }, formParams = {
@FormParam("users") })
@DocumentationExample("...\n\n--------boundaryString\n" +
"Content-Disposition: form-data; name=\"users\"\n\n" +
"user1,user2,user3\n" +
"--------boundaryString--")
@PUT
@Path("{id}/unshare")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
public String unshare(@FormDataParam("users") Set<String> users){
InnerMethodName.set("unshareFolder");
Session ses = null;

View File

@ -39,8 +39,13 @@ import org.glassfish.jersey.media.multipart.FormDataParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.DocumentationExample;
import com.webcohesion.enunciate.metadata.Ignore;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import com.webcohesion.enunciate.metadata.rs.ResourceMethodSignature;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import jakarta.inject.Inject;
import jakarta.servlet.ServletContext;
@ -50,17 +55,20 @@ import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
/**
* Manage item creation.
*/
@Path("items")
@ManagedBy(StorageHubApplicationManager.class)
@RequestHeaders({
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
})
public class ItemsCreator extends Impersonable{
@RequestHeader(name = "Authorization", description = "Bearer token, see <a href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>")
})
public class ItemsCreator extends Impersonable {
private static final Logger log = LoggerFactory.getLogger(ItemsCreator.class);
@ -72,8 +80,29 @@ public class ItemsCreator extends Impersonable{
@Inject
ItemHandler itemHandler;
/**
* Create a folder.
*
* @param id destination parent folder id
* @param name destination folder name
* @param description description meta-info for the created folder
* @param hidden hidden folder if true<br>
* <strong>Possible values:</strong> <code>true</code>, <code>false</code> <br>
* <strong>Optional</strong> default: <code>false</code>
* @return id of the created folder
* @responseExample text/plain 5f4b3b4e-4b3b- ... -4b3b4e4b3b4e
*/
@ResourceMethodSignature(output = String.class, pathParams = { @PathParam("id") }, formParams = {
@FormParam("name"), @FormParam("description"), @FormParam("hidden") })
@DocumentationExample(" ...\n\nname=sampleFolder&description=This+is+a+sample+folder&hidden=false")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.TEXT_PLAIN)
@StatusCodes({
@ResponseCode ( code = 200, condition = "Folder created."),
@ResponseCode ( code = 400, condition = "Wrong set of parameters."),
@ResponseCode ( code = 415, condition = "Wrong content type."),
})
@Path("/{id}/create/FOLDER")
public Response createFolder(@PathParam("id") String id, @FormParam("name") String name,
@FormParam("description") String description, @FormParam("hidden") boolean hidden) {
@ -81,29 +110,31 @@ public class ItemsCreator extends Impersonable{
log.info("create folder item called");
Session ses = null;
String toReturn = null;
try{
try {
ses = repository.getRepository().login(Constants.JCR_CREDENTIALS);
ItemsParameterBuilder<FolderCreationParameters> builder = FolderCreationParameters.builder().name(name).description(description).hidden(hidden).on(id).with(ses).author(currentUser);
ItemsParameterBuilder<FolderCreationParameters> builder = FolderCreationParameters.builder().name(name)
.description(description).hidden(hidden).on(id).with(ses).author(currentUser);
toReturn = itemHandler.create(builder.build());
}catch(StorageHubException she ){
} catch (StorageHubException she) {
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(RepositoryException re ){
} catch (RepositoryException re) {
log.error("jcr error creating item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re));
}catch(Throwable e ){
} catch (Throwable e) {
log.error("unexpected error", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
}finally{
if (ses!=null)
} finally {
if (ses != null)
ses.logout();
}
return Response.ok(toReturn).build();
}
@Ignore
@POST
@AuthorizationControl(allowedRoles={INFRASTRUCTURE_MANAGER_ROLE})
@AuthorizationControl(allowedRoles = { INFRASTRUCTURE_MANAGER_ROLE })
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/{id}/create/EXTERNALFOLDER")
public Response createExternalFolder(@PathParam("id") String id, @FormParam("name") String name,
@ -114,7 +145,7 @@ public class ItemsCreator extends Impersonable{
log.info("create folder item called");
Session ses = null;
String toReturn = null;
try{
try {
Iterator<String> paramIt = request.getParameterNames().asIterator();
Iterable<String> iterable = () -> paramIt;
Stream<String> targetStream = StreamSupport.stream(iterable.spliterator(), false);
@ -124,60 +155,81 @@ public class ItemsCreator extends Impersonable{
targetStream.filter(v -> v.startsWith("plugin."))
.forEach(v -> pluginParams.add(v.replace("plugin.", ""), request.getParameter(v)));
log.debug("parameters for external folder with plugin {} are {}",pluginName, pluginParams.toString());
log.debug("parameters for external folder with plugin {} are {}", pluginName, pluginParams.toString());
ses = repository.getRepository().login(Constants.JCR_CREDENTIALS);
ItemsParameterBuilder<FolderCreationParameters> builder = FolderCreationParameters.builder().name(name)
.description(description).onRepository(pluginName).withParameters(pluginParams.getParameters()).hidden(hidden).on(id).with(ses).author(currentUser);
.description(description).onRepository(pluginName).withParameters(pluginParams.getParameters())
.hidden(hidden).on(id).with(ses).author(currentUser);
toReturn = itemHandler.create(builder.build());
}catch(StorageHubException she ){
} catch (StorageHubException she) {
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(RepositoryException re ){
} catch (RepositoryException re) {
log.error("jcr error creating item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re));
}catch(Throwable e ){
} catch (Throwable e) {
log.error("unexpected error", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
}finally{
if (ses!=null)
} finally {
if (ses != null)
ses.logout();
}
return Response.ok(toReturn).build();
}
/**
* Create a URL.
*
* @param id destination parent folder id
* @param name destination URL name
* @param description description meta-info for the created URL
* @param value URL address
* @return id of the created URL
* @responseExample text/plain 5f4b3b4e-4b3b- ... -4b3b4e4b3b4e
*/
@DocumentationExample(" ...\n\nname=d4science&description=D4Science+URL&value=www.d4science.org")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.TEXT_PLAIN)
@StatusCodes({
@ResponseCode ( code = 200, condition = "URL created."),
@ResponseCode ( code = 400, condition = "Wrong set of parameters."),
@ResponseCode ( code = 406, condition = "Unable to create URL."),
@ResponseCode ( code = 415, condition = "Wrong content type."),
})
@Path("/{id}/create/URL")
public Response createURL(@PathParam("id") String id, @FormParam("name") String name, @FormParam("description") String description, @FormParam("value") URL value) {
public Response createURL(@PathParam("id") String id, @FormParam("name") String name,
@FormParam("description") String description, @FormParam("value") URL value) {
InnerMethodName.set("createItem(URL)");
log.info("create url called");
Session ses = null;
String toReturn = null;
try{
try {
ses = repository.getRepository().login(Constants.JCR_CREDENTIALS);
ItemsParameterBuilder<URLCreationParameters> builder = URLCreationParameters.builder().name(name).description(description).url(value).on(id).with(ses).author(currentUser);
ItemsParameterBuilder<URLCreationParameters> builder = URLCreationParameters.builder().name(name)
.description(description).url(value).on(id).with(ses).author(currentUser);
toReturn = itemHandler.create(builder.build());
}catch(StorageHubException she ){
} catch (StorageHubException she) {
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(RepositoryException re ){
} catch (RepositoryException re) {
log.error("jcr error creating item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re));
}catch(Throwable e ){
} catch (Throwable e) {
log.error("unexpected error", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
}finally{
if (ses!=null)
} finally {
if (ses != null)
ses.logout();
}
return Response.ok(toReturn).build();
}
@Ignore
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Path("/{id}/create/GCUBEITEM")
@ -187,39 +239,57 @@ public class ItemsCreator extends Impersonable{
Session ses = null;
String toReturn = null;
try{
try {
ses = repository.getRepository().login(Constants.JCR_CREDENTIALS);
ItemsParameterBuilder<GCubeItemCreationParameters> builder = GCubeItemCreationParameters.builder().item(item).on(id).with(ses).author(currentUser);
ItemsParameterBuilder<GCubeItemCreationParameters> builder = GCubeItemCreationParameters.builder()
.item(item).on(id).with(ses).author(currentUser);
toReturn = itemHandler.create(builder.build());
}catch(StorageHubException she ){
} catch (StorageHubException she) {
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(RepositoryException re ){
} catch (RepositoryException re) {
log.error("jcr error creating item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating item", re));
}catch(Throwable e ){
} catch (Throwable e) {
log.error("unexpected error", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
}finally{
if (ses!=null)
} finally {
if (ses != null)
ses.logout();
}
return toReturn;
}
/**
* Upload a file retrieved from the provided url.
*
* @param id destination folder id
* @param name destination file name
* @param description description meta-info for the created file
* @param url address of the file to be uploaded
* @return id of the created file
* @responseExample text/plain 5f4b3b4e-4b3b- ... -4b3b4e4b3b4e
*/
@DocumentationExample(" ...\n\nname=d4science_logo.png&description=D4Science+logo&url=\"https://www.d4science.org/image/layout_set_logo?img_id=12630&t=1720538711657\"")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.TEXT_PLAIN)
@StatusCodes({
@ResponseCode ( code = 200, condition = "File created."),
@ResponseCode ( code = 400, condition = "Wrong set of parameters."),
@ResponseCode ( code = 406, condition = "Unable to create file."),
@ResponseCode ( code = 415, condition = "Wrong content type."),
})
@Path("/{id}/create/FILE")
public String createFileItemFromUrl(@PathParam("id") String id, @FormParam("name") String name,
@FormParam("description") String description,
@FormParam("url") String url){
@FormParam("url") String url) {
InnerMethodName.set("createItem(FILEFromUrl)");
Session ses = null;
String toReturn = null;
try{
try {
log.debug("UPLOAD: call started");
ses = repository.getRepository().login(Constants.JCR_CREDENTIALS);
@ -228,24 +298,25 @@ public class ItemsCreator extends Impersonable{
long fileLength = connectionURL.getContentLengthLong();
try(InputStream stream = connectionURL.getInputStream()){
ItemsParameterBuilder<FileCreationParameters> builder = FileCreationParameters.builder().name(name).fileDetails(FormDataContentDisposition.name(name).size(fileLength).build())
try (InputStream stream = connectionURL.getInputStream()) {
ItemsParameterBuilder<FileCreationParameters> builder = FileCreationParameters.builder().name(name)
.fileDetails(FormDataContentDisposition.name(name).size(fileLength).build())
.description(description).stream(stream)
.on(id).with(ses).author(currentUser);
toReturn = itemHandler.create(builder.build());
}
log.debug("UPLOAD: call finished");
}catch(RepositoryException re ){
} catch (RepositoryException re) {
log.error("jcr error creating file item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating file item", re));
}catch(StorageHubException she ){
} catch (StorageHubException she) {
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(Throwable e ){
} catch (Throwable e) {
log.error("unexpected error", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
}finally{
if (ses!=null && ses.isLive()) {
} finally {
if (ses != null && ses.isLive()) {
log.info("session closed");
ses.logout();
}
@ -254,39 +325,71 @@ public class ItemsCreator extends Impersonable{
}
/**
* Upload the provided file.
*
* @param id destination folder id
* @param name destination file name
* @param description description meta-info for the created file
* @param file multipart/form-data file parameter, with optional
* 'filename' and 'size' (see example below)
* @return id of the created file
* @responseExample text/plain 5f4b3b4e-4b3b- ... -4b3b4e4b3b4e
*/
@ResourceMethodSignature(output = String.class, pathParams = { @PathParam("id") }, formParams = {
@FormParam("name"), @FormParam("description"), @FormParam("file") })
@DocumentationExample(value = "...\n\n--------boundaryString\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"doc.pdf\"; size=426018;\n" +
"Content-Type: application/pdf\n\n" +
"(data)\n" +
"--------boundaryString\n" +
"Content-Disposition: form-data; name=\"name\"\n\n" +
"doc.pdf\n" +
"--------boundaryString\n" +
"Content-Disposition: form-data; name=\"description\"\n\n" +
"\"This is just a sample PDF file\"\n" +
"--------boundaryString--")
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
@StatusCodes({
@ResponseCode ( code = 200, condition = "File created."),
@ResponseCode ( code = 400, condition = "Wrong set of parameters."),
@ResponseCode ( code = 406, condition = "Unable to create file."),
@ResponseCode ( code = 415, condition = "Wrong content type."),
})
@Path("/{id}/create/FILE")
public String createFileItem(@PathParam("id") String id, @FormDataParam("name") String name,
@FormDataParam("description") String description,
@FormDataParam("file") InputStream file,
@FormDataParam("file") FormDataContentDisposition fileDetail){
@FormDataParam("file") FormDataContentDisposition fileDetail) {
InnerMethodName.set("createItem(FILE)");
Session ses = null;
String toReturn = null;
try(InputStream is = new BufferedInputStream(file)){
try (InputStream is = new BufferedInputStream(file)) {
long size = fileDetail.getSize();
log.info("UPLOAD: call started with file size {}",size);
log.info("UPLOAD: call started with file size {}", size);
ses = repository.getRepository().login(Constants.JCR_CREDENTIALS);
ItemsParameterBuilder<FileCreationParameters> builder = FileCreationParameters.builder().name(name).description(description).stream(file).fileDetails(fileDetail)
ItemsParameterBuilder<FileCreationParameters> builder = FileCreationParameters.builder().name(name)
.description(description).stream(file).fileDetails(fileDetail)
.on(id).with(ses).author(currentUser);
log.debug("UPLOAD: item prepared");
toReturn = itemHandler.create(builder.build());
log.debug("UPLOAD: call finished");
}catch(RepositoryException re ){
} catch (RepositoryException re) {
log.error("jcr error creating file item", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error creating file item", re));
}catch(StorageHubException she ){
} catch (StorageHubException she) {
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(Throwable e ){
} catch (Throwable e) {
log.error("unexpected error", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
}finally{
if (ses!=null && ses.isLive()) {
} finally {
if (ses != null && ses.isLive()) {
log.info("session closed");
ses.logout();
}
@ -295,79 +398,123 @@ public class ItemsCreator extends Impersonable{
}
/**
* Upload an archive from the provided url and extract its content on-the-fly in a new folder.
*
* @param id destination folder id
* @param parentFolderName name of the newly-created folder containing extracted
* files
* @param url address of the archive to be uploaded
* @return id of the created folder containing extracted files
* @responseExample text/plain 5f4b3b4e-4b3b- ... -4b3b4e4b3b4e
*/
@DocumentationExample(" ...\n\nname=sampleZip.zip&description=This+is+a+sample+zip&url=\"https://getsamplefiles.com/download/zip/sample-1.zip\"")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.TEXT_PLAIN)
@StatusCodes({
@ResponseCode ( code = 200, condition = "Archive extracted."),
@ResponseCode ( code = 400, condition = "Wrong set of parameters."),
@ResponseCode ( code = 406, condition = "Unable to extract archive."),
@ResponseCode ( code = 415, condition = "Wrong content type."),
})
@Path("/{id}/create/ARCHIVE")
public String uploadArchiveFromURL(@PathParam("id") String id, @FormParam("parentFolderName") String parentFolderName,
@FormParam("url") String url){
public String uploadArchiveFromURL(@PathParam("id") String id,
@FormParam("parentFolderName") String parentFolderName,
@FormParam("url") String url) {
InnerMethodName.set("createItem(ARCHIVEFromURL)");
Session ses = null;
String toReturn = null;
try{
try {
ses = repository.getRepository().login(Constants.JCR_CREDENTIALS);
try(InputStream stream = new URI(url).toURL().openStream()){
ItemsParameterBuilder<ArchiveStructureCreationParameter> builder = ArchiveStructureCreationParameter.builder().parentName(parentFolderName).stream(stream)
try (InputStream stream = new URI(url).toURL().openStream()) {
ItemsParameterBuilder<ArchiveStructureCreationParameter> builder = ArchiveStructureCreationParameter
.builder().parentName(parentFolderName).stream(stream)
.on(id).with(ses).author(currentUser);
toReturn = itemHandler.create(builder.build());
}
}catch(RepositoryException | ArchiveException | IOException re){
} catch (RepositoryException | ArchiveException | IOException re) {
log.error("jcr error extracting archive", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error extracting archive", re));
}catch(StorageHubException she ){
} catch (StorageHubException she) {
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(Throwable e ){
} catch (Throwable e) {
log.error("unexpected error", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
} finally{
if (ses!=null)
} finally {
if (ses != null)
ses.logout();
}
return toReturn;
}
/**
* Upload the provided archive and extract its content on-the-fly in a new folder.
*
* @param id destination folder id
* @param parentFolderName name of the newly-created folder containing extracted
* files
* @param file multipart/form-data file parameter, with optional
* 'filename' and 'size' (see example below)
* @return id of the created folder containing extracted files
* @responseExample text/plain 5f4b3b4e-4b3b- ... -4b3b4e4b3b4e
*/
@ResourceMethodSignature(output = String.class, pathParams = { @PathParam("id") }, formParams = {
@FormParam("parentFolderName"), @FormParam("file") })
@DocumentationExample(value = "...\n\n--------boundaryString\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"archive.zip\"; size=1560238;\n" +
"Content-Type: application/zip\n\n" +
"(compressed data)\n" +
"--------boundaryString\n" +
"Content-Disposition: form-data; name=\"parentFolderName=\"\n\n" +
"my documents\n" +
"--------boundaryString--")
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
@StatusCodes({
@ResponseCode ( code = 200, condition = "Archive extracted."),
@ResponseCode ( code = 400, condition = "Wrong set of parameters."),
@ResponseCode ( code = 406, condition = "Unable to extract archive."),
@ResponseCode ( code = 415, condition = "Wrong content type."),
})
@Path("/{id}/create/ARCHIVE")
public String uploadArchive(@PathParam("id") String id, @FormDataParam("parentFolderName") String parentFolderName,
@FormDataParam("file") InputStream stream,
@FormDataParam("file") FormDataContentDisposition fileDetail){
@FormDataParam("file") InputStream file,
@FormDataParam("file") FormDataContentDisposition fileDetail) {
InnerMethodName.set("createItem(ARCHIVE)");
Session ses = null;
String toReturn = null;
try(InputStream is = new BufferedInputStream(stream)){
try (InputStream is = new BufferedInputStream(file)) {
ses = repository.getRepository().login(Constants.JCR_CREDENTIALS);
ItemsParameterBuilder<ArchiveStructureCreationParameter> builder = ArchiveStructureCreationParameter.builder().parentName(parentFolderName).stream(is).fileDetails(fileDetail)
ItemsParameterBuilder<ArchiveStructureCreationParameter> builder = ArchiveStructureCreationParameter
.builder().parentName(parentFolderName).stream(is).fileDetails(fileDetail)
.on(id).with(ses).author(currentUser);
toReturn = itemHandler.create(builder.build());
}catch(RepositoryException | ArchiveException | IOException re){
} catch (RepositoryException | ArchiveException | IOException re) {
log.error("jcr error extracting archive", re);
GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error extracting archive", re));
}catch(StorageHubException she ){
} catch (StorageHubException she) {
log.error(she.getErrorMessage(), she);
GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus()));
}catch(Throwable e ){
} catch (Throwable e) {
log.error("unexpected error", e);
GXOutboundErrorResponse.throwException(new BackendGenericError(e));
} finally{
if (ses!=null)
} finally {
if (ses != null)
ses.logout();
}
return toReturn;
}
}

View File

@ -63,8 +63,13 @@ import org.gcube.smartgears.utils.InnerMethodName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.DocumentationExample;
import com.webcohesion.enunciate.metadata.Ignore;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import com.webcohesion.enunciate.metadata.rs.ResourceMethodSignature;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import jakarta.inject.Inject;
import jakarta.servlet.ServletContext;
@ -82,11 +87,13 @@ import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
/**
* Manage item retrieval, deletion, and update.
*/
@Path("items")
@ManagedBy(StorageHubApplicationManager.class)
@RequestHeaders({
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
@RequestHeader( name = "Authorization", description = "Bearer token, see <a href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>"),
})
public class ItemsManager extends Impersonable{
@ -130,7 +137,22 @@ public class ItemsManager extends Impersonable{
PublicLinkHandler publicLinkHandler;
/**
* Retrieve an item by its id.
*
* @param id item id
* @param exclude a list of fields to exclude from the returned item<br>
* <strong>Multivalued</strong><br>
* <strong>Optional</strong>
* @return a JSON representation of the item
* @pathExample /{id}?exclude=content
*/
@ResourceMethodSignature(output = ItemWrapper.class, pathParams = @PathParam("id"), queryParams = @QueryParam("exclude"))
@GET
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item found."),
@ResponseCode ( code = 406, condition = "Unable to find this item."),
})
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public ItemWrapper<Item> getById(@QueryParam("exclude") List<String> excludes){
@ -159,6 +181,18 @@ public class ItemsManager extends Impersonable{
return new ItemWrapper<Item>(toReturn);
}
/**
* Retrieve an item by its relative path.
*
* @param id item id
* @param path the relative path of the item to retrieve
* @param exclude a list of fields to exclude from the returned item<br>
* <strong>Multivalued</strong><br>
* <strong>Optional</strong>
* @return a JSON representation of the item
* @pathExample /{id}/path?path=folder1&exclude=content
*/
@ResourceMethodSignature(output = ItemWrapper.class, pathParams = @PathParam("id"), queryParams = {@QueryParam("path"), @QueryParam("exclude")})
@GET
@Path("{id}/path")
@Produces(MediaType.APPLICATION_JSON)
@ -220,6 +254,7 @@ public class ItemsManager extends Impersonable{
return null;
}
@Ignore
@Deprecated
@GET
@Path("{id}/items/{name}")
@ -229,6 +264,22 @@ public class ItemsManager extends Impersonable{
return _findChildrenByNamePattern(excludes, name);
}
/**
* Retrieve a child item by its name.
*
* @param id parent item id
* @param exclude a list of fields to exclude from the returned item<br>
* <strong>Multivalued</strong><br>
* <strong>Optional</strong>
* @param name the name of the child item to retrieve
* @return a JSON representation of the item list
* @pathExample /{id}/items?name=doc.pdf&exclude=content
*/
@ResourceMethodSignature(output = String.class, pathParams = { @PathParam("id") }, queryParams = { @QueryParam("exclude"), @QueryParam("name") })
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item found."),
@ResponseCode ( code = 406, condition = "Parent item does not exist."),
})
@GET
@Path("{id}/items")
@Produces(MediaType.APPLICATION_JSON)
@ -278,9 +329,26 @@ public class ItemsManager extends Impersonable{
}
/**
* Retrieve a child item by its name.
*
* @param id parent item id
* @param showHidden if true, hidden items are shown<br>
* <strong>Possible values:</strong> <code>true</code>, <code>false</code> <br>
* <strong>Optional</strong> default: <code>false</code>
* @param onlyType only items of the specified type are counted <br>
* <strong>Optional</strong>
* @return the number of children
* @pathExample /{id}/children/count?showHidden=true&onlyType=File
*/
@ResourceMethodSignature(output = Long.class, pathParams = @PathParam("id"), queryParams = {@QueryParam("showHidden"), @QueryParam("onlyType")})
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item found."),
@ResponseCode ( code = 406, condition = "Parent item does not exist"),
})
@GET
@Path("{id}/children/count")
public Long countById(@QueryParam("showHidden") Boolean showHidden, @QueryParam("exclude") List<String> excludes, @QueryParam("onlyType") String nodeType){
public Long countById(@QueryParam("showHidden") Boolean showHidden, @QueryParam("onlyType") String nodeType){
InnerMethodName.set("countById");
Session ses = null;
Long toReturn = null;
@ -307,6 +375,26 @@ public class ItemsManager extends Impersonable{
return toReturn ;
}
/**
* Retrieve a list of children items.
*
* @param id parent item id
* @param exclude a list of fields to exclude from the returned item<br>
* <strong>Multivalued</strong><br>
* <strong>Optional</strong>
* @param showHidden if true, hidden items are shown<br>
* <strong>Possible values:</strong> <code>true</code>, <code>false</code> <br>
* <strong>Optional</strong> default: <code>false</code>
* @param onlyType only items of the specified type are counted <br>
* <strong>Optional</strong>
* @return a JSON representation of the item list
* @pathExample /{id}/children?showHidden=true&onlyType=File&exclude=content
*/
@ResourceMethodSignature(output = ItemList.class, pathParams = @PathParam("id"), queryParams = {@QueryParam("showHidden"), @QueryParam("exclude"), @QueryParam("onlyType")})
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item found."),
@ResponseCode ( code = 406, condition = "Parent item does not exist."),
})
@GET
@Path("{id}/children")
@Produces(MediaType.APPLICATION_JSON)
@ -337,6 +425,9 @@ public class ItemsManager extends Impersonable{
return new ItemList(toReturn);
}
// tipo di nodo ROOT - per ora non funziona
@Ignore
@GET
@Path("{id}/search")
@Produces(MediaType.APPLICATION_JSON)
@ -369,6 +460,32 @@ public class ItemsManager extends Impersonable{
return new ItemList(toReturn);
}
/**
* Retrieve a paged list of children items.
*
* @param id parent item id
* @param exclude a list of fields to exclude from the returned item<br>
* <strong>Multivalued</strong><br>
* <strong>Optional</strong>
* @param showHidden if true, hidden items are shown<br>
* <strong>Possible values:</strong> <code>true</code>, <code>false</code> <br>
* <strong>Optional</strong> default: <code>false</code>
* @param onlyType only items of the specified type are counted <br>
* <strong>Optional</strong>
* @param start start index, counting from 0 <br>
* <strong>Possible values:</strong> integers
* @param limit maximum number of items returned <br>
* <strong>Possible values:</strong> integers
* @return a JSON representation of the item list
* @pathExample /{id}/children/paged?start=1&limit=100&showHidden=true&onlyType=File&exclude=content
*/
@ResourceMethodSignature(output = ItemList.class, pathParams = @PathParam("id"),
queryParams = {@QueryParam("showHidden"), @QueryParam("exclude"), @QueryParam("onlyType"), @QueryParam("start"), @QueryParam("limit")})
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item found."),
@ResponseCode ( code = 406, condition = "Parent item does not exist."),
@ResponseCode ( code = 500, condition = "Missing 'start' or 'limit' parameter."),
})
@GET
@Path("{id}/children/paged")
@Produces(MediaType.APPLICATION_JSON)
@ -399,6 +516,7 @@ public class ItemsManager extends Impersonable{
return new ItemList(toReturn);
}
@Ignore
@GET
@Path("publiclink/{id}")
@AuthorizationControl(allowedUsers={"URIResolver"})
@ -434,6 +552,21 @@ public class ItemsManager extends Impersonable{
return Response.serverError().build();
}
/**
* Retrieves the public link for a specified item.
*
* @param id item id
* @param version version of the item for which the public link is requested <br>
* <strong>Optional</strong> default: <em>last version</em>
* @return URL of the public link for the specified item
* @pathExample /{id}/publiclink?version=1.0
*/
@ResourceMethodSignature(output = URL.class, pathParams = @PathParam("id"), queryParams = @QueryParam("version"))
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item found."),
@ResponseCode ( code = 400, condition = "This item is not a file."),
@ResponseCode ( code = 406, condition = "Item does not exist."),
})
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("{id}/publiclink")
@ -497,6 +630,21 @@ public class ItemsManager extends Impersonable{
/**
* Set the access level of the specified folder.
*
* @param id item id
* @param publish if true, the folder is made public <br>
* <strong>Possible values:</strong> <code>true</code>, <code>false</code>
* @return the id of the folder
*/
@ResourceMethodSignature(output = String.class, pathParams = @PathParam("id"), formParams = @FormParam("publish"))
@DocumentationExample("...\n\npublish=true")
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item found."),
@ResponseCode ( code = 400, condition = "This item is not a file."),
@ResponseCode ( code = 406, condition = "Item does not exist."),
})
@PUT
@Path("{id}/publish")
@Produces(MediaType.APPLICATION_JSON)
@ -533,6 +681,21 @@ public class ItemsManager extends Impersonable{
}
/**
* Retrieves the root shared folder for the specified item.
*
* @param id item id
* @param exclude a list of fields to exclude from the returned item<br>
* <strong>Multivalued</strong><br>
* <strong>Optional</strong>
* @return the id of the root folder
*/
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item found."),
@ResponseCode ( code = 400, condition = "This item is not a shared."),
@ResponseCode ( code = 406, condition = "Item does not exist."),
})
@ResourceMethodSignature(output = ItemWrapper.class, pathParams = @PathParam("id"), queryParams = @QueryParam("exclude"))
@GET
@Path("{id}/rootSharedFolder")
@Produces(MediaType.APPLICATION_JSON)
@ -576,6 +739,17 @@ public class ItemsManager extends Impersonable{
return currentNode;
}
/**
* Retrieves the list of versions for the specified item.
*
* @param id item id
* @return the version list
*/
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item found."),
@ResponseCode ( code = 406, condition = "Item does not exist."),
})
@ResourceMethodSignature(output = VersionList.class, pathParams = @PathParam("id"))
@GET
@Path("{id}/versions")
@Produces(MediaType.APPLICATION_JSON)
@ -613,6 +787,20 @@ public class ItemsManager extends Impersonable{
return new VersionList(versions);
}
/**
* Download a specific version of a given item.
*
* @param id item id
* @param version the requested version
* @return download the item
* @pathExample /{id}/versions/1.0/download
*/
@ResourceMethodSignature(output = String.class, pathParams = { @PathParam("id") })
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item found."),
@ResponseCode ( code = 406, condition = "Item does not exist."),
@ResponseCode ( code = 500, condition = "Invalid version."),
})
@GET
@Path("{id}/versions/{version}/download")
public Response downloadVersion(@PathParam("version") String versionName){
@ -641,6 +829,21 @@ public class ItemsManager extends Impersonable{
return Response.serverError().build();
}
/**
* Delete a specific version of a given item.
*
* @param id item id
* @param version the requested version
* @return download the item
* @pathExample /{id}/versions/1.0
*/
@ResourceMethodSignature(output = void.class, pathParams = { @PathParam("id"), @PathParam("version") })
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item deleted."),
@ResponseCode ( code = 400, condition = "This item's version cannot be removed."),
@ResponseCode ( code = 406, condition = "Item does not exist."),
@ResponseCode ( code = 500, condition = "Invalid version."),
})
@DELETE
@Path("{id}/versions/{version}")
public void deleteVersion(@PathParam("version") String versionName){
@ -684,6 +887,21 @@ public class ItemsManager extends Impersonable{
}
/**
* Retrieve the anchestors of a given item.
*
* @param id item id
* @param exclude a list of fields to exclude from the returned item list<br>
* <strong>Multivalued</strong> <br>
* <strong>Optional</strong>
* @return the list of anchestors
* @pathExample /{id}/anchestors?exclude=content
*/
@ResourceMethodSignature(output = ItemList.class, pathParams = { @PathParam("id")}, queryParams = { @QueryParam("exclude")})
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item found."),
@ResponseCode ( code = 406, condition = "Item does not exist."),
})
@GET
@Path("{id}/anchestors")
@Produces(MediaType.APPLICATION_JSON)
@ -741,9 +959,21 @@ public class ItemsManager extends Impersonable{
/**
* Download a given item.
*
* @param id item id
* @return download the item
* @pathExample /{id}/download
*/
@ResourceMethodSignature(output = Response.class, pathParams = { @PathParam("id")})
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item found."),
@ResponseCode ( code = 406, condition = "Item does not exist."),
})
@GET
@Path("{id}/download")
public Response download(@QueryParam("exclude") List<String> excludes){
public Response download(){
InnerMethodName.set("downloadById");
Session ses = null;
try{
@ -770,6 +1000,22 @@ public class ItemsManager extends Impersonable{
}
/**
* Move a given item.
*
* @param id item id
* @param destinationId destination folder id
* @return id of the moved item
* @pathExample /{id}/move
* @responseExample text/plain 5f4b3b4e-4b3b- ... -4b3b4e4b3b4e
*/
@ResourceMethodSignature(output = String.class, pathParams = { @PathParam("id") }, formParams = { @FormParam("destinationId")})
@DocumentationExample("...\n\ndestinationId=19863b4e-b33f- ... -5b6d2e0e1eee")
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item moved."),
@ResponseCode ( code = 406, condition = "Source or destination item does not exist."),
@ResponseCode ( code = 409, condition = "Source and destination are the same item."),
})
@PUT
@Path("{id}/move")
public String move(@FormParam("destinationId") String destinationId){
@ -848,6 +1094,23 @@ public class ItemsManager extends Impersonable{
return id;
}
/**
* Copy a given item.
*
* @param id item id
* @param destinationId destination folder id
* @param fileName name of the copied item
* @return id of the copied item
* @pathExample /{id}/copy
* @responseExample text/plain 5f4b3b4e-4b3b- ... -4b3b4e4b3b4e
*/
@ResourceMethodSignature(output = String.class, pathParams = @PathParam("id"),
formParams = { @FormParam("destinationId"), @FormParam("fileName")})
@DocumentationExample("...\n\ndestinationId=19863b4e-b33f- ... -5b6d2e0e1eee&fileName=myCopy.jpg")
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item copied."),
@ResponseCode ( code = 406, condition = "Source or destination item does not exist."),
})
@PUT
@Path("{id}/copy")
public String copy(@FormParam("destinationId") String destinationId, @FormParam("fileName") String newFileName){
@ -923,6 +1186,21 @@ public class ItemsManager extends Impersonable{
return newFileIdentifier;
}
/**
* Rename a given item.
*
* @param id item id
* @param newName new name for the item
* @return id of the renamed item
* @pathExample /{id}/rename
* @responseExample text/plain 5f4b3b4e-4b3b- ... -4b3b4e4b3b4e
*/
@ResourceMethodSignature(output = String.class, pathParams = @PathParam("id"), formParams = @FormParam("newName"))
@DocumentationExample("...\n\nnewName=newFileName.txt")
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item renamed."),
@ResponseCode ( code = 406, condition = "Item does not exist."),
})
@PUT
@Path("{id}/rename")
public Response rename(@FormParam("newName") String newName){
@ -979,9 +1257,25 @@ public class ItemsManager extends Impersonable{
return Response.ok(id).build();
}
//TODO: transform this and setMetadata in a generic method for all properties
/**
* Set the item as hidden or visible. The body request is interpreted as the boolean
* value for the visibility.
*
* @param id item id
* @return id of the item
* @pathExample /{id}/hidden
* @responseExample text/plain 5f4b3b4e-4b3b- ... -4b3b4e4b3b4e
*/
@ResourceMethodSignature(output = String.class, pathParams = { @PathParam("id") })
@DocumentationExample("...\n\ntrue")
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item visibility set."),
@ResponseCode ( code = 400, condition = "Unable to parse request body."),
@ResponseCode ( code = 406, condition = "Item does not exist."),
})
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.TEXT_PLAIN)
@Path("/{id}/hidden")
public Response setItemAsHidden(Boolean hidden){
InnerMethodName.set("setHidden");
@ -1024,9 +1318,24 @@ public class ItemsManager extends Impersonable{
return Response.ok(id).build();
}
//TODO: transform this and setMetadata in a generic method for all properties
/**
* Set description for the specified item. The body request is interpreted as the description string.
*
* @param id item id
* @return id of the item
* @pathExample /{id}/description
* @responseExample text/plain 5f4b3b4e-4b3b- ... -4b3b4e4b3b4e
*/
@ResourceMethodSignature(output = String.class, pathParams = { @PathParam("id") })
@DocumentationExample("...\n\nThis is a sample description")
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item description set."),
@ResponseCode ( code = 400, condition = "Unable to parse request body."),
@ResponseCode ( code = 406, condition = "Item does not exist."),
})
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.TEXT_PLAIN)
@Path("/{id}/description")
public Response setDescription(String description){
InnerMethodName.set("setDescription");
@ -1069,8 +1378,29 @@ public class ItemsManager extends Impersonable{
return Response.ok(id).build();
}
/**
* Set metadata for the specified item. The body request is interpreted as the metadata JSON.
*
* @param id item id
* @return id of the item
* @pathExample /{id}/metadata
* @responseExample text/plain 5f4b3b4e-4b3b- ... -4b3b4e4b3b4e
*/
@ResourceMethodSignature(output = String.class, pathParams = { @PathParam("id") })
@DocumentationExample("...\n\n{\n" +
" \"map\": {\n" +
" \"meta1\": \"value1\",\n" +
" \"meta2\": \"value2\"\n" +
" }\n" +
"}")
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item metadata set."),
@ResponseCode ( code = 400, condition = "Unable to parse request body."),
@ResponseCode ( code = 406, condition = "Item does not exist."),
})
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.TEXT_PLAIN)
@Path("/{id}/metadata")
public Response setMetadata(org.gcube.common.storagehub.model.Metadata metadata){
InnerMethodName.set("updateMetadata");
@ -1115,7 +1445,22 @@ public class ItemsManager extends Impersonable{
/**
* Move the specified item to the trash or delete it permanently if it is already trashed.
*
* @param id item id
* @param force permanently delete <br>
* <strong>Possible values:</strong> <code>true</code>, <code>false</code> <br>
* <strong>Optional</strong> default: <code>false</code>
* @pathExample /{id}?force=true
*/
@ResourceMethodSignature (output = String.class, pathParams = { @PathParam("id") }, queryParams = { @QueryParam("force") })
@DELETE
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item deleted."),
@ResponseCode ( code = 406, condition = "Unable to find this item."),
@ResponseCode ( code = 500, condition = "Unable to delete this item."),
})
@Path("{id}")
public Response deleteItem(@QueryParam("force") boolean force){
InnerMethodName.set("deleteItem("+force+")");
@ -1163,6 +1508,18 @@ public class ItemsManager extends Impersonable{
return Response.ok().build();
}
/**
* Get info of the specified folder.
*
* @param id item id
* @return folder info
*/
@ResourceMethodSignature(output = String.class, pathParams = { @PathParam("id") })
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item found."),
@ResponseCode ( code = 400, condition = "This item is not a folder."),
@ResponseCode ( code = 406, condition = "Unable to find this folder."),
})
@Path("{id}/info")
@GET
@Produces(MediaType.APPLICATION_JSON)

View File

@ -52,6 +52,7 @@ import org.gcube.smartgears.utils.InnerMethodName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.Ignore;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
@ -71,10 +72,11 @@ import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
@Ignore
@Path("messages")
@ManagedBy(StorageHubApplicationManager.class)
@RequestHeaders({
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
@RequestHeader( name = "Authorization", description = "Bearer token, see <a href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>"),
})
public class MessageManager extends Impersonable{

View File

@ -11,6 +11,8 @@ import org.gcube.smartgears.utils.InnerMethodName;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
@ -18,17 +20,28 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
/**
* Manage underlying storage backends.
*/
@Path("storages")
@ManagedBy(StorageHubApplicationManager.class)
@RequestHeaders({
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
@RequestHeader( name = "Authorization", description = "Bearer token, see <a href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>"),
})
public class StorageManager {
@Inject
StorageBackendHandler storageBackendHandler;
/**
* Get a list of available storages
*
* @return list of available storages
*/
@GET
@StatusCodes({
@ResponseCode ( code = 200, condition = "Success."),
})
@Path("/")
@Produces(MediaType.APPLICATION_JSON)
public List<StorageDescriptor> getStorages(){

View File

@ -22,8 +22,11 @@ import org.gcube.smartgears.utils.InnerMethodName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.DocumentationExample;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import jakarta.inject.Inject;
import jakarta.ws.rs.Consumes;
@ -38,10 +41,13 @@ import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
/**
* Manage users
*/
@Path("users")
@ManagedBy(StorageHubApplicationManager.class)
@RequestHeaders({
@RequestHeader(name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"), })
@RequestHeader(name = "Authorization", description = "Bearer token, see <a href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>"), })
public class UserManager {
private static final String INFRASTRUCTURE_MANAGER_ROLE = "Infrastructure-Manager";
@ -53,7 +59,15 @@ public class UserManager {
@Inject
UserManagerDelegate userHandler;
/**
* Get a list of users
*
* @return list of users
*/
@GET
@StatusCodes({
@ResponseCode ( code = 200, condition = "Success."),
})
@Path("")
@Produces(MediaType.APPLICATION_JSON)
public UsersList getUsers() {
@ -72,7 +86,17 @@ public class UserManager {
return null;
}
/**
* Get user details
*
* @param user user name
* @return user detail
*/
@GET
@StatusCodes({
@ResponseCode ( code = 200, condition = "User found."),
@ResponseCode ( code = 406, condition = "User does not exist."),
})
@Path("{user}")
@Produces(MediaType.APPLICATION_JSON)
public SHUBUser getUser(@PathParam("user") String user) {
@ -100,9 +124,26 @@ public class UserManager {
return null;
}
/**
* Create a new user <br>
* <strong>Only users with <code>Infrastructure-Manager</code> role allowed</strong>
*
* @param user user name
* @param password user password
* @return user name
* @responseExample name.surname
*/
@DocumentationExample(" ...\n\nuser=nome.utente&password=passw0rd")
@POST
@StatusCodes({
@ResponseCode ( code = 200, condition = "User created."),
@ResponseCode ( code = 400, condition = "Wrong set of parameters."),
@ResponseCode ( code = 403, condition = "You're not allowed to create users."),
@ResponseCode ( code = 415, condition = "Wrong content type."),
})
@Path("")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.TEXT_PLAIN)
@AuthorizationControl(allowedRoles = { INFRASTRUCTURE_MANAGER_ROLE })
public String createUser(@FormParam("user") String user, @FormParam("password") String password) {
@ -130,9 +171,21 @@ public class UserManager {
return userId;
}
/**
* Update user to the last 'home' version <br>
* <strong>Only users with <code>Infrastructure-Manager</code> role allowed</strong>
*
* @param user user name
* @return user id
*/
@PUT
@Path("{user}")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@StatusCodes({
@ResponseCode ( code = 200, condition = "Home update done."),
@ResponseCode ( code = 403, condition = "You're not allowed to create users."),
@ResponseCode ( code = 415, condition = "Wrong content type."),
})
@AuthorizationControl(allowedRoles = { INFRASTRUCTURE_MANAGER_ROLE })
public String updateHomeUserToLatestVersion(@PathParam("user") String user) {
@ -160,8 +213,21 @@ public class UserManager {
return userId;
}
/**
* Delete a user <br>
* <strong>Only users with <code>Infrastructure-Manager</code> role allowed</strong>
*
* @param user user name
* @return user name
*/
@DELETE
@StatusCodes({
@ResponseCode ( code = 200, condition = "User deleted."),
@ResponseCode ( code = 403, condition = "You're not allowed to delete users."),
@ResponseCode ( code = 406, condition = "User does not exist."),
})
@Path("{user}")
@Produces(MediaType.TEXT_PLAIN)
@AuthorizationControl(allowedRoles = { INFRASTRUCTURE_MANAGER_ROLE })
public String deleteUser(@PathParam("user") final String user) {
@ -189,7 +255,17 @@ public class UserManager {
return user;
}
/**
* Get a list of groups for the specified user
*
* @param user user name
* @return List of groups
*/
@GET
@StatusCodes({
@ResponseCode ( code = 200, condition = "User found."),
@ResponseCode ( code = 500, condition = "User does not exist."),
})
@Path("{user}/groups")
@Produces(MediaType.APPLICATION_JSON)
public List<String> getGroupsPerUser(@PathParam("user") final String user) {

View File

@ -56,8 +56,13 @@ import org.glassfish.jersey.media.multipart.FormDataParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.DocumentationExample;
import com.webcohesion.enunciate.metadata.Ignore;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import com.webcohesion.enunciate.metadata.rs.ResourceMethodSignature;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
@ -75,10 +80,13 @@ import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
/**
* Manage the workspace
*/
@Path("/")
@ManagedBy(StorageHubApplicationManager.class)
@RequestHeaders({
@RequestHeader(name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"), })
@RequestHeader(name = "Authorization", description = "Bearer token, see <a href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>"), })
public class WorkspaceManager extends Impersonable {
private static final Logger log = LoggerFactory.getLogger(WorkspaceManager.class);
@ -121,6 +129,21 @@ public class WorkspaceManager extends Impersonable {
@Inject
Item2NodeConverter item2Node;
/**
* Get info of an item referred through its path
*
* @param relPath relative path
* @param exclude a list of fields to exclude from the returned item<br>
* <strong>Multivalued</strong><br>
* <strong>Optional</strong>
* @return workspace item
* @pathExample /?relPath=folder1/folder2/file.txt&exclude=owner
*/
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item retrieved."),
@ResponseCode ( code = 406, condition = "Item not found."),
})
@ResourceMethodSignature(output = ItemWrapper.class, queryParams = { @QueryParam("relPath"), @QueryParam("exclude") })
@Path("/")
@GET
@Produces(MediaType.APPLICATION_JSON)
@ -164,19 +187,33 @@ public class WorkspaceManager extends Impersonable {
}
/**
* Uploads a file in the volatile area returning a public link
* Upload a file in the volatile area
*
* @param id
* @param name
* @param description
* @param stream
* @param fileDetail
* @return
* @param file multipart/form-data file parameter, with optional
* 'filename' and 'size' (see example below)
* @param exclude a list of fields to exclude from the returned item<br>
* <strong>Multivalued</strong><br>
* <strong>Optional</strong>
* @return public link to the created file
* @responseExample https://data.dev.d4science.org/shub/VLT_V1...9PQ==
*/
@ResourceMethodSignature(output = String.class, formParams = { @FormParam("file") }, queryParams = { @QueryParam("exclude") })
@DocumentationExample(value = "...\n\n--------boundaryString\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"doc.pdf\"; size=426018;\n" +
"Content-Type: application/pdf\n\n" +
"(data)\n" +
"--------boundaryString--")
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
@StatusCodes({
@ResponseCode ( code = 200, condition = "File created."),
@ResponseCode ( code = 400, condition = "File not provided."),
@ResponseCode ( code = 415, condition = "Wrong content type."),
@ResponseCode ( code = 500, condition = "Wrong set of parameters."),
})
@Path("volatile")
public String uploadVolatileFile(@FormDataParam("file") InputStream stream,
public String uploadVolatileFile(@FormDataParam("file") InputStream file,
@FormDataParam("file") FormDataContentDisposition fileDetail) {
InnerMethodName.set("uploadToVolatileArea");
@ -193,7 +230,7 @@ public class WorkspaceManager extends Impersonable {
StorageBackend sb = sbf.create(payloadBackend);
log.info("UPLOAD: call started with file size {}", size);
MetaInfo info = sb.upload(stream, null, fileDetail.getFileName(), currentUser);
MetaInfo info = sb.upload(file, null, fileDetail.getFileName(), currentUser);
log.debug("UPLOAD: call finished");
toReturn = publicLinkHandler.getForVolatile(info.getStorageId(), GCubeVolatileStorageBackendFactory.NAME,
@ -208,11 +245,23 @@ public class WorkspaceManager extends Impersonable {
return toReturn;
}
/**
* Get info on the VRE folder associated to the current token
*
* @param exclude a list of fields to exclude from the returned item<br>
* <strong>Multivalued</strong><br>
* <strong>Optional</strong>
* @return VRE folder info
*/
@ResourceMethodSignature(output = ItemWrapper.class, queryParams = { @QueryParam("exclude") })
@Path("vrefolder")
@GET
@StatusCodes({
@ResponseCode ( code = 200, condition = "Success."),
})
@Produces(MediaType.APPLICATION_JSON)
public ItemWrapper<Item> getVreRootFolder() {
InnerMethodName.set("getVreRootFolder");
public ItemWrapper<Item> getCurrentVreFolder() {
InnerMethodName.set("getCurrentVreFolder");
JackrabbitSession ses = null;
Item vreItem = null;
try {
@ -239,6 +288,18 @@ public class WorkspaceManager extends Impersonable {
return new ItemWrapper<Item>(vreItem);
}
/**
* Get a list of recent documents from VRE folders
*
* @param exclude a list of fields to exclude from the returned item<br>
* <strong>Multivalued</strong><br>
* <strong>Optional</strong>
* @return list of recent documents
*/
@ResourceMethodSignature(output = ItemList.class, queryParams = { @QueryParam("exclude") })
@StatusCodes({
@ResponseCode ( code = 200, condition = "Success."),
})
@Path("vrefolder/recents")
@GET
@Produces(MediaType.APPLICATION_JSON)
@ -277,6 +338,18 @@ public class WorkspaceManager extends Impersonable {
}
/**
* Get info on the trash folder
*
* @param exclude a list of fields to exclude from the returned item<br>
* <strong>Multivalued</strong><br>
* <strong>Optional</strong>
* @return info on the trash folder
*/
@ResourceMethodSignature(output = ItemWrapper.class, queryParams = { @QueryParam("exclude") })
@StatusCodes({
@ResponseCode ( code = 200, condition = "Success."),
})
@Path("trash")
@GET
@Produces(MediaType.APPLICATION_JSON)
@ -307,7 +380,21 @@ public class WorkspaceManager extends Impersonable {
return new ItemWrapper<Item>(item);
}
/**
* Empty the trash folder
*
* @param exclude a list of fields to exclude from the returned item<br>
* <strong>Multivalued</strong><br>
* <strong>Optional</strong>
* @return trash folder id
* @responseExample text/plain 5f4b3b4e-4b3b- ... -4b3b4e4b3b4e
*/
@ResourceMethodSignature(output = String.class, queryParams = { @QueryParam("exclude") })
@StatusCodes({
@ResponseCode ( code = 200, condition = "Trash emptied."),
})
@Path("trash/empty")
@Produces(MediaType.TEXT_PLAIN)
@DELETE
public String emptyTrash() {
InnerMethodName.set("emptyTrash");
@ -334,6 +421,25 @@ public class WorkspaceManager extends Impersonable {
return toReturn;
}
/**
* Restore a trashed item.
*
* @param trashedItemId item id
* @param destinationId destination folder id
* @param exclude a list of fields to exclude from the returned item<br>
* <strong>Multivalued</strong><br>
* <strong>Optional</strong>
* @return id of the restored item
* @responseExample text/plain 5f4b3b4e-4b3b- ... -4b3b4e4b3b4e
*/
@ResourceMethodSignature(output = String.class, queryParams = { @QueryParam("exclude")}, formParams = { @FormParam("trashedItemId"), @FormParam("destinationId") })
@DocumentationExample("...\n\ntrashedId=17dae181-f33c- ... - 3fa22198dd30&destinationId=19863b4e-b33f- ... -5b6d2e0e1eee")
@StatusCodes({
@ResponseCode ( code = 200, condition = "Item restored."),
@ResponseCode ( code = 406, condition = "Source or destination item does not exist."),
@ResponseCode ( code = 409, condition = "Source and destination are the same item."),
@ResponseCode ( code = 415, condition = "Wrong content type."),
})
@PUT
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("trash/restore")
@ -383,6 +489,15 @@ public class WorkspaceManager extends Impersonable {
return toReturn;
}
/**
* Get a list of VRE folders for which the user is member
*
* @param exclude a list of fields to exclude from the returned item<br>
* <strong>Multivalued</strong><br>
* <strong>Optional</strong>
* @return list of VRE folders
*/
@ResourceMethodSignature(output = ItemList.class, queryParams = { @QueryParam("exclude") })
@Path("vrefolders")
@GET
@Produces(MediaType.APPLICATION_JSON)
@ -411,6 +526,20 @@ public class WorkspaceManager extends Impersonable {
return new ItemList(toReturn);
}
/**
* Get a paged list of VRE folders for which the user is member
*
* @param start start index, counting from 0 <br>
* <strong>Possible values:</strong> integers
* @param limit maximum number of items returned <br>
* <strong>Possible values:</strong> integers
* @param exclude a list of fields to exclude from the returned item<br>
* <strong>Multivalued</strong><br>
* <strong>Optional</strong>
* @return list of VRE folders
* @pathExample /vrefolders/paged?start=0&limit=10
*/
@ResourceMethodSignature(output = ItemList.class, queryParams = { @QueryParam("start"), @QueryParam("limit"), @QueryParam("exclude") })
@Path("vrefolders/paged")
@GET
@Produces(MediaType.APPLICATION_JSON)
@ -437,30 +566,13 @@ public class WorkspaceManager extends Impersonable {
return new ItemList(toReturn);
}
/*
* @Path("shared-by-me")
*
* @GET
*
* @Produces(MediaType.APPLICATION_JSON) public ItemList getMySharedFolders(){
* InnerMethodName.set("getMySharedFolders"); Session ses = null; List<? extends
* Item> toReturn = null; org.gcube.common.storagehub.model.Path sharedPath =
* null; try{ ses = repository.getRepository().login(Constants.JCR_CREDENTIALS);
* sharedPath = pathUtil.getMySharedPath(currentUser);
* log.info("my shared folder path is folder path is {}",sharedPath.toPath());
*
* toReturn = Utils.getItemList(ses.getNode(sharedPath.toPath()) , excludes,
* null, false, SharedFolder.class); }catch(RepositoryException re ){
* log.error("error reading my shared folder ({})",sharedPath, re);
* GXOutboundErrorResponse.throwException(new BackendGenericError(re));
* }catch(StorageHubException she ){ log.error(she.getErrorMessage(), she);
* GXOutboundErrorResponse.throwException(she,
* Response.Status.fromStatusCode(she.getStatus())); }finally{ if (ses!=null)
* ses.logout(); }
*
* return new ItemList(toReturn); }
*/
// TODO: boh! Sviluppo a metà. Valutare se finirlo o ammazzarlo male
@Ignore
@StatusCodes({
@ResponseCode ( code = 200, condition = "List retrieved."),
@ResponseCode ( code = 406, condition = "Error retrieving shared-with-me folder."),
})
@Path("shared-with-me")
@GET
@Produces(MediaType.APPLICATION_JSON)
@ -489,6 +601,7 @@ public class WorkspaceManager extends Impersonable {
return new ItemList(toReturn);
}
@Ignore
@Path("count")
@GET
@Deprecated
@ -513,6 +626,7 @@ public class WorkspaceManager extends Impersonable {
return "0";
}
@Ignore
@Path("size")
@GET
@Deprecated

View File

@ -41,12 +41,17 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.webcohesion.enunciate.metadata.DocumentationExample;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import com.webcohesion.enunciate.metadata.rs.ResourceMethodSignature;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import jakarta.inject.Inject;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
@ -55,9 +60,12 @@ import jakarta.ws.rs.Produces;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
/**
* Manage "script" classes
*/
@Path("admin/script")
@RequestHeaders({
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
@RequestHeader( name = "Authorization", description = "Bearer token, see <a href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>"),
})
public class ScriptManager {
@ -84,21 +92,62 @@ public class ScriptManager {
protected static HashMap<String, ScriptStatus> scriptStatusMap = new HashMap<String, ScriptStatus>();
/**
* Execute a "script" class <br>
* <strong>Only users with <code>Infrastructure-Manager</code> role allowed</strong>
*
* @param name name of the script
* @param asynch if true, execute script asynchronously <br>
* <strong>Possible values:</strong> <code>true</code>, <code>false</code> <br>
* <strong>Optional</strong> default: <code>false</code>
* @param writeResult if true, write the result in the workspace <br>
* <strong>Possible values:</strong> <code>true</code>, <code>false</code> <br>
* <strong>Optional</strong> default: <code>false</code>
* @param destinationFolderId id of the destination folder
* @param file multipart/form-data file parameter, with optional
* 'filename' and 'size' (see example below)
* @return outcome of the script execution
*/
@ResourceMethodSignature(output = ScriptStatus.class, formParams = { @FormParam("name"),
@FormParam("asynch"), @FormParam("writeResult"), @FormParam("destinationFolderId"),
@FormParam("file") })
@DocumentationExample(value = "...\n\n--------boundaryString\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"MoveFiles.class\"; size=171018;\n" +
"Content-Type: application/octet-stream\n\n" +
"(binary data)\n" +
"--------boundaryString\n" +
"Content-Disposition: form-data; name=\"name\"\n\n" +
"MoveFile\n" +
"--------boundaryString\n" +
"Content-Disposition: form-data; name=\"async\"\n\n" +
"false\n" +
"--------boundaryString\n" +
"Content-Disposition: form-data; name=\"writeResult\"\n\n" +
"true\n" +
"--------boundaryString\n" +
"Content-Disposition: form-data; name=\"destinationFolderId=\"\n\n" +
"5f4b3b4e-4b3b- ... -4b3b4e4b3b4e\n" +
"--------boundaryString--")
@POST
@Path("execute")
@AuthorizationControl(allowedRoles = {INFRASTRUCTURE_MANAGER_ROLE})
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes({
@ResponseCode ( code = 200, condition = "Script correctly loaded."),
@ResponseCode ( code = 403, condition = "You're not allowed to run scripts."),
@ResponseCode ( code = 500, condition = "Error loading the script."),
})
public ScriptStatus run( @FormDataParam("name") String name,
@FormDataParam("asynch") @DefaultValue("false") Boolean asynch,
@FormDataParam("writeResult") @DefaultValue("false") Boolean writeResult ,
@FormDataParam("destinationFolderId") String destinationFolderId,
@FormDataParam("file") InputStream stream,
@FormDataParam("file") InputStream file,
@FormDataParam("file") FormDataContentDisposition fileDetail) {
try {
InnerMethodName.set("executeScript");
ScriptClassLoader scriptClassLoader = new ScriptClassLoader(Thread.currentThread().getContextClassLoader());
Class<?> scriptClass = uploadClass(stream, scriptClassLoader, fileDetail.getFileName().replace(".class", ""));
Class<?> scriptClass = uploadClass(file, scriptClassLoader, fileDetail.getFileName().replace(".class", ""));
return internalRun(scriptClass, name, destinationFolderId, asynch, writeResult);
}catch(Throwable e) {
log.error("error executing script {}", name,e);
@ -106,6 +155,19 @@ public class ScriptManager {
}
}
/**
* Get the status of a script <br>
* <strong>Only users with <code>Infrastructure-Manager</code> role allowed</strong>
*
* @param id id of the script
* @return outcome of the script execution
*/
@ResourceMethodSignature(output = ScriptStatus.class, pathParams = { @PathParam("id") })
@StatusCodes({
@ResponseCode ( code = 200, condition = "Script status retrieved."),
@ResponseCode ( code = 403, condition = "You're not allowed to inquire script status."),
@ResponseCode ( code = 404, condition = "Script not found."),
})
@GET
@Path("{id}/status")
@AuthorizationControl(allowedRoles = {INFRASTRUCTURE_MANAGER_ROLE})
@ -123,6 +185,17 @@ public class ScriptManager {
}
/**
* Export the data <br>
* <strong>Only users with <code>Infrastructure-Manager</code> role allowed</strong>
*
* @return outcome of the export
*/
@StatusCodes({
@ResponseCode ( code = 200, condition = "Script status retrieved."),
@ResponseCode ( code = 403, condition = "You're not allowed to inquire script status."),
@ResponseCode ( code = 404, condition = "Script not found."),
})
@GET
@Path("export")
@AuthorizationControl(allowedRoles = {INFRASTRUCTURE_MANAGER_ROLE})