Aligning the project structure with Hello World example

This commit is contained in:
Luca Frosini 2024-05-27 11:17:12 +02:00
parent 74e05671bf
commit 0970e29e86
29 changed files with 1863 additions and 109 deletions

27
Dockerfile Normal file
View File

@ -0,0 +1,27 @@
# docker build -t $DOCKER_BUILD_NAME \
# --build-arg="MVN_FINALNAME=$MVN_FINALNAME" \
# --build-arg="MVN_NAME=${MVN_NAME}" \
# --build-arg="CONTAINER_INI=${CONTAINER_INI}" \
# --build-arg="JAVA_VERSION=${JAVA_VERSION}" \
# --build-arg="SMARTGEARS_VERSION=${SMARTGEARS_VERSION}" \
# -f Dockerfile \
# $PLATFORMS .
ARG JAVA_VERSION
ARG SMARTGEARS_VERSION
#FROM d4science/smartgears-distribution:${SMARTGEARS_VERSION}-java${JAVA_VERSION}-tomcat10.1.19
FROM hub.dev.d4science.org/gcube/smartgears-distribution:${SMARTGEARS_VERSION}-java${JAVA_VERSION}-tomcat10.1.19
ARG CONTAINER_INI="./dockerize/configuration/container.ini"
ARG MVN_FINALNAME
ARG MVN_NAME
#FROM smartgears-distribution:${SMARTGEARS_VERSION}-java${JAVA_VERSION}-tomcat10.1.19
COPY ./dockerize/configuration/logback.xml /etc/
COPY ./dockerize/configuration/*.gcubekey /tomcat/lib
COPY ./target/${MVN_FINALNAME}.war /tmp
RUN unzip /tmp/${MVN_FINALNAME}.war -d /tomcat/webapps/${MVN_NAME}
COPY ${CONTAINER_INI} /etc/container.ini
EXPOSE 8080

180
dockerize/buildImageAndStart.sh Executable file
View File

@ -0,0 +1,180 @@
#!/bin/bash
# set -x # uncomment to debug script
set -a
source ./build_conf
set +a
ACCEPTED_JAVA_VERSIONs=(11 17)
PORT=8080
DEBUG_PORT=5005
DEBUG=false
EXECUTE=false
TEST=false
COMPILE=true
GOAL="clean package"
PUSH_DOCKER=false
PUSH_HARBOR=false
LOGIN_HARBOR=false
################################################################################
# Help #
################################################################################
Help() {
# Display Help
echo "build, create and run in docker the identity manager service"
echo
echo "Syntax: buildDistribution "
echo "options:"
echo "-e execute the docker image"
echo "-g arg specifies the maven [g]oals {package, install, deploy etc} default is $GOAL."
echo "-d arg? enable java debug mode for execution"
echo "-r push image to d4science harbo[r] (with login already done, or -l to login)"
echo "-u p[u]sh image to dockerhub (with docker login already done)"
echo "-p arg specifies the port to be exposed for the docker container to access the service (default $PORT)"
echo "-c arg path of the file to deploy as container.ini (default ./docker/container.ini)"
echo "-j arg specify java version (default is $JAVA_VERSION)"
echo " accepted version are: ${ACCEPTED_JAVA_VERSIONs[@]}"
echo " arg is the debug port (default is $DEBUG_PORT)"
echo "-s skip maven package"
echo "-t exec also maven tests"
echo "-n arg specifies the docker image name (default is $MVN_NAME)."
echo "-l [l]ogin to d4science harbor"
echo "-h Print this Help."
echo
echo "to compile and push to harbor registry with a custom container.ini file: "
echo " ./buildImageAndStart.sh -r -m -l -c \"./docker/container-XXX.ini\" "
echo
echo "to debug locally: "
echo " ./buildImageAndStart.sh -d "
}
################################################################################
################################################################################
# Main program #
################################################################################
################################################################################
set -e
#OPTSTRING=":sn:p:d:j:?h"
OPTSTRING=":c:n:p:g:d:?jsmulrteh"
while getopts $OPTSTRING opt; do
# echo "Option -${opt} was triggered, Argument: ${OPTARG}"
case "${opt}" in
s) COMPILE=false && echo "compile $COMPILE" ;;
g) GOAL=${OPTARG} ;;
c)
CONTAINER_INI=${OPTARG}
echo "CONTAINER_INI: $CONTAINER_INI"
;;
m) MULTI_PLATFORM=true ;;
n) NAME=${OPTARG} ;;
p) PORT=${OPTARG} ;;
u) PUSH_DOCKER=true ;;
l) LOGIN_HARBOR=true ;;
r) PUSH_HARBOR=true ;;
t) TEST=true ;;
e) EXECUTE=true ;;
d)
DEBUG=true
DEBUG_PORT=${OPTARG}
echo "debug enabled, port $DEBUG_PORT, execute $EXECUTE"
;;
j)
if [[ ${ACCEPTED_JAVA_VERSIONs[@]} =~ ${OPTARG} ]]; then
JAVA_VERSION=${OPTARG}
else
echo "Invalid java version" && echo "accepted version are: ${ACCEPTED_JAVA_VERSIONs[@]}" && exit 1
fi
;;
h) Help && exit 0 ;;
:)
# matched when an option that is expected to have an argument is passed without one
if [ ${OPTARG} = "d" ]; then
DEBUG=true
EXECUTE=true
echo "debug enabled, port $DEBUG_PORT"
else
# matched when an option that is expected to have an argument is passed without one
echo "Option -${OPTARG} requires an argument."
exit 1
fi
;;
?) # match any invalid option that is passed
echo "Invalid option: -${OPTARG}."
exit 1
;;
esac
done
if [ $COMPILE = true ]; then
SKIP_TEST=""
if [ $TEST = false ]; then
SKIP_TEST="-Dmaven.test.skip"
fi
( cd .. && mvn $GOAL $SKIP_TEST );
else
echo "skipping mvn package"
fi
echo "SMARTGEAR IMAGE: $SMARTGEAR_IMAGE"
docker pull $SMARTGEAR_IMAGE
if [ $MULTI_PLATFORM ]; then
PLATFORMS="--platform=linux/amd64,linux/arm64,linux/arm/v7"
echo "build multiple platform $PLATFORMS"
fi
echo "DOCKER_BUILD_NAME: $DOCKER_BUILD_NAME"
( cd .. && docker build -t $DOCKER_BUILD_NAME \
--build-arg="MVN_FINALNAME=$MVN_FINALNAME" \
--build-arg="MVN_NAME=${MVN_NAME}" \
--build-arg="CONTAINER_INI=${CONTAINER_INI}" \
--build-arg="JAVA_VERSION=${JAVA_VERSION}" \
--build-arg="SMARTGEARS_VERSION=${SMARTGEARS_VERSION}" \
-f Dockerfile \
$PLATFORMS . );
if [ ${PUSH_DOCKER} = true ]; then
docker tag $DOCKER_BUILD_NAME $DOCKER_IMAGE_NAME
docker push $DOCKER_IMAGE_NAME
echo ">>> pushed on dockerhub the image $DOCKER_IMAGE_NAME"
fi
if [ ${LOGIN_HARBOR} = true ]; then
./loginHarborHub.sh
fi
if [ $PUSH_HARBOR = true ]; then
echo ">>> PUSHING on hub.dev.d4science.org the image $HARBOR_IMAGE_NAME"
docker tag $DOCKER_BUILD_NAME $HARBOR_IMAGE_NAME
echo ">>> docker push $HARBOR_IMAGE_NAME"
docker push $HARBOR_IMAGE_NAME
echo ">>> pushed on hub.dev.d4science.org the image $HARBOR_IMAGE_NAME"
fi
if [ ${EXECUTE} = true ]; then
if [ $DEBUG = true ]; then
docker run -p $PORT:8080 -p $DEBUG_PORT:5005 -e JAVA_TOOL_OPTIONS="-agentlib:jdwp=transport=dt_socket,address=*:5005,server=y,suspend=y" $DOCKER_BUILD_NAME
else
docker run -p $PORT:8080 $DOCKER_BUILD_NAME
fi
fi

55
dockerize/build_conf Normal file
View File

@ -0,0 +1,55 @@
MVN_VERSION=$(cd .. && mvn -q \
-Dexec.executable=echo \
-Dexec.args='${project.version}' \
--non-recursive \
exec:exec)
echo "MVN_VERSION: ${MVN_VERSION}"
MVN_NAME=$(cd .. && mvn -q \
-Dexec.executable=echo \
-Dexec.args='${project.artifactId}' \
--non-recursive \
exec:exec)
echo "MVN_NAME: ${MVN_NAME}"
MVN_FINALNAME=$(cd .. && mvn -q \
-Dexec.executable=echo \
-Dexec.args='${project.build.finalName}' \
--non-recursive \
exec:exec)
echo "MVN_FINALNAME: ${MVN_FINALNAME}"
JAVA_VERSION=$(cd .. && mvn -q \
-Dexec.executable=echo \
-Dexec.args='${maven.compiler.target}' \
--non-recursive \
exec:exec)
echo "JAVA_VERSION: ${JAVA_VERSION}"
TOMCAT_VERSION=tomcat10.1.19
echo "TOMCAT_VERSION: ${TOMCAT_VERSION}"
SMARTGEARS_VERSION=$(cd .. && mvn -q \
-Dexec.executable=echo \
-Dexec.args='${docker.smartgear.version}' \
--non-recursive \
exec:exec)
echo "SMARTGEARS_VERSION: ${SMARTGEARS_VERSION}"
CONTAINER_INI="./dockerize/configuration/container.ini"
echo "CONTAINER_INI: ${CONTAINER_INI}"
IMAGE_VERSION=${MVN_VERSION}-java${JAVA_VERSION}-smartgears${SMARTGEARS_VERSION}
echo "IMAGE_VERSION: ${IMAGE_VERSION}"
DOCKER_BUILD_NAME=$MVN_NAME:$IMAGE_VERSION
echo "DOCKER_BUILD_NAME: ${DOCKER_BUILD_NAME}"
SMARTGEAR_IMAGE=hub.dev.d4science.org/gcube/smartgears-distribution:${SMARTGEARS_VERSION}-java${JAVA_VERSION}-${TOMCAT_VERSION}
echo "SMARTGEAR_IMAGE: ${SMARTGEAR_IMAGE}"
DOCKER_IMAGE_NAME=d4science/$DOCKER_BUILD_NAME
echo "DOCKER_IMAGE_NAME: ${DOCKER_IMAGE_NAME}"
HARBOR_IMAGE_NAME=hub.dev.d4science.org/gcube/$DOCKER_BUILD_NAME
echo "HARBOR_IMAGE_NAME: ${HARBOR_IMAGE_NAME}"

3
dockerize/configuration/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
container*.ini
!container.default.ini
*.gcubekey

View File

@ -0,0 +1,26 @@
[node]
mode = offline
hostname = localhost
protocol= http
port = 8080
infrastructure = gcube
authorizeChildrenContext = true
publicationFrequencyInSeconds = 60
[properties]
SmartGearsDistribution = 4.0.1-SNAPSHOT
SmartGearsDistributionBundle = UnBundled
[site]
country = it
location = pisa
[authorization]
factory = org.gcube.smartgears.security.defaults.DefaultAuthorizationProviderFactory
factory.endpoint = https://accounts.dev.d4science.org/auth/realms/d4science/protocol/openid-connect/token
credentials.class = org.gcube.smartgears.security.SimpleCredentials
credentials.clientID =
credentials.secret =

View File

@ -0,0 +1,27 @@
<configuration scan="true" debug="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>Ï <pattern>%-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.gcube.service.helloword" level="DEBUG" />
<logger name="org.gcube.smartgears" level="DEBUG" />
<!--
<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.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" />
-->
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

12
dockerize/docker.conf Normal file
View File

@ -0,0 +1,12 @@
MVN_VERSION: 1.0.0-SNAPSHOT
MVN_NAME: hello-world-service
MVN_FINALNAME: hello-world-service-1.0.0-SNAPSHOT
JAVA_VERSION: 11
TOMCAT_VERSION: tomcat10.1.19
SMARTGEARS_VERSION: 4.0.1-SNAPSHOT
CONTAINER_INI: ./dockerize/configuration/container.ini
IMAGE_VERSION: 1.0.0-SNAPSHOT-java11-smartgears4.0.1-SNAPSHOT
DOCKER_BUILD_NAME: hello-world-service:1.0.0-SNAPSHOT-java11-smartgears4.0.1-SNAPSHOT
SMARTGEAR_IMAGE: hub.dev.d4science.org/gcube/smartgears-distribution:4.0.1-SNAPSHOT-java11-tomcat10.1.19
DOCKER_IMAGE_NAME: d4science/hello-world-service:1.0.0-SNAPSHOT-java11-smartgears4.0.1-SNAPSHOT
HARBOR_IMAGE_NAME: hub.dev.d4science.org/gcube/hello-world-service:1.0.0-SNAPSHOT-java11-smartgears4.0.1-SNAPSHOT

13
dockerize/loginHarborHub.sh Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash
REGISTRY_URL="hub.dev.d4science.org"
#USERNAME="alfredo.oliviero"
echo "to obtain Harbor username and CLI secret:"
echo "https://hub.dev.d4science.org/ -> user profile -> CLI secret"
read -p "username:" USERNAME
echo ""
read -s -p "CLI secret:" ACCESS_TOKEN
echo "$ACCESS_TOKEN" | docker login $REGISTRY_URL -u $USERNAME --password-stdin
unset ACCESS_TOKEN

12
dockerize/pull_docker.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh
set -a
# source build_conf > docker.conf # generate current conf
# source docker.conf
source build_conf
docker pull $HARBOR_IMAGE_NAME
echo "Docker image $HARBOR_IMAGE_NAME"
set +a

12
dockerize/start_docker.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh
set -a
# source build_conf > docker.conf # generate current conf
# source docker.conf
source build_conf
echo "Docker image $HARBOR_IMAGE_NAME"
docker pull $HARBOR_IMAGE_NAME
docker run -d -p 9991:8080 --name $NAME $HARBOR_IMAGE_NAME
set +a

11
dockerize/stop_docker.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/sh
set -a
# source build_conf > docker.conf # generate current conf
# source docker.conf
source build_conf
docker stop $NAME
docker rm $NAME
set +a

BIN
documentation/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,5 @@
[visual studio code - Dev Containers tutorial](https://code.visualstudio.com/docs/devcontainers/tutorial)
installare su vscode estensione [Dev Containers extension](vscode:extension/ms-vscode-remote.remote-containers)

View File

@ -0,0 +1,32 @@
<?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">
<description package="org.gcube.service.idm" />
<api-classes>
<include pattern="org.gcube.service.helloworld.rest.*" />
<exclude pattern="org.gcube.service.helloworld.*" />
</api-classes>
<modules>
<gwt-json-overlay disabled="true" />
<php-json-client disabled="true" />
<ruby-json-client disabled="true" />
<java-json-client disabled="true" />
<javascript-client disabled="false" />
<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" />
<docs docsDir="${project.build.directory}" docsSubdir="api-docs" />
<swagger basePath="/${project.artifactId}" />
<docs
freemarkerTemplate="${project.basedir}/src/main/resources/META-INF/enunciate/d4science_docs.fmt">
<additional-css
file="css/d4science_enunciate_custom.css" />
</docs>
</modules>
</enunciate>

View File

@ -0,0 +1,75 @@
{
"id": "ff7036fd-e12d-4dd7-9d0f-816bb5a0f06a",
"name": "Hello World Env for devVRE",
"values": [
{
"key": "introspect_secret",
"value": "",
"type": "secret",
"enabled": true
},
{
"key": "password",
"value": "",
"type": "secret",
"enabled": true
},
{
"key": "service_client_secret",
"value": "",
"type": "secret",
"enabled": true
},
{
"key": "realm",
"value": "d4science",
"type": "any",
"enabled": true
},
{
"key": "username",
"value": "luca.frosini",
"type": "default",
"enabled": true
},
{
"key": "service_client_id",
"value": "id.d4science.org",
"type": "default",
"enabled": true
},
{
"key": "context",
"value": "/gcube/devsec/devVRE",
"type": "default",
"enabled": true
},
{
"key": "keycloak_url",
"value": "https://accounts.dev.d4science.org/auth",
"type": "any",
"enabled": true
},
{
"key": "encoded_context",
"value": "%2Fgcube%2Fdevsec%2FdevVRE",
"type": "any",
"enabled": true
},
{
"key": "client-id-user",
"value": "next.d4science.org",
"type": "default",
"enabled": true
},
{
"key": "introspect_client",
"value": "token-exchange-dedicated",
"type": "default",
"enabled": true
}
],
"_postman_variable_scope": "environment",
"_postman_exported_at": "2024-05-24T16:06:23.560Z",
"_postman_exported_using": "Postman/11.1.14"
}

View File

@ -0,0 +1,692 @@
{
"info": {
"_postman_id": "e152bab3-7be5-4fe2-81bd-625fec76373d",
"name": "Hello World Service",
"description": "An example collection that can be used for communicating with gcube services\n\nget TOKEN and at UMA_TOKEN from url: [https://next.dev.d4science.org/group/gcube/home](https://next.dev.d4science.org/group/gcube/home)\n\nupdate the collection's variables gcube_token and uma_token",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"_exporter_id": "3092110"
},
"item": [
{
"name": "LOGIN",
"item": [
{
"name": "Clear Env",
"event": [
{
"listen": "prerequest",
"script": {
"exec": [
"console.log(\"executing clear script\");",
"",
"variables = [",
" 'token',",
" 'access_token',",
" 'refresh_token',",
"",
" 'uma_token',",
" 'uma_refresh',",
"",
" 'profile_token',",
" 'gcube_token',",
" 'oidc_access_token',",
" 'oidc_refresh_token',",
" 'id_token',",
"",
" 'clientId',",
"",
" 'current_username',",
" 'current_password',",
" 'current_context',",
" 'current_url-encoded-context',",
" 'current_iam-url',",
" 'current_client-id',",
" 'current_uma-token',",
" 'current_access-token',",
"",
" 'oidc_token',",
" 'oidc_access_token',",
" 'oidc_refresh_token',",
" 'uma_token',",
" 'exchanged_token'",
"",
"]",
"",
"for (var v of variables) {",
" pm.environment.unset(v);",
" pm.globals.unset(v);",
"}",
"",
" ",
"",
"",
" ",
""
],
"type": "text/javascript",
"packages": {}
}
}
],
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "/",
"path": [
""
]
}
},
"response": []
},
{
"name": "Get User Token For Context",
"event": [
{
"listen": "test",
"script": {
"exec": [
"var jsonData = JSON.parse(responseBody);",
"postman.setEnvironmentVariable(\"oidc_access_token\", jsonData.access_token);",
"postman.setEnvironmentVariable(\"oidc_refresh_token\", jsonData.refresh_token);",
"",
"postman.setEnvironmentVariable(\"uma_token\", jsonData.access_token);",
"",
"postman.setEnvironmentVariable(\"access_token\", jsonData.access_token);",
"postman.setEnvironmentVariable(\"refresh_token\", jsonData.refresh_token);",
""
],
"type": "text/javascript",
"packages": {}
}
},
{
"listen": "prerequest",
"script": {
"exec": [
""
],
"type": "text/javascript",
"packages": {}
}
}
],
"request": {
"auth": {
"type": "noauth"
},
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/x-www-form-urlencoded"
},
{
"key": "X-D4Science-Context",
"value": "{{encoded_context}}"
}
],
"body": {
"mode": "urlencoded",
"urlencoded": [
{
"key": "client_id",
"value": "{{service_client_id}}",
"type": "text"
},
{
"key": "username",
"value": "{{username}}",
"type": "text"
},
{
"key": "password",
"value": "{{password}}",
"type": "text"
},
{
"key": "grant_type",
"value": "password",
"type": "text"
},
{
"key": "client_secret",
"value": "{{service_client_secret}}",
"type": "text"
}
]
},
"url": {
"raw": "{{keycloak_url}}/realms/{{realm}}/protocol/openid-connect/token",
"host": [
"{{keycloak_url}}"
],
"path": [
"realms",
"{{realm}}",
"protocol",
"openid-connect",
"token"
]
},
"description": "Obtain UAT = user access token from a user in realm"
},
"response": []
},
{
"name": "Get ClientId Token For Context",
"event": [
{
"listen": "test",
"script": {
"exec": [
"var jsonData = JSON.parse(responseBody);",
"",
"// postman.setEnvironmentVariable(\"refresh_token\", jsonData.refresh_token);",
"// postman.setEnvironmentVariable(\"id_token\", jsonData.id_token);",
"",
"postman.setEnvironmentVariable(\"oidc_access_token\", jsonData.access_token);",
"",
"postman.setEnvironmentVariable(\"access_token\", jsonData.access_token);",
"postman.setEnvironmentVariable(\"token\", jsonData.access_token);",
"",
""
],
"type": "text/javascript",
"packages": {}
}
}
],
"request": {
"auth": {
"type": "noauth"
},
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/x-www-form-urlencoded"
},
{
"key": "X-D4Science-Context",
"value": "{{encoded_context}}"
}
],
"body": {
"mode": "urlencoded",
"urlencoded": [
{
"key": "client_id",
"value": "{{service_client_id}}",
"type": "text"
},
{
"key": "client_secret",
"value": "{{service_client_secret}}",
"type": "text"
},
{
"key": "grant_type",
"value": "client_credentials",
"type": "text"
}
]
},
"url": {
"raw": "{{keycloak_url}}/realms/{{realm}}/protocol/openid-connect/token",
"host": [
"{{keycloak_url}}"
],
"path": [
"realms",
"{{realm}}",
"protocol",
"openid-connect",
"token"
]
},
"description": "Obtain SAT (service account token)"
},
"response": []
},
{
"name": "Validate Access Token",
"request": {
"auth": {
"type": "noauth"
},
"method": "POST",
"header": [
{
"key": "Authorization",
"value": "Basic YWxmcmVkby1pZG0tc2VydmljZS1kZXY6OTc5YmQzYmMtNWNjNC0xMWVjLWJmNjMtMDI0MmFjMTMwMDAy"
}
],
"body": {
"mode": "urlencoded",
"urlencoded": [
{
"key": "token",
"value": "{{access_token}}",
"type": "text"
},
{
"key": "username\n",
"value": "{{introspect_client}}",
"type": "text",
"disabled": true
},
{
"key": "password",
"value": "{{introspect_secret}}",
"type": "text",
"disabled": true
},
{
"key": "client_id",
"value": "{{introspect_client}}",
"type": "text"
},
{
"key": "client_secret",
"value": "{{introspect_secret}}",
"type": "text"
}
]
},
"url": {
"raw": "{{keycloak_url}}/realms/{{realm}}/protocol/openid-connect/token/introspect",
"host": [
"{{keycloak_url}}"
],
"path": [
"realms",
"{{realm}}",
"protocol",
"openid-connect",
"token",
"introspect"
]
}
},
"response": []
}
]
},
{
"name": "REST",
"item": [
{
"name": "authorized",
"item": [
{
"name": "legacy-token",
"item": [
{
"name": "hello TOKEN PARAM",
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [
{
"key": "",
"value": "{{token}}"
}
],
"url": {
"raw": "{{base_url}}/{{application}}/hello?gcube-token={{gcube_token}}",
"host": [
"{{base_url}}"
],
"path": [
"{{application}}",
"hello"
],
"query": [
{
"key": "gcube-token",
"value": "{{gcube_token}}"
}
]
}
},
"response": []
},
{
"name": "details TOKEN PARAM",
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/{{application}}/details?gcube-token={{gcube_token}}",
"host": [
"{{base_url}}"
],
"path": [
"{{application}}",
"details"
],
"query": [
{
"key": "gcube-token",
"value": "{{gcube_token}}"
}
]
}
},
"response": []
},
{
"name": "auth org member TOKEN PARAM",
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/{{application}}/auth/org_member?gcube-token={{gcube_token}}",
"host": [
"{{base_url}}"
],
"path": [
"{{application}}",
"auth",
"org_member"
],
"query": [
{
"key": "gcube-token",
"value": "{{gcube_token}}"
}
]
}
},
"response": []
},
{
"name": "auth member TOKEN PARAM",
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/{{application}}/auth/member?gcube-token={{gcube_token}}",
"host": [
"{{base_url}}"
],
"path": [
"{{application}}",
"auth",
"member"
],
"query": [
{
"key": "gcube-token",
"value": "{{gcube_token}}"
}
]
}
},
"response": []
}
]
},
{
"name": "hello",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/{{application}}/hello",
"host": [
"{{base_url}}"
],
"path": [
"{{application}}",
"hello"
]
}
},
"response": []
},
{
"name": "details",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/{{application}}/details",
"host": [
"{{base_url}}"
],
"path": [
"{{application}}",
"details"
]
}
},
"response": []
},
{
"name": "auth org member",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{uma_token}}",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/{{application}}/auth/org_member",
"host": [
"{{base_url}}"
],
"path": [
"{{application}}",
"auth",
"org_member"
]
}
},
"response": []
},
{
"name": "auth member",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{uma_token}}",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/{{application}}/auth/member",
"host": [
"{{base_url}}"
],
"path": [
"{{application}}",
"auth",
"member"
]
}
},
"response": []
}
]
},
{
"name": "No Auth Required",
"item": [
{
"name": "guest",
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/{{application}}/guest",
"host": [
"{{base_url}}"
],
"path": [
"{{application}}",
"guest"
]
}
},
"response": []
},
{
"name": "404 Not Found",
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/{{application}}/guest/not-found",
"host": [
"{{base_url}}"
],
"path": [
"{{application}}",
"guest",
"not-found"
]
}
},
"response": []
},
{
"name": "400 Bad Request",
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/{{application}}/guest/bad-request",
"host": [
"{{base_url}}"
],
"path": [
"{{application}}",
"guest",
"bad-request"
]
}
},
"response": []
},
{
"name": "PURGE with 204 No Content",
"request": {
"auth": {
"type": "noauth"
},
"method": "PURGE",
"header": [],
"url": {
"raw": "{{base_url}}/{{application}}/guest/test-purge",
"host": [
"{{base_url}}"
],
"path": [
"{{application}}",
"guest",
"test-purge"
]
}
},
"response": []
}
]
}
]
}
],
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{access_token}}",
"type": "string"
}
]
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"packages": {},
"exec": [
""
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"packages": {},
"exec": [
""
]
}
}
],
"variable": [
{
"key": "base_url",
"value": "http://localhost:8080",
"type": "string"
},
{
"key": "role_name",
"value": "Member"
},
{
"key": "application",
"value": "hello-world-service",
"type": "string"
},
{
"key": "base_url_marco",
"value": "http://146.48.85.179:9999",
"type": "string"
},
{
"key": "base_url_local",
"value": "http://localhost:8080",
"type": "string"
}
]
}

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)

View File

@ -0,0 +1,62 @@
# 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('.'))
_POM_PATH = "../.."
def getMvnVariable(variable):
cmd = """cd %s && mvn -q \
-Dexec.executable=echo \
-Dexec.args='${project.%s}' \
--non-recursive \
exec:exec""" % (_POM_PATH, variable)
stream = os.popen(cmd)
return stream.read().strip()
# -- Project information -----------------------------------------------------
# The full version, including alpha/beta/rc tags
release = getMvnVariable("version")
project = getMvnVariable("name")
copyright = '2024, %s' % getMvnVariable("organization.name")
author = 'Luca Frosini (ISTI-CNR)'
# -- General configuration ---------------------------------------------------
# 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 = 'sphinxdoc'
# 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']

View File

@ -0,0 +1,258 @@
***********************************************************
Welcome to Hello World SG4 documentation
***********************************************************
Hello World SG4 is a RESTful application that exposes operations via REST-API.
See the available `REST-API docs <../api-docs/index.html>`_.
Base URL
========
In the production environment, its current value is
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
Service
=======
You can call the methods of the Web Service by writing your REST client application or using existing REST client plugins.
HTTP Statuses
-------------
Any successful operation returns a *200 OK* HTTP status code.
The create operation returns *201 Created*.
Any Background operation returns *202 Accepted*.
Any operation that does not provide any content returns *204 No Content*.
The most common error statuses a client can obtain are:
* **400 Bad Request** used to indicate a clients error `<https://tools.ietf.org/html/rfc7231#section-6.5.1>`_;
* **401 Unauthorized** used to indicate that the client does not provide the authorization token in the HTTP Header or the client does not have enough right to perform such request `<https://tools.ietf.org/html/rfc7235#section-3.1>`_;
* **404 Not Found** used to indicate that the requested instance does not exist `<https://tools.ietf.org/html/rfc7231#section-6.5.4>`_;
* **405 Method Not Allowed** the used HTTP method is not supported for the requested URL `<https://tools.ietf.org/html/rfc7231#section-6.5.5>`_.
The response contains the *Allow* HTTP Header indicating the supported HTTP method for such URL `<https://tools.ietf.org/html/rfc7231#section-7.4.1>`_;
* **409 Conflict** the request could not be completed due to a conflict with the current state of the target resource (e.g. the name of the resource already exists) `<https://tools.ietf.org/html/rfc7231#section-6.5.8>`_;
* **500 Internal Server Error** indicate a server failure `<https://tools.ietf.org/html/rfc7231#section-6.6.1>`_.
You can find a complete list of HTTP Status at `<https://httpstatuses.com/>`_ or `<https://httpstatuses.io/>`_
If you get a *500 Internal Server Error*, please report it in the `gCube ticketing system <https://support.d4science.org>`_.
Please use this checklist before reporting an error:
* Replicate the request;
* The failure could be temporal due to a network error, a server issue, and many other temporal issues. For this reason, please retry the request after a certain amount of time before reporting the issue;
* indicate how to replicate the error;
* indicate the time when the error occurred (this simplifies identifying the issue).
HTTP Methods
------------
gCat is a pure RESTful service. It uses standard HTTP Methods to perform a listing of collections and CRUD (Create Read Update Delete) operations on instances.
.. table::
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
| Operation | HTTP Method | URL | Success HTTP Status | Safe | Idempotent |
+==============+=============+========================================+=====================+========+============+
| Supported | OPTIONS | /{COLLECTION} | 204 No Content | Y | Y |
| HTTP Methods | | | [#allow]_ | | |
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
| List | GET | /{COLLECTION} | 200 OK | Y | Y |
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
| Count | GET | /{COLLECTION}?count=true | 200 OK | Y | Y |
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
| Exists | HEAD | /{COLLECTION} | 204 No Content | Y | Y |
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
| Create | POST | /{COLLECTION} | 201 Created | N | N |
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
| Supported | OPTIONS | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | Y | Y |
| HTTP Methods | | | [#allow]_ | | |
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
| Exist | HEAD | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | Y | Y |
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
| Read | GET | /{COLLECTION}/{INSTANCE_ID} | 200 OK | Y | Y |
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
| Update | PUT | /{COLLECTION}/{INSTANCE_ID} | 200 OK | N | Y |
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
| Patch | PATCH | /{COLLECTION}/{INSTANCE_ID} | 200 OK | N | Y |
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
| Delete | DELETE | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | N | N [#del]_ |
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
| Purge | DELETE | /{COLLECTION}/{INSTANCE_ID}?purge=true | 204 No Content | N | N [#del]_ |
+ +-------------+----------------------------------------+---------------------+--------+------------+
| | PURGE | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | N | N [#del]_ |
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
.. [#allow] Supported HTTP Methods in **Allow** HTTP Header
.. [#del] DELETE has been defined as idempotent.
*Allamaraju* [#Allamaraju]_ argues that DELETE idempotency should be accomplished client-side.
The server should inform the client if the delete operation succeeded because the resource was deleted or it was not found, i.e., **404 Not Found** error is suggested instead of **204 No Content**.
The latter situation should be treated as idempotent by the client.
We share the same vision. For this reason, gCat does not provide server-side idempotency for DELETE and PURGE operations.
.. [#Allamaraju] Allamaraju S. RESTful Web Services Cookbook: Solutions for Improving Scalability and Simplicity. OReilly. first ed. 2010
About URL
^^^^^^^^^
The presented URL uses the following convention:
* **{COLLECTION}** is the plural name of the entity type;
* **{INSTANCE_ID}** is an identification that enables univocally identifying the instance in the collection.
About Safety and Idempotency properties
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* A method is *Safe* if it does not produce any side effects.
"This does not prevent an implementation from including behaviour that is potentially harmful, that is not entirely read-only, or that causes side effects while invoking a safe method"
`<https://tools.ietf.org/html/rfc7231#section-4.2.1>`_;
* A method is *Idempotent* if the same operation repeated multiple times has the same side effect as using it one time.
"repeating the request will have the same intended effect, even if the original request succeeded, though the response might differ"
`<https://tools.ietf.org/html/rfc7231#section-4.2.2>`_.
You can find more information about HTTP Methods at `<https://restfulapi.net/http-methods/>`_
Uncommon HTTP Methods
^^^^^^^^^^^^^^^^^^^^^
* PATCH method allows to perform a differential update (i.e. an update which provides only the differences and not the whole new representation);
* PURGE method is not a standard but is widely used in services that require this action
(e.g. `Varnish <https://varnish-cache.org/docs/3.0/tutorial/purging.html>`_, `Squid <https://wiki.squid-cache.org/SquidFaq/OperatingSquid#How_can_I_purge_an_object_from_my_cache.3F>`_).
gCat provides support for this method, but to support a wider range of clients, it also provides the Purge action via *DELETE* with the additional get parameter ``purge=true``.
Content-Type
------------
Any request must contain an indication of the interesting content type.
The client must specify the **Accept** HTTP Header for any operation returning a result.
.. code-block:: rest
Accept: application/json
For any operation sending content to the service, it is necessary to specify the **Content-Type** HTTP Header.
.. code-block:: rest
Content-Type: application/json
The service accepts and returns only JSON objects.
Collections
-----------
Roles
-----
Java Client
===========
We provide the following Java Client out-of-the-box.
.. TIP::
If you're coding in Java, it is recommended that you use this Java Client.
**Maven Coordinates**
.. code:: xml
<groupId>org.gcube.service</groupId>
<artifactId>helloworld-client</artifactId>
<version>[1.0.0-SNAPSHOT, 2.0.0-SNAPSHOT)</version>
**Methods Result**
The service exposes `its methods <../api-docs/index.html>`_ using a standard naming approach. Moreover, they accept (in the case of HTTP POST/PUT methods) JSON objects.
.. IMPORTANT::
The result of all methods is always a JSON object as per below:
.. code:: javascript
{
....
}
*Inputs are automatically validated before the request is served.*
**Usage examples**
- Example 1
.. code:: java
import org.gcube....;
Service Discovery on IS
=======================
The service can be discovered in the gCore IS as gCore Endpoint with the following parameter:
.. code:: xml
<ServiceClass>org.gcube.service</ServiceClass>
<ServiceName>helloworld</ServiceName>
The service can be discovered in the Facet-Based IS as EService with the following JSON query:
.. code:: json
{
"@class": "EService",
"consistsOf": [
{
"@class": "IsIdentifiedBy",
"target": {
"@class": "SoftwareFacet",
"group": "org.gcube.service",
"name": "helloworld"
}
}
]
}
Service Maven Coordinates
=========================
The maven coordinates of the Hello World SG4 service are:
.. code:: xml
<groupId>org.gcube.service</groupId>
<artifactId>helloworld</artifactId>

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

@ -0,0 +1,23 @@
Dev and Pre Users used for moderation tests
========
To perform moderation tests in dev and preproduction infrastructure we use different users with the indicated roles.
.. table::
+---------------+---------------+----------------------------------------+
| User | Username | Role |
+===============+===============+========================================+
| Mister Blonde | mister.blonde | Catalogue-Admin + Catalogue-Moderator |
+---------------+---------------+----------------------------------------+
| Mister Blue | mister.blue | Catalogue-Admin |
+---------------+---------------+----------------------------------------+
| Mister Brown | mister.brown | Catalogue-Moderator |
+---------------+---------------+----------------------------------------+
| Mister Orange | mister.orange | Catalogue-Editor |
+---------------+---------------+----------------------------------------+
| Mister Pink | mister.pink | NO ROLE (means Catalogue-Member) |
+---------------+---------------+----------------------------------------+
| Mister White | mister.white | Catalogue-Manager |
+---------------+---------------+----------------------------------------+

160
pom.xml
View File

@ -1,23 +1,49 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.gcube.tools</groupId>
<artifactId>maven-parent</artifactId>
<version>1.2.0</version>
</parent>
<groupId>org.gcube.resource-management</groupId>
<artifactId>resource-manager</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>Resource Manager</name>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.gcube.tools</groupId>
<artifactId>maven-parent</artifactId>
<version>1.2.0</version>
</parent>
<groupId>org.gcube.resource-management</groupId>
<artifactId>resource-manager</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>Resource Manager</name>
<properties>
<java.version>11</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<organization>
<name>gCube System</name>
<url>https://www.gcube-system.org/</url>
</organization>
<developers>
<developer>
<id>luca.frosini</id>
<name>Luca Frosini</name>
<email>luca.frosini@isti.cnr.it</email>
<url>https://www.isti.cnr.it/en/about/people-detail/141/Frosini_Luca</url>
<organization>ISTI-CNR</organization>
<organizationUrl>https://www.isti.cnr.it/</organizationUrl>
<roles>
<role>researcher</role>
<role>developer</role>
</roles>
<timezone>Europe/Rome</timezone>
</developer>
</developers>
<properties>
<java.version>11</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<webappDirectory>${project.basedir}${file.separator}src${file.separator}main${file.separator}webapp${file.separator}WEB-INF</webappDirectory>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<webappDirectory>
${project.basedir}${file.separator}src${file.separator}main${file.separator}webapp${file.separator}WEB-INF
</webappDirectory>
<enunciate.version>2.14.0</enunciate.version>
<docker.smartgear.version>4.0.1-SNAPSHOT</docker.smartgear.version>
</properties>
<scm>
@ -79,7 +105,6 @@
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>gxHTTP</artifactId>
@ -93,6 +118,7 @@
<groupId>org.gcube.core</groupId>
<artifactId>common-smartgears</artifactId>
</dependency>
<!-- Required for Enunciate plugin -->
<dependency>
<groupId>com.webcohesion.enunciate</groupId>
@ -107,10 +133,10 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- END Required for Enunciate plugin -->
@ -130,17 +156,21 @@
</dependencies>
<build>
<plugins>
<plugins>
<!-- Sphinx plugin' -->
<plugin>
<groupId>kr.motd.maven</groupId>
<artifactId>sphinx-maven-plugin</artifactId>
<version>2.10.0</version>
<configuration>
<outputDirectory>${project.build.directory}/${project.artifactId}-${project.version}/docs</outputDirectory>
<outputDirectory>
${project.build.directory}${file.separator}${project.build.finalName}${file.separator}docs</outputDirectory>
<builder>html</builder>
<configDirectory>${basedir}/docs</configDirectory>
<sourceDirectory>${basedir}/docs</sourceDirectory>
<configDirectory>${project.basedir}${file.separator}documentation${file.separator}sphinx</configDirectory>
<sourceDirectory>${project.basedir}${file.separator}documentation${file.separator}sphinx</sourceDirectory>
<!-- brew install sphinx-doc -->
<binaryUrl>
file:/opt/homebrew/opt/sphinx-doc/bin/sphinx-build</binaryUrl>
</configuration>
<executions>
<execution>
@ -153,43 +183,51 @@
</plugin>
<!-- Enunciate Maven plugin -->
<!-- <plugin>-->
<!-- <groupId>com.webcohesion.enunciate</groupId>-->
<!-- <artifactId>enunciate-maven-plugin</artifactId>-->
<!-- <version>${enunciate.version}</version>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <id>assemble</id>-->
<!-- <goals>-->
<!-- <goal>assemble</goal>-->
<!-- </goals>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- </plugin>-->
<plugin>
<groupId>com.webcohesion.enunciate</groupId>
<artifactId>enunciate-maven-plugin</artifactId>
<version>${enunciate.version}</version>
<executions>
<execution>
<id>assemble</id>
<goals>
<goal>assemble</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- This does not works as expected. We need to use the maven-resources-plugin below
<docsDir>${project.build.directory}${file.separator}${project.artifactId}-${project.version}${file.separator}api-docs</docsDir>
-->
<configFile>${project.basedir}${file.separator}documentation${file.separator}enunciate.xml</configFile>
</configuration>
</plugin>
<!-- Copy Enunciate Documentation from your-application/api-docs into your war -->
<!-- <plugin>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-resources-plugin</artifactId>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <id>copy-enunciate-docs</id>-->
<!-- <phase>process-resources</phase>-->
<!-- <goals>-->
<!-- <goal>copy-resources</goal>-->
<!-- </goals>-->
<!-- <configuration>-->
<!-- <outputDirectory>target</outputDirectory>-->
<!-- <resources>-->
<!-- <resource>-->
<!-- <targetPath>${project.build.directory}/${project.artifactId}-${project.version}/api-docs</targetPath>-->
<!-- <directory>${project.build.directory}/api-docs</directory>-->
<!-- <filtering>true</filtering>-->
<!-- </resource>-->
<!-- </resources>-->
<!-- </configuration>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- </plugin>-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-enunciate-docs</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>target</outputDirectory>
<resources>
<resource>
<targetPath>${project.build.directory}${file.separator}${project.artifactId}-${project.version}${file.separator}api-docs</targetPath>
<directory>${project.build.directory}/api-docs</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -12,9 +12,9 @@ import jakarta.ws.rs.ApplicationPath;
*/
@ApplicationPath("/")
@ManagedBy(ResourceManager.class)
public class ResourceInitializer extends ResourceConfig {
public class RMInitializer extends ResourceConfig {
public ResourceInitializer() {
public RMInitializer() {
packages(BaseREST.class.getPackage().toString());
packages(Configuration.class.getPackage().toString());
}

View File

@ -38,7 +38,8 @@ public class ResourceManager implements ApplicationManager {
context);
ApplicationContext applicationContext = ContextProvider.get();
String rrEServiceID = applicationContext.id();
String rmEServiceID = applicationContext.id();
logger.info("Resource Manager ahs the following ID {}", rmEServiceID);
logger.trace(

View File

@ -20,7 +20,7 @@ import jakarta.ws.rs.core.UriInfo;
})
public class BaseREST {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
public static final String APPLICATION_JSON_CHARSET_UTF_8 = "application/json;charset=UTF-8";
public static final String APPLICATION_JSON_API = "application/vnd.api+json";
@ -35,11 +35,6 @@ public class BaseREST {
protected static final String LOCATION_HEADER = "Location";
protected void setCalledMethod(String method) {
InnerMethodName.set(method);
logger.info("{}", uriInfo.getAbsolutePath());
}
protected ResponseBuilder addLocation(ResponseBuilder responseBuilder, String id) {
return responseBuilder.header(LOCATION_HEADER,
String.format("%s/%s", uriInfo.getAbsolutePath().toString(), id));
@ -55,4 +50,33 @@ public class BaseREST {
return stringBuilder.toString();
}
protected void setAccountingMethod(String method) {
InnerMethodName.set(method);
logger.info("{}", uriInfo.getAbsolutePath());
}
// protected void setAccountingMethod(Method method, String type) {
// StringBuffer accountingMethod = new StringBuffer();
// accountingMethod.append(method.getPrefix());
// accountingMethod.append(type);
// accountingMethod.append(method.getSuffix());
// setAccountingMethod(accountingMethod.toString());
// }
//
// private ServerRequestInfo initRequestInfo(ServerRequestInfo requestInfo) {
// requestInfo.setUriInfo(uriInfo);
// RequestUtility.getRequestInfo().set(requestInfo);
// return requestInfo;
// }
//
// protected ServerRequestInfo initRequestInfo(int offset, int limit) {
// ServerRequestInfo requestInfo = new ServerRequestInfo(offset, limit);
// return initRequestInfo(requestInfo);
// }
//
// protected ServerRequestInfo initRequestInfo() {
// ServerRequestInfo requestInfo = new ServerRequestInfo();
// return initRequestInfo(requestInfo);
// }
}

View File

@ -0,0 +1,114 @@
package org.gcube.resourcemanagement.rest;
import org.gcube.informationsystem.contexts.reference.entities.Context;
import org.gcube.informationsystem.resourceregistry.api.exceptions.ResourceRegistryException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.contexts.ContextNotFoundException;
import org.gcube.informationsystem.resourceregistry.api.rest.ContextPath;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
/**
* @author Luca Frosini (ISTI - CNR)
*/
@Path(ContextManager.CONTEXTS_PATH_PART)
public class ContextManager extends BaseREST {
public static final String CONTEXTS_PATH_PART = "contexts";
public static final String CONTEXT_UUID_PATH_PARAMETER = "CONTEXT_UUID";
public static final String CURRENT_CONTEXT_PATH_PART = "CURRENT_CONTEXT";
public ContextManager() {
super();
}
/*
* Create a new Context suing the provided definition
*
* POST /contexts
*
* BODY: {...}
*
*/
@POST
@Path("{" + ContextManager.CONTEXT_UUID_PATH_PARAMETER + "}")
@Consumes({MediaType.TEXT_PLAIN, BaseREST.APPLICATION_JSON_CHARSET_UTF_8})
@Produces(BaseREST.APPLICATION_JSON_CHARSET_UTF_8)
public String create(String json)
throws ResourceRegistryException {
logger.info("Requested to update/create {} with json {} ", Context.NAME, json);
// setAccountingMethod(Method.UPDATE, Context.NAME);
return "";
}
/*
* Allow to read the context definition
* GET /contexts/{UUID}
* e.g. GET /contexts/c0f314e7-2807-4241-a792-2a6c79ed4fd0
*
*/
@GET
@Path("{" + ContextManager.CONTEXT_UUID_PATH_PARAMETER + "}")
@Consumes({MediaType.TEXT_PLAIN, BaseREST.APPLICATION_JSON_CHARSET_UTF_8})
@Produces(BaseREST.APPLICATION_JSON_CHARSET_UTF_8)
public String read(@PathParam(ContextManager.CONTEXT_UUID_PATH_PARAMETER) String uuid)
throws ContextNotFoundException, ResourceRegistryException {
if(uuid.compareTo(ContextPath.CURRENT_CONTEXT_PATH_PART)==0){
//uuid = ContextUtility.getCurrentSecurityContext().getUUID().toString();
// TODO get from IS
}
logger.info("Requested to read {} with id {} ", Context.NAME, uuid);
// setAccountingMethod(Method.READ, Context.NAME);
return "";
}
/*
* Update an existing Context definition
*
* PUT /contexts/{UUID}
* e.g. PUT /contexts/c0f314e7-2807-4241-a792-2a6c79ed4fd0
*
* BODY: {...}
*
*/
@PUT
@Path("{" + ContextManager.CONTEXT_UUID_PATH_PARAMETER + "}")
@Consumes({MediaType.TEXT_PLAIN, BaseREST.APPLICATION_JSON_CHARSET_UTF_8})
@Produces(BaseREST.APPLICATION_JSON_CHARSET_UTF_8)
public String update(@PathParam(ContextManager.CONTEXT_UUID_PATH_PARAMETER) String uuid, String json)
throws ResourceRegistryException {
logger.info("Requested to update/create {} with json {} ", Context.NAME, json);
// setAccountingMethod(Method.UPDATE, Context.NAME);
return "";
}
/*
* DELETE /contexts/{UUID}
* e.g. DELETE /contexts/c0f314e7-2807-4241-a792-2a6c79ed4fd0
*/
@DELETE
@Consumes({MediaType.TEXT_PLAIN, BaseREST.APPLICATION_JSON_CHARSET_UTF_8})
@Path("{" + ContextManager.CONTEXT_UUID_PATH_PARAMETER + "}")
public Response delete(@PathParam(ContextManager.CONTEXT_UUID_PATH_PARAMETER) String uuid)
throws ContextNotFoundException, ResourceRegistryException {
logger.info("Requested to delete {} with id {} ", Context.NAME, uuid);
setAccountingMethod("deleteContext");
return Response.status(Status.NO_CONTENT).build();
}
}

View File

@ -11,7 +11,7 @@ import jakarta.ws.rs.ext.Provider;
* @author Luca Frosini (ISTI - CNR)
*/
@Provider
public class ResourceManagerExceptionMapper implements ExceptionMapper<Exception> {
public class RMExceptionMapper implements ExceptionMapper<Exception> {
@Override
public Response toResponse(Exception exception) {

View File

@ -32,10 +32,10 @@ import jakarta.ws.rs.core.Response.Status;
import jakarta.xml.ws.WebServiceException;
/**
* The catalogue configuration for the context of the request
* The Resource Manager configuration for the context of the request
* (i.e. the context where the token has been generated).
*
* Only Catalogue-Managers are able to invoke non-safe methods.
* Only Managers are able to invoke non-safe methods.
*
* @author Luca Frosini (ISTI - CNR)
*/
@ -100,18 +100,18 @@ public class Configuration extends BaseREST {
}
/**
* This API allows to read the RR configuration for the
* This API allows to read the RM configuration for the
* current context (i.e. the context where the token has been generated).<br/>
*/
@GET
@Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}")
@Produces(BaseREST.APPLICATION_JSON_CHARSET_UTF_8)
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
@StatusCodes ({
@ResponseCode(code = 200, condition = "Resource Manager configuration successfully read."),
@ResponseCode(code = 401, condition = "Only User with role Manager above can read the configuration."),
@ResponseCode(code = 500, condition = "Error while reading catalogue configuration."),
@ResponseCode(code = 500, condition = "Error while reading the configuration."),
})
/**
* @param context
* @return
@ -153,11 +153,10 @@ public class Configuration extends BaseREST {
@Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}")
@Consumes(BaseREST.APPLICATION_JSON_CHARSET_UTF_8)
@Produces(BaseREST.APPLICATION_JSON_CHARSET_UTF_8)
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
@StatusCodes ({
@ResponseCode(code = 200, condition = "Catalogue configuration successfully created/updated."),
@ResponseCode(code = 401, condition = "Only Catalogue-Managers can create/update catalogue configuration."),
@ResponseCode(code = 500, condition = "Error while creating/updating catalogue configuration."),
@ResponseCode(code = 200, condition = "Resource Manager configuration successfully created/updated."),
@ResponseCode(code = 401, condition = "Only User with role Manager above can create/update the configuration."),
@ResponseCode(code = 500, condition = "Error while creating/updating the configuration."),
})
@OperationId("Create or Update")
public String createOrUpdate(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context, String json) throws WebServiceException {
@ -187,7 +186,7 @@ public class Configuration extends BaseREST {
}
/**
* This API allows to patch the catalogue configuration for the
* This API allows to patch the Resource Manager configuration for the
* context of the request (i.e. the context where the token has been generated)
* using the json provided as request body.<br/>
*/
@ -195,11 +194,10 @@ public class Configuration extends BaseREST {
@Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}")
@Consumes(BaseREST.APPLICATION_JSON_CHARSET_UTF_8)
@Produces(BaseREST.APPLICATION_JSON_CHARSET_UTF_8)
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
@StatusCodes ({
@ResponseCode(code = 200, condition = "Catalogue configuration successfully updated."),
@ResponseCode(code = 401, condition = "Only Catalogue-Managers can update catalogue configuration."),
@ResponseCode(code = 500, condition = "Error while updating catalogue configuration."),
@ResponseCode(code = 200, condition = "Resource Manager configuration successfully patched."),
@ResponseCode(code = 401, condition = "Only User with role Manager above can patch the configuration."),
@ResponseCode(code = 500, condition = "Error while patching the configuration."),
})
public Response patch(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context, String json) throws WebServiceException {
try {
@ -233,11 +231,11 @@ public class Configuration extends BaseREST {
* context of the request (i.e. the context where the token has been generated).<br/>
*
* This API forces the service to read again from the Information System (IS)
* the resource manager configuration for the context of the request.<br/>
* the Resource Manager configuration for the context of the request.<br/>
*
* If the user specifies the <code>purge</code> query parameter this API
* remove the configuration from the IS. Please note that this implies that
* the resource manager is no more configured for the context of the request.
* the Resource Manager is no more configured for the context of the request.
*
*
* @param context context must contains the context of the request
@ -253,9 +251,9 @@ public class Configuration extends BaseREST {
@Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}")
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
@StatusCodes ({
@ResponseCode(code = 200, condition = "Catalogue configuration successfully deleted."),
@ResponseCode(code = 401, condition = "Only Catalogue-Managers can delete catalogue configuration."),
@ResponseCode(code = 500, condition = "Error while deleting catalogue configuration."),
@ResponseCode(code = 200, condition = "Resource Manager configuration successfully deleted."),
@ResponseCode(code = 401, condition = "Only User with role Manager above can delete the configuration."),
@ResponseCode(code = 500, condition = "Error while deleting the configuration."),
})
public Response delete(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context,
@QueryParam(BaseREST.PURGE_QUERY_PARAMETER) @DefaultValue("false") Boolean purge) throws WebServiceException {
@ -304,9 +302,9 @@ public class Configuration extends BaseREST {
@Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}")
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
@StatusCodes ({
@ResponseCode(code = 200, condition = "Catalogue configuration successfully deleted."),
@ResponseCode(code = 401, condition = "Only Catalogue-Managers can delete catalogue configuration."),
@ResponseCode(code = 500, condition = "Error while deleting catalogue configuration."),
@ResponseCode(code = 200, condition = "Resource Manager configuration purged deleted."),
@ResponseCode(code = 401, condition = "Only User with role Manager above can purge the configuration."),
@ResponseCode(code = 500, condition = "Error while purging the configuration."),
})
public Response purge(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context) throws WebServiceException {
try {

View File

@ -3,20 +3,19 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>Hello World</display-name>
<description>
A gcube HelloWorld service - smartgears 4
</description>
<servlet>
<servlet-name>HelloWorld</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>org.gcube.service.helloworld.services</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorld</servlet-name>
<url-pattern>/*</url-pattern>
<servlet-name>org.gcube.resourcemanagement.RMInitializer</servlet-name>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/docs/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/api-docs/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>org.gcube.resourcemanagement.RMInitializer</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>