diff --git a/.gitignore b/.gitignore index 61e87bc0e..efe2cd61d 100644 --- a/.gitignore +++ b/.gitignore @@ -326,6 +326,9 @@ scripts/dockerize/dockerfiles /jdk_25_maven/cs/rest/commafeed/commafeed-client/target/ /jdk_25_maven/em/external/rest/commafeed/target/ /jdk_25_maven/em/embedded/rest/commafeed/target/ +/jdk_25_maven/cs/rest/jasper/target/ +/jdk_25_maven/em/external/rest/jasper/target/ +/jdk_25_maven/em/embedded/rest/jasper/target/ /jdk_11_maven/em/embedded/rest/tracking-system/target/ /jdk_11_maven/em/external/rest/tracking-system/target/ /jdk_17_maven/em/embedded/rest/tiltaksgjennomforing/target/ diff --git a/README.md b/README.md index e7d34296c..8238051c0 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ How to setup authentication information, based on the current content of the ini Auth configuration files can found in the [auth](auth) folder. -### REST: Java/Kotlin (37) +### REST: Java/Kotlin (38) * **Bibliothek** (MIT), [jdk_17_gradle/cs/rest/bibliothek](jdk_17_gradle/cs/rest/bibliothek), from [https://github.com/PaperMC/bibliothek](https://github.com/PaperMC/bibliothek) @@ -109,6 +109,8 @@ Auth configuration files can found in the [auth](auth) folder. * **HTTP Patch Spring** (MIT), [jdk_11_maven/cs/rest/http-patch-spring](jdk_11_maven/cs/rest/http-patch-spring), from [https://github.com/cassiomolin/http-patch-spring](https://github.com/cassiomolin/http-patch-spring) +* **Jasper** (MIT), [jdk_25_maven/cs/rest/jasper](jdk_25_maven/cs/rest/jasper), from [https://github.com/cjmalloy/jasper](https://github.com/cjmalloy/jasper) + * **Languagetool** (LGPL), [jdk_8_maven/cs/rest/original/languagetool](jdk_8_maven/cs/rest/original/languagetool), from [https://github.com/languagetool-org/languagetool](https://github.com/languagetool-org/languagetool) * **Market** (MIT), [jdk_11_maven/cs/rest-gui/market](jdk_11_maven/cs/rest-gui/market), from [https://github.com/aleksey-lukyanets/market](https://github.com/aleksey-lukyanets/market) diff --git a/auth/jasper-auth.yaml b/auth/jasper-auth.yaml new file mode 100644 index 000000000..6c57dd3f2 --- /dev/null +++ b/auth/jasper-auth.yaml @@ -0,0 +1,31 @@ +auth: + - name: admin + fixedHeaders: + - name: User-Tag + value: +user/admin + - name: User-Role + value: ROLE_ADMIN + - name: X-XSRF-TOKEN + value: evomaster + - name: Cookie + value: XSRF-TOKEN=evomaster + - name: alice + fixedHeaders: + - name: User-Tag + value: +user/alice + - name: User-Role + value: ROLE_USER + - name: X-XSRF-TOKEN + value: evomaster + - name: Cookie + value: XSRF-TOKEN=evomaster + - name: bob + fixedHeaders: + - name: User-Tag + value: +user/bob + - name: User-Role + value: ROLE_USER + - name: X-XSRF-TOKEN + value: evomaster + - name: Cookie + value: XSRF-TOKEN=evomaster diff --git a/dockerfiles/jasper.dockerfile b/dockerfiles/jasper.dockerfile new file mode 100644 index 000000000..eb99db196 --- /dev/null +++ b/dockerfiles/jasper.dockerfile @@ -0,0 +1,17 @@ +FROM amazoncorretto:25-alpine-jdk + +COPY ./dist/jasper-sut.jar . +COPY ./dist/jacocoagent.jar . + + + +#ENV TOOL="undefined" +#ENV RUN="0" + +ENTRYPOINT \ + java \ +# unfortunately dumponexit is completely unreliable in Docker :( +# -javaagent:jacocoagent.jar=destfile=./jacoco/jasper__${TOOL}__${RUN}__jacoco.exec,append=false,dumponexit=true \ + -javaagent:jacocoagent.jar=output=tcpserver,address=*,port=6300,append=false,dumponexit=false \ + -jar jasper-sut.jar \ + --server.port=8080 --spring.profiles.active=api-docs --spring.datasource.url=jdbc:postgresql://db:5432/jasper --spring.datasource.username=postgres --spring.datasource.password=password --spring.datasource.hikari.auto-commit=false --spring.jpa.database-platform=jasper.config.PostgreSQLDialect --springdoc.api-docs.path=/api/v3/api-docs --jasper.allow-user-tag-header=true --jasper.allow-user-role-header=true --jasper.allow-auth-headers=true \ No newline at end of file diff --git a/dockerfiles/jasper.yaml b/dockerfiles/jasper.yaml new file mode 100644 index 000000000..81e10372b --- /dev/null +++ b/dockerfiles/jasper.yaml @@ -0,0 +1,37 @@ +services: + mitmproxy: + image: mitmproxy/mitmproxy:10.2.4 + user: "0:0" + environment: + MITM_LOG_DIR: /custom-logs + MITM_LOG_FILE: ${MITM_LOG_FILE:-minlog.csv} + command: > + mitmdump + --mode reverse:http://sut-jasper:8080 + --listen-host 0.0.0.0 + --listen-port 8080 + --set keep_host_header=true + -s /addons/minlog.py + volumes: + - ${HOST_LOG_DIR:-./mitm-logs}:/custom-logs + - ./addons:/addons:ro + ports: + - "${HOST_PORT:-8080}:8080" + depends_on: + - sut-jasper + sut-jasper: + build: + dockerfile: ./dockerfiles/jasper.dockerfile + context: .. + environment: + AUTH_PORT: ${AUTH_PORT:-8081} + ports: + - "${JACOCO_PORT:-6300}:6300" + db: + image: postgres:18 + tmpfs: + - '/var/lib/postgresql' + environment: + POSTGRES_PASSWORD: password + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_DB: jasper \ No newline at end of file diff --git a/experiments/wb-exp.py b/experiments/wb-exp.py index 293df66d9..572a119c2 100644 --- a/experiments/wb-exp.py +++ b/experiments/wb-exp.py @@ -193,6 +193,7 @@ def __init__(self, name, platform): Sut("genome-nexus", JDK_8), Sut("gestaohospital", JDK_8), Sut("http-patch-spring", JDK_11), + Sut("jasper", JDK_25), Sut("languagetool", JDK_8), Sut("market", JDK_11), Sut("microcks", JDK_21), diff --git a/jdk_21_maven/cs/rest/redis-sample/.gitattributes b/jdk_21_maven/cs/rest/redis-sample/.gitattributes new file mode 100644 index 000000000..3b41682ac --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/.gitattributes @@ -0,0 +1,2 @@ +/mvnw text eol=lf +*.cmd text eol=crlf diff --git a/jdk_21_maven/cs/rest/redis-sample/.gitignore b/jdk_21_maven/cs/rest/redis-sample/.gitignore new file mode 100644 index 000000000..02ed1531e --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/.gitignore @@ -0,0 +1,33 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +logs + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/jdk_21_maven/cs/rest/redis-sample/HELP.md b/jdk_21_maven/cs/rest/redis-sample/HELP.md new file mode 100644 index 000000000..5b5b9d319 --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/HELP.md @@ -0,0 +1,36 @@ +# Read Me First + +The following was discovered as part of building this project: + +* No Docker Compose services found. As of now, the application won't start! Please add at least one service to the + `compose.yaml` file. + +# Getting Started + +### Reference Documentation + +For further reference, please consider the following sections: + +* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) +* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/3.4.4/maven-plugin) +* [Create an OCI image](https://docs.spring.io/spring-boot/3.4.4/maven-plugin/build-image.html) +* [Spring Configuration Processor](https://docs.spring.io/spring-boot/3.4.4/specification/configuration-metadata/annotation-processor.html) +* [Spring Boot DevTools](https://docs.spring.io/spring-boot/3.4.4/reference/using/devtools.html) +* [Docker Compose Support](https://docs.spring.io/spring-boot/3.4.4/reference/features/dev-services.html#features.dev-services.docker-compose) + +### Docker Compose support + +This project contains a Docker Compose file named `compose.yaml`. + +However, no services were found. As of now, the application won't start! + +Please make sure to add at least one service in the `compose.yaml` file. + +### Maven Parent overrides + +Due to Maven's design, elements are inherited from the parent POM to the project POM. +While most of the inheritance is fine, it also inherits unwanted elements like `` and `` from the +parent. +To prevent this, the project POM contains empty overrides for these elements. +If you manually switch to a different parent and actually want the inheritance, you need to remove those overrides. + diff --git a/jdk_21_maven/cs/rest/redis-sample/README.md b/jdk_21_maven/cs/rest/redis-sample/README.md new file mode 100644 index 000000000..1eb05a94c --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/README.md @@ -0,0 +1,267 @@ +# Spring Boot Redis Sample + +A comprehensive Spring Boot 3.5.6 application demonstrating Redis integration with caching, data persistence, and +RESTful APIs. + +## Features + +- Spring Boot 3.5.6 with Java 21 +- Redis Stack integration for data storage and caching +- Spring Data Redis for repository management +- Spring Security with BCrypt password encoding +- RESTful API endpoints for Books and Users management +- Redis caching with configurable TTL +- Docker Compose integration for easy Redis setup +- Sample data initialization on startup +- Testcontainers for integration testing + +## Technologies + +- **Spring Boot**: 3.5.6 +- **Java**: 21 +- **Redis**: Redis Stack (latest) +- **Spring Data Redis**: Repository pattern implementation +- **Spring Security**: User authentication and authorization +- **Spring Validation**: Input validation +- **Lombok**: Reduce boilerplate code +- **Docker Compose**: Container orchestration +- **Testcontainers**: Integration testing + +## Prerequisites + +- Java 21 or higher +- Docker and Docker Compose +- Maven 3.x + +## Project Structure + +``` +spring-boot-redis-sample/ +├── src/main/java/ +│ └── id/my/hendisantika/springbootredissample/ +│ ├── SpringBootRedisSampleApplication.java # Main application class +│ ├── boot/ # Data initialization +│ │ ├── CreateBookRatings.java +│ │ ├── CreateBooks.java +│ │ ├── CreateRoles.java +│ │ └── CreateUsers.java +│ ├── controller/ # REST Controllers +│ │ ├── BookController.java +│ │ └── UserController.java +│ ├── model/ # Domain models +│ │ ├── Book.java +│ │ ├── BookRating.java +│ │ ├── Category.java +│ │ ├── Role.java +│ │ └── User.java +│ └── repository/ # Redis repositories +│ ├── BookRatingRepository.java +│ ├── BookRepository.java +│ ├── CategoryRepository.java +│ ├── RoleRepository.java +│ └── UserRepository.java +├── src/main/resources/ +│ ├── application.properties # Application configuration +│ └── data/books/ # Sample book data +├── compose.yaml # Docker Compose configuration +└── pom.xml # Maven dependencies +``` + +## Configuration + +### Application Properties (application.properties:1) + +```properties +spring.application.name=bookstore +spring.data.redis.host=localhost +spring.data.redis.port=6379 +spring.data.redis.password=${REDIS_PASSWORD:53cret} +spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration +app.numberOfRatings=5000 +app.ratingStars=5 +``` + +### Redis Configuration (compose.yaml:1) + +The application uses Redis Stack with the following configuration: + +- Port: 6379 +- Password: 53cret +- Persistence: AOF (Append-Only File) +- Data directory: ./data + +## Getting Started + +### 1. Clone the repository + +```bash +git clone +cd spring-boot-redis-sample2 +``` + +### 2. Start Redis with Docker Compose + +```bash +docker compose up -d +``` + +### 3. Build the application + +```bash +./mvnw clean install +``` + +### 4. Run the application + +```bash +./mvnw spring-boot:run +``` + +The application will start on http://localhost:8080 + +## API Endpoints + +### Books API + +#### Get all books (with pagination) + +```bash +GET /api/books?page=0&size=10 +``` + +Response: + +```json +{ + "total": 2, + "page": 0, + "pages": 1, + "books": [ + ... + ] +} +``` + +#### Get book by ISBN + +```bash +GET /api/books/{isbn} +``` + +#### Get all categories + +```bash +GET /api/books/categories +``` + +### Users API + +#### Get users by email + +```bash +GET /api/users?email=yuji@yopmail.com +``` + +## Caching + +The application uses Redis caching with the following configuration (SpringBootRedisSampleApplication.java:36): + +- Cache TTL: 1 hour +- Cache name prefix: Package name +- Null values caching: Disabled + +### Cached Endpoints + +- `/api/books` - Cached with key: `page-size` +- `/api/books/categories` - Cached + +## Data Models + +### Book (Book.java:1) + +- ISBN (ID) +- Title, Subtitle, Description +- Language, Page Count +- Price, Currency +- Authors (Set) +- Categories (Set) +- Thumbnail, Info Link + +### User (User.java:1) + +- ID +- Name, Email +- Password (BCrypt encrypted) +- Roles (Set) + +### Category (Category.java:1) + +- ID +- Name + +### BookRating (BookRating.java:1) + +- ID +- User reference +- Book reference +- Rating (1-5 stars) + +## Testing + +Run tests with: + +```bash +./mvnw test +``` + +The project includes Testcontainers for integration testing with Redis. + +## Docker Compose Services + +The `compose.yaml` defines a Redis Stack service: + +- Image: redis/redis-stack:latest +- Container name: redis +- Ports: 6379:6379 +- Volume: ./data:/data +- Password protected: 53cret + +## Development + +### Spring Boot DevTools + +The application includes Spring Boot DevTools for: + +- Automatic application restart on code changes +- LiveReload server on port 35729 + +### Sample Data + +On startup, the application automatically creates: + +- 2 Roles (admin, customer) +- 5 Users +- 2 Books +- 5000 Book Ratings + +## Building for Production + +```bash +./mvnw clean package -DskipTests +java -jar target/spring-boot-redis-sample-0.0.1-SNAPSHOT.jar +``` + +## License + +This project is for educational purposes. + +## Author + +- Name: Hendi Santika +- Link: s.id/hendisantika +- Email: hendisantika@yahoo.co.id +- Telegram: @hendisantika34 + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. diff --git a/jdk_21_maven/cs/rest/redis-sample/compose.yaml b/jdk_21_maven/cs/rest/redis-sample/compose.yaml new file mode 100644 index 000000000..eac09ff42 --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/compose.yaml @@ -0,0 +1,18 @@ +services: + redis-stack: + image: redis/redis-stack:latest + container_name: redis + ports: + - "6379:6379" + volumes: + - ./data:/data + command: > + redis-server + --appendonly yes + --appendfilename "appendonly.aof" + --appendfsync everysec + --requirepass 53cret + deploy: + replicas: 1 + restart_policy: + condition: on-failure diff --git a/jdk_21_maven/cs/rest/redis-sample/mvnw b/jdk_21_maven/cs/rest/redis-sample/mvnw new file mode 100644 index 000000000..bd8896bf2 --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/mvnw @@ -0,0 +1,295 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.4 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + 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" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" + +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi +fi + +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f +fi + +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi + +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/jdk_21_maven/cs/rest/redis-sample/mvnw.cmd b/jdk_21_maven/cs/rest/redis-sample/mvnw.cmd new file mode 100644 index 000000000..92450f932 --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/mvnw.cmd @@ -0,0 +1,189 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.4 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/jdk_21_maven/cs/rest/redis-sample/pom.xml b/jdk_21_maven/cs/rest/redis-sample/pom.xml new file mode 100644 index 000000000..c4330e501 --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/pom.xml @@ -0,0 +1,121 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 4.1.0 + + + id.my.hendisantika + spring-boot-redis-sample + 0.0.1-SNAPSHOT + spring-boot-redis-sample + spring-boot-redis-sample + + 21 + + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.security + spring-security-test + test + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.springframework.boot + spring-boot-docker-compose + runtime + true + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + org.testcontainers + testcontainers + 2.0.5 + test + + + org.testcontainers + junit-jupiter + 1.21.4 + test + + + com.redis + testcontainers-redis + 2.2.4 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.springframework.boot + spring-boot-configuration-processor + + + org.projectlombok + lombok + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/SpringBootRedisSampleApplication.java b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/SpringBootRedisSampleApplication.java new file mode 100644 index 000000000..8dfd4c35d --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/SpringBootRedisSampleApplication.java @@ -0,0 +1,57 @@ +package id.my.hendisantika.springbootredissample; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import java.time.Duration; + +@EnableCaching // Enables Spring’s annotation-driven cache management capability +@SpringBootApplication +public class SpringBootRedisSampleApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootRedisSampleApplication.class, args); + } + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + return template; + } + + @Bean + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + + /** + * Creates and configures a RedisCacheManager bean. + * RedisCacheManager: Manages caching operations and configurations for Redis. + * RedisCacheConfiguration: Configures caching behavior, including cache name prefixes, TTL, and whether null values should be cached. + * + * @param connectionFactory the RedisConnectionFactory used to connect to the Redis server. + * @return a configured RedisCacheManager instance. + * - **Prefix Cache Names:** Cache names are prefixed with the package name of the class to avoid naming collisions. + * - **Entry TTL (Time-to-Live):** Cache entries will expire after 1 hour. + * - **Disable Caching Null Values:** Null values are not cached. + */ + @Bean + public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) { + RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() + .prefixCacheNameWith(this.getClass().getPackageName() + ".") + .entryTtl(Duration.ofHours(1)) + .disableCachingNullValues(); + + return RedisCacheManager.builder(connectionFactory).cacheDefaults(config).build(); + } + +} diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/boot/CreateBookRatings.java b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/boot/CreateBookRatings.java new file mode 100644 index 000000000..b933209cd --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/boot/CreateBookRatings.java @@ -0,0 +1,67 @@ +package id.my.hendisantika.springbootredissample.boot; + +import id.my.hendisantika.springbootredissample.model.Book; +import id.my.hendisantika.springbootredissample.model.BookRating; +import id.my.hendisantika.springbootredissample.model.User; +import id.my.hendisantika.springbootredissample.repository.BookRatingRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.CommandLineRunner; +import org.springframework.core.annotation.Order; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.Random; +import java.util.stream.IntStream; + +/** + * Created by IntelliJ IDEA. + * Project : spring-boot-redis-sample + * User: hendisantika + * Link: s.id/hendisantika + * Email: hendisantika@yahoo.co.id + * Telegram : @hendisantika34 + * Date: 05/04/25 + * Time: 07.43 + * To change this template use File | Settings | File Templates. + */ +@Slf4j +@Order(4) +@Component +@RequiredArgsConstructor +public class CreateBookRatings implements CommandLineRunner { + + private final RedisTemplate redisTemplate; + private final BookRatingRepository bookRatingRepo; + @Value("${app.numberOfRatings}") + private Integer numberOfRatings; + @Value("${app.ratingStars}") + private Integer ratingStars; + + @Override + public void run(String... args) throws Exception { + if (bookRatingRepo.count() == 0) { + Random random = new Random(); + IntStream.range(0, numberOfRatings).forEach(n -> { + String bookId = redisTemplate.opsForSet().randomMember(Book.class.getName()); + String userId = redisTemplate.opsForSet().randomMember(User.class.getName()); + int stars = random.nextInt(ratingStars) + 1; + + User user = new User(); + user.setId(userId); + + Book book = new Book(); + book.setId(bookId); + + BookRating rating = BookRating.builder() // + .user(user) // + .book(book) // + .rating(stars).build(); + bookRatingRepo.save(rating); + }); + + log.info(">>>> BookRating created..."); + } + } +} diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/boot/CreateBooks.java b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/boot/CreateBooks.java new file mode 100644 index 000000000..0d9808476 --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/boot/CreateBooks.java @@ -0,0 +1,96 @@ +package id.my.hendisantika.springbootredissample.boot; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import id.my.hendisantika.springbootredissample.model.Book; +import id.my.hendisantika.springbootredissample.model.Category; +import id.my.hendisantika.springbootredissample.repository.BookRepository; +import id.my.hendisantika.springbootredissample.repository.CategoryRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.CommandLineRunner; +import org.springframework.core.annotation.Order; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by IntelliJ IDEA. + * Project : spring-boot-redis-sample + * User: hendisantika + * Link: s.id/hendisantika + * Email: hendisantika@yahoo.co.id + * Telegram : @hendisantika34 + * Date: 05/04/25 + * Time: 07.44 + * To change this template use File | Settings | File Templates. + */ +@Slf4j +@Order(3) +@Component +@RequiredArgsConstructor +public class CreateBooks implements CommandLineRunner { + + private final BookRepository bookRepository; + + private final CategoryRepository categoryRepository; + private final ResourceLoader resourceLoader; + + @Override + public void run(String... args) throws Exception { + if (bookRepository.count() == 0) { + ObjectMapper mapper = new ObjectMapper(); + TypeReference> typeReference = new TypeReference<>() { + }; + + Resource resource = resourceLoader.getResource("classpath:/data/books/"); + File directory = resource.getFile(); + File[] files = directory.listFiles((dir, name) -> name.endsWith(".json")); + + if (files == null || files.length == 0) { + log.warn("No JSON files found in /data/books/ directory."); + return; + } + + Map categories = new HashMap<>(); + log.info("files -> {}", files); + + Arrays.stream(files).forEach(file -> { + try { + log.info(">>>> Processing Book File: {}", file.getPath()); + String categoryName = file.getName().substring(0, file.getName().lastIndexOf("_")); + log.info(">>>> Category: {}", categoryName); + + Category category; + if (!categories.containsKey(categoryName)) { + category = Category.builder().name(categoryName).build(); + categoryRepository.save(category); + categories.put(categoryName, category); + } else { + category = categories.get(categoryName); + } + + InputStream inputStream = resourceLoader.getResource("classpath:/data/books/" + file.getName()).getInputStream(); + List books = mapper.readValue(inputStream, typeReference); + books.forEach((book) -> { + book.addCategory(category); + bookRepository.save(book); + }); + log.info(">>>> {} Books Saved!", books.size()); + } catch (IOException e) { + log.error("Unable to import books from file: {}", file.getName(), e); + } + }); + + log.info(">>>> Loaded Book Data and Created books..."); + } + } +} diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/boot/CreateRoles.java b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/boot/CreateRoles.java new file mode 100644 index 000000000..a71b5c1ff --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/boot/CreateRoles.java @@ -0,0 +1,39 @@ +package id.my.hendisantika.springbootredissample.boot; + +import id.my.hendisantika.springbootredissample.model.Role; +import id.my.hendisantika.springbootredissample.repository.RoleRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.CommandLineRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +/** + * Created by IntelliJ IDEA. + * Project : spring-boot-redis-sample + * User: hendisantika + * Link: s.id/hendisantika + * Email: hendisantika@yahoo.co.id + * Telegram : @hendisantika34 + * Date: 05/04/25 + * Time: 07.45 + * To change this template use File | Settings | File Templates. + */ +@Slf4j +@Order(1) +@Component +@RequiredArgsConstructor +public class CreateRoles implements CommandLineRunner { + private final RoleRepository roleRepository; + + @Override + public void run(String... args) throws Exception { + if (roleRepository.count() == 0) { + Role adminRole = Role.builder().name("admin").build(); + roleRepository.save(adminRole); + Role customerRole = Role.builder().name("customer").build(); + roleRepository.save(customerRole); + log.info(">>>> Created admin and customer roles..."); + } + } +} diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/boot/CreateUsers.java b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/boot/CreateUsers.java new file mode 100644 index 000000000..5d7f43e9e --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/boot/CreateUsers.java @@ -0,0 +1,88 @@ +package id.my.hendisantika.springbootredissample.boot; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import id.my.hendisantika.springbootredissample.model.Role; +import id.my.hendisantika.springbootredissample.model.User; +import id.my.hendisantika.springbootredissample.repository.RoleRepository; +import id.my.hendisantika.springbootredissample.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.CommandLineRunner; +import org.springframework.core.annotation.Order; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * Project : spring-boot-redis-sample + * User: hendisantika + * Link: s.id/hendisantika + * Email: hendisantika@yahoo.co.id + * Telegram : @hendisantika34 + * Date: 05/04/25 + * Time: 07.46 + * To change this template use File | Settings | File Templates. + */ +@Slf4j +@Order(2) +@Component +@RequiredArgsConstructor +public class CreateUsers implements CommandLineRunner { + + private final RoleRepository roleRepository; + + private final UserRepository userRepository; + + private final BCryptPasswordEncoder passwordEncoder; + + private final ResourceLoader resourceLoader; + + + @Override + public void run(String... args) throws Exception { + if (userRepository.count() == 0) { + // load the roles + Role admin = roleRepository.findFirstByname("admin"); + Role customer = roleRepository.findFirstByname("customer"); + + try { + // create a Jackson object mapper + ObjectMapper mapper = new ObjectMapper(); + // create a type definition to convert the array of JSON into a List of Users + TypeReference> typeReference = new TypeReference<>() { + }; + // make the JSON data available as an input stream + Resource resource = resourceLoader.getResource("classpath:data/users/users.json"); + InputStream inputStream = resource.getInputStream(); + // convert the JSON to objects + List users = mapper.readValue(inputStream, typeReference); + + users.stream().forEach((user) -> { + user.setPassword(passwordEncoder.encode(user.getPassword())); + user.addRole(customer); +// userRepository.save(user); + }); + userRepository.saveAll(users); + log.info(">>>> {} Users Saved!", users.size()); + } catch (IOException e) { + log.info(">>>> Unable to import users: {}", e.getMessage()); + } + + User adminUser = new User(); + adminUser.setName("Itadori Yuji"); + adminUser.setEmail("yuji@yopmail.com"); + adminUser.setPassword(passwordEncoder.encode("53cret"));// + adminUser.addRole(admin); + + userRepository.save(adminUser); + log.info(">>>> Loaded User Data and Created users..."); + } + } +} diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/controller/BookController.java b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/controller/BookController.java new file mode 100644 index 000000000..e1002b6cf --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/controller/BookController.java @@ -0,0 +1,91 @@ +package id.my.hendisantika.springbootredissample.controller; + +import id.my.hendisantika.springbootredissample.model.Book; +import id.my.hendisantika.springbootredissample.model.Category; +import id.my.hendisantika.springbootredissample.repository.BookRepository; +import id.my.hendisantika.springbootredissample.repository.CategoryRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ResponseStatusException; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Created by IntelliJ IDEA. + * Project : spring-boot-redis-sample + * User: hendisantika + * Link: s.id/hendisantika + * Email: hendisantika@yahoo.co.id + * Telegram : @hendisantika34 + * Date: 05/04/25 + * Time: 07.48 + * To change this template use File | Settings | File Templates. + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/books") +public class BookController { + + private final BookRepository bookRepository; + + private final CategoryRepository categoryRepository; + + /** + * Retrieves a paginated list of books. + * Caches the result to avoid hitting the database on repeated requests with the same parameters. + * + * @param page the page number to retrieve (default is 0). + * @param size the number of items per page (default is 10). + * @return a ResponseEntity containing the paginated books, page number, total pages, and total elements. + */ + @GetMapping + @Cacheable(value = "booksCache", key = "#page + '-' + #size") + public Map getBooks(@RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "10") Integer size) { + Pageable paging = PageRequest.of(page, size); + Page pagedResult = bookRepository.findAll(paging); + List books = pagedResult.hasContent() ? pagedResult.getContent() : Collections.emptyList(); + + Map response = new HashMap<>(); + response.put("books", books); + response.put("page", pagedResult.getNumber()); + response.put("pages", pagedResult.getTotalPages()); + response.put("total", pagedResult.getTotalElements()); + + return response; + } + + /** + * Retrieves all categories. + * Caches the result to avoid hitting the database on repeated requests. + * + * @return an Iterable of categories. + */ + @GetMapping("/categories") + @Cacheable(value = "categoriesCache") + public Iterable getCategories() { + return categoryRepository.findAll(); + } + + @GetMapping("/{isbn}") + public Book getIsbn(@PathVariable("isbn") String isbn) { + Optional book = bookRepository.findById(isbn); + if (book.isPresent()) { + return book.get(); + } else { + throw new ResponseStatusException(HttpStatus.NOT_FOUND); + } + } +} diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/controller/UserController.java b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/controller/UserController.java new file mode 100644 index 000000000..b399aa230 --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/controller/UserController.java @@ -0,0 +1,42 @@ +package id.my.hendisantika.springbootredissample.controller; + +import id.my.hendisantika.springbootredissample.model.User; +import id.my.hendisantika.springbootredissample.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * Created by IntelliJ IDEA. + * Project : spring-boot-redis-sample + * User: hendisantika + * Link: s.id/hendisantika + * Email: hendisantika@yahoo.co.id + * Telegram : @hendisantika34 + * Date: 05/04/25 + * Time: 07.49 + * To change this template use File | Settings | File Templates. + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/users") +public class UserController { + + private final UserRepository userRepository; + + @GetMapping + public Iterable getUsers(@RequestParam(defaultValue = "yuji@yopmail.com") String email) { + if (email.isEmpty()) { + return userRepository.findAll(); + } else { + Optional user = Optional.ofNullable(userRepository.findFirstByEmail(email)); + return user.isPresent() ? List.of(user.get()) : Collections.emptyList(); + } + } +} diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/model/Book.java b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/model/Book.java new file mode 100644 index 000000000..5557a8536 --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/model/Book.java @@ -0,0 +1,81 @@ +package id.my.hendisantika.springbootredissample.model; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Reference; +import org.springframework.data.redis.core.RedisHash; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; + +/** + * Created by IntelliJ IDEA. + * Project : spring-boot-redis-sample + * User: hendisantika + * Link: s.id/hendisantika + * Email: hendisantika@yahoo.co.id + * Telegram : @hendisantika34 + * Date: 05/04/25 + * Time: 07.37 + * To change this template use File | Settings | File Templates. + */ + +/** + * Represents a book entity stored in a Redis database. + * This class is annotated with {@code @RedisHash} to indicate that it should be stored as a Redis hash. + * It uses Lombok annotations to automatically generate boilerplate code like getters, setters, + * and {@code equals()} and {@code hashCode()} methods. + * + *

