diff --git a/.github/workflows/run-terraform.yml b/.github/workflows/run-terraform.yml index 98c3afbf..a9114175 100644 --- a/.github/workflows/run-terraform.yml +++ b/.github/workflows/run-terraform.yml @@ -44,7 +44,7 @@ jobs: uses: helm/kind-action@v1.10.0 with: config: mxd/kind.config.yaml - + cluster_name: mxd - name: "Install nginx ingress controller" run: |- echo "::notice title=nginx ingress on KinD::For details how to run nginx ingress on KinD check https://kind.sigs.k8s.io/docs/user/ingress/#ingress-nginx" @@ -53,7 +53,15 @@ jobs: --for=condition=ready pod \ --selector=app.kubernetes.io/component=controller \ --timeout=90s - + - name: Setup JDK 21 + uses: actions/setup-java@v3.13.0 + with: + java-version: '21' + distribution: 'temurin' + - name: "Backend-Service Build" + working-directory: mxd/backend-service + run: |- + ./gradlew clean dockerize - name: "Terraform init" working-directory: mxd run: |- diff --git a/.github/workflows/verify-backend-service.yaml b/.github/workflows/verify-backend-service.yaml new file mode 100644 index 00000000..e7568bff --- /dev/null +++ b/.github/workflows/verify-backend-service.yaml @@ -0,0 +1,47 @@ +# +# Copyright (c) 2024 SAP SE +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# SAP SE - initial API and implementation + +--- +name: "Run Tests" + +on: + push: + branches: + - main + + pull_request: + branches: + - main + paths: + - 'mxd/backend-service/**' + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + + End-To-End-Tests: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '21' + + - run: ./gradlew test + working-directory: mxd/backend-service \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2b61c6c9..1e3c4fb0 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,11 @@ override.tf.json terraform.rc .idea/ -mxd/.terraform.lock.hcl \ No newline at end of file +mxd/.terraform.lock.hcl +#Ignore Java Classes and Gradle Build Files +.class.* +mxd/backend-service/.gradle +.lock +.bin +mxd/backend-service/build +*.log \ No newline at end of file diff --git a/mxd/README.md b/mxd/README.md index 25efdf90..0f1d508b 100644 --- a/mxd/README.md +++ b/mxd/README.md @@ -31,6 +31,8 @@ once and are accessible by all participants. For the most bare-bones installation of the dataspace, execute the following commands in a shell: ```shell +cd +./gradlew clean dockerize # firstly go to the folder containing the config files cd kind create cluster -n mxd --config kind.config.yaml @@ -41,6 +43,7 @@ kubectl wait --namespace ingress-nginx \ --for=condition=ready pod \ --selector=app.kubernetes.io/component=controller \ --timeout=90s +kind load docker-image --name mxd backend-service:1.0.0 terraform init terraform apply # type "yes" and press enter when prompted to do so diff --git a/mxd/backend-service.tf b/mxd/backend-service.tf new file mode 100644 index 00000000..d7099843 --- /dev/null +++ b/mxd/backend-service.tf @@ -0,0 +1,93 @@ +# +# Copyright (c) 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +resource "kubernetes_deployment" "backend-service" { + + metadata { + name = "backend-service" + labels = { + App = "backend-service" + } + } + + spec { + replicas = 1 + selector { + match_labels = { + App = "backend-service" + } + } + template { + metadata { + labels = { + App = "backend-service" + } + } + spec { + container { + name = "backend-service" + image = "backend-service:1.0.0" + image_pull_policy = "Never" + + port { + container_port = 8080 + name = "backend-port" + } + env { + name = "edc.datasource.backendservice.url" + value = "jdbc:postgresql://${local.backendservice-postgres.database-url}/${local.databases.backendservice.database-name}" + } + + env { + name = "edc.datasource.backendservice.user" + value = local.databases.backendservice.database-username + } + env { + name = "edc.datasource.backendservice.password" + value = local.databases.backendservice.database-password + } + env { + name = "web.http.port" + value = 8080 + } + env { + name = "JAVA_TOOL_OPTIONS" + value = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044" + } + } + } + } + } +} + +resource "kubernetes_service" "backend-service" { + metadata { + name = "backend-service" + } + spec { + selector = { + App = "backend-service" + } + port { + port = 8080 + name = "backend-port" + } + } +} + diff --git a/mxd/backend-service/Dockerfile b/mxd/backend-service/Dockerfile new file mode 100644 index 00000000..d96f3c53 --- /dev/null +++ b/mxd/backend-service/Dockerfile @@ -0,0 +1,23 @@ +# +# Copyright (c) 2024 SAP SE +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# SAP SE - initial API and implementation +# +# + +FROM eclipse-temurin:21.0.2_13-jre-alpine + +WORKDIR /app + +COPY backend-service.jar /app/backend-service.jar + +EXPOSE 8080 + +CMD java -jar backend-service.jar diff --git a/mxd/backend-service/README.md b/mxd/backend-service/README.md new file mode 100644 index 00000000..9c2bd556 --- /dev/null +++ b/mxd/backend-service/README.md @@ -0,0 +1,134 @@ +# Tractus-X Backend Service + +## API Details Summary + +The Backend Service is used to simulate a data source or sink enabling the transfer of data and supporting the validation of the end-to-end process. It has the following APIs: +- [Contents API](#contents-api) +- [Transfer API](#transfer-api) + +## Contents API + +### Generate a random content + +- Method: GET +- URL: http://localhost/backend-service/api/v1/contents/random +- Sample response: + +```json +{ + "userId": 718895412, + "title": "tmnx", + "text": "ivj" +} +``` + +### Save the generated content + +- Method: POST +- URL: http://localhost/backend-service/api/v1/contents +- Request body: + +```json +{ + "userId": 718895412, + "title": "tmnx", + "text": "ivj" +} +``` + +- Sample response containing content id and URL: + +```json +{ + "id": "3b777103-5e06-461b-90c6-1f99e597f60d", + "url": "http://localhost:8080/api/v1/contents/3b777103-5e06-461b-90c6-1f99e597f60d" +} +``` +This URL will be used as an endpoint in the transfer API. + +### Fetch a content + +- Method: GET +- URL: http://localhost/backend-service/api/v1/contents/{content-id} +- Sample response: + +```json +{ + "userId": 718895412, + "title": "tmnx", + "text": "ivj" +} +``` + +### Fetch all contents + +- Method: GET +- URL: http://localhost/backend-service/api/v1/contents +- Sample response: + +```json +[ + { + "userId": 1, + "id": 1, + "title": "delectus aut autem", + "completed": false + }, + { + "userId": 1, + "id": 2, + "title": "quis ut nam facilis et officia qui", + "completed": false + } +] +``` + +## Transfer API + +### Accept transfer data + +- Method: POST +- URL: http://localhost/backend-service/api/v1/transfers +- Request/response: + +```json +{ + "id": "123456789011", + "endpoint": "http://localhost:8080/api/v1/contents/3b777103-5e06-461b-90c6-1f99e597f60d", + "authKey": "Authorization", + "authCode": "100000", + "properties": {} +} +``` + +### Get transfer data with ID + +- Method: GET +- URL: http://localhost/backend-service/api/v1/transfers/{id} +- Sample response: + +```json +{ + "id": "123456789011", + "endpoint": "http://localhost:8080/api/v1/contents/3b777103-5e06-461b-90c6-1f99e597f60d", + "authKey": "Authorization", + "authCode": "100000", + "properties": {} +} +``` + +### Get transfer content +Get the content which is stored at the above endpoint: +- Method: GET +- URL: http://localhost/backend-service/api/v1/transfers/{id}/contents +- Sample response: + +```json +{ + "userId": 718895412, + "title": "tmnx", + "text": "ivj" +} +``` + +Alternatively, please check out the [Postman collections here](./assets/postman) \ No newline at end of file diff --git a/mxd/backend-service/assets/postman/BackendService.postman_collection.json b/mxd/backend-service/assets/postman/BackendService.postman_collection.json new file mode 100644 index 00000000..1cf7a2ef --- /dev/null +++ b/mxd/backend-service/assets/postman/BackendService.postman_collection.json @@ -0,0 +1,211 @@ +{ + "info": { + "_postman_id": "6ab595a8-0851-4288-b9c2-c9c2b9984a60", + "name": "BackendService", + "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json", + "_exporter_id": "34215553" + }, + "item": [ + { + "name": "GenerateRandomContent", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "http://localhost/backend-service/api/v1/contents/random" + }, + "response": [] + }, + { + "name": "CreateContent", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const responseJson = pm.response.json();\r", + "const id = responseJson.id\r", + "\r", + "pm.collectionVariables.set('content-id', id);" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"userId\": 918704604,\r\n \"title\": \"agwng\",\r\n \"text\": \"oz\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "http://localhost/backend-service/api/v1/contents" + }, + "response": [] + }, + { + "name": "GetAllContents", + "request": { + "method": "GET", + "header": [], + "url": "http://localhost/backend-service/api/v1/contents" + }, + "response": [] + }, + { + "name": "FetchAContent", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "http://localhost/backend-service/api/v1/contents/{{content-id}}" + }, + "response": [] + }, + { + "name": "AcceptTransfer", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const responseJson = pm.response.json();\r", + "const id = responseJson.id\r", + "\r", + "pm.collectionVariables.set('transfer-id', id);" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": \"31\",\r\n \"endpoint\": \"http://localhost:8080/api/v1/contents/{{content-id}}\",\r\n \"authKey\": \"Authorization\",\r\n \"authCode\": \"100000\",\r\n \"properties\": {}\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "http://localhost/backend-service/api/v1/transfers" + }, + "response": [] + }, + { + "name": "GetTransfer", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "http://localhost/backend-service/api/v1/transfers/{{transfer-id}}" + }, + "response": [] + }, + { + "name": "GetTransferContent", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": \"123456789011\",\r\n \"endpoint\": \"http://alice-tractusx-connector-dataplane:8081/api/public\",\r\n \"authKey\": \"Authorization\",\r\n \"authCode\": \"100000\",\r\n \"properties\": {}\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "http://localhost/backend-service/api/v1/transfers/{{transfer-id}}/contents" + }, + "response": [] + } + ], + "variable": [ + { + "key": "content-id", + "value": "" + }, + { + "key": "create-req", + "value": "" + }, + { + "key": "content-url", + "value": "" + }, + { + "key": "transfer-id", + "value": "" + } + ] +} \ No newline at end of file diff --git a/mxd/backend-service/build.gradle.kts b/mxd/backend-service/build.gradle.kts new file mode 100644 index 00000000..bad86d49 --- /dev/null +++ b/mxd/backend-service/build.gradle.kts @@ -0,0 +1,92 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage + +plugins { + id("java") + `java-library` + `java-test-fixtures` + id("application") + alias(libs.plugins.shadow) + id("com.bmuschko.docker-remote-api") version "9.3.6" +} + +group = "org.eclipse.tractusx.mxd.backendservice" +version = "1.0.0" + +repositories { + mavenCentral() +} +application { + mainClass.set("org.eclipse.edc.boot.system.runtime.BaseRuntime") +} + +dependencies { + implementation(libs.restAssured) + implementation(libs.edc.configuration.filesystem) + implementation(libs.edc.json.ld) + implementation(libs.edc.http) + implementation(libs.edc.http.lib) + implementation(libs.edc.connector.core) + implementation(libs.edc.sql.core) + implementation(libs.apache.commons) + implementation(libs.postgresql) + implementation(libs.edc.boot) + + testImplementation(libs.junit.jupiter.params) + testImplementation(libs.assertj) + testImplementation(libs.edc.junit) + testImplementation(libs.postgres.containers) + testImplementation(testFixtures(libs.edc.sql.core)) + +} +tasks.withType { + mergeServiceFiles() + archiveFileName.set("backend-service.jar") +} +tasks { + "test"(Test::class) { + useJUnitPlatform() + + testLogging { + showStandardStreams = true + } + } +} + +tasks.register("printClasspath") { + doLast { + println("${sourceSets["main"].runtimeClasspath.asPath}") + } +} + +val copyDockerFile = tasks.create("copyDockerFile", Copy::class) { + from(project.projectDir) + into("${project.buildDir}/docker") + + include("Dockerfile") +} +val copyJar = tasks.create("copyJar", Copy::class) { + from("${project.buildDir}/libs") + into("${project.buildDir}/docker") + include("${project.name}.jar") +} + +val dockerTask: DockerBuildImage = tasks.create("dockerize", DockerBuildImage::class) { + images.add("${project.name}:${project.version}") + images.add("${project.name}:latest") +} +dockerTask.dependsOn(tasks.named("build"), tasks.named("copyDockerFile"), tasks.named("copyJar")) +copyJar.dependsOn(tasks.named("build")) \ No newline at end of file diff --git a/mxd/backend-service/build/resources/main/schema.sql b/mxd/backend-service/build/resources/main/schema.sql new file mode 100644 index 00000000..4b866650 --- /dev/null +++ b/mxd/backend-service/build/resources/main/schema.sql @@ -0,0 +1,35 @@ +-- +-- Copyright (c) 2024 SAP SE +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- SAP SE - initial API and implementation +-- + +-- Statements are designed for and tested with Postgres only! +CREATE TABLE IF NOT EXISTS content +( + id text, + asset text, + createddate timestamp(6) DEFAULT CURRENT_TIMESTAMP, + updateddate timestamp(6) DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT content_pkey PRIMARY KEY (id) +); + + +CREATE TABLE IF NOT EXISTS transfer +( + transferid text, + asset text, + contents text, + createddate timestamp(6) DEFAULT CURRENT_TIMESTAMP, + updateddate timestamp(6) DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT transfer_pkey PRIMARY KEY (transferid) +); + + diff --git a/mxd/backend-service/dataspaceconnector-configuration.properties b/mxd/backend-service/dataspaceconnector-configuration.properties new file mode 100644 index 00000000..47e6362e --- /dev/null +++ b/mxd/backend-service/dataspaceconnector-configuration.properties @@ -0,0 +1,6 @@ +edc.hostname=localhost +web.http.port=8080 +web.http.path=/api +edc.datasource.backendservice.url=jdbc:postgresql://localhost:5432/backendservice +edc.datasource.backendservice.user=backendservice +edc.datasource.backendservice.password=backendservice diff --git a/mxd/backend-service/gradle/libs.versions.toml b/mxd/backend-service/gradle/libs.versions.toml new file mode 100644 index 00000000..b2fe0624 --- /dev/null +++ b/mxd/backend-service/gradle/libs.versions.toml @@ -0,0 +1,28 @@ +[metadata] +format.version = "1.1" +[versions] +assertj = "3.25.3" +edc = "0.7.0" +commons = "2.11.0" +jupiter = "5.10.1" +restAssured = "5.4.0" +postgresql = "42.7.2" +testcontainers = "1.19.7" + +[libraries] +edc-boot = {module = "org.eclipse.edc:boot" , version.ref="edc"} +edc-junit = { module = "org.eclipse.edc:junit" , version.ref = "edc" } +assertj = { module = "org.assertj:assertj-core", version.ref = "assertj" } +restAssured = { module = "io.rest-assured:rest-assured", version.ref = "restAssured" } +junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "jupiter" } +edc-json-ld = { module = "org.eclipse.edc:json-ld", version.ref = "edc" } +edc-http = { module = "org.eclipse.edc:http", version.ref = "edc" } +edc-http-lib = { module = "org.eclipse.edc:http-lib", version.ref = "edc" } +edc-connector-core = { module = "org.eclipse.edc:connector-core", version.ref = "edc" } +edc-sql-core = {module = "org.eclipse.edc:sql-core",version.ref = "edc"} +apache-commons = { module = "org.apache.commons:commons-dbcp2", version.ref = "commons" } +edc-configuration-filesystem = { module = "org.eclipse.edc:configuration-filesystem", version.ref = "edc" } +postgres-containers = { module = "org.testcontainers:postgresql", version.ref = "testcontainers" } +postgresql = { module = "org.postgresql:postgresql", version.ref = "postgresql" } +[plugins] +shadow = { id = "com.github.johnrengelman.shadow", version = "8.1.1" } diff --git a/mxd/backend-service/gradle/wrapper/gradle-wrapper.jar b/mxd/backend-service/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..249e5832 Binary files /dev/null and b/mxd/backend-service/gradle/wrapper/gradle-wrapper.jar differ diff --git a/mxd/backend-service/gradle/wrapper/gradle-wrapper.properties b/mxd/backend-service/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..880fc25d --- /dev/null +++ b/mxd/backend-service/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Dec 18 15:03:56 IST 2024 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/mxd/backend-service/gradlew b/mxd/backend-service/gradlew new file mode 100755 index 00000000..1b6c7873 --- /dev/null +++ b/mxd/backend-service/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/mxd/backend-service/gradlew.bat b/mxd/backend-service/gradlew.bat new file mode 100755 index 00000000..107acd32 --- /dev/null +++ b/mxd/backend-service/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/mxd/backend-service/settings.gradle.kts b/mxd/backend-service/settings.gradle.kts new file mode 100644 index 00000000..fc841886 --- /dev/null +++ b/mxd/backend-service/settings.gradle.kts @@ -0,0 +1,16 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +rootProject.name = "backend-service" + diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/BackendServiceExtension.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/BackendServiceExtension.java new file mode 100644 index 00000000..2a1bd005 --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/BackendServiceExtension.java @@ -0,0 +1,160 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice; + +import org.apache.commons.dbcp2.BasicDataSource; +import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.runtime.metamodel.annotation.Setting; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.http.spi.EdcHttpClient; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.edc.sql.QueryExecutor; +import org.eclipse.edc.transaction.datasource.spi.DataSourceRegistry; +import org.eclipse.edc.transaction.spi.TransactionContext; +import org.eclipse.edc.web.spi.WebService; +import org.eclipse.tractusx.mxd.backendservice.controller.ContentApiController; +import org.eclipse.tractusx.mxd.backendservice.controller.TransferApiController; +import org.eclipse.tractusx.mxd.backendservice.service.*; +import org.eclipse.tractusx.mxd.backendservice.statements.ContentStatementsServiceImpl; +import org.eclipse.tractusx.mxd.backendservice.statements.TransferStatementsServiceImpl; +import org.eclipse.tractusx.mxd.backendservice.store.SqlContentStoreServiceImpl; +import org.eclipse.tractusx.mxd.backendservice.store.SqlTransferStoreServiceImpl; +import org.eclipse.tractusx.mxd.util.Constants; + +import javax.sql.DataSource; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.sql.Connection; + +@Extension(BackendServiceExtension.NAME) +public class BackendServiceExtension implements ServiceExtension { + + public static final String NAME = "Backend Services"; + + @Inject + private WebService webService; + + @Inject + private DataSourceRegistry dataSourceRegistry; + + @Inject + private TransactionContext transactionContext; + + @Inject + private TypeManager typeManager; + + @Inject + private QueryExecutor queryExecutor; + + @Inject + private EdcHttpClient edcHttpClient; + + @Setting(value = "host name") + private final static String HOSTNAME_SETTING = "edc.hostname"; + @Setting(value = "host name") + private final static String PORT_SETTING = "web.http.port"; + + private final static String DEFAULT_HOST = "localhost"; + private final static String DEFAULT_PORT = "8080"; + + @Override + public String name() { + return NAME; + } + + @Override + public void initialize(ServiceExtensionContext context) { + dataSourceRegistry.register(Constants.DATA_SOURCE_NAME, createLocalDataSource(context)); + + Monitor monitor = context.getMonitor(); + String dataSourceName = context.getConfig().getString(Constants.DATASOURCE_NAME_SETTING, + DataSourceRegistry.DEFAULT_DATASOURCE); + + // Initialize SQL content store + var sqlContentStore = new SqlContentStoreServiceImpl( + dataSourceRegistry, + dataSourceName, + transactionContext, + typeManager.getMapper(), + queryExecutor, + new ContentStatementsServiceImpl(), + monitor); + ContentService contentService = new ContentServiceImpl(sqlContentStore, monitor); + webService.registerResource( + new ContentApiController( + contentService, + monitor, + typeManager.getMapper(), + context.getSetting(HOSTNAME_SETTING, DEFAULT_HOST), + context.getSetting(PORT_SETTING, DEFAULT_PORT) + )); + + // Initialize SQL transfer store + var sqlTransferStore = new SqlTransferStoreServiceImpl( + dataSourceRegistry, + dataSourceName, + transactionContext, + typeManager.getMapper(), + queryExecutor, + new TransferStatementsServiceImpl(), + monitor); + + TransferService transferService = new TransferServiceImpl( + sqlTransferStore, + edcHttpClient, + monitor, + new HttpConnectionService(edcHttpClient, monitor)); + webService.registerResource( + new TransferApiController( + transferService, + monitor, + typeManager.getMapper() + )); + } + + public DataSource createLocalDataSource(ServiceExtensionContext context) { + BasicDataSource dataSource = new BasicDataSource(); + + var url = context.getConfig().getString(Constants.DATABASE_URL); + var userName = context.getConfig().getString(Constants.DATABASE_USER); + var password = context.getConfig().getString(Constants.DATABASE_PASSWORD); + + try { + dataSource.setDriverClassName(Constants.DEFAULT_DRIVE); + dataSource.setUrl(url); + dataSource.setUsername(userName); + dataSource.setPassword(password); + Connection connections = dataSource.getConnection(); + InputStream inputStream = BackendServiceExtension.class + .getClassLoader() + .getResourceAsStream(Constants.SCHEMA_PATH); + if (inputStream != null) { + String schema = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); + transactionContext.execute(() -> queryExecutor.execute(connections, schema)); + context.getMonitor().info(String.valueOf(connections)); + } else { + context.getMonitor().info("schema.sql File not found on the classpath."); + } + return dataSource; + } catch (Exception e) { + context.getMonitor().severe(e.getMessage()); + throw new EdcException(e.getMessage()); + } + } +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/controller/ContentApiController.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/controller/ContentApiController.java new file mode 100644 index 00000000..6d742362 --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/controller/ContentApiController.java @@ -0,0 +1,96 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.controller; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.UriBuilder; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.tractusx.mxd.backendservice.service.ContentService; +import org.eclipse.tractusx.mxd.util.Constants; +import org.eclipse.tractusx.mxd.util.Converter; + +import java.io.IOException; +import java.util.Optional; + +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Path("/v1/contents") +public class ContentApiController { + + private final Monitor monitor; + + private final ContentService service; + + private final ObjectMapper objectMapper; + + private final String host; + private final String port; + + public ContentApiController(ContentService service, Monitor monitor, ObjectMapper objectMapper, + String host, String port) { + this.service = service; + this.monitor = monitor; + this.objectMapper = objectMapper; + this.host = host; + this.port = port; + } + + @POST + public String createContent(Object contentJson) { + var contentID = this.service.create(contentJson); + return createJsonResponse(contentID); + } + + @GET + public String getAllContent() { + return this.service.getAllContent().toString(); + } + + @GET + @Path("/{contentId}") + public String getContentByID(@PathParam("contentId") String contentId) { + return Optional.of(contentId) + .map(id -> service.getContent(contentId)) + .map(content -> content.getContent() != null ? content.getContent().getData() : Converter.toJson(content.getFailure(), objectMapper)) + .orElse(Constants.DEFAULT_ERROR_MESSAGE); + } + + @GET + @Path("/random") + public String getRandomContent() { + return this.service.getRandomContent(); + } + + private String createJsonResponse(String id) { + JsonNode jsonResponse = objectMapper.createObjectNode() + .put("id", id) + .put("url", UriBuilder.fromUri("http://" + host + ":" + port) + .path("api") + .path("v1") + .path("contents") + .path(String.valueOf(id)) + .build().toString()); + try { + return objectMapper.writeValueAsString(jsonResponse); + } catch (IOException e) { + throw new EdcException(e.getMessage()); + } + } + +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/controller/TransferApiController.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/controller/TransferApiController.java new file mode 100644 index 00000000..4a6ef78b --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/controller/TransferApiController.java @@ -0,0 +1,93 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.tractusx.mxd.backendservice.entity.Transfer; +import org.eclipse.tractusx.mxd.backendservice.entity.TransferRequest; +import org.eclipse.tractusx.mxd.backendservice.entity.TransferResponse; +import org.eclipse.tractusx.mxd.backendservice.service.TransferService; +import org.eclipse.tractusx.mxd.util.Constants; +import org.eclipse.tractusx.mxd.util.Converter; + +import java.util.List; +import java.util.Optional; + + +@Consumes(MediaType.APPLICATION_JSON) +@Produces((MediaType.APPLICATION_JSON)) +@Path("/v1/transfers") +public class TransferApiController { + + private final TransferService transferService; + private final Monitor monitor; + + private final ObjectMapper objectMapper; + + public TransferApiController(TransferService transferService, Monitor monitor, ObjectMapper objectMapper) { + this.transferService = transferService; + this.monitor = monitor; + this.objectMapper = objectMapper; + } + + @POST + @Produces((MediaType.APPLICATION_JSON)) + @Consumes((MediaType.APPLICATION_JSON)) + public TransferRequest insertTransfer(TransferRequest transferRequest) { + Transfer transfer = Transfer.Builder.newInstance() + .id(transferRequest.getId()) + .endpoint(transferRequest.getEndpoint()) + .authKey(transferRequest.getAuthKey()) + .authCode(transferRequest.getAuthCode()) + .properties(transferRequest.getProperties()) + .build(); + var transferResponse = this.transferService.create(transfer, monitor) + .map(a -> TransferRequest.builder() + .id(a.getId()) + .endpoint(a.getEndpoint()) + .authKey(a.getAuthKey()) + .authCode(a.getAuthCode()) + .properties(a.getProperties()) + .build()); + return transferResponse.getContent(); + + } + + @GET + public List getAllTransfer() { + return this.transferService.getAllTransfer(); + } + + @GET + @Path("/{transferId}") + public String getTransfer(@PathParam("transferId") String transferId) { + return Optional.of(transferId) + .map(id -> transferService.getTransfer(transferId)) + .map(transfer -> transfer.getContent() != null ? transfer.getContent().getAsset() : Converter.toJson(transfer.getFailure(), objectMapper)) + .orElse(Constants.DEFAULT_ERROR_MESSAGE); + } + + @GET + @Path("/{id}/contents") + public Object getTransferContents(@PathParam("id") String transferId) { + return Optional.of(transferId) + .map(id -> transferService.getTransfer(transferId)) + .map(transfer -> transfer.getContent() != null ? transfer.getContent().getContents() : Converter.toJson(transfer.getFailure(), objectMapper)) + .orElse(Constants.DEFAULT_ERROR_MESSAGE); + } +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/entity/Content.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/entity/Content.java new file mode 100644 index 00000000..e4a1d29e --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/entity/Content.java @@ -0,0 +1,52 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.entity; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import org.eclipse.edc.spi.entity.Entity; + +@JsonDeserialize(builder = Content.Builder.class) +public class Content extends Entity { + + private String data; + + private Content() { + } + + @JsonPOJOBuilder(withPrefix = "") + public static class Builder extends Entity.Builder { + + protected Builder(Content content) { + super(content); + } + + @JsonCreator + public static Builder newInstance() { + return new Builder(new Content()); + } + + public Builder data(String data) { + entity.data = data; + return self(); + } + + @Override + public Builder self() { + return this; + } + } +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/entity/ContentResponse.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/entity/ContentResponse.java new file mode 100644 index 00000000..8558dea5 --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/entity/ContentResponse.java @@ -0,0 +1,65 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.entity; + +public class ContentResponse { + + private String id; + private String data; + + private ContentResponse() { + } + + public String getId() { + return id; + } + + public String getData() { + return data; + } + + @Override + public String toString() { + return "{" + + "id=" + id + + ",data=" + data + + "}"; + } + + public static class Builder { + private String id; + private String data; + + public Builder() { + } + + public ContentResponse.Builder setId(String id) { + this.id = id; + return this; + } + + public ContentResponse.Builder setData(String data) { + this.data = data; + return this; + } + + public ContentResponse build() { + ContentResponse contentModel = new ContentResponse(); + contentModel.id = this.id; + contentModel.data = this.data; + return contentModel; + } + } +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/entity/Transfer.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/entity/Transfer.java new file mode 100644 index 00000000..1e251fff --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/entity/Transfer.java @@ -0,0 +1,110 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.entity; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import org.eclipse.edc.spi.entity.Entity; + +import java.util.Map; + +import static java.util.Objects.requireNonNull; + +@JsonDeserialize(builder = Transfer.Builder.class) +public class Transfer extends Entity { + + private String endpoint; + private String authKey; + private String authCode; + private Map properties; + + private Transfer() { + } + + public String getEndpoint() { + return endpoint; + } + + public String getAuthKey() { + return authKey; + } + + public String getAuthCode() { + return authCode; + } + + public Map getProperties() { + return properties; + } + + public Builder toBuilder() { + return Transfer.Builder.newInstance() + .id(id) + .authCode(authCode) + .authKey(authKey) + .endpoint(endpoint) + .properties(properties) + .createdAt(createdAt); + } + + @JsonPOJOBuilder(withPrefix = "") + public static class Builder extends Entity.Builder { + + protected Builder(Transfer transfer) { + super(transfer); + } + + @JsonCreator + public static Builder newInstance() { + return new Builder(new Transfer()); + } + + public Builder endpoint(String endpoint) { + entity.endpoint = endpoint; + return self(); + } + + public Builder authKey(String authKey) { + entity.authKey = authKey; + return self(); + } + + public Builder authCode(String authCode) { + entity.authCode = authCode; + return self(); + } + + public Builder properties(Map properties) { + entity.properties = properties; + return self(); + } + + @Override + public Builder self() { + return this; + } + + @Override + public Transfer build() { + super.build(); + requireNonNull(entity.id); + requireNonNull(entity.endpoint); + requireNonNull(entity.authKey); + requireNonNull(entity.authCode); + return entity; + } + } +} \ No newline at end of file diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/entity/TransferRequest.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/entity/TransferRequest.java new file mode 100644 index 00000000..7f8082f6 --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/entity/TransferRequest.java @@ -0,0 +1,112 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.entity; + +import java.util.Map; + +public class TransferRequest { + + private String id; + private String endpoint; + private String authKey; + private String authCode; + private Map properties; + + private TransferRequest() { + } + + private TransferRequest(String id, String endpoint, String authKey, String authCode, Map properties) { + this.id = id; + this.endpoint = endpoint; + this.authKey = authKey; + this.authCode = authCode; + this.properties = properties; + } + + public static Builder builder() { + return new Builder(); + } + + public String getId() { + return id; + } + + public String getEndpoint() { + return endpoint; + } + + public String getAuthKey() { + return authKey; + } + + public String getAuthCode() { + return authCode; + } + + public Map getProperties() { + return properties; + } + + @Override + public String toString() { + return "TransferRequest{" + + "id='" + id + '\'' + + ", endpoint='" + endpoint + '\'' + + ", authKey='" + authKey + '\'' + + ", authCode='" + authCode + '\'' + + ", properties=" + properties + + '}'; + } + + public static class Builder { + private String id; + private String endpoint; + private String authKey; + private String authCode; + private Map properties; + + private Builder() { + } + + public Builder id(String id) { + this.id = id; + return this; + } + + public Builder endpoint(String endpoint) { + this.endpoint = endpoint; + return this; + } + + public Builder authKey(String authKey) { + this.authKey = authKey; + return this; + } + + public Builder authCode(String authCode) { + this.authCode = authCode; + return this; + } + + public Builder properties(Map properties) { + this.properties = properties; + return this; + } + + public TransferRequest build() { + return new TransferRequest(id, endpoint, authKey, authCode, properties); + } + } +} \ No newline at end of file diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/entity/TransferResponse.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/entity/TransferResponse.java new file mode 100644 index 00000000..1a080e86 --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/entity/TransferResponse.java @@ -0,0 +1,109 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.entity; + +import java.util.Date; + +public class TransferResponse { + + private String asset; + private String contents; + private String transferID; + private Date createdDate; + private Date updatedDate; + + public TransferResponse() { + } + + public TransferResponse(String asset, String contents, String transferID, Date createdDate, Date updatedDate) { + this.asset = asset; + this.contents = contents; + this.transferID = transferID; + this.createdDate = createdDate; + this.updatedDate = updatedDate; + } + + public static Builder builder() { + return new Builder(); + } + + public String getAsset() { + return asset; + } + + public String getContents() { + return contents; + } + + public String getTransferID() { + return transferID; + } + + public Date getCreatedDate() { + return createdDate; + } + + public Date getUpdatedDate() { + return updatedDate; + } + + @Override + public String toString() { + return "TransferResponse{" + + "asset='" + asset + '\'' + + ", contents='" + contents + '\'' + + ", transferID='" + transferID + '\'' + + ", createdDate=" + createdDate + + ", updatedDate=" + updatedDate + + '}'; + } + + public static class Builder { + private String asset; + private String contents; + private String transferID; + private Date createdDate; + private Date updatedDate; + + public Builder asset(String asset) { + this.asset = asset; + return this; + } + + public Builder contents(String contents) { + this.contents = contents; + return this; + } + + public Builder transferID(String transferID) { + this.transferID = transferID; + return this; + } + + public Builder createdDate(Date createdDate) { + this.createdDate = createdDate; + return this; + } + + public Builder updatedDate(Date updatedDate) { + this.updatedDate = updatedDate; + return this; + } + + public TransferResponse build() { + return new TransferResponse(asset, contents, transferID, createdDate, updatedDate); + } + } +} \ No newline at end of file diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/service/ContentService.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/service/ContentService.java new file mode 100644 index 00000000..de0908fd --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/service/ContentService.java @@ -0,0 +1,32 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.service; + +import org.eclipse.edc.spi.result.ServiceResult; +import org.eclipse.tractusx.mxd.backendservice.entity.ContentResponse; + +import java.util.List; + +public interface ContentService { + + String create(Object content); + + List getAllContent(); + + ServiceResult getContent(String contentId); + + String getRandomContent(); + +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/service/ContentServiceImpl.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/service/ContentServiceImpl.java new file mode 100644 index 00000000..fbbdee3c --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/service/ContentServiceImpl.java @@ -0,0 +1,62 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.service; + +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.ext.Provider; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.result.ServiceResult; +import org.eclipse.edc.spi.result.StoreResult; +import org.eclipse.tractusx.mxd.backendservice.entity.ContentResponse; +import org.eclipse.tractusx.mxd.backendservice.store.ContentStoreService; +import org.eclipse.tractusx.mxd.util.RandomWordUtil; + +import java.util.List; + +@Provider +@Produces(MediaType.APPLICATION_JSON) +public class ContentServiceImpl implements ContentService { + + private final ContentStoreService contentStoreService; + private final Monitor monitor; + + public ContentServiceImpl(ContentStoreService contentStoreService, Monitor monitor) { + this.contentStoreService = contentStoreService; + this.monitor = monitor; + } + + @Override + public String create(Object content) { + return contentStoreService.save(content); + } + + @Override + public List getAllContent() { + return contentStoreService.findAll(new QuerySpec()).map(ContentResponse::getData).toList(); + } + + @Override + public ServiceResult getContent(String contentId) { + StoreResult response = contentStoreService.findById(contentId); + return ServiceResult.success(response.getContent()); + } + + @Override + public String getRandomContent() { + return RandomWordUtil.generateRandom(); + } +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/service/HttpConnectionService.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/service/HttpConnectionService.java new file mode 100644 index 00000000..15b7cdd1 --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/service/HttpConnectionService.java @@ -0,0 +1,62 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.service; + +import okhttp3.Request; +import okhttp3.Response; +import org.eclipse.edc.http.spi.EdcHttpClient; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.persistence.EdcPersistenceException; +import org.eclipse.tractusx.mxd.backendservice.entity.Transfer; + +import java.io.IOException; + +public class HttpConnectionService { + + private final EdcHttpClient httpClient; + + private final Monitor monitor; + + public HttpConnectionService(EdcHttpClient httpClient, Monitor monitor) { + this.httpClient = httpClient; + this.monitor = monitor; + } + + public String getUrlAssets(Transfer receivedModel, Monitor monitor) { + monitor.info("getUrlAssets " + receivedModel); + Request getRequest = new Request.Builder() + .url(receivedModel.getEndpoint()) + .header(receivedModel.getAuthKey(), receivedModel.getAuthCode()) + .get() + .build(); + try (Response response = httpClient.execute(getRequest)) { + if (response.isSuccessful()) { + if (response.body() != null) { + monitor.info("response " + response.body()); + return response.body().string(); + } else { + monitor.warning("Response body is null"); + throw new EdcPersistenceException("Response body is null"); + } + } else { + monitor.warning("HTTP request failed with status code: " + response.code()); + throw new EdcPersistenceException("HTTP request failed with status code: " + response.code()); + } + } catch (IOException e) { + monitor.warning("IOException occurred: " + e.getMessage()); + throw new EdcPersistenceException("IOException occurred: " + e.getMessage()); + } + } +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/service/TransferService.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/service/TransferService.java new file mode 100644 index 00000000..9621dd24 --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/service/TransferService.java @@ -0,0 +1,34 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.service; + +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.result.ServiceResult; +import org.eclipse.tractusx.mxd.backendservice.entity.Transfer; +import org.eclipse.tractusx.mxd.backendservice.entity.TransferResponse; + +import java.util.List; + +public interface TransferService { + + ServiceResult create(Transfer asset, Monitor monitor); + + List getAllTransfer(); + + ServiceResult getTransfer(String transferId); + + ServiceResult getTransferContent(String transferId); + +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/service/TransferServiceImpl.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/service/TransferServiceImpl.java new file mode 100644 index 00000000..780e45d0 --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/service/TransferServiceImpl.java @@ -0,0 +1,89 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.service; + +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.ext.Provider; +import org.eclipse.edc.http.spi.EdcHttpClient; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.result.ServiceResult; +import org.eclipse.edc.spi.result.StoreResult; +import org.eclipse.tractusx.mxd.backendservice.entity.Transfer; +import org.eclipse.tractusx.mxd.backendservice.entity.TransferResponse; +import org.eclipse.tractusx.mxd.backendservice.store.TransferStoreService; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +@Provider +@Produces(MediaType.APPLICATION_JSON) +public class TransferServiceImpl implements TransferService { + + private final TransferStoreService transferStoreService; + + private final EdcHttpClient edcHttpClient; + + private final Monitor monitor; + + private final HttpConnectionService httpConnectionService; + + public TransferServiceImpl(TransferStoreService transferStoreService, EdcHttpClient edcHttpClient, Monitor monitor, HttpConnectionService httpConnectionService) { + this.transferStoreService = transferStoreService; + this.edcHttpClient = edcHttpClient; + this.monitor = monitor; + this.httpConnectionService = httpConnectionService; + } + + @Override + public ServiceResult create(Transfer asset, Monitor monitor) { + String content = httpConnectionService.getUrlAssets(asset, monitor); + transferStoreService.save(asset, content); + return ServiceResult.success(asset); + } + + @Override + public List getAllTransfer() { + QuerySpec querySpec = new QuerySpec(); + Stream transferResponse = this.transferStoreService.findAll(querySpec); + List response = new ArrayList(); + transferResponse.forEach(data -> { + TransferResponse transferResp = TransferResponse.builder() + .transferID(data.getTransferID()) + .asset(data.getAsset()) + .contents(data.getContents()) + .updatedDate(data.getUpdatedDate()) + .createdDate(data.getCreatedDate()) + .build(); + response.add(transferResp); + }); + return response; + } + + @Override + public ServiceResult getTransfer(String transferId) { + StoreResult response = this.transferStoreService.findById(transferId); + return ServiceResult.success(response.getContent()); + } + + @Override + public ServiceResult getTransferContent(String transferId) { + StoreResult response = this.transferStoreService.findById(transferId); + return ServiceResult.success(response.getContent()); + } + +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/statements/ContentStatementsService.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/statements/ContentStatementsService.java new file mode 100644 index 00000000..a6f8c6d5 --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/statements/ContentStatementsService.java @@ -0,0 +1,49 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.statements; + +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.sql.statement.SqlStatements; +import org.eclipse.edc.sql.translation.SqlQueryStatement; + +public interface ContentStatementsService extends SqlStatements { + + default String getContentTable() { + return "content"; + } + + default String getIdColumn() { + return "id"; + } + + default String getAssetColumn() { + return "asset"; + } + + default String getCreatedDateColumn() { + return "createddate"; + } + + default String getUpdatedDateColumn() { + return "updateddate"; + } + + String getFindByTemplate(); + + String getInsertTemplate(); + + SqlQueryStatement createQuery(QuerySpec querySpec); + +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/statements/ContentStatementsServiceImpl.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/statements/ContentStatementsServiceImpl.java new file mode 100644 index 00000000..31829dd4 --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/statements/ContentStatementsServiceImpl.java @@ -0,0 +1,45 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.statements; + +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.sql.translation.SqlQueryStatement; +import org.eclipse.edc.sql.translation.PostgresqlOperatorTranslator; + +import static java.lang.String.format; + +public class ContentStatementsServiceImpl implements ContentStatementsService { + + @Override + public String getFindByTemplate() { + return format("SELECT * FROM %s WHERE %s = ?", getContentTable(), getIdColumn()); + } + + @Override + public String getInsertTemplate() { + return executeStatement() + .column(getIdColumn()) + .column(getAssetColumn()) + .column(getCreatedDateColumn()) + .column(getUpdatedDateColumn()) + .insertInto(getContentTable()); + } + + @Override + public SqlQueryStatement createQuery(QuerySpec querySpec) { + var select = format("SELECT * FROM %s", getContentTable()); + return new SqlQueryStatement(select, querySpec, null, new PostgresqlOperatorTranslator()); + } +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/statements/TransferStatementsService.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/statements/TransferStatementsService.java new file mode 100644 index 00000000..06c4bb9f --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/statements/TransferStatementsService.java @@ -0,0 +1,53 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.statements; + +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.sql.statement.SqlStatements; +import org.eclipse.edc.sql.translation.SqlQueryStatement; + +public interface TransferStatementsService extends SqlStatements { + + default String getTransferTable() { + return "transfer"; + } + + default String getTransferIdColumn() { + return "transferid"; + } + + default String getAssetColumn() { + return "asset"; + } + + default String getContentsColumn() { + return "contents"; + } + + default String getCreatedDateColumn() { + return "createddate"; + } + + default String getUpdatedDateColumn() { + return "updateddate"; + } + + String getFindByTemplate(); + + String getInsertTemplate(); + + SqlQueryStatement createQuery(QuerySpec querySpec); + +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/statements/TransferStatementsServiceImpl.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/statements/TransferStatementsServiceImpl.java new file mode 100644 index 00000000..eb4fa3e9 --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/statements/TransferStatementsServiceImpl.java @@ -0,0 +1,46 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.statements; + +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.sql.translation.SqlQueryStatement; +import org.eclipse.edc.sql.translation.PostgresqlOperatorTranslator; + +import static java.lang.String.format; + +public class TransferStatementsServiceImpl implements TransferStatementsService { + + @Override + public String getFindByTemplate() { + return format("SELECT * FROM %s WHERE %s = ?", getTransferTable(), getTransferIdColumn()); + } + + @Override + public String getInsertTemplate() { + return executeStatement() + .column(getTransferIdColumn()) + .column(getAssetColumn()) + .column(getContentsColumn()) + .column(getCreatedDateColumn()) + .column(getUpdatedDateColumn()) + .insertInto(getTransferTable()); + } + + @Override + public SqlQueryStatement createQuery(QuerySpec querySpec) { + var select = format("SELECT * FROM %s", getTransferTable()); + return new SqlQueryStatement(select, querySpec, null, new PostgresqlOperatorTranslator()); + } +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/store/ContentStoreService.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/store/ContentStoreService.java new file mode 100644 index 00000000..d2b12d7b --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/store/ContentStoreService.java @@ -0,0 +1,37 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.store; + +import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.result.StoreResult; +import org.eclipse.tractusx.mxd.backendservice.entity.ContentResponse; +import org.jetbrains.annotations.NotNull; + +import java.util.stream.Stream; + +@ExtensionPoint +public interface ContentStoreService { + + String CONTENT_NOT_FOUND_TEMPLATE = "Content with ID %s not found"; + + @NotNull + Stream findAll(QuerySpec spec); + + StoreResult findById(String contentId); + + String save(Object content); + +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/store/SqlContentStoreServiceImpl.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/store/SqlContentStoreServiceImpl.java new file mode 100644 index 00000000..832e677f --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/store/SqlContentStoreServiceImpl.java @@ -0,0 +1,125 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.store; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.persistence.EdcPersistenceException; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.result.StoreResult; +import org.eclipse.edc.sql.QueryExecutor; +import org.eclipse.edc.sql.store.AbstractSqlStore; +import org.eclipse.edc.transaction.datasource.spi.DataSourceRegistry; +import org.eclipse.edc.transaction.spi.TransactionContext; +import org.eclipse.tractusx.mxd.backendservice.entity.ContentResponse; +import org.eclipse.tractusx.mxd.backendservice.statements.ContentStatementsService; +import org.jetbrains.annotations.NotNull; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Date; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Stream; + +import static java.lang.String.format; + +public class SqlContentStoreServiceImpl extends AbstractSqlStore implements ContentStoreService { + + private final ContentStatementsService statements; + private final Monitor monitor; + + public SqlContentStoreServiceImpl(DataSourceRegistry dataSourceRegistry, + String dataSourceName, + TransactionContext transactionContext, + ObjectMapper objectMapper, + QueryExecutor queryExecutor, + ContentStatementsService statements, + Monitor monitor) { + super(dataSourceRegistry, dataSourceName, transactionContext, objectMapper, queryExecutor); + this.statements = statements; + this.monitor = monitor; + } + + @Override + public @NotNull Stream findAll(QuerySpec spec) { + return transactionContext.execute(() -> { + Objects.requireNonNull(statements.createQuery(spec)); + try { + var queryStmt = statements.createQuery(spec); + return queryExecutor.query(getConnection(), true, this::mapResultSet, queryStmt.getQueryAsString(), queryStmt.getParameters()); + } catch (SQLException exception) { + monitor.warning(exception.getMessage()); + throw new EdcPersistenceException(exception); + } + }); + } + + @Override + public StoreResult findById(String contentId) { + Objects.requireNonNull(contentId); + return transactionContext.execute(() -> { + try (var connection = getConnection()) { + var content = findContentById(connection, contentId); + if (content == null) { + return StoreResult.notFound(format(CONTENT_NOT_FOUND_TEMPLATE, contentId)); + } + return StoreResult.success(content); + } catch (Exception exception) { + monitor.warning(exception.getMessage()); + throw new EdcPersistenceException(exception.getMessage(), exception); + } + }); + } + + @Override + public String save(Object content) { + try (var connection = getConnection()) { + return insertInternal(connection, content).toString(); + } catch (Exception exception) { + monitor.warning(exception.getMessage()); + throw new EdcPersistenceException(exception.getMessage(), exception); + } + } + + private UUID insertInternal(Connection connection, Object content) { + try { + monitor.info(toJson(content)); + UUID uuid = UUID.randomUUID(); + int affectedRow = queryExecutor.execute(connection, statements.getInsertTemplate(), uuid, toJson(content), new Date(), new Date()); + if (affectedRow > 0) + return uuid; + else + throw new EdcPersistenceException("Failed to insert content. 0 rows were affected."); + } catch (Exception exception) { + monitor.warning(exception.getMessage()); + throw new EdcPersistenceException(exception.getMessage(), exception); + } + } + + private ContentResponse mapResultSet(ResultSet resultSet) throws Exception { + + return new ContentResponse.Builder() + .setId(resultSet.getString(statements.getIdColumn())) + .setData(resultSet.getString(statements.getAssetColumn())) + .build(); + } + + private ContentResponse findContentById(Connection connection, String id) { + var sql = statements.getFindByTemplate(); + return queryExecutor.single(connection, false, this::mapResultSet, sql, id); + } +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/store/SqlTransferStoreServiceImpl.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/store/SqlTransferStoreServiceImpl.java new file mode 100644 index 00000000..586e7da6 --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/store/SqlTransferStoreServiceImpl.java @@ -0,0 +1,121 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.store; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.persistence.EdcPersistenceException; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.result.StoreResult; +import org.eclipse.edc.sql.QueryExecutor; +import org.eclipse.edc.sql.store.AbstractSqlStore; +import org.eclipse.edc.transaction.datasource.spi.DataSourceRegistry; +import org.eclipse.edc.transaction.spi.TransactionContext; +import org.eclipse.tractusx.mxd.backendservice.entity.Transfer; +import org.eclipse.tractusx.mxd.backendservice.entity.TransferResponse; +import org.eclipse.tractusx.mxd.backendservice.statements.TransferStatementsService; +import org.jetbrains.annotations.NotNull; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Date; +import java.util.Objects; +import java.util.stream.Stream; + +import static java.lang.String.format; + +public class SqlTransferStoreServiceImpl extends AbstractSqlStore implements TransferStoreService { + + private final TransferStatementsService statements; + private final Monitor monitor; + + public SqlTransferStoreServiceImpl(DataSourceRegistry dataSourceRegistry, + String dataSourceName, + TransactionContext transactionContext, + ObjectMapper objectMapper, + QueryExecutor queryExecutor, + TransferStatementsService statements, + Monitor monitor) { + super(dataSourceRegistry, dataSourceName, transactionContext, objectMapper, queryExecutor); + this.statements = statements; + this.monitor = monitor; + } + + @Override + public @NotNull Stream findAll(QuerySpec spec) { + return transactionContext.execute(() -> { + Objects.requireNonNull(statements.createQuery(spec)); + try { + var queryStmt = statements.createQuery(spec); + return queryExecutor.query(getConnection(), true, this::mapResultSet, queryStmt.getQueryAsString(), queryStmt.getParameters()); + } catch (SQLException exception) { + throw new EdcPersistenceException(exception); + } + }); + } + + @Override + public StoreResult findById(String transferId) { + Objects.requireNonNull(transferId); + return transactionContext.execute(() -> { + try (var connection = getConnection()) { + var content = findById(connection, transferId); + if (content == null) { + return StoreResult.notFound(format(CONTRACT_NOT_FOUND, transferId)); + } + return StoreResult.success(content); + } catch (Exception exception) { + return StoreResult.notFound(format(exception.getMessage(), exception)); + } + }); + } + + @Override + public void save(Transfer transfer, String content) { + try (var connection = getConnection()) { + insertInternal(connection, transfer, content); + } catch (Exception e) { + throw new EdcPersistenceException(e.getMessage(), e); + } + } + + private TransferResponse mapResultSet(ResultSet resultSet) throws Exception { + return TransferResponse.builder() + .asset(resultSet.getString(statements.getAssetColumn())) + .contents(resultSet.getString(statements.getContentsColumn())) + .transferID(resultSet.getString(statements.getTransferIdColumn())) + .createdDate(resultSet.getDate(statements.getCreatedDateColumn())) + .updatedDate(resultSet.getDate(statements.getUpdatedDateColumn())) + .build(); + } + + private TransferResponse findById(Connection connection, String id) { + var sql = statements.getFindByTemplate(); + return queryExecutor.single(connection, false, this::mapResultSet, sql, id); + } + + private void insertInternal(Connection connection, Transfer transfer, String content) { + try { + transactionContext.execute(() -> { + queryExecutor.execute(connection, statements.getInsertTemplate(), transfer.getId(), toJson(transfer), toJson(content), new Date(), new Date()); + }); + } catch (Exception e) { + monitor.warning(e.getMessage(), e); + throw new EdcException("Error creating transfer" + e.getMessage()); + } + } +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/store/TransferStoreService.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/store/TransferStoreService.java new file mode 100644 index 00000000..ea7ce49a --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/backendservice/store/TransferStoreService.java @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.backendservice.store; + +import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.result.StoreResult; +import org.eclipse.tractusx.mxd.backendservice.entity.Transfer; +import org.eclipse.tractusx.mxd.backendservice.entity.TransferResponse; +import org.jetbrains.annotations.NotNull; + +import java.util.stream.Stream; + +@ExtensionPoint +public interface TransferStoreService { + + String CONTRACT_NOT_FOUND = "Transfer with ID %s not found"; + + @NotNull + Stream findAll(QuerySpec spec); + + StoreResult findById(String contentId); + + void save(Transfer transfer, String content); + +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/util/Constants.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/util/Constants.java new file mode 100644 index 00000000..9959d615 --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/util/Constants.java @@ -0,0 +1,31 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.util; + +public class Constants { + + public static final String DEFAULT_ERROR_MESSAGE = "Unexpected error from backend service"; + public static final String DEFAULT_DRIVE = "org.postgresql.Driver"; + public static final String DATASOURCE_NAME_SETTING = "edc.datasource.backendservice.name"; + + public static final String DATA_SOURCE_NAME = "default"; + + public static final String SCHEMA_PATH = "schema.sql"; + + public static final String DATABASE_URL = "edc.datasource.backendservice.url"; + public static final String DATABASE_USER = "edc.datasource.backendservice.user"; + public static final String DATABASE_PASSWORD = "edc.datasource.backendservice.password"; + +} \ No newline at end of file diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/util/Converter.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/util/Converter.java new file mode 100644 index 00000000..8c34d2f1 --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/util/Converter.java @@ -0,0 +1,33 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.eclipse.edc.spi.persistence.EdcPersistenceException; + +public class Converter { + + public static String toJson(Object object, ObjectMapper objectMapper) { + if (object == null) { + return null; + } + try { + return object instanceof String ? object.toString() : objectMapper.writeValueAsString(object); + } catch (JsonProcessingException e) { + throw new EdcPersistenceException(e); + } + } +} diff --git a/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/util/RandomWordUtil.java b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/util/RandomWordUtil.java new file mode 100644 index 00000000..3b1fa714 --- /dev/null +++ b/mxd/backend-service/src/main/java/org/eclipse/tractusx/mxd/util/RandomWordUtil.java @@ -0,0 +1,83 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.eclipse.edc.spi.EdcException; + +import java.security.SecureRandom; + +public class RandomWordUtil { + + public static String generateRandom() { + try { + ObjectMapper objectMapper = new ObjectMapper(); + RandomData randomData = new RandomData(); + randomData.setUserId(generateRandomUserId()); + randomData.setTitle(generateRandomString()); + randomData.setText(generateRandomString()); + return objectMapper.writeValueAsString(randomData); + } catch (Exception e) { + throw new EdcException(e.getMessage()); + } + } + + private static int generateRandomUserId() { + return Math.abs(new SecureRandom().nextInt()); + } + + private static String generateRandomString() { + String characters = "abcdefghijklmnopqrstuvwxyz"; + SecureRandom random = new SecureRandom(); + StringBuilder sb = new StringBuilder(); + + int length = random.nextInt(8) + 1; + for (int i = 0; i < length; i++) { + int index = random.nextInt(characters.length()); + sb.append(characters.charAt(index)); + } + return sb.toString(); + } + + private static class RandomData { + private int userId; + private String title; + private String text; + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + } +} diff --git a/mxd/backend-service/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/mxd/backend-service/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 00000000..c5600126 --- /dev/null +++ b/mxd/backend-service/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1 @@ +org.eclipse.tractusx.mxd.backendservice.BackendServiceExtension diff --git a/mxd/backend-service/src/main/resources/schema.sql b/mxd/backend-service/src/main/resources/schema.sql new file mode 100644 index 00000000..4b866650 --- /dev/null +++ b/mxd/backend-service/src/main/resources/schema.sql @@ -0,0 +1,35 @@ +-- +-- Copyright (c) 2024 SAP SE +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- SAP SE - initial API and implementation +-- + +-- Statements are designed for and tested with Postgres only! +CREATE TABLE IF NOT EXISTS content +( + id text, + asset text, + createddate timestamp(6) DEFAULT CURRENT_TIMESTAMP, + updateddate timestamp(6) DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT content_pkey PRIMARY KEY (id) +); + + +CREATE TABLE IF NOT EXISTS transfer +( + transferid text, + asset text, + contents text, + createddate timestamp(6) DEFAULT CURRENT_TIMESTAMP, + updateddate timestamp(6) DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT transfer_pkey PRIMARY KEY (transferid) +); + + diff --git a/mxd/backend-service/src/test/java/org/eclipse/tractusx/mxd/e2e/BackendServiceApiEndToEndTestBase.java b/mxd/backend-service/src/test/java/org/eclipse/tractusx/mxd/e2e/BackendServiceApiEndToEndTestBase.java new file mode 100644 index 00000000..204bfeca --- /dev/null +++ b/mxd/backend-service/src/test/java/org/eclipse/tractusx/mxd/e2e/BackendServiceApiEndToEndTestBase.java @@ -0,0 +1,49 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.e2e; + +import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.junit.extensions.EdcRuntimeExtension; + +import java.net.URI; +import java.util.Map; + +import static io.restassured.RestAssured.given; + +public abstract class BackendServiceApiEndToEndTestBase { + + protected final EdcRuntimeExtension runtime; + + public BackendServiceApiEndToEndTestBase(EdcRuntimeExtension runtime) { + this.runtime = runtime; + } + + protected RequestSpecification baseRequest() { + URI uri = getBaseUri(); + + return given() + .baseUri(uri.toString()) + .when(); + } + + public URI getBaseUri() { + Map configs = runtime.getContext().getConfig().getEntries(); + String port = configs.get("web.http.port").toString(); + String host = configs.get("edc.hostname").toString(); + String path = configs.get("web.http.path").toString(); + return URI.create("http://" + host + ":" + port + path); + } + +} diff --git a/mxd/backend-service/src/test/java/org/eclipse/tractusx/mxd/e2e/ContentApiEndToEndTest.java b/mxd/backend-service/src/test/java/org/eclipse/tractusx/mxd/e2e/ContentApiEndToEndTest.java new file mode 100644 index 00000000..c0e849e2 --- /dev/null +++ b/mxd/backend-service/src/test/java/org/eclipse/tractusx/mxd/e2e/ContentApiEndToEndTest.java @@ -0,0 +1,148 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.e2e; + +import io.restassured.http.ContentType; +import jakarta.json.JsonObject; +import org.eclipse.edc.junit.annotations.PostgresqlIntegrationTest; +import org.eclipse.edc.junit.extensions.EdcRuntimeExtension; +import org.eclipse.tractusx.mxd.testfixtures.PostgresRuntime; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static io.restassured.http.ContentType.JSON; +import static jakarta.json.Json.createObjectBuilder; +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.tractusx.mxd.testfixtures.PostgresqlEndToEndInstance.createContainer; +import static org.eclipse.tractusx.mxd.testfixtures.PostgresqlEndToEndInstance.destroyContainer; +import static org.hamcrest.Matchers.*; + +public class ContentApiEndToEndTest { + + @BeforeAll + static void setUp() { + createContainer(); + } + + @AfterAll + static void tearDown() { + destroyContainer(); + } + + @Nested + @PostgresqlIntegrationTest + class Postgres extends Tests implements PostgresRuntime { + Postgres() { + super(RUNTIME); + } + } + + abstract static class Tests extends BackendServiceApiEndToEndTestBase { + + private final static String ENDPOINT = "/v1/contents/"; + + Tests(EdcRuntimeExtension runtime) { + super(runtime); + } + + @Test + void getContentWithId() { + JsonObject contentJson = getContentJson(); + String contentId = createContent(contentJson); + + baseRequest() + .when() + .get(ENDPOINT + contentId) + .then() + .log().ifValidationFails() + .statusCode(200) + .contentType(JSON) + .body("size()", equalTo(3)); + } + + @Test + void getAllContents() { + JsonObject contentJson = getContentJson(); + createContent(contentJson); + + baseRequest() + .when() + .get(ENDPOINT) + .then() + .log().ifValidationFails() + .statusCode(200) + .contentType(JSON) + .body("size()", is(greaterThan(0))); + } + + @Test + void createAsset_shouldBeStored() { + JsonObject contentJson = getContentJson(); + String contentId = createContent(contentJson); + + baseRequest() + .when() + .get(ENDPOINT + contentId) + .then() + .log().ifValidationFails() + .statusCode(200) + .contentType(JSON) + .body("userId", is(contentJson.getInt("userId"))) + .body("title", is(contentJson.getString("title"))) + .body("text", is(contentJson.getString("text"))); + } + + @Test + void getRandomContent() { + JsonObject content = getContentJson(); + + assertThat(content.getString("title")).isNotNull(); + assertThat(content.getString("text")).isNotNull(); + assertThat(content.getInt("userId")).isGreaterThan(-1); + } + + String createContent(JsonObject contentJson) { + var responseBody = baseRequest() + .contentType(ContentType.JSON) + .body(contentJson) + .post(ENDPOINT) + .then() + .log().ifError() + .statusCode(200); + + return responseBody.extract().jsonPath() + .getString("id"); + } + + public JsonObject getContentJson() { + var responseBody = baseRequest() + .when() + .get(ENDPOINT + "random") + .then() + .log().ifValidationFails() + .statusCode(200) + .contentType(JSON); + + return createObjectBuilder() + .add("userId", responseBody.extract().jsonPath().getInt("userId")) + .add("title", responseBody.extract().jsonPath().getString("title")) + .add("text", responseBody.extract().jsonPath().getString("text")) + .build(); + + } + } +} \ No newline at end of file diff --git a/mxd/backend-service/src/test/java/org/eclipse/tractusx/mxd/e2e/TransferApiEndToEndTest.java b/mxd/backend-service/src/test/java/org/eclipse/tractusx/mxd/e2e/TransferApiEndToEndTest.java new file mode 100644 index 00000000..4d582c64 --- /dev/null +++ b/mxd/backend-service/src/test/java/org/eclipse/tractusx/mxd/e2e/TransferApiEndToEndTest.java @@ -0,0 +1,185 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.e2e; + +import io.restassured.response.ValidatableResponse; +import jakarta.json.JsonObject; +import org.eclipse.edc.junit.annotations.PostgresqlIntegrationTest; +import org.eclipse.edc.junit.extensions.EdcRuntimeExtension; +import org.eclipse.tractusx.mxd.testfixtures.PostgresRuntime; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static io.restassured.http.ContentType.JSON; +import static jakarta.json.Json.createObjectBuilder; +import static org.eclipse.tractusx.mxd.testfixtures.PostgresqlEndToEndInstance.createContainer; +import static org.eclipse.tractusx.mxd.testfixtures.PostgresqlEndToEndInstance.destroyContainer; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; + +public class TransferApiEndToEndTest { + + @BeforeAll + static void setUp() { + createContainer(); + } + + @AfterAll + static void tearDown() { + destroyContainer(); + } + + @Nested + @PostgresqlIntegrationTest + class Postgres extends Tests implements PostgresRuntime { + Postgres() { + super(RUNTIME); + } + } + + abstract static class Tests extends BackendServiceApiEndToEndTestBase { + + private final static String TRANSFER_API_ENDPOINT = "/v1/transfers/"; + private final static String CONTENT_API_ENDPOINT = "/v1/contents/"; + + Tests(EdcRuntimeExtension runtime) { + super(runtime); + } + + @Test + void createTransfer_shouldBeStored() { + String contentId = createContent(createContentJson()); + String contentUrl = getBaseUri().toString() + CONTENT_API_ENDPOINT + contentId; + + String transferId = UUID.randomUUID().toString(); + JsonObject transferJson = getTransferJson(transferId, contentUrl); + + var responseBody = createTransfer(transferJson); + + responseBody + .body("id", is(transferId)) + .body("authCode", is(transferJson.getString("authCode"))) + .body("authKey", is(transferJson.getString("authKey"))) + .body("endpoint", is(transferJson.getString("endpoint"))); + } + + @Test + void getTransferWithId() { + String contentId = createContent(createContentJson()); + String contentUrl = getBaseUri().toString() + CONTENT_API_ENDPOINT + contentId; + + String transferId = UUID.randomUUID().toString(); + JsonObject transferJson = getTransferJson(transferId, contentUrl); + + createTransfer(transferJson); + + baseRequest() + .get(TRANSFER_API_ENDPOINT + transferId) + .then() + .log().ifValidationFails() + .statusCode(200) + .contentType(JSON) + .body("id", is(transferId)) + .body("authCode", is(transferJson.getString("authCode"))) + .body("authKey", is(transferJson.getString("authKey"))) + .body("endpoint", is(transferJson.getString("endpoint"))); + } + + @Test + void getAllTransfer() { + String contentId = createContent(createContentJson()); + String contentUrl = getBaseUri().toString() + CONTENT_API_ENDPOINT + contentId; + + String transferId = UUID.randomUUID().toString(); + JsonObject transferJson = getTransferJson(transferId, contentUrl); + + createTransfer(transferJson); + + baseRequest() + .get(TRANSFER_API_ENDPOINT + transferId) + .then() + .log().ifValidationFails() + .statusCode(200) + .contentType(JSON) + .body("size()", is(greaterThan(0))); + } + + @Test + void getAssetContent() { + JsonObject contentJson = createContentJson(); + String contentId = createContent(contentJson); + String contentUrl = getBaseUri().toString() + CONTENT_API_ENDPOINT + contentId; + + String transferId = UUID.randomUUID().toString(); + JsonObject transferJson = getTransferJson(transferId, contentUrl); + + createTransfer(transferJson); + + baseRequest() + .get(TRANSFER_API_ENDPOINT + transferId + "/contents") + .then() + .log().ifValidationFails() + .statusCode(200) + .contentType(JSON) + .body("userId", is(contentJson.getString("userId"))) + .body("title", is(contentJson.getString("title"))) + .body("text", is(contentJson.getString("text"))) + ; + + } + + public JsonObject getTransferJson(String id, String contentUrl) { + return createObjectBuilder() + .add("id", id) + .add("endpoint", contentUrl) + .add("authKey", "Authorization") + .add("authCode", "100000") + .build(); + } + + ValidatableResponse createTransfer(JsonObject transferJson) { + return baseRequest() + .contentType(JSON) + .body(transferJson) + .post(TRANSFER_API_ENDPOINT) + .then() + .statusCode(200); + } + String createContent(JsonObject contentJson) { + var responseBody = baseRequest() + .contentType(JSON) + .body(contentJson) + .post(CONTENT_API_ENDPOINT) + .then() + .log().ifError() + .statusCode(200); + + return responseBody.extract().jsonPath() + .getString("id"); + } + + JsonObject createContentJson() { + return createObjectBuilder() + .add("userId", UUID.randomUUID().toString()) + .add("title", "Test") + .add("text", "Test") + .build(); + } + } +} diff --git a/mxd/backend-service/src/test/java/org/eclipse/tractusx/mxd/testfixtures/PostgresRuntime.java b/mxd/backend-service/src/test/java/org/eclipse/tractusx/mxd/testfixtures/PostgresRuntime.java new file mode 100644 index 00000000..69cead87 --- /dev/null +++ b/mxd/backend-service/src/test/java/org/eclipse/tractusx/mxd/testfixtures/PostgresRuntime.java @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.testfixtures; + +import org.eclipse.edc.junit.extensions.EdcRuntimeExtension; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.RegisterExtension; + +import java.util.Map; + +public interface PostgresRuntime { + + @RegisterExtension + BeforeAllCallback CREATE_DATABASE = context -> PostgresqlEndToEndInstance.createDatabase(PostgresqlEndToEndInstance.DB_NAME); + + @RegisterExtension + EdcRuntimeExtension RUNTIME = new EdcRuntimeExtension( + "backend", + Map.of( + "edc.datasource.default.url", PostgresqlEndToEndInstance.JDBC_URL_PREFIX + PostgresqlEndToEndInstance.DB_NAME, + "edc.datasource.default.user", PostgresqlEndToEndInstance.USER, + "edc.datasource.default.password", PostgresqlEndToEndInstance.PASSWORD + ) + ); + +} diff --git a/mxd/backend-service/src/test/java/org/eclipse/tractusx/mxd/testfixtures/PostgresqlEndToEndInstance.java b/mxd/backend-service/src/test/java/org/eclipse/tractusx/mxd/testfixtures/PostgresqlEndToEndInstance.java new file mode 100644 index 00000000..b40e030d --- /dev/null +++ b/mxd/backend-service/src/test/java/org/eclipse/tractusx/mxd/testfixtures/PostgresqlEndToEndInstance.java @@ -0,0 +1,76 @@ +/******************************************************************************** + * Copyright (c) 2024 SAP SE + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + * + ********************************************************************************/ + +package org.eclipse.tractusx.mxd.testfixtures; + +import org.eclipse.edc.junit.testfixtures.TestUtils; +import org.eclipse.edc.spi.persistence.EdcPersistenceException; +import org.eclipse.edc.sql.testfixtures.PostgresqlLocalInstance; +import org.testcontainers.containers.PostgreSQLContainer; + +import java.util.List; + +public interface PostgresqlEndToEndInstance { + + String USER = "backendservice"; + String PASSWORD = "backendservice"; + String PORT = "5432"; + String JDBC_URL_PREFIX = "jdbc:postgresql://localhost:" + PORT + "/"; + String DB_NAME = "backendservice"; + PostgreSQLContainer postgreSQLContainer = + new PostgreSQLContainer<>("postgres:latest") + .withExposedPorts(Integer.valueOf(PORT)) + .withUsername(USER) + .withPassword(PASSWORD); + + static void createContainer() { + postgreSQLContainer.setPortBindings(List.of(PORT + ":" + PORT)); + postgreSQLContainer.start(); + } + + static void destroyContainer() { + if(postgreSQLContainer != null) + postgreSQLContainer.stop(); + } + + static void createDatabase(String dbName) { + try { + Class.forName("org.postgresql.Driver"); + } catch (ClassNotFoundException e) { + throw new EdcPersistenceException(e); + } + var postgres = new PostgresqlLocalInstance( + postgreSQLContainer.getUsername(), + postgreSQLContainer.getPassword(), + postgreSQLContainer.getJdbcUrl(), + DB_NAME); + + try { + postgres.createDatabase(); + var connection = postgres.getConnection(dbName); + var sql = TestUtils.getResourceFileContentAsString("schema.sql"); + + try (var statement = connection.createStatement()) { + statement.execute(sql); + } catch (Exception exception) { + + throw new EdcPersistenceException(exception.getMessage(), exception); + } + } catch (Exception e) { + System.out.println("Ex : " + e.getMessage()); + throw new EdcPersistenceException(e); + } + } + +} \ No newline at end of file diff --git a/mxd/modules/connector/ingress.tf b/mxd/modules/connector/ingress.tf index a8ca02c8..781178b6 100644 --- a/mxd/modules/connector/ingress.tf +++ b/mxd/modules/connector/ingress.tf @@ -73,6 +73,17 @@ resource "kubernetes_ingress_v1" "mxd-ingress" { } } } + path { + path = "/backend-service(/|$)(.*)" + backend { + service { + name = "backend-service" + port { + number = 8080 + } + } + } + } } } } diff --git a/mxd/postgres-init.tf b/mxd/postgres-init.tf index d56e5f84..208bc59f 100644 --- a/mxd/postgres-init.tf +++ b/mxd/postgres-init.tf @@ -56,10 +56,11 @@ resource "kubernetes_config_map" "postgres-initdb-config" { } locals { - keycloak-postgres = var.common-postgres-instance ? module.common-postgres[0] : module.postgres["keycloak"] - miw-postgres = var.common-postgres-instance ? module.common-postgres[0] : module.postgres["miw"] - alice-postgres = var.common-postgres-instance ? module.common-postgres[0] : module.postgres["alice"] - bob-postgres = var.common-postgres-instance ? module.common-postgres[0] : module.postgres["bob"] + keycloak-postgres = var.common-postgres-instance ? module.common-postgres[0] : module.postgres["keycloak"] + miw-postgres = var.common-postgres-instance ? module.common-postgres[0] : module.postgres["miw"] + alice-postgres = var.common-postgres-instance ? module.common-postgres[0] : module.postgres["alice"] + bob-postgres = var.common-postgres-instance ? module.common-postgres[0] : module.postgres["bob"] + backendservice-postgres = var.common-postgres-instance ? module.common-postgres[0] : module.postgres["backendservice"] databases = { keycloak = { @@ -91,5 +92,11 @@ locals { database-username = "trudy" database-password = "trudy" } + + backendservice = { + database-name = "backendservice", + database-username = "backendservice" + database-password = "backendservice" + } } } diff --git a/mxd/postman/mxd-backend-services.postman_collection-v3.json b/mxd/postman/mxd-backend-services.postman_collection-v3.json new file mode 100644 index 00000000..5e504bc5 --- /dev/null +++ b/mxd/postman/mxd-backend-services.postman_collection-v3.json @@ -0,0 +1,209 @@ +{ + "info": { + "_postman_id": "64d8cc24-47b2-4fc3-a94a-6faf5c3408d6", + "name": "Backend-services", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "31550443" + }, + "item": [ + { + "name": "Get All Contents", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{backend-service-url}}/api/v1/content/", + "host": [ + "{{backend-service-url}}" + ], + "path": [ + "api", + "v1", + "content", + "" + ] + } + }, + "response": [] + }, + { + "name": "Post Contents", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{backend-service-url}}/api/v1/content/", + "host": [ + "{{backend-service-url}}" + ], + "path": [ + "api", + "v1", + "content", + "" + ] + } + }, + "response": [] + }, + { + "name": "Get Contents By ID", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": 1,\r\n \"name\": \"java\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{backend-service-url}}/api/v1/content/1", + "host": [ + "{{backend-service-url}}" + ], + "path": [ + "api", + "v1", + "content", + "1" + ] + } + }, + "response": [] + }, + { + "name": "Post Transfer", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{backend-service-url}}/api/v1/transfer/", + "host": [ + "{{backend-service-url}}" + ], + "path": [ + "api", + "v1", + "transfer", + "" + ] + } + }, + "response": [] + }, + { + "name": "Get Transfer Content By ID", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{backend-service-url}}/api/v1/id/contents/", + "host": [ + "{{backend-service-url}}" + ], + "path": [ + "api", + "v1", + "id", + "contents", + "" + ] + } + }, + "response": [] + }, + { + "name": "Get Transfer By ID", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{backend-service-url}}/api/v1/transfer/id/", + "host": [ + "{{backend-service-url}}" + ], + "path": [ + "api", + "v1", + "transfer", + "id", + "" + ] + } + }, + "response": [] + }, + { + "name": "Get Random Content", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{backend-service-url}}/api/v1/content/random", + "host": [ + "{{backend-service-url}}" + ], + "path": [ + "api", + "v1", + "content", + "random" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "backend-service-url", + "value": "http://localhost:8080", + "type": "string" + } + ] +} \ No newline at end of file