diff --git a/src/main/java/org/gcube/data/access/storagehub/services/ACLManager.java b/src/main/java/org/gcube/data/access/storagehub/services/ACLManager.java
index 481b97e..8b769f7 100644
--- a/src/main/java/org/gcube/data/access/storagehub/services/ACLManager.java
+++ b/src/main/java/org/gcube/data/access/storagehub/services/ACLManager.java
@@ -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,6 +58,9 @@ 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({
@@ -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)
@@ -121,16 +133,27 @@ 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
+ * Possible values: READ_ONLY
, WRITE_OWNER
, WRITE_ALL
, ADMINISTRATOR
*/
+ @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
- *
- *
- * @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
+ * Remove a user from the shared folder
+ *
+ * @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");
diff --git a/src/main/java/org/gcube/data/access/storagehub/services/DocsGenerator.java b/src/main/java/org/gcube/data/access/storagehub/services/DocsGenerator.java
index 406e114..b7f84bd 100644
--- a/src/main/java/org/gcube/data/access/storagehub/services/DocsGenerator.java
+++ b/src/main/java/org/gcube/data/access/storagehub/services/DocsGenerator.java
@@ -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 {
diff --git a/src/main/java/org/gcube/data/access/storagehub/services/GroupManager.java b/src/main/java/org/gcube/data/access/storagehub/services/GroupManager.java
index f95d392..3833e40 100644
--- a/src/main/java/org/gcube/data/access/storagehub/services/GroupManager.java
+++ b/src/main/java/org/gcube/data/access/storagehub/services/GroupManager.java
@@ -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,6 +50,9 @@ 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)
@@ -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,43 @@ public class GroupManager {
return groups;
}
+ /**
+ * Create a new group
+ *
+ * @param group group name
+ * @param accessType access type
Possible values: READ_ONLY
, WRITE_OWNER
, WRITE_ALL
, ADMINISTRATOR
+ * @param folderOwner folder owner
+ * @param useDefaultStorage use default storage if true
+ * Possible values: true
, false
+ * Optional default: true
+ * @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 +162,21 @@ public class GroupManager {
return group;
}
+ /**
+ * Delete a group
+ *
+ * @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 +201,18 @@ public class GroupManager {
public boolean isVREManager() { return SecretManagerProvider.get().getOwner().getRoles().contains(VREMANAGER_ROLE); }
+ /**
+ * Add an administrator to a group
+ *
+ * @param id group id
+ * @param userId user id
+ */
+ @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,6 +250,18 @@ public class GroupManager {
}
+ /**
+ * Remove an administrator from a group
+ *
+ * @param id group id
+ * @param userId user id
+ */
+ @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)
@@ -210,6 +297,16 @@ public class GroupManager {
}
}
+ /**
+ * Get the list of administrators of a group
+ *
+ * @param groupId group id
+ * @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 +333,22 @@ public class GroupManager {
}
+ /**
+ * Add a user to a group
+ *
+ * @param id group id
+ * @param userId user id
+ * @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 +384,18 @@ public class GroupManager {
+ /**
+ * Remove a user from a group
+ *
+ * @param groupId group id
+ * @param userId user id
+ * @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 +429,16 @@ public class GroupManager {
return success;
}
+ /**
+ * Get the list of users of a group
+ *
+ * @param groupId group id
+ * @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)
diff --git a/src/main/java/org/gcube/data/access/storagehub/services/ItemSharing.java b/src/main/java/org/gcube/data/access/storagehub/services/ItemSharing.java
index bcf5399..2e3348c 100644
--- a/src/main/java/org/gcube/data/access/storagehub/services/ItemSharing.java
+++ b/src/main/java/org/gcube/data/access/storagehub/services/ItemSharing.java
@@ -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,7 +63,9 @@ 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"),
@@ -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
+ * Possible values: READ_ONLY
, WRITE_OWNER
, WRITE_ALL
, ADMINISTRATOR
+ * @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 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 users){
InnerMethodName.set("unshareFolder");
Session ses = null;
diff --git a/src/main/java/org/gcube/data/access/storagehub/services/ItemsCreator.java b/src/main/java/org/gcube/data/access/storagehub/services/ItemsCreator.java
index f86fe8e..6fb16be 100644
--- a/src/main/java/org/gcube/data/access/storagehub/services/ItemsCreator.java
+++ b/src/main/java/org/gcube/data/access/storagehub/services/ItemsCreator.java
@@ -60,6 +60,9 @@ 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({
@@ -83,10 +86,14 @@ public class ItemsCreator extends Impersonable {
* @param id destination parent folder id
* @param name destination folder name
* @param description description meta-info for the created folder
- * @param hidden Optional: default false hidden folder if true
+ * @param hidden hidden folder if true
+ * Possible values: true
, false
+ * Optional default: false
* @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)
diff --git a/src/main/java/org/gcube/data/access/storagehub/services/ItemsManager.java b/src/main/java/org/gcube/data/access/storagehub/services/ItemsManager.java
index 6c52548..b5c26fa 100644
--- a/src/main/java/org/gcube/data/access/storagehub/services/ItemsManager.java
+++ b/src/main/java/org/gcube/data/access/storagehub/services/ItemsManager.java
@@ -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,7 +87,9 @@ 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({
@@ -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
+ * Multivalued
+ * Optional
+ * @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- getById(@QueryParam("exclude") List excludes){
@@ -159,6 +181,18 @@ public class ItemsManager extends Impersonable{
return new ItemWrapper
- (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
+ * Multivalued
+ * Optional
+ * @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
+ * Multivalued
+ * Optional
+ * @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
+ * Possible values: true
, false
+ * Optional default: false
+ * @param onlyType only items of the specified type are counted
+ * Optional
+ * @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 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
+ * Multivalued
+ * Optional
+ * @param showHidden if true, hidden items are shown
+ * Possible values: true
, false
+ * Optional default: false
+ * @param onlyType only items of the specified type are counted
+ * Optional
+ * @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
+ * Multivalued
+ * Optional
+ * @param showHidden if true, hidden items are shown
+ * Possible values: true
, false
+ * Optional default: false
+ * @param onlyType only items of the specified type are counted
+ * Optional
+ * @param start start index, counting from 0
+ * Possible values: integers
+ * @param limit maximum number of items returned
+ * Possible values: 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
+ * Optional default: last version
+ * @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
+ * Possible values: true
, false
+ * @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
+ * Multivalued
+ * Optional
+ * @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
+ * Multivalued
+ * Optional
+ * @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 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
+ * Possible values: true
, false
+ * Optional default: false
+ * @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)
diff --git a/src/main/java/org/gcube/data/access/storagehub/services/MessageManager.java b/src/main/java/org/gcube/data/access/storagehub/services/MessageManager.java
index 5022ced..c067711 100644
--- a/src/main/java/org/gcube/data/access/storagehub/services/MessageManager.java
+++ b/src/main/java/org/gcube/data/access/storagehub/services/MessageManager.java
@@ -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,6 +72,7 @@ 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({
diff --git a/src/main/java/org/gcube/data/access/storagehub/services/StorageManager.java b/src/main/java/org/gcube/data/access/storagehub/services/StorageManager.java
index 58e0dcf..71c0c65 100644
--- a/src/main/java/org/gcube/data/access/storagehub/services/StorageManager.java
+++ b/src/main/java/org/gcube/data/access/storagehub/services/StorageManager.java
@@ -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,6 +20,9 @@ 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({
@@ -28,7 +33,15 @@ 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 getStorages(){
diff --git a/src/main/java/org/gcube/data/access/storagehub/services/UserManager.java b/src/main/java/org/gcube/data/access/storagehub/services/UserManager.java
index e5760e6..0fa9b20 100644
--- a/src/main/java/org/gcube/data/access/storagehub/services/UserManager.java
+++ b/src/main/java/org/gcube/data/access/storagehub/services/UserManager.java
@@ -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,6 +41,9 @@ 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({
@@ -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,24 @@ public class UserManager {
return null;
}
+ /**
+ * Create a new user
+ *
+ * @param user user name
+ * @param password user password
+ * @return user id
+ */
+ @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 +169,20 @@ public class UserManager {
return userId;
}
+ /**
+ * Update user to the last 'home' version
+ *
+ * @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 +210,20 @@ public class UserManager {
return userId;
}
+ /**
+ * Delete a user
+ *
+ * @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 +251,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 getGroupsPerUser(@PathParam("user") final String user) {
diff --git a/src/main/java/org/gcube/data/access/storagehub/services/WorkspaceManager.java b/src/main/java/org/gcube/data/access/storagehub/services/WorkspaceManager.java
index dca2e55..2556819 100644
--- a/src/main/java/org/gcube/data/access/storagehub/services/WorkspaceManager.java
+++ b/src/main/java/org/gcube/data/access/storagehub/services/WorkspaceManager.java
@@ -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,6 +80,9 @@ 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({
@@ -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
+ * Multivalued
+ * Optional
+ * @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
+ * Multivalued
+ * Optional
+ * @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
+ * Multivalued
+ * Optional
+ * @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- getVreRootFolder() {
- InnerMethodName.set("getVreRootFolder");
+ public ItemWrapper
- getCurrentVreFolder() {
+ InnerMethodName.set("getCurrentVreFolder");
JackrabbitSession ses = null;
Item vreItem = null;
try {
@@ -239,6 +288,18 @@ public class WorkspaceManager extends Impersonable {
return new ItemWrapper
- (vreItem);
}
+ /**
+ * Get a list of recent documents from VRE folders
+ *
+ * @param exclude a list of fields to exclude from the returned item
+ * Multivalued
+ * Optional
+ * @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
+ * Multivalued
+ * Optional
+ * @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);
}
+ /**
+ * Empty the trash folder
+ *
+ * @param exclude a list of fields to exclude from the returned item
+ * Multivalued
+ * Optional
+ * @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
+ * Multivalued
+ * Optional
+ * @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
+ * Multivalued
+ * Optional
+ * @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
+ * Possible values: integers
+ * @param limit maximum number of items returned
+ * Possible values: integers
+ * @param exclude a list of fields to exclude from the returned item
+ * Multivalued
+ * Optional
+ * @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
diff --git a/src/main/java/org/gcube/data/access/storagehub/services/admin/ScriptManager.java b/src/main/java/org/gcube/data/access/storagehub/services/admin/ScriptManager.java
index bc3483e..db8ab0d 100644
--- a/src/main/java/org/gcube/data/access/storagehub/services/admin/ScriptManager.java
+++ b/src/main/java/org/gcube/data/access/storagehub/services/admin/ScriptManager.java
@@ -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,6 +60,9 @@ 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"),
@@ -84,21 +92,61 @@ public class ScriptManager {
protected static HashMap scriptStatusMap = new HashMap();
+ /**
+ * Execute a "script" class
+ *
+ * @param name name of the script
+ * @param asynch if true, execute script asynchronously
+ * Possible values: true
, false
+ * Optional default: false
+ * @param writeResult if true, write the result in the workspace
+ * Possible values: true
, false
+ * Optional default: false
+ * @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 +154,18 @@ public class ScriptManager {
}
}
+ /**
+ * Get the status of a script
+ *
+ * @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})
@@ -122,7 +182,17 @@ public class ScriptManager {
return status;
}
-
+
+ /**
+ * Export the data
+ *
+ * @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})