+ * The {@code Book} class includes various attributes related to a book, such as its title, description, + * language, and more. It also maintains relationships with authors and categories. + *

+ * + *

+ * The {@code @EqualsAndHashCode.Include} annotation is applied to the {@code id} field to include it + * in the {@code equals()} and {@code hashCode()} methods generated by Lombok. + *

+ * + *

+ * The class supports adding categories to a book through the {@code addCategory} method. + *

+ * + *
{@code
+ * Example usage:
+ * Book book = new Book();
+ * book.setTitle("Redis in Action");
+ * book.addCategory(new Category("Technology"));
+ * }
+ * + * @author Lynne + */ +@Data +@RedisHash +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +public class Book implements Serializable { + + @Id + @EqualsAndHashCode.Include + private String id; + + private String title; + private String subtitle; + private String description; + private String language; + private Long pageCount; + private String thumbnail; + private Double price; + private String currency; + private String infoLink; + + private Set authors; + + @Reference + private Set categories = new HashSet(); + + public void addCategory(Category category) { + categories.add(category); + } +} diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/model/BookRating.java b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/model/BookRating.java new file mode 100644 index 000000000..f8df13875 --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/model/BookRating.java @@ -0,0 +1,38 @@ +package id.my.hendisantika.springbootredissample.model; + +import jakarta.validation.constraints.NotNull; +import lombok.Builder; +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Reference; +import org.springframework.data.redis.core.RedisHash; + +/** + * Created by IntelliJ IDEA. + * Project : spring-boot-redis-sample + * User: hendisantika + * Link: s.id/hendisantika + * Email: hendisantika@yahoo.co.id + * Telegram : @hendisantika34 + * Date: 05/04/25 + * Time: 07.38 + * To change this template use File | Settings | File Templates. + */ +@Data +@Builder +@RedisHash +public class BookRating { + @Id + private String id; + + @NotNull + @Reference + private User user; + + @NotNull + @Reference + private Book book; + + @NotNull + private Integer rating; +} diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/model/Category.java b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/model/Category.java new file mode 100644 index 000000000..2f0856c8b --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/model/Category.java @@ -0,0 +1,32 @@ +package id.my.hendisantika.springbootredissample.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; + +import java.io.Serializable; + +/** + * Created by IntelliJ IDEA. + * Project : spring-boot-redis-sample + * User: hendisantika + * Link: s.id/hendisantika + * Email: hendisantika@yahoo.co.id + * Telegram : @hendisantika34 + * Date: 05/04/25 + * Time: 07.38 + * To change this template use File | Settings | File Templates. + */ +@Data +@Builder +@RedisHash +@AllArgsConstructor +@NoArgsConstructor +public class Category implements Serializable { + @Id + private String id; + private String name; +} diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/model/Role.java b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/model/Role.java new file mode 100644 index 000000000..b185bc108 --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/model/Role.java @@ -0,0 +1,52 @@ +package id.my.hendisantika.springbootredissample.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.index.Indexed; + +/** + * Created by IntelliJ IDEA. + * Project : spring-boot-redis-sample + * User: hendisantika + * Link: s.id/hendisantika + * Email: hendisantika@yahoo.co.id + * Telegram : @hendisantika34 + * Date: 05/04/25 + * Time: 07.38 + * To change this template use File | Settings | File Templates. + */ + +/** + * Represents a role entity stored in Redis. + * + *

This class is annotated with {@link Builder} to provide a builder pattern for creating instances, and + * {@link Data} to generate getters, setters, equals, hashCode, and toString methods. The class is also annotated + * with {@link RedisHash} to indicate that it is a Redis hash stored in Redis.

+ */ +@Data +@Builder +@RedisHash +@AllArgsConstructor +@NoArgsConstructor +public class Role { + + /** + * The unique identifier for the role. + * + *

This field is marked with {@link Id} to indicate it is the primary key in Redis.

+ */ + @Id + private String id; + + /** + * The name of the role. + * + *

This field is indexed to allow for efficient querying by role name.

+ */ + @Indexed + private String name; +} diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/model/User.java b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/model/User.java new file mode 100644 index 000000000..542f752b1 --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/model/User.java @@ -0,0 +1,128 @@ +package id.my.hendisantika.springbootredissample.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Reference; +import org.springframework.data.annotation.Transient; +import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.index.Indexed; + +import java.util.HashSet; +import java.util.Set; + +/** + * Created by IntelliJ IDEA. + * Project : spring-boot-redis-sample + * User: hendisantika + * Link: s.id/hendisantika + * Email: hendisantika@yahoo.co.id + * Telegram : @hendisantika34 + * Date: 05/04/25 + * Time: 07.40 + * To change this template use File | Settings | File Templates. + */ + +/** + * Represents a user entity stored in Redis with associated attributes and behavior. + * + *

This class is annotated with {@link EqualsAndHashCode} and {@link ToString} to include only + * explicitly specified fields in equality checks and string representation, respectively. It also + * uses the {@link Data} annotation to generate getters, setters, and other common methods. + *

+ * + *

Each user has an ID, a name, an email, a password, and an optional password confirmation. + * Additionally, users can be associated with multiple roles. + *

+ * + *

The class is annotated with {@link RedisHash} to indicate that it is a Redis hash stored in Redis.

+ */ +@Data +@RedisHash +@JsonIgnoreProperties(value = {"password", "passwordConfirm"}, allowSetters = true) +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +@ToString(onlyExplicitlyIncluded = true) +@AllArgsConstructor +@NoArgsConstructor +public class User { + + /** + * The unique identifier for the user. + * + *

This field is marked with {@link Id} to indicate it is the primary key in Redis. It is also + * included in the string representation of the user.

+ */ + @Id + @ToString.Include + private String id; + + /** + * The name of the user. + * + *

This field must be non-null and have a length between 2 and 48 characters. It is included in + * the string representation of the user.

+ */ + @NotNull + @Size(min = 2, max = 48) + @ToString.Include + private String name; + + /** + * The email address of the user. + * + *

This field must be non-null, a valid email address, and is included in both the equality + * checks and string representation of the user. It is also indexed for faster lookups.

+ */ + @Email + @NotNull + @EqualsAndHashCode.Include + @ToString.Include + @Indexed + private String email; + + /** + * The password for the user. + * + *

This field is required but not included in equality checks or the string representation of the + * user for security reasons.

+ */ + @NotNull + private String password; + + /** + * A confirmation password used for validation purposes. + * + *

This field is transient and not persisted in Redis. It is used for password confirmation + * during user creation or updates.

+ */ + @Transient + private String passwordConfirm; + + /** + * The roles associated with the user. + * + *

This field is a set of {@link Role} objects and is used to manage the user's roles. It is not + * included in equality checks or the string representation of the user. The default is an empty + * set.

+ */ + @Reference + private Set roles = new HashSet(); + + /** + * Adds a role to the user. + * + *

This method allows adding a {@link Role} to the user's set of roles.

+ * + * @param role the {@link Role} to be added + */ + public void addRole(Role role) { + roles.add(role); + } +} diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/repository/BookRatingRepository.java b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/repository/BookRatingRepository.java new file mode 100644 index 000000000..a89272405 --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/repository/BookRatingRepository.java @@ -0,0 +1,20 @@ +package id.my.hendisantika.springbootredissample.repository; + +import id.my.hendisantika.springbootredissample.model.BookRating; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +/** + * Created by IntelliJ IDEA. + * Project : spring-boot-redis-sample + * User: hendisantika + * Link: s.id/hendisantika + * Email: hendisantika@yahoo.co.id + * Telegram : @hendisantika34 + * Date: 05/04/25 + * Time: 07.41 + * To change this template use File | Settings | File Templates. + */ +@Repository +public interface BookRatingRepository extends CrudRepository { +} diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/repository/BookRepository.java b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/repository/BookRepository.java new file mode 100644 index 000000000..9a9a96855 --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/repository/BookRepository.java @@ -0,0 +1,21 @@ +package id.my.hendisantika.springbootredissample.repository; + +import id.my.hendisantika.springbootredissample.model.Book; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.stereotype.Repository; + +/** + * Created by IntelliJ IDEA. + * Project : spring-boot-redis-sample + * User: hendisantika + * Link: s.id/hendisantika + * Email: hendisantika@yahoo.co.id + * Telegram : @hendisantika34 + * Date: 05/04/25 + * Time: 07.41 + * To change this template use File | Settings | File Templates. + */ +@Repository +public interface BookRepository extends PagingAndSortingRepository, CrudRepository { +} diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/repository/CategoryRepository.java b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/repository/CategoryRepository.java new file mode 100644 index 000000000..54f6c98e7 --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/repository/CategoryRepository.java @@ -0,0 +1,20 @@ +package id.my.hendisantika.springbootredissample.repository; + +import id.my.hendisantika.springbootredissample.model.Category; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +/** + * Created by IntelliJ IDEA. + * Project : spring-boot-redis-sample + * User: hendisantika + * Link: s.id/hendisantika + * Email: hendisantika@yahoo.co.id + * Telegram : @hendisantika34 + * Date: 05/04/25 + * Time: 07.41 + * To change this template use File | Settings | File Templates. + */ +@Repository +public interface CategoryRepository extends CrudRepository { +} diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/repository/RoleRepository.java b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/repository/RoleRepository.java new file mode 100644 index 000000000..e735bcabb --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/repository/RoleRepository.java @@ -0,0 +1,38 @@ +package id.my.hendisantika.springbootredissample.repository; + +/** + * Created by IntelliJ IDEA. + * Project : spring-boot-redis-sample + * User: hendisantika + * Link: s.id/hendisantika + * Email: hendisantika@yahoo.co.id + * Telegram : @hendisantika34 + * Date: 05/04/25 + * Time: 07.42 + * To change this template use File | Settings | File Templates. + */ + +import id.my.hendisantika.springbootredissample.model.Role; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +/** + * Repository interface for {@link Role} entities. + * + *

This interface extends {@link CrudRepository} to provide basic CRUD operations for {@link Role} entities. + * It also includes a custom query method to find a {@link Role} by its name.

+ */ +@Repository +public interface RoleRepository extends CrudRepository { + + /** + * Finds the first {@link Role} entity by its name. + * + *

This method retrieves a {@link Role} entity where the name matches the specified role name. + * If multiple roles have the same name, only the first one is returned.

+ * + * @param role the name of the role to search for + * @return the first {@link Role} with the given name, or {@code null} if no role is found + */ + Role findFirstByname(String role); +} diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/repository/UserRepository.java b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/repository/UserRepository.java new file mode 100644 index 000000000..2bb563ace --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/java/id/my/hendisantika/springbootredissample/repository/UserRepository.java @@ -0,0 +1,21 @@ +package id.my.hendisantika.springbootredissample.repository; + +import id.my.hendisantika.springbootredissample.model.User; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +/** + * Created by IntelliJ IDEA. + * Project : spring-boot-redis-sample + * User: hendisantika + * Link: s.id/hendisantika + * Email: hendisantika@yahoo.co.id + * Telegram : @hendisantika34 + * Date: 05/04/25 + * Time: 07.42 + * To change this template use File | Settings | File Templates. + */ +@Repository +public interface UserRepository extends CrudRepository { + User findFirstByEmail(String email); +} diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/resources/application.properties b/jdk_21_maven/cs/rest/redis-sample/src/main/resources/application.properties new file mode 100644 index 000000000..58c226445 --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/resources/application.properties @@ -0,0 +1,7 @@ +spring.application.name=bookstore +spring.data.redis.host=localhost +spring.data.redis.port=6379 +spring.data.redis.password=${REDIS_PASSWORD:53cret} +spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration +app.numberOfRatings=5000 +app.ratingStars=5 diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/resources/data/books/_books.json b/jdk_21_maven/cs/rest/redis-sample/src/main/resources/data/books/_books.json new file mode 100644 index 000000000..e2813fba2 --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/resources/data/books/_books.json @@ -0,0 +1,52 @@ +[ + { + "id": "book123", + "title": "The Hitchhiker's Guide to the Galaxy", + "subtitle": "A Trilogy in Five Parts", + "description": "Seconds before the Earth is demolished to make way for a galactic freeway, Arthur Dent is plucked off the planet by his friend Ford Prefect, a researcher for the revised edition of The Hitchhiker's Guide to the Galaxy who, for the last fifteen years, has been posing as an out-of-work actor.", + "language": "English", + "pageCount": 224, + "thumbnail": "https://example.com/hitchhikers-guide-thumbnail.jpg", + "price": 12.99, + "currency": "USD", + "infoLink": "https://example.com/hitchhikers-guide-info", + "authors": [ + "Douglas Adams" + ], + "categories": [ + { + "id": "category_scifi", + "name": "Science Fiction" + }, + { + "id": "category_humor", + "name": "Humor" + } + ] + }, + { + "id": "book1234", + "title": "The Hitchhiker's Guide to the Galaxy", + "subtitle": "A Trilogy in Five Parts", + "description": "Seconds before the Earth is demolished to make way for a galactic freeway, Arthur Dent is plucked off the planet by his friend Ford Prefect, a researcher for the revised edition of The Hitchhiker's Guide to the Galaxy who, for the last fifteen years, has been posing as an out-of-work actor.", + "language": "English", + "pageCount": 212, + "thumbnail": "https://example.com/hitchhikers-guide-thumbnail.jpg", + "price": 12.99, + "currency": "USD", + "infoLink": "https://example.com/hitchhikers-guide-info", + "authors": [ + "Douglas Adams" + ], + "categories": [ + { + "id": "category_scifi", + "name": "Science Fiction" + }, + { + "id": "category_humor", + "name": "Humor" + } + ] + } +] diff --git a/jdk_21_maven/cs/rest/redis-sample/src/main/resources/data/users/users.json b/jdk_21_maven/cs/rest/redis-sample/src/main/resources/data/users/users.json new file mode 100644 index 000000000..0891454aa --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/main/resources/data/users/users.json @@ -0,0 +1,71 @@ +[ + { + "id": "18c72298-80e6-401a-be63-eef50ed2f4c0", + "name": "Yuji Itadori", + "email": "yuji.itadori@jujutsukaisen.com", + "password": "hashedPasswordYuji", + "passwordConfirm": "hashedPasswordYuji", + "roles": [ + { + "id": "admin", + "name": "admin" + } + ] + }, + { + "id": "18c72298-80e6-401a-be63-eef50ed2f4c1", + "name": "Megumi Fushiguro", + "email": "megumi.fushiguro@jujutsukaisen.com", + "password": "hashedPasswordMegumi", + "passwordConfirm": "hashedPasswordMegumi", + "roles": [ + { + "id": "customer", + "name": "customer" + } + ] + }, + { + "id": "18c72298-80e6-401a-be63-eef50ed2f4c2", + "name": "Nobara Kugisaki", + "email": "nobara.kugisaki@jujutsukaisen.com", + "password": "hashedPasswordNobara", + "passwordConfirm": "hashedPasswordNobara", + "roles": [ + { + "id": "customer", + "name": "customer" + } + ] + }, + { + "id": "18c72298-80e6-401a-be63-eef50ed2f4c3", + "name": "Satoru Gojo", + "email": "satoru.gojo@jujutsukaisen.com", + "password": "hashedPasswordSatoru", + "passwordConfirm": "hashedPasswordSatoru", + "roles": [ + { + "id": "customer", + "name": "customer" + } + ] + }, + { + "id": "18c72298-80e6-401a-be63-eef50ed2f4c4", + "name": "Ryomen Sukuna", + "email": "ryomen.sukuna@jujutsukaisen.com", + "password": "hashedPasswordSukuna", + "passwordConfirm": "hashedPasswordSukuna", + "roles": [ + { + "id": "customer", + "name": "customer" + }, + { + "id": "customer", + "name": "customer" + } + ] + } +] diff --git a/jdk_21_maven/cs/rest/redis-sample/src/test/java/id/my/hendisantika/springbootredissample/SpringBootRedisSampleApplicationTests.java b/jdk_21_maven/cs/rest/redis-sample/src/test/java/id/my/hendisantika/springbootredissample/SpringBootRedisSampleApplicationTests.java new file mode 100644 index 000000000..ac8586e93 --- /dev/null +++ b/jdk_21_maven/cs/rest/redis-sample/src/test/java/id/my/hendisantika/springbootredissample/SpringBootRedisSampleApplicationTests.java @@ -0,0 +1,56 @@ +package id.my.hendisantika.springbootredissample; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +@Testcontainers +class SpringBootRedisSampleApplicationTests { + + private static final String REDIS_IMAGE_NAME = "redis:7.0-alpine"; + private static final int REDIS_PORT = 6379; + @Container + private static final GenericContainer redis = + new GenericContainer<>(DockerImageName.parse(REDIS_IMAGE_NAME)) + .withExposedPorts(REDIS_PORT); + @Autowired + private StringRedisTemplate redisTemplate; + + @BeforeAll + static void beforeAll() { + // No need to start the container here, @Container will handle it + } + + @AfterAll + static void afterAll() { + // No need to stop the container here, @Container will handle it + } + + @DynamicPropertySource + static void redisProperties(DynamicPropertyRegistry registry) { + registry.add("spring.data.redis.host", redis::getHost); + registry.add("spring.data.redis.port", () -> redis.getMappedPort(REDIS_PORT)); + } + + @Test + void testSaveKeyValueAndGetValue() { + final String key = "testKey", value = "testValue"; + + redisTemplate.opsForValue().set(key, value); + + assertThat(redisTemplate.opsForValue().get(key)).isEqualTo(value); + } + +} diff --git a/jdk_25_maven/cs/rest/jasper/pom.xml b/jdk_25_maven/cs/rest/jasper/pom.xml index 7ccc98cf6..2ed378b8f 100644 --- a/jdk_25_maven/cs/rest/jasper/pom.xml +++ b/jdk_25_maven/cs/rest/jasper/pom.xml @@ -455,6 +455,12 @@ + + + jasper + sut + ${start-class} true diff --git a/jdk_25_maven/cs/rest/pom.xml b/jdk_25_maven/cs/rest/pom.xml index e203c7806..fcb4818e4 100644 --- a/jdk_25_maven/cs/rest/pom.xml +++ b/jdk_25_maven/cs/rest/pom.xml @@ -14,6 +14,7 @@ commafeed + jasper diff --git a/jdk_25_maven/em/embedded/rest/jasper/pom.xml b/jdk_25_maven/em/embedded/rest/jasper/pom.xml new file mode 100644 index 000000000..afc009a12 --- /dev/null +++ b/jdk_25_maven/em/embedded/rest/jasper/pom.xml @@ -0,0 +1,54 @@ + + + + 4.0.0 + + evomaster-benchmark-jdk25-em-embedded-rest-jasper + jar + + + org.evomaster + evomaster-benchmark-jdk25-em-embedded-rest + 4.3.1-SNAPSHOT + + + + + + + org.springframework.boot + spring-boot-dependencies + 4.1.0 + pom + import + + + org.springframework.cloud + spring-cloud-dependencies + 2025.1.2 + pom + import + + + + + + + + jasper + jasper + 1.3.0-SNAPSHOT + + + + org.testcontainers + testcontainers + compile + + + + diff --git a/jdk_25_maven/em/embedded/rest/jasper/src/main/java/em/embedded/jasper/EmbeddedEvoMasterController.java b/jdk_25_maven/em/embedded/rest/jasper/src/main/java/em/embedded/jasper/EmbeddedEvoMasterController.java new file mode 100644 index 000000000..08eb3b921 --- /dev/null +++ b/jdk_25_maven/em/embedded/rest/jasper/src/main/java/em/embedded/jasper/EmbeddedEvoMasterController.java @@ -0,0 +1,208 @@ +package em.embedded.jasper; + +import com.webfuzzing.commons.auth.Header; +import jasper.JasperApplication; +import org.evomaster.client.java.controller.EmbeddedSutController; +import org.evomaster.client.java.controller.InstrumentedSutStarter; +import org.evomaster.client.java.controller.api.dto.auth.AuthenticationDto; +import org.evomaster.client.java.controller.api.dto.SutInfoDto; +import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType; +import org.evomaster.client.java.sql.DbSpecification; +import org.evomaster.client.java.controller.problem.ProblemInfo; +import org.evomaster.client.java.controller.problem.RestProblem; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.testcontainers.containers.GenericContainer; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Class used to start/stop the SUT. This will be controller by the EvoMaster process + */ +public class EmbeddedEvoMasterController extends EmbeddedSutController { + + private static final String API_DOCS_PATH = "/api/v3/api-docs"; + + private static final String POSTGRES_VERSION = "18"; + + private static final int POSTGRES_PORT = 5432; + + private static final String POSTGRES_DB = "jasper"; + + private static final String POSTGRES_USER = "postgres"; + + private static final String POSTGRES_PASSWORD = "password"; + + + private static final GenericContainer postgres = new GenericContainer("postgres:" + POSTGRES_VERSION) + .withEnv("POSTGRES_PASSWORD", POSTGRES_PASSWORD) + .withEnv("POSTGRES_HOST_AUTH_METHOD", "trust") + .withEnv("POSTGRES_DB", POSTGRES_DB) + .withTmpFs(Collections.singletonMap("/var/lib/postgresql", "rw")) + .withExposedPorts(POSTGRES_PORT); + + + public static void main(String[] args) { + + int port = 40100; + if (args.length > 0) { + port = Integer.parseInt(args[0]); + } + + EmbeddedEvoMasterController controller = new EmbeddedEvoMasterController(port); + InstrumentedSutStarter starter = new InstrumentedSutStarter(controller); + + starter.start(); + } + + + private ConfigurableApplicationContext ctx; + + private Connection sqlConnection; + + private List dbSpecification; + + public EmbeddedEvoMasterController() { + this(0); + } + + public EmbeddedEvoMasterController(int port) { + setControllerPort(port); + } + + + @Override + public String startSut() { + + postgres.start(); + + ctx = SpringApplication.run(JasperApplication.class, new String[]{ + "--server.port=0", + "--spring.profiles.active=api-docs", + "--spring.datasource.url=" + dbUrl(), + "--spring.datasource.username=" + POSTGRES_USER, + "--spring.datasource.password=" + POSTGRES_PASSWORD, + "--spring.datasource.hikari.auto-commit=false", + "--spring.jpa.database-platform=jasper.config.PostgreSQLDialect", + "--springdoc.api-docs.path=" + API_DOCS_PATH, + "--jasper.allow-user-tag-header=true", + "--jasper.allow-user-role-header=true", + "--jasper.allow-auth-headers=true" + }); + + closeDatabaseConnection(); + try { + sqlConnection = DriverManager.getConnection(dbUrl(), POSTGRES_USER, POSTGRES_PASSWORD); + dbSpecification = Arrays.asList(new DbSpecification(DatabaseType.POSTGRES, sqlConnection)); + } catch (Exception e) { + throw new RuntimeException(e); + } + + return "http://localhost:" + getSutPort(); + } + + private String dbUrl() { + String host = postgres.getHost(); + int port = postgres.getMappedPort(POSTGRES_PORT); + return "jdbc:postgresql://" + host + ":" + port + "/" + POSTGRES_DB; + } + + protected int getSutPort() { + return (Integer) ((Map) ctx.getEnvironment() + .getPropertySources().get("server.ports").getSource()) + .get("local.server.port"); + } + + @Override + public boolean isSutRunning() { + return ctx != null && ctx.isRunning(); + } + + @Override + public void stopSut() { + closeDatabaseConnection(); + + if (ctx != null) { + ctx.stop(); + ctx.close(); + ctx = null; + } + + postgres.stop(); + } + + private void closeDatabaseConnection() { + if (sqlConnection != null) { + try { + sqlConnection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + sqlConnection = null; + } + } + + @Override + public String getPackagePrefixesToCover() { + return "jasper."; + } + + @Override + public void resetStateOfSUT() { + } + + @Override + public List getDbSpecifications() { + return dbSpecification; + } + + @Override + public List getInfoForAuthentication() { + return Arrays.asList( + auth("admin", "+user/admin", "ROLE_ADMIN"), + auth("alice", "+user/alice", "ROLE_USER"), + auth("bob", "+user/bob", "ROLE_USER") + ); + } + + /* + CSRF uses a cookie-based repository, so a request is accepted as long as the header + and the cookie carry the same value. Without them every write answers 403. + */ + private static final String CSRF_TOKEN = "evomaster"; + + private static AuthenticationDto auth(String name, String userTag, String role) { + AuthenticationDto dto = new AuthenticationDto(name); + dto.getFixedHeaders().add(header("User-Tag", userTag)); + dto.getFixedHeaders().add(header("User-Role", role)); + dto.getFixedHeaders().add(header("X-XSRF-TOKEN", CSRF_TOKEN)); + dto.getFixedHeaders().add(header("Cookie", "XSRF-TOKEN=" + CSRF_TOKEN)); + return dto; + } + + private static Header header(String name, String value) { + Header h = new Header(); + h.setName(name); + h.setValue(value); + return h; + } + + @Override + public ProblemInfo getProblemInfo() { + return new RestProblem( + "http://localhost:" + getSutPort() + API_DOCS_PATH + "/jasper", + null + ); + } + + @Override + public SutInfoDto.OutputFormat getPreferredOutputFormat() { + return SutInfoDto.OutputFormat.JAVA_JUNIT_5; + } +} diff --git a/jdk_25_maven/em/embedded/rest/pom.xml b/jdk_25_maven/em/embedded/rest/pom.xml index 2da139dca..fddd9420d 100644 --- a/jdk_25_maven/em/embedded/rest/pom.xml +++ b/jdk_25_maven/em/embedded/rest/pom.xml @@ -13,6 +13,7 @@ commafeed + jasper diff --git a/jdk_25_maven/em/external/rest/jasper/pom.xml b/jdk_25_maven/em/external/rest/jasper/pom.xml new file mode 100644 index 000000000..3262777c0 --- /dev/null +++ b/jdk_25_maven/em/external/rest/jasper/pom.xml @@ -0,0 +1,85 @@ + + + + 4.0.0 + + evomaster-benchmark-jdk25-em-external-rest-jasper + jar + + + org.evomaster + evomaster-benchmark-jdk25-em-external-rest + 4.3.1-SNAPSHOT + + + + + + + org.testcontainers + testcontainers + compile + + + + org.postgresql + postgresql + 42.7.11 + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + 3.6.2 + + + package + + shade + + + jasper-evomaster-runner + + + + em.external.jasper.ExternalEvoMasterController + + org.evomaster.client.java.instrumentation.InstrumentingAgent + + org.evomaster.client.java.instrumentation.InstrumentingAgent + + true + true + + + + + + + org.slf4j + shaded.emb.org.slf4j + + + com.fasterxml.jackson.annotation + shaded.emb.com.fasterxml.jackson.annotation + + + + + + + + + diff --git a/jdk_25_maven/em/external/rest/jasper/src/main/java/em/external/jasper/ExternalEvoMasterController.java b/jdk_25_maven/em/external/rest/jasper/src/main/java/em/external/jasper/ExternalEvoMasterController.java new file mode 100644 index 000000000..f9b69b1f4 --- /dev/null +++ b/jdk_25_maven/em/external/rest/jasper/src/main/java/em/external/jasper/ExternalEvoMasterController.java @@ -0,0 +1,264 @@ +package em.external.jasper; + + +import com.webfuzzing.commons.auth.Header; +import org.evomaster.client.java.controller.ExternalSutController; +import org.evomaster.client.java.controller.InstrumentedSutStarter; +import org.evomaster.client.java.controller.api.dto.auth.AuthenticationDto; +import org.evomaster.client.java.controller.api.dto.SutInfoDto; +import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType; +import org.evomaster.client.java.sql.DbSpecification; +import org.evomaster.client.java.controller.problem.ProblemInfo; +import org.evomaster.client.java.controller.problem.RestProblem; +import org.testcontainers.containers.GenericContainer; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class ExternalEvoMasterController extends ExternalSutController { + + private static final int DEFAULT_CONTROLLER_PORT = 40100; + + private static final int DEFAULT_SUT_PORT = 12345; + + private static final String POSTGRES_VERSION = "18"; + + private static final String API_DOCS_PATH = "/api/v3/api-docs"; + + private static final int POSTGRES_PORT = 5432; + + private static final String POSTGRES_DB = "jasper"; + + private static final String POSTGRES_USER = "postgres"; + + private static final String POSTGRES_PASSWORD = "password"; + + private static final GenericContainer postgres = new GenericContainer("postgres:" + POSTGRES_VERSION) + .withEnv("POSTGRES_PASSWORD", POSTGRES_PASSWORD) + .withEnv("POSTGRES_HOST_AUTH_METHOD", "trust") + .withEnv("POSTGRES_DB", POSTGRES_DB) + .withTmpFs(Collections.singletonMap("/var/lib/postgresql", "rw")) + .withExposedPorts(POSTGRES_PORT); + + + public static void main(String[] args) { + + int controllerPort = DEFAULT_CONTROLLER_PORT; + if (args.length > 0) { + controllerPort = Integer.parseInt(args[0]); + } + int sutPort = DEFAULT_SUT_PORT; + if (args.length > 1) { + sutPort = Integer.parseInt(args[1]); + } + String jarLocation = "cs/rest/jasper/target"; + if (args.length > 2) { + jarLocation = args[2]; + } + if (!jarLocation.endsWith(".jar")) { + jarLocation += "/jasper-sut.jar"; + } + + int timeoutSeconds = 120; + if (args.length > 3) { + timeoutSeconds = Integer.parseInt(args[3]); + } + + String command = "java"; + if (args.length > 4) { + command = args[4]; + } + + ExternalEvoMasterController controller = + new ExternalEvoMasterController(controllerPort, jarLocation, sutPort, timeoutSeconds, command); + + controller.setNeedsJdk17Options(true); + + InstrumentedSutStarter starter = new InstrumentedSutStarter(controller); + + starter.start(); + } + + + private final int timeoutSeconds; + + private final int sutPort; + + private String jarLocation; + private Connection sqlConnection; + private List dbSpecification; + + public ExternalEvoMasterController() { + this(DEFAULT_CONTROLLER_PORT, "../target/jasper-sut.jar", DEFAULT_SUT_PORT, 120, "java"); + } + + public ExternalEvoMasterController(String jarLocation) { + this(); + this.jarLocation = jarLocation; + } + + public ExternalEvoMasterController(int controllerPort, String jarLocation, int sutPort, int timeoutSeconds, String command) { + this.sutPort = sutPort; + this.jarLocation = jarLocation; + this.timeoutSeconds = timeoutSeconds; + + setControllerPort(controllerPort); + setJavaCommand(command); + } + + @Override + public String[] getInputParameters() { + return new String[]{ + "--server.port=" + sutPort, + "--spring.profiles.active=api-docs", + "--spring.datasource.url=" + dbUrl(), + "--spring.datasource.username=" + POSTGRES_USER, + "--spring.datasource.password=" + POSTGRES_PASSWORD, + "--spring.datasource.hikari.auto-commit=false", + "--spring.jpa.database-platform=jasper.config.PostgreSQLDialect", + "--springdoc.api-docs.path=" + API_DOCS_PATH, + "--jasper.allow-user-tag-header=true", + "--jasper.allow-user-role-header=true", + "--jasper.allow-auth-headers=true" + }; + } + + @Override + public String[] getJVMParameters() { + return new String[]{}; + } + + + private String dbUrl() { + String host = postgres.getHost(); + int port = postgres.getMappedPort(POSTGRES_PORT); + return "jdbc:postgresql://" + host + ":" + port + "/" + POSTGRES_DB; + } + + @Override + public String getBaseURL() { + return "http://localhost:" + sutPort; + } + + @Override + public String getPathToExecutableJar() { + return jarLocation; + } + + @Override + public String getLogMessageOfInitializedServer() { + return "Started JasperApplication in"; + } + + @Override + public long getMaxAwaitForInitializationInSeconds() { + return timeoutSeconds; + } + + @Override + public void preStart() { + postgres.start(); + } + + @Override + public void postStart() { + closeDatabaseConnection(); + + try { + sqlConnection = DriverManager.getConnection(dbUrl(), POSTGRES_USER, POSTGRES_PASSWORD); + dbSpecification = Arrays.asList(new DbSpecification(DatabaseType.POSTGRES, sqlConnection)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public void preStop() { + closeDatabaseConnection(); + } + + private void closeDatabaseConnection() { + if (sqlConnection != null) { + try { + sqlConnection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + sqlConnection = null; + } + } + + @Override + public void postStop() { + postgres.stop(); + } + + @Override + public String getPackagePrefixesToCover() { + return "jasper."; + } + + @Override + public ProblemInfo getProblemInfo() { + return new RestProblem( + "http://localhost:" + sutPort + API_DOCS_PATH + "/jasper", + null + ); + } + + @Override + public SutInfoDto.OutputFormat getPreferredOutputFormat() { + return SutInfoDto.OutputFormat.JAVA_JUNIT_5; + } + + /* + Jasper has no user registration: an identity is whatever the User-Tag and User-Role + headers say, once the SUT is started with the allow-*-header flags above. + "alice" and "bob" share the same role on purpose, so that broken access control + between two users of equal privilege is detectable. + */ + @Override + public List getInfoForAuthentication() { + return Arrays.asList( + auth("admin", "+user/admin", "ROLE_ADMIN"), + auth("alice", "+user/alice", "ROLE_USER"), + auth("bob", "+user/bob", "ROLE_USER") + ); + } + + /* + CSRF uses a cookie-based repository, so a request is accepted as long as the header + and the cookie carry the same value. Without them every write answers 403. + */ + private static final String CSRF_TOKEN = "evomaster"; + + private static AuthenticationDto auth(String name, String userTag, String role) { + AuthenticationDto dto = new AuthenticationDto(name); + dto.getFixedHeaders().add(header("User-Tag", userTag)); + dto.getFixedHeaders().add(header("User-Role", role)); + dto.getFixedHeaders().add(header("X-XSRF-TOKEN", CSRF_TOKEN)); + dto.getFixedHeaders().add(header("Cookie", "XSRF-TOKEN=" + CSRF_TOKEN)); + return dto; + } + + private static Header header(String name, String value) { + Header h = new Header(); + h.setName(name); + h.setValue(value); + return h; + } + + @Override + public void resetStateOfSUT() { + } + + @Override + public List getDbSpecifications() { + return dbSpecification; + } + +} diff --git a/jdk_25_maven/em/external/rest/pom.xml b/jdk_25_maven/em/external/rest/pom.xml index 8759a381c..b525c4ac8 100644 --- a/jdk_25_maven/em/external/rest/pom.xml +++ b/jdk_25_maven/em/external/rest/pom.xml @@ -13,6 +13,7 @@ commafeed + jasper diff --git a/openapi-swagger/jasper.json b/openapi-swagger/jasper.json new file mode 100644 index 000000000..5acc002b9 --- /dev/null +++ b/openapi-swagger/jasper.json @@ -0,0 +1,5599 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Jasper KMS API", + "description": "Jasper Knowledge Management Server API documentation", + "license": { + "name": "MIT", + "url": "https://github.com/cjmalloy/jasper/blob/master/LICENSE" + }, + "version": "1.2.0" + }, + "servers": [ + { + "url": "http://localhost:12345", + "description": "Generated server url" + }, + { + "url": "https://virtserver.swaggerhub.com/cjmalloy/Jasper/1.2.0", + "description": "SwaggerHub API Auto Mocking" + } + ], + "paths": { + "/api/v1/user": { + "get": { + "tags": [ + "User" + ], + "operationId": "getUser", + "parameters": [ + { + "name": "tag", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "[_+]user(?:/[a-z0-9]+(?:[./][a-z0-9]+)*)?(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/UserDto" + } + } + } + }, + "304": { + "description": "Not Modified" + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "put": { + "tags": [ + "User" + ], + "operationId": "updateUser", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "post": { + "tags": [ + "User" + ], + "operationId": "createUser", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "delete": { + "tags": [ + "User" + ], + "operationId": "deleteUser", + "parameters": [ + { + "name": "tag", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "[_+]user(?:/[a-z0-9]+(?:[./][a-z0-9]+)*)?(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "patch": { + "tags": [ + "User" + ], + "operationId": "mergeUser", + "parameters": [ + { + "name": "tag", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*(?:@|(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?)" + } + }, + { + "name": "cursor", + "in": "query", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "requestBody": { + "content": { + "application/merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/JsonMergePatch" + } + }, + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/JsonPatch" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/template": { + "get": { + "tags": [ + "Template" + ], + "operationId": "getTemplate", + "parameters": [ + { + "name": "tag", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:_?[a-z0-9]+(?:[./][a-z0-9]+)*)?(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/TemplateDto" + } + } + } + }, + "304": { + "description": "Not Modified" + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "put": { + "tags": [ + "Template" + ], + "operationId": "updateTemplate", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Template" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "post": { + "tags": [ + "Template" + ], + "operationId": "createTemplate", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Template" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Template" + ], + "operationId": "deleteTemplate", + "parameters": [ + { + "name": "tag", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:_?[a-z0-9]+(?:[./][a-z0-9]+)*)?(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Template" + ], + "operationId": "mergeTemplate", + "parameters": [ + { + "name": "tag", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*(?:@|(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?)" + } + }, + { + "name": "cursor", + "in": "query", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "requestBody": { + "content": { + "application/merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/JsonMergePatch" + } + }, + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/JsonPatch" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/ref": { + "get": { + "tags": [ + "Ref" + ], + "operationId": "getRef", + "parameters": [ + { + "name": "url", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/RefDto" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "304": { + "description": "Not Modified" + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "put": { + "tags": [ + "Ref" + ], + "operationId": "updateRef", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Ref" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "post": { + "tags": [ + "Ref" + ], + "operationId": "createRef", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Ref" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Ref" + ], + "operationId": "deleteRef", + "parameters": [ + { + "name": "url", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Ref" + ], + "operationId": "mergeRef", + "parameters": [ + { + "name": "url", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + }, + { + "name": "cursor", + "in": "query", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "requestBody": { + "content": { + "application/merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/JsonMergePatch" + } + }, + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/JsonPatch" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/plugin": { + "get": { + "tags": [ + "Plugin" + ], + "operationId": "getPlugin", + "parameters": [ + { + "name": "tag", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "[_+]?plugin(?:/[a-z0-9]+(?:[./][a-z0-9]+)*)?(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PluginDto" + } + } + } + }, + "304": { + "description": "Not Modified" + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "put": { + "tags": [ + "Plugin" + ], + "operationId": "updatePlugin", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Plugin" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "post": { + "tags": [ + "Plugin" + ], + "operationId": "createPlugin", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Plugin" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Plugin" + ], + "operationId": "deletePlugin", + "parameters": [ + { + "name": "tag", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "[_+]?plugin(?:/[a-z0-9]+(?:[./][a-z0-9]+)*)?(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Plugin" + ], + "operationId": "mergePlugin", + "parameters": [ + { + "name": "tag", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*(?:@|(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?)" + } + }, + { + "name": "cursor", + "in": "query", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "requestBody": { + "content": { + "application/merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/JsonMergePatch" + } + }, + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/JsonPatch" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/ext": { + "get": { + "tags": [ + "Ext" + ], + "operationId": "getExt", + "parameters": [ + { + "name": "tag", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*(?:@|(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?)" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/ExtDto" + } + } + } + }, + "304": { + "description": "Not Modified" + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "put": { + "tags": [ + "Ext" + ], + "operationId": "updateExt", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Ext" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "post": { + "tags": [ + "Ext" + ], + "operationId": "createExt", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Ext" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Ext" + ], + "operationId": "deleteExt", + "parameters": [ + { + "name": "tag", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*(?:@|(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?)" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Ext" + ], + "operationId": "mergeExt", + "parameters": [ + { + "name": "tag", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*(?:@|(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?)" + } + }, + { + "name": "cursor", + "in": "query", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "requestBody": { + "content": { + "application/merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/JsonMergePatch" + } + }, + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/JsonPatch" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/webhook/smtp": { + "post": { + "tags": [ + "webhook" + ], + "operationId": "smtp", + "parameters": [ + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SmtpWebhookDto" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created" + }, + "400": { + "description": "Bad Request" + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/user/keygen": { + "post": { + "tags": [ + "User" + ], + "operationId": "keygen", + "parameters": [ + { + "name": "tag", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "[_+]user(?:/[a-z0-9]+(?:[./][a-z0-9]+)*)?(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "responses": { + "204": { + "description": "No Content", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/tags": { + "post": { + "tags": [ + "Tagging" + ], + "operationId": "createTags", + "parameters": [ + { + "name": "tag", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*" + } + }, + { + "name": "url", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Tagging" + ], + "operationId": "deleteTags", + "parameters": [ + { + "name": "tag", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*" + } + }, + { + "name": "url", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Tagging" + ], + "operationId": "patchTags", + "parameters": [ + { + "name": "tags", + "in": "query", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "string", + "pattern": "[-]?[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*" + } + } + }, + { + "name": "url", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "date-time" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/tags/response": { + "get": { + "tags": [ + "Tagging" + ], + "operationId": "getResponse", + "parameters": [ + { + "name": "url", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/RefDto" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "post": { + "tags": [ + "Tagging" + ], + "operationId": "createResponse", + "parameters": [ + { + "name": "tag", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*" + } + }, + { + "name": "url", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + } + ], + "responses": { + "201": { + "description": "Created" + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Tagging" + ], + "operationId": "deleteResponse", + "parameters": [ + { + "name": "tag", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*" + } + }, + { + "name": "url", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "409": { + "description": "Conflict", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Tagging" + ], + "operationId": "respond", + "parameters": [ + { + "name": "tags", + "in": "query", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "string", + "pattern": "[-]?[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*" + } + } + }, + { + "name": "url", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + } + ], + "requestBody": { + "content": { + "application/merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/JsonMergePatch" + } + }, + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/JsonPatch" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/proxy": { + "get": { + "tags": [ + "Proxy" + ], + "operationId": "fetch", + "parameters": [ + { + "name": "Range", + "in": "header", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "url", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + }, + { + "name": "thumbnail", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/StreamingResponseBody" + } + } + } + }, + "206": { + "description": "Partial Content", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/StreamingResponseBody" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/StreamingResponseBody" + } + } + } + }, + "416": { + "description": "Requested range not satisfiable", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "503": { + "description": "Service Unavailable", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "post": { + "tags": [ + "Proxy" + ], + "operationId": "save", + "parameters": [ + { + "name": "title", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "mime", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/RefDto" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "503": { + "description": "Service Unavailable", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Proxy" + ], + "operationId": "clearDeleted", + "parameters": [ + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "500": { + "description": "Internal Server Error", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "503": { + "description": "Service Unavailable", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/oembed/defaults": { + "post": { + "tags": [ + "oEmbed" + ], + "operationId": "defaults", + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/api/v1/backup": { + "get": { + "tags": [ + "Backup" + ], + "operationId": "listBackups", + "parameters": [ + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BackupDto" + } + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "post": { + "tags": [ + "Backup" + ], + "operationId": "createBackup", + "parameters": [ + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BackupOptionsDto" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/backup/upload/{id}": { + "post": { + "tags": [ + "Backup" + ], + "operationId": "uploadBackup", + "parameters": [ + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + }, + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "Created" + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/backup/restore/{id}": { + "post": { + "tags": [ + "Backup" + ], + "operationId": "restoreBackup", + "parameters": [ + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + }, + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BackupOptionsDto" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/backup/regen": { + "post": { + "tags": [ + "Backup" + ], + "operationId": "regen", + "parameters": [ + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/backup/key": { + "post": { + "tags": [ + "Backup" + ], + "operationId": "getBackupKey", + "parameters": [ + { + "name": "key", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not Modified" + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/user/whoami": { + "get": { + "tags": [ + "User" + ], + "operationId": "whoAmI", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/RolesDto" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/user/page": { + "get": { + "tags": [ + "User" + ], + "operationId": "getUserPage", + "parameters": [ + { + "name": "page", + "in": "query", + "description": "Zero-based page index (0..N)", + "required": false, + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + } + }, + { + "name": "size", + "in": "query", + "description": "The size of the page to be returned", + "required": false, + "schema": { + "type": "integer", + "default": 10, + "minimum": 1 + } + }, + { + "name": "sort", + "in": "query", + "description": "Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.", + "required": false, + "schema": { + "type": "array", + "default": [ + "tag,ASC" + ], + "items": { + "type": "string" + } + } + }, + { + "name": "query", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "[!_+a-z0-9/.@|:()*]+" + } + }, + { + "name": "nesting", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "level", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "deleted", + "in": "query", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "modifiedBefore", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "modifiedAfter", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PagedModelUserDto" + } + } + } + }, + "304": { + "description": "Not Modified" + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/template/page": { + "get": { + "tags": [ + "Template" + ], + "operationId": "getTemplatePage", + "parameters": [ + { + "name": "page", + "in": "query", + "description": "Zero-based page index (0..N)", + "required": false, + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + } + }, + { + "name": "size", + "in": "query", + "description": "The size of the page to be returned", + "required": false, + "schema": { + "type": "integer", + "default": 10, + "minimum": 1 + } + }, + { + "name": "sort", + "in": "query", + "description": "Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.", + "required": false, + "schema": { + "type": "array", + "default": [ + "tag,ASC" + ], + "items": { + "type": "string" + } + } + }, + { + "name": "query", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "[!_+a-z0-9/.@|:()*]+" + } + }, + { + "name": "nesting", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "level", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "deleted", + "in": "query", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "modifiedBefore", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "modifiedAfter", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PagedModelTemplateDto" + } + } + } + }, + "304": { + "description": "Not Modified" + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/ref/page": { + "get": { + "tags": [ + "Ref" + ], + "operationId": "getRefPage", + "parameters": [ + { + "name": "page", + "in": "query", + "description": "Zero-based page index (0..N)", + "required": false, + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + } + }, + { + "name": "size", + "in": "query", + "description": "The size of the page to be returned", + "required": false, + "schema": { + "type": "integer", + "default": 10, + "minimum": 1 + } + }, + { + "name": "sort", + "in": "query", + "description": "Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "name": "query", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "[!_+a-z0-9/.@|:()*]+" + } + }, + { + "name": "noDescendents", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*" + } + }, + { + "name": "url", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "obsolete", + "in": "query", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "scheme", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:" + } + }, + { + "name": "sources", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "noSources", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "responses", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "noResponses", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "modifiedBefore", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "modifiedAfter", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "publishedBefore", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "publishedAfter", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "createdBefore", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "createdAfter", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "responseBefore", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "responseAfter", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "pluginResponse", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string", + "pattern": "[_+]?plugin(?:/[a-z0-9]+(?:[./][a-z0-9]+)*)?" + }, + "maxItems": 100, + "minItems": 0 + } + }, + { + "name": "noPluginResponse", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string", + "pattern": "[_+]?plugin(?:/[a-z0-9]+(?:[./][a-z0-9]+)*)?" + }, + "maxItems": 100, + "minItems": 0 + } + }, + { + "name": "userResponse", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string", + "pattern": "[_+]?plugin(?:/[a-z0-9]+(?:[./][a-z0-9]+)*)?" + }, + "maxItems": 100, + "minItems": 0 + } + }, + { + "name": "noUserResponse", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string", + "pattern": "[_+]?plugin(?:/[a-z0-9]+(?:[./][a-z0-9]+)*)?" + }, + "maxItems": 100, + "minItems": 0 + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PagedModelRefDto" + } + } + } + }, + "304": { + "description": "Not Modified" + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/ref/count": { + "get": { + "tags": [ + "Ref" + ], + "operationId": "countRefs", + "parameters": [ + { + "name": "query", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "[!_+a-z0-9/.@|:()*]+" + } + }, + { + "name": "noDescendents", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*" + } + }, + { + "name": "nesting", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "url", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "obsolete", + "in": "query", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "scheme", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:" + } + }, + { + "name": "sources", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "noSources", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "responses", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "noResponses", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "modifiedBefore", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "modifiedAfter", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "publishedBefore", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "publishedAfter", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "createdBefore", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "createdAfter", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "responseBefore", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "responseAfter", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "pluginResponse", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string", + "pattern": "[_+]?plugin(?:/[a-z0-9]+(?:[./][a-z0-9]+)*)?" + }, + "maxItems": 100, + "minItems": 0 + } + }, + { + "name": "noPluginResponse", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string", + "pattern": "[_+]?plugin(?:/[a-z0-9]+(?:[./][a-z0-9]+)*)?" + }, + "maxItems": 100, + "minItems": 0 + } + }, + { + "name": "userResponse", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string", + "pattern": "[_+]?plugin(?:/[a-z0-9]+(?:[./][a-z0-9]+)*)?" + }, + "maxItems": 100, + "minItems": 0 + } + }, + { + "name": "noUserResponse", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string", + "pattern": "[_+]?plugin(?:/[a-z0-9]+(?:[./][a-z0-9]+)*)?" + }, + "maxItems": 100, + "minItems": 0 + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "integer", + "format": "int64" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/proxy/prefetch": { + "get": { + "tags": [ + "Proxy" + ], + "operationId": "preFetch", + "parameters": [ + { + "name": "url", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + }, + { + "name": "thumbnail", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "503": { + "description": "Service Unavailable", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/proxy/prefetch/{filename}": { + "get": { + "tags": [ + "Proxy" + ], + "operationId": "preFetch_1", + "parameters": [ + { + "name": "url", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + }, + { + "name": "thumbnail", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false + } + }, + { + "name": "filename", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "503": { + "description": "Service Unavailable", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/proxy/{filename}": { + "get": { + "tags": [ + "Proxy" + ], + "operationId": "fetch_1", + "parameters": [ + { + "name": "Range", + "in": "header", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "url", + "in": "query", + "required": true, + "schema": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + }, + { + "name": "thumbnail", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false + } + }, + { + "name": "filename", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/StreamingResponseBody" + } + } + } + }, + "206": { + "description": "Partial Content", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/StreamingResponseBody" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/StreamingResponseBody" + } + } + } + }, + "416": { + "description": "Requested range not satisfiable", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "503": { + "description": "Service Unavailable", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/plugin/page": { + "get": { + "tags": [ + "Plugin" + ], + "operationId": "getPluginPage", + "parameters": [ + { + "name": "page", + "in": "query", + "description": "Zero-based page index (0..N)", + "required": false, + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + } + }, + { + "name": "size", + "in": "query", + "description": "The size of the page to be returned", + "required": false, + "schema": { + "type": "integer", + "default": 10, + "minimum": 1 + } + }, + { + "name": "sort", + "in": "query", + "description": "Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.", + "required": false, + "schema": { + "type": "array", + "default": [ + "tag,ASC" + ], + "items": { + "type": "string" + } + } + }, + { + "name": "query", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "[!_+a-z0-9/.@|:()*]+" + } + }, + { + "name": "nesting", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "level", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "deleted", + "in": "query", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "modifiedBefore", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "modifiedAfter", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PagedModelPluginDto" + } + } + } + }, + "304": { + "description": "Not Modified" + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/origin": { + "get": { + "tags": [ + "Origin" + ], + "operationId": "listOrigins", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Origin" + ], + "operationId": "deleteOrigin", + "parameters": [ + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + }, + { + "name": "olderThan", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/oembed": { + "get": { + "tags": [ + "oEmbed" + ], + "operationId": "oembed", + "parameters": [ + { + "name": "params", + "in": "query", + "required": true, + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/JsonNode" + } + } + } + }, + "400": { + "description": "Bad Request" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/api/v1/graph/list": { + "get": { + "tags": [ + "Graph" + ], + "operationId": "getGraphList", + "parameters": [ + { + "name": "urls", + "in": "query", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + }, + "maxItems": 100, + "minItems": 0 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RefNodeDto" + } + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/ext/page": { + "get": { + "tags": [ + "Ext" + ], + "operationId": "getExtPage", + "parameters": [ + { + "name": "page", + "in": "query", + "description": "Zero-based page index (0..N)", + "required": false, + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + } + }, + { + "name": "size", + "in": "query", + "description": "The size of the page to be returned", + "required": false, + "schema": { + "type": "integer", + "default": 10, + "minimum": 1 + } + }, + { + "name": "sort", + "in": "query", + "description": "Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.", + "required": false, + "schema": { + "type": "array", + "default": [ + "tag,ASC" + ], + "items": { + "type": "string" + } + } + }, + { + "name": "query", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "[!_+a-z0-9/.@|:()*]+" + } + }, + { + "name": "nesting", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "level", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "deleted", + "in": "query", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "modifiedAfter", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "modifiedBefore", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PagedModelExtDto" + } + } + } + }, + "304": { + "description": "Not Modified" + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/ext/count": { + "get": { + "tags": [ + "Ext" + ], + "operationId": "countExts", + "parameters": [ + { + "name": "query", + "in": "query", + "required": false, + "schema": { + "type": "string", + "pattern": "[!_+a-z0-9/.@|:()*]+" + } + }, + { + "name": "nesting", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "level", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "deleted", + "in": "query", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "modifiedAfter", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "modifiedBefore", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "integer", + "format": "int64" + } + } + } + }, + "304": { + "description": "Not Modified" + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + }, + "/api/v1/backup/{id}": { + "get": { + "tags": [ + "Backup" + ], + "operationId": "downloadBackup", + "parameters": [ + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + }, + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "p", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/StreamingResponseBody" + } + } + } + }, + "304": { + "description": "Not Modified" + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Backup" + ], + "operationId": "deleteBackup", + "parameters": [ + { + "name": "origin", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + } + }, + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "403": { + "description": "Forbidden", + "content": { + "*/*": { + "schema": { + "$ref": "https://opensource.zalando.com/problem/schema.yaml#/Problem" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "External": { + "type": "object", + "properties": { + "ids": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "User": { + "type": "object", + "properties": { + "tag": { + "type": "string", + "minLength": 1, + "pattern": "[_+]user(?:/[a-z0-9]+(?:[./][a-z0-9]+)*)?" + }, + "origin": { + "type": "string", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + }, + "role": { + "type": "string", + "pattern": "\\w*" + }, + "name": { + "type": "string" + }, + "readAccess": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*" + } + }, + "writeAccess": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*" + } + }, + "tagReadAccess": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*" + } + }, + "tagWriteAccess": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*" + } + }, + "modified": { + "type": "string", + "format": "date-time" + }, + "key": { + "type": "string", + "format": "byte" + }, + "pubKey": { + "type": "string", + "format": "byte" + }, + "authorizedKeys": { + "type": "string", + "maxLength": 65000, + "minLength": 0 + }, + "external": { + "$ref": "#/components/schemas/External" + } + }, + "required": [ + "tag" + ] + }, + "Template": { + "type": "object", + "properties": { + "tag": { + "type": "string", + "pattern": "(?:_?[a-z0-9]+(?:[./][a-z0-9]+)*)?" + }, + "origin": { + "type": "string", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + }, + "name": { + "type": "string" + }, + "config": { + "type": "object" + }, + "defaults": { + "type": "object" + }, + "schema": { + "type": "object" + }, + "modified": { + "type": "string", + "format": "date-time" + } + } + }, + "Metadata": { + "type": "object", + "properties": { + "modified": { + "type": "string" + }, + "expandedTags": { + "type": "array", + "items": { + "type": "string" + } + }, + "responses": { + "type": "array", + "items": { + "type": "string" + } + }, + "internalResponses": { + "type": "array", + "items": { + "type": "string" + } + }, + "plugins": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int64" + } + }, + "userUrls": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "obsolete": { + "type": "boolean" + }, + "regen": { + "type": "boolean" + } + } + }, + "Ref": { + "type": "object", + "properties": { + "url": { + "type": "string", + "minLength": 1, + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + }, + "origin": { + "type": "string", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + }, + "title": { + "type": "string" + }, + "comment": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*" + } + }, + "sources": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + "alternateUrls": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "pattern": "^[^:/?#]+:(?://[^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?" + } + }, + "plugins": { + "type": "object" + }, + "metadata": { + "$ref": "#/components/schemas/Metadata" + }, + "scheme": { + "type": "string" + }, + "published": { + "type": "string", + "format": "date-time" + }, + "created": { + "type": "string", + "format": "date-time" + }, + "modified": { + "type": "string", + "format": "date-time" + }, + "textsearchEn": { + "type": "string" + } + }, + "required": [ + "published", + "url" + ] + }, + "JsonNode": {}, + "Plugin": { + "type": "object", + "properties": { + "tag": { + "type": "string", + "minLength": 1, + "pattern": "[_+]?plugin(?:/[a-z0-9]+(?:[./][a-z0-9]+)*)?" + }, + "origin": { + "type": "string", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + }, + "name": { + "type": "string" + }, + "config": { + "type": "object" + }, + "defaults": { + "$ref": "#/components/schemas/JsonNode" + }, + "schema": { + "type": "object" + }, + "modified": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "tag" + ] + }, + "Ext": { + "type": "object", + "properties": { + "tag": { + "type": "string", + "minLength": 1, + "pattern": "[_+]?[a-z0-9]+(?:[./][a-z0-9]+)*" + }, + "origin": { + "type": "string", + "pattern": "(?:@[a-z0-9]+(?:[.][a-z0-9]+)*)?" + }, + "name": { + "type": "string" + }, + "config": { + "type": "object" + }, + "modified": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "tag" + ] + }, + "Addresses": { + "type": "object", + "properties": { + "from": { + "$ref": "#/components/schemas/EmailAddress" + }, + "to": { + "$ref": "#/components/schemas/EmailAddress" + }, + "replyTo": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EmailAddress" + } + }, + "cc": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EmailAddress" + } + }, + "bcc": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EmailAddress" + } + }, + "inReplyTo": { + "type": "array", + "items": { + "type": "string" + } + }, + "html": { + "type": "string" + }, + "resentFrom": { + "$ref": "#/components/schemas/EmailAddress" + }, + "resentTo": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EmailAddress" + } + }, + "resentCc": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EmailAddress" + } + }, + "resentBcc": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EmailAddress" + } + } + } + }, + "Body": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "html": { + "type": "string" + } + } + }, + "EmailAddress": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "address": { + "type": "string" + } + } + }, + "EmailAttachment": { + "type": "object", + "properties": { + "filename": { + "type": "string" + }, + "contentType": { + "type": "string" + }, + "data": { + "type": "string" + } + } + }, + "EmailEmbeddedFile": { + "type": "object", + "properties": { + "cid": { + "type": "string" + }, + "contentType": { + "type": "string" + }, + "data": { + "type": "string" + } + } + }, + "SmtpWebhookDto": { + "type": "object", + "properties": { + "spf": { + "type": "string" + }, + "references": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "string" + }, + "date": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "resentDate": { + "type": "string" + }, + "resentId": { + "type": "string" + }, + "body": { + "$ref": "#/components/schemas/Body" + }, + "addresses": { + "$ref": "#/components/schemas/Addresses" + }, + "attachments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EmailAttachment" + } + }, + "embeddedFiles": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EmailEmbeddedFile" + } + } + } + }, + "MetadataDto": { + "type": "object", + "properties": { + "modified": { + "type": "string" + }, + "responses": { + "type": "integer", + "format": "int32" + }, + "internalResponses": { + "type": "integer", + "format": "int32" + }, + "plugins": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + }, + "userUrls": { + "type": "array", + "items": { + "type": "string" + } + }, + "obsolete": { + "type": "boolean" + } + } + }, + "RefDto": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "origin": { + "type": "string" + }, + "title": { + "type": "string" + }, + "comment": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "sources": { + "type": "array", + "items": { + "type": "string" + } + }, + "alternateUrls": { + "type": "array", + "items": { + "type": "string" + } + }, + "plugins": { + "type": "object" + }, + "metadata": { + "$ref": "#/components/schemas/MetadataDto" + }, + "published": { + "type": "string", + "format": "date-time" + }, + "created": { + "type": "string", + "format": "date-time" + }, + "modified": { + "type": "string", + "format": "date-time" + } + } + }, + "BackupOptionsDto": { + "type": "object", + "properties": { + "cache": { + "type": "boolean" + }, + "ref": { + "type": "boolean" + }, + "ext": { + "type": "boolean" + }, + "user": { + "type": "boolean" + }, + "plugin": { + "type": "boolean" + }, + "template": { + "type": "boolean" + }, + "newerThan": { + "type": "string", + "format": "date-time" + } + } + }, + "JsonMergePatch": {}, + "JsonPatch": {}, + "ExternalDto": { + "type": "object", + "properties": { + "ids": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "UserDto": { + "type": "object", + "properties": { + "tag": { + "type": "string" + }, + "origin": { + "type": "string" + }, + "name": { + "type": "string" + }, + "role": { + "type": "string" + }, + "readAccess": { + "type": "array", + "items": { + "type": "string" + } + }, + "writeAccess": { + "type": "array", + "items": { + "type": "string" + } + }, + "tagReadAccess": { + "type": "array", + "items": { + "type": "string" + } + }, + "tagWriteAccess": { + "type": "array", + "items": { + "type": "string" + } + }, + "modified": { + "type": "string", + "format": "date-time" + }, + "pubKey": { + "type": "string", + "format": "byte" + }, + "authorizedKeys": { + "type": "string" + }, + "external": { + "$ref": "#/components/schemas/ExternalDto" + } + } + }, + "RolesDto": { + "type": "object", + "properties": { + "debug": { + "type": "boolean" + }, + "tag": { + "type": "string" + }, + "admin": { + "type": "boolean" + }, + "mod": { + "type": "boolean" + }, + "editor": { + "type": "boolean" + }, + "user": { + "type": "boolean" + }, + "viewer": { + "type": "boolean" + }, + "banned": { + "type": "boolean" + } + } + }, + "PageMetadata": { + "type": "object", + "properties": { + "size": { + "type": "integer", + "format": "int64" + }, + "number": { + "type": "integer", + "format": "int64" + }, + "totalElements": { + "type": "integer", + "format": "int64" + }, + "totalPages": { + "type": "integer", + "format": "int64" + } + } + }, + "PagedModelUserDto": { + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UserDto" + } + }, + "page": { + "$ref": "#/components/schemas/PageMetadata" + } + } + }, + "TemplateDto": { + "type": "object", + "properties": { + "tag": { + "type": "string" + }, + "origin": { + "type": "string" + }, + "name": { + "type": "string" + }, + "config": { + "type": "object" + }, + "defaults": { + "type": "object" + }, + "schema": { + "type": "object" + }, + "modified": { + "type": "string", + "format": "date-time" + } + } + }, + "PagedModelTemplateDto": { + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TemplateDto" + } + }, + "page": { + "$ref": "#/components/schemas/PageMetadata" + } + } + }, + "PagedModelRefDto": { + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RefDto" + } + }, + "page": { + "$ref": "#/components/schemas/PageMetadata" + } + } + }, + "StreamingResponseBody": {}, + "PluginDto": { + "type": "object", + "properties": { + "tag": { + "type": "string" + }, + "origin": { + "type": "string" + }, + "name": { + "type": "string" + }, + "config": { + "type": "object" + }, + "defaults": { + "$ref": "#/components/schemas/JsonNode" + }, + "schema": { + "type": "object" + }, + "modified": { + "type": "string", + "format": "date-time" + } + } + }, + "PagedModelPluginDto": { + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PluginDto" + } + }, + "page": { + "$ref": "#/components/schemas/PageMetadata" + } + } + }, + "RefNodeDto": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "origin": { + "type": "string" + }, + "title": { + "type": "string" + }, + "comment": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "sources": { + "type": "array", + "items": { + "type": "string" + } + }, + "responses": { + "type": "array", + "items": { + "type": "string" + } + }, + "alternateUrls": { + "type": "array", + "items": { + "type": "string" + } + }, + "plugins": { + "type": "object" + }, + "metadata": { + "$ref": "#/components/schemas/MetadataDto" + }, + "published": { + "type": "string", + "format": "date-time" + }, + "created": { + "type": "string", + "format": "date-time" + }, + "modified": { + "type": "string", + "format": "date-time" + } + } + }, + "ExtDto": { + "type": "object", + "properties": { + "tag": { + "type": "string" + }, + "origin": { + "type": "string" + }, + "name": { + "type": "string" + }, + "config": { + "type": "object" + }, + "modified": { + "type": "string", + "format": "date-time" + } + } + }, + "PagedModelExtDto": { + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ExtDto" + } + }, + "page": { + "$ref": "#/components/schemas/PageMetadata" + } + } + }, + "BackupDto": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "size": { + "type": "integer", + "format": "int64" + } + } + } + } + } +} diff --git a/scripts/build/docker-compose.build.yml b/scripts/build/docker-compose.build.yml index 03d813218..45b2871d7 100644 --- a/scripts/build/docker-compose.build.yml +++ b/scripts/build/docker-compose.build.yml @@ -285,8 +285,10 @@ services: (cd cs && mvn clean install -DskipTests) fi cp cs/rest/commafeed/commafeed-server/target/commafeed-sut.jar /dist/ + cp cs/rest/jasper/target/jasper-sut.jar /dist/ if [ "$${BUILD_EVOMASTER:-false}" = "true" ]; then cp em/external/rest/commafeed/target/commafeed-evomaster-runner.jar /dist/ + cp em/external/rest/jasper/target/jasper-evomaster-runner.jar /dist/ fi echo 'JDK 25 Maven build completed' diff --git a/scripts/dist.py b/scripts/dist.py index 3eb6d54ba..f28332b73 100755 --- a/scripts/dist.py +++ b/scripts/dist.py @@ -308,6 +308,9 @@ def build_jdk_25_maven(): copy(folder + "/cs/rest/commafeed/commafeed-server/target/commafeed-sut.jar", DIST) copy(folder + "/em/external/rest/commafeed/target/commafeed-evomaster-runner.jar", DIST) + copy(folder + "/cs/rest/jasper/target/jasper-sut.jar", DIST) + copy(folder + "/em/external/rest/jasper/target/jasper-evomaster-runner.jar", DIST) + #################### def call_gradle(java_home, folder): env_vars = os.environ.copy() diff --git a/scripts/dockerize/data/sut.csv b/scripts/dockerize/data/sut.csv index f80b3c8fb..7ae52af33 100644 --- a/scripts/dockerize/data/sut.csv +++ b/scripts/dockerize/data/sut.csv @@ -36,3 +36,4 @@ spring-ecommerce,TRUE,-Dfile.encoding=ISO-8859-1,--server.port=8080 --spring.dat quartz-manager,TRUE,,--server.port=8080 --quartz-manager.security.accounts.in-memory.users[0].username=foo --quartz-manager.security.accounts.in-memory.users[0].password=bar --quartz-manager.security.accounts.in-memory.users[0].roles[0]=admin --quartz-manager.security.accounts.in-memory.users[1].username=foo2 --quartz-manager.security.accounts.in-memory.users[1].password=bar --quartz-manager.security.accounts.in-memory.users[1].roles[0]=admin,http://localhost:8080/v3/api-docs,http://localhost:8080,FALSE,"", microcks,TRUE,-DPOSTMAN_RUNNER_URL=http://postman:3000 -DSERVICES_UPDATE_INTERVAL='0 0 0/2 * * *' -DKEYCLOAK_URL=http://keycloak:8080 -DKEYCLOAK_PUBLIC_URL=http://localhost:${AUTH_PORT:-8081} -DENABLE_CORS_POLICY=false -DCORS_REST_ALLOW_CREDENTIALS=true -DTEST_CALLBACK_URL=http://localhost:${HOST_PORT:-8080},--server.port=8080 --spring.profiles.active=prod --grpc.server.port=0 --spring.data.mongodb.uri=mongodb://mongodb:27017/test --spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:${AUTH_PORT:-8081}/realms/microcks --spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://keycloak:8080/realms/microcks/protocol/openid-connect/certs,http://localhost:8080/v3/api-docs,http://localhost:8080,FALSE,"","[{""name"": ""mongodb"", ""image_name"": ""mongo:7.0"", ""tmp_fs"": ""/data/db"", ""environment"": ""MONGODB_REPLICA_SET_MODE: primary;ALLOW_EMPTY_PASSWORD: yes"", ""volume"": """", ""health_check_command"": """"},{""name"": ""keycloak"",""image_name"": ""quay.io/keycloak/keycloak:26.0.0"", ""command"":[""start-dev"", ""--hostname=http://localhost:${AUTH_PORT:-8081}"", ""--import-realm"", ""--health-enabled=true""], ""port"": ""${AUTH_PORT:-8081}:8080"", ""tmp_fs"": """", ""environment"": ""KEYCLOAK_ADMIN: 'admin';KEYCLOAK_ADMIN_PASSWORD: 'admin';KC_HEALTH_ENABLED: 'true';KC_METRICS_ENABLED: 'true'"", ""volume"": ""../scripts/dockerize/data/additional_files/microcks/microcks-realm-sample.json:/opt/keycloak/data/import/microcks-realm.json"", ""health_check_command"": [""CMD"", ""sh"", ""-c"", ""echo -e 'GET /health/live HTTP/1.1\r\nHost: localhost\r\n\r\n' > /dev/tcp/localhost/9000""]},{""name"": ""postman"", ""image_name"": ""quay.io/microcks/microcks-postman-runtime:0.6.0"", ""tmp_fs"": """", ""environment"": """", ""volume"": """", ""health_check_command"": [""CMD"", ""curl"", ""-f"", ""http://localhost:3000/health""]}]" commafeed,TRUE,"-Dquarkus.http.port=8080 -Dquarkus.datasource.db-kind=h2 -Dquarkus.datasource.jdbc.url=""jdbc:h2:mem:commafeed;DB_CLOSE_DELAY=-1"" -Dquarkus.datasource.username=sa -Dquarkus.datasource.password=sa","",http://localhost:8080/openapi?format=json,http://localhost:8080,FALSE,"","" +jasper,TRUE,,--server.port=8080 --spring.profiles.active=api-docs --spring.datasource.url=jdbc:postgresql://db:5432/jasper --spring.datasource.username=postgres --spring.datasource.password=password --spring.datasource.hikari.auto-commit=false --spring.jpa.database-platform=jasper.config.PostgreSQLDialect --springdoc.api-docs.path=/api/v3/api-docs --jasper.allow-user-tag-header=true --jasper.allow-user-role-header=true --jasper.allow-auth-headers=true,http://localhost:8080/api/v3/api-docs/jasper,http://localhost:8080,FALSE,,"[{""image_name"": ""postgres:18"", ""tmp_fs"": ""/var/lib/postgresql"", ""environment"": ""POSTGRES_PASSWORD: password;POSTGRES_HOST_AUTH_METHOD: trust;POSTGRES_DB: jasper"", ""volume"": """", ""health_check_command"": """"}]" diff --git a/statistics/data.csv b/statistics/data.csv index 5664a94bb..71ea4f7c2 100644 --- a/statistics/data.csv +++ b/statistics/data.csv @@ -46,6 +46,7 @@ TRUE,grpc-scs,gRPC,Java,JDK 8,Maven,14,894,,UNDEFINED,11,FALSE,UNDEFINED TRUE,signal-registration,gRPC,Java,JDK 17,Maven,177,13652,,UNDEFINED,5,FALSE,https://github.com/signalapp/registration-service TRUE,webgoat,REST,Java,JDK 21,Maven,355,27638,H2,GPL,204,TRUE,https://github.com/WebGoat/WebGoat TRUE,commafeed,REST,Java,JDK 25,Maven,201,21287,H2,Apache,44,TRUE,https://github.com/Athou/commafeed +TRUE,jasper,REST,Java,JDK 25,Maven,337,46791,PostgreSQL,MIT,61,TRUE,https://github.com/cjmalloy/jasper FALSE,ind0,REST,Java,JDK 8,Gradle,103,17039,PostgreSQL,Proprietary,20,FALSE,UNDEFINED FALSE,ind1,REST,Java;Kotlin,JDK 11,Gradle,163,15240,PostgreSQL,Proprietary,53,FALSE,UNDEFINED diff --git a/statistics/table_emb.md b/statistics/table_emb.md index 21de20361..4eb795fd6 100644 --- a/statistics/table_emb.md +++ b/statistics/table_emb.md @@ -19,6 +19,7 @@ |REST|__genome-nexus__|30004|405|23|Java|JDK 8|Maven|MongoDB|| |REST|__gestaohospital__|3506|33|20|Java|JDK 8|Maven|MongoDB|| |REST|__http-patch-spring__|1450|30|6|Java|JDK 11|Maven||| +|REST|__jasper__|46791|337|61|Java|JDK 25|Maven|PostgreSQL|✓| |REST|__languagetool__|174781|1385|2|Java|JDK 8|Maven||| |REST|__market__|9861|124|13|Java|JDK 11|Maven|H2|✓| |REST|__microcks__|66186|471|88|Java|JDK 21|Maven|MongoDB|✓|