From 0dbbde17a0fe309d10e738c81e7d8ad4849dc793 Mon Sep 17 00:00:00 2001 From: Willem Veelenturf Date: Sat, 4 Nov 2023 13:07:30 +0100 Subject: [PATCH] WIP --- examples/spring-boot-integration/.gitignore | 2 + .../.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 59925 bytes .../.mvn/wrapper/maven-wrapper.properties | 18 + examples/spring-boot-integration/mvnw | 287 +++ examples/spring-boot-integration/mvnw.cmd | 187 ++ examples/spring-boot-integration/pom.xml | 87 + .../PetstoreApplication.java | 17 + .../PetstoreController.java | 42 + .../src/main/openapi/petstore.json | 1148 ++++++++++ .../src/main/openapi/petstore.json | 1148 ++++++++++ scripts/example.sh | 13 +- .../compiler/core/emit/JavaEmitter.kt | 1 + src/integration/spring/build.gradle.kts | 2 + .../flock/wirespec/{kotlin => }/Wirespec.kt | 18 +- .../integration/spring/annotations/Util.kt | 25 + .../annotations/WirespecConfiguration.kt | 47 +- .../WirespecMethodArgumentResolver.kt | 21 +- .../annotations/WirespecResponseBodyAdvice.kt | 2 +- .../annotations/WirespecWebMvcConfigurer.kt | 4 +- .../spring/generated/Petstorev3.kt | 1945 +++++------------ .../maven/src/main/kotlin/GenerateMojo.kt | 2 +- 21 files changed, 3598 insertions(+), 1418 deletions(-) create mode 100644 examples/spring-boot-integration/.gitignore create mode 100644 examples/spring-boot-integration/.mvn/wrapper/maven-wrapper.jar create mode 100644 examples/spring-boot-integration/.mvn/wrapper/maven-wrapper.properties create mode 100755 examples/spring-boot-integration/mvnw create mode 100644 examples/spring-boot-integration/mvnw.cmd create mode 100644 examples/spring-boot-integration/pom.xml create mode 100644 examples/spring-boot-integration/src/main/java/community/flock/wirespec/examples/spring_boot_integration/PetstoreApplication.java create mode 100644 examples/spring-boot-integration/src/main/java/community/flock/wirespec/examples/spring_boot_integration/PetstoreController.java create mode 100644 examples/spring-boot-integration/src/main/openapi/petstore.json create mode 100644 examples/spring-boot-maven-plugin/src/main/openapi/petstore.json rename src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/{kotlin => }/Wirespec.kt (50%) create mode 100644 src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/Util.kt diff --git a/examples/spring-boot-integration/.gitignore b/examples/spring-boot-integration/.gitignore new file mode 100644 index 000000000..4f0777236 --- /dev/null +++ b/examples/spring-boot-integration/.gitignore @@ -0,0 +1,2 @@ +target +src/main/frontend/generated \ No newline at end of file diff --git a/examples/spring-boot-integration/.mvn/wrapper/maven-wrapper.jar b/examples/spring-boot-integration/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..bf82ff01c6cdae4a1bb754a6e062954d77ac5c11 GIT binary patch literal 59925 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

dy!05UMhSKsK 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 VCNKd3JGtOvEm(5t0lFyE{{i--k}m)N literal 0 HcmV?d00001 diff --git a/examples/spring-boot-integration/.mvn/wrapper/maven-wrapper.properties b/examples/spring-boot-integration/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 000000000..30052152b --- /dev/null +++ b/examples/spring-boot-integration/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# 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 +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar diff --git a/examples/spring-boot-integration/mvnw b/examples/spring-boot-integration/mvnw new file mode 100755 index 000000000..b7f064624 --- /dev/null +++ b/examples/spring-boot-integration/mvnw @@ -0,0 +1,287 @@ +#!/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.1.1 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="`/usr/libexec/java_home`"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + printf '%s' "$(cd "$basedir"; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname $0)") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) wrapperUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $wrapperUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + QUIET="--quiet" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + QUIET="" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" + fi + [ $? -eq 0 ] || rm -f "$wrapperJarPath" + elif command -v curl > /dev/null; then + QUIET="--silent" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + QUIET="" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L + fi + [ $? -eq 0 ] || rm -f "$wrapperJarPath" + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaSource="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=`cygpath --path --windows "$javaSource"` + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/examples/spring-boot-integration/mvnw.cmd b/examples/spring-boot-integration/mvnw.cmd new file mode 100644 index 000000000..474c9d6b7 --- /dev/null +++ b/examples/spring-boot-integration/mvnw.cmd @@ -0,0 +1,187 @@ +@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.1.1 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/examples/spring-boot-integration/pom.xml b/examples/spring-boot-integration/pom.xml new file mode 100644 index 000000000..f18805393 --- /dev/null +++ b/examples/spring-boot-integration/pom.xml @@ -0,0 +1,87 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.5 + + + community.flock.wirespec.example + spring_boot_integration + 0.0.0-SNAPSHOT + Demo project for Wirespec + + 17 + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + community.flock.wirespec.integration.spring + spring-jvm + 0.0.0-SNAPSHOT + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + community.flock.wirespec.plugin.maven + wirespec-maven-plugin + 0.0.0-SNAPSHOT + + + java + + generate + + + ${project.basedir}/src/main/openapi/petstore.json + ${project.build.directory}/generated-sources + v3 + + Java + + true + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + + + add-source + generate-sources + + add-source + + + + ${project.build.directory}/generated-sources + + + + + + + + + diff --git a/examples/spring-boot-integration/src/main/java/community/flock/wirespec/examples/spring_boot_integration/PetstoreApplication.java b/examples/spring-boot-integration/src/main/java/community/flock/wirespec/examples/spring_boot_integration/PetstoreApplication.java new file mode 100644 index 000000000..a3625f72c --- /dev/null +++ b/examples/spring-boot-integration/src/main/java/community/flock/wirespec/examples/spring_boot_integration/PetstoreApplication.java @@ -0,0 +1,17 @@ +package community.flock.wirespec.examples.spring_boot_integration; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import community.flock.wirespec.integration.spring.annotations.WirespecConfiguration; +import org.springframework.context.annotation.Import; + +@SpringBootApplication +@Import(WirespecConfiguration.class) +public class PetstoreApplication { + + public static void main(String[] args) { + SpringApplication.run(PetstoreApplication.class, args); + } + +} diff --git a/examples/spring-boot-integration/src/main/java/community/flock/wirespec/examples/spring_boot_integration/PetstoreController.java b/examples/spring-boot-integration/src/main/java/community/flock/wirespec/examples/spring_boot_integration/PetstoreController.java new file mode 100644 index 000000000..abd392821 --- /dev/null +++ b/examples/spring-boot-integration/src/main/java/community/flock/wirespec/examples/spring_boot_integration/PetstoreController.java @@ -0,0 +1,42 @@ +package community.flock.wirespec.examples.spring_boot_integration; + +import community.flock.wirespec.generated.*; +import community.flock.wirespec.integration.spring.annotations.WirespecController; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + + +@WirespecController +class PetstoreController implements AddPet, GetPetById, UpdatePet, DeletePet { + + @Override + public AddPet.Response addPet(AddPet.Request request) { + Pet pet = new Pet( + Optional.empty(), + "Willem", + Optional.empty(), + List.of(), + Optional.empty(), + Optional.empty() + ); + AddPet.Response200ApplicationJson res = new AddPet.Response200ApplicationJson(Map.of(), pet); + return res; + } + + @Override + public DeletePet.Response deletePet(DeletePet.Request request) { + return null; + } + + @Override + public GetPetById.Response getPetById(GetPetById.Request request) { + return null; + } + + @Override + public UpdatePet.Response updatePet(UpdatePet.Request request) { + return null; + } +} diff --git a/examples/spring-boot-integration/src/main/openapi/petstore.json b/examples/spring-boot-integration/src/main/openapi/petstore.json new file mode 100644 index 000000000..d9e56db2b --- /dev/null +++ b/examples/spring-boot-integration/src/main/openapi/petstore.json @@ -0,0 +1,1148 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Swagger Petstore - OpenAPI 3.0", + "description": "This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.\n\nSome useful links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "1.0.17" + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + }, + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + }, + { + "name": "user", + "description": "Operations about user" + } + ], + "paths": { + "/pet": { + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "Update an existing pet by Id", + "operationId": "updatePet", + "requestBody": { + "description": "Update an existent pet in the store", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + } + }, + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "Add a new pet to the store", + "operationId": "addPet", + "requestBody": { + "description": "Create a new pet in the store", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "405": { + "description": "Invalid input" + } + } + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": false, + "explode": true, + "schema": { + "type": "string", + "default": "available", + "enum": [ + "available", + "pending", + "sold" + ] + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + } + } + }, + "400": { + "description": "Invalid status value" + } + } + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": false, + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + } + } + }, + "400": { + "description": "Invalid tag value" + } + } + } + }, + "/pet/{petId}": { + "get": { + "tags": [ + "pet" + ], + "summary": "Find pet by ID", + "description": "Returns a single pet", + "operationId": "getPetById", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to return", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + } + }, + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "name", + "in": "query", + "description": "Name of pet that needs to be updated", + "schema": { + "type": "string" + } + }, + { + "name": "status", + "in": "query", + "description": "Status of pet that needs to be updated", + "schema": { + "type": "string" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + } + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "parameters": [ + { + "name": "api_key", + "in": "header", + "description": "", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "400": { + "description": "Invalid pet value" + } + } + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "additionalMetadata", + "in": "query", + "description": "Additional Metadata", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + } + } + } + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "Place a new order in the store", + "operationId": "placeOrder", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Order" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "405": { + "description": "Invalid input" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions.", + "operationId": "getOrderById", + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of order that needs to be fetched", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Order" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", + "operationId": "deleteOrder", + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "requestBody": { + "description": "Created user object", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "responses": { + "default": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "Creates list of users with given input array", + "operationId": "createUsersWithListInput", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + } + } + }, + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "X-Rate-Limit": { + "description": "calls per hour allowed by the user", + "schema": { + "type": "integer", + "format": "int32" + } + }, + "X-Expires-After": { + "description": "date in UTC when token expires", + "schema": { + "type": "string", + "format": "date-time" + } + } + }, + "content": { + "application/xml": { + "schema": { + "type": "string" + } + }, + "application/json": { + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Update user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be deleted", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Update an existent user in the store", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "responses": { + "default": { + "description": "successful operation" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "components": { + "schemas": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "petId": { + "type": "integer", + "format": "int64", + "example": 198772 + }, + "quantity": { + "type": "integer", + "format": "int32", + "example": 7 + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "example": "approved", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean" + } + }, + "xml": { + "name": "order" + } + }, + "Customer": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 100000 + }, + "username": { + "type": "string", + "example": "fehguy" + }, + "address": { + "type": "array", + "xml": { + "name": "addresses", + "wrapped": true + }, + "items": { + "$ref": "#/components/schemas/Address" + } + } + }, + "xml": { + "name": "customer" + } + }, + "Address": { + "type": "object", + "properties": { + "street": { + "type": "string", + "example": "437 Lytton" + }, + "city": { + "type": "string", + "example": "Palo Alto" + }, + "state": { + "type": "string", + "example": "CA" + }, + "zip": { + "type": "string", + "example": "94301" + } + }, + "xml": { + "name": "address" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "name": { + "type": "string", + "example": "Dogs" + } + }, + "xml": { + "name": "category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "username": { + "type": "string", + "example": "theUser" + }, + "firstName": { + "type": "string", + "example": "John" + }, + "lastName": { + "type": "string", + "example": "James" + }, + "email": { + "type": "string", + "example": "john@email.com" + }, + "password": { + "type": "string", + "example": "12345" + }, + "phone": { + "type": "string", + "example": "12345" + }, + "userStatus": { + "type": "integer", + "description": "User Status", + "format": "int32", + "example": 1 + } + }, + "xml": { + "name": "user" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "tag" + } + }, + "Pet": { + "required": [ + "name", + "photoUrls" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "name": { + "type": "string", + "example": "doggie" + }, + "category": { + "$ref": "#/components/schemas/Category" + }, + "photoUrls": { + "type": "array", + "xml": { + "wrapped": true + }, + "items": { + "type": "string", + "xml": { + "name": "photoUrl" + } + } + }, + "tags": { + "type": "array", + "xml": { + "wrapped": true + }, + "items": { + "$ref": "#/components/schemas/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "xml": { + "name": "##default" + } + } + }, + "requestBodies": { + "Pet": { + "description": "Pet object that needs to be added to the store", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "UserArray": { + "description": "List of user object", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + } + } + } + }, + "securitySchemes": { + "petstore_auth": { + "type": "oauth2", + "flows": { + "implicit": { + "authorizationUrl": "https://petstore3.swagger.io/oauth/authorize", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + } + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + } + } +} \ No newline at end of file diff --git a/examples/spring-boot-maven-plugin/src/main/openapi/petstore.json b/examples/spring-boot-maven-plugin/src/main/openapi/petstore.json new file mode 100644 index 000000000..d9e56db2b --- /dev/null +++ b/examples/spring-boot-maven-plugin/src/main/openapi/petstore.json @@ -0,0 +1,1148 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Swagger Petstore - OpenAPI 3.0", + "description": "This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.\n\nSome useful links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "1.0.17" + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + }, + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + }, + { + "name": "user", + "description": "Operations about user" + } + ], + "paths": { + "/pet": { + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "Update an existing pet by Id", + "operationId": "updatePet", + "requestBody": { + "description": "Update an existent pet in the store", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + } + }, + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "Add a new pet to the store", + "operationId": "addPet", + "requestBody": { + "description": "Create a new pet in the store", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "405": { + "description": "Invalid input" + } + } + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": false, + "explode": true, + "schema": { + "type": "string", + "default": "available", + "enum": [ + "available", + "pending", + "sold" + ] + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + } + } + }, + "400": { + "description": "Invalid status value" + } + } + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": false, + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + } + } + }, + "400": { + "description": "Invalid tag value" + } + } + } + }, + "/pet/{petId}": { + "get": { + "tags": [ + "pet" + ], + "summary": "Find pet by ID", + "description": "Returns a single pet", + "operationId": "getPetById", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to return", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + } + }, + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "name", + "in": "query", + "description": "Name of pet that needs to be updated", + "schema": { + "type": "string" + } + }, + { + "name": "status", + "in": "query", + "description": "Status of pet that needs to be updated", + "schema": { + "type": "string" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + } + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "parameters": [ + { + "name": "api_key", + "in": "header", + "description": "", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "400": { + "description": "Invalid pet value" + } + } + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "additionalMetadata", + "in": "query", + "description": "Additional Metadata", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + } + } + } + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "Place a new order in the store", + "operationId": "placeOrder", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Order" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "405": { + "description": "Invalid input" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions.", + "operationId": "getOrderById", + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of order that needs to be fetched", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Order" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", + "operationId": "deleteOrder", + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "requestBody": { + "description": "Created user object", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "responses": { + "default": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "Creates list of users with given input array", + "operationId": "createUsersWithListInput", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + } + } + }, + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "X-Rate-Limit": { + "description": "calls per hour allowed by the user", + "schema": { + "type": "integer", + "format": "int32" + } + }, + "X-Expires-After": { + "description": "date in UTC when token expires", + "schema": { + "type": "string", + "format": "date-time" + } + } + }, + "content": { + "application/xml": { + "schema": { + "type": "string" + } + }, + "application/json": { + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Update user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be deleted", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Update an existent user in the store", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "responses": { + "default": { + "description": "successful operation" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "components": { + "schemas": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "petId": { + "type": "integer", + "format": "int64", + "example": 198772 + }, + "quantity": { + "type": "integer", + "format": "int32", + "example": 7 + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "example": "approved", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean" + } + }, + "xml": { + "name": "order" + } + }, + "Customer": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 100000 + }, + "username": { + "type": "string", + "example": "fehguy" + }, + "address": { + "type": "array", + "xml": { + "name": "addresses", + "wrapped": true + }, + "items": { + "$ref": "#/components/schemas/Address" + } + } + }, + "xml": { + "name": "customer" + } + }, + "Address": { + "type": "object", + "properties": { + "street": { + "type": "string", + "example": "437 Lytton" + }, + "city": { + "type": "string", + "example": "Palo Alto" + }, + "state": { + "type": "string", + "example": "CA" + }, + "zip": { + "type": "string", + "example": "94301" + } + }, + "xml": { + "name": "address" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "name": { + "type": "string", + "example": "Dogs" + } + }, + "xml": { + "name": "category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "username": { + "type": "string", + "example": "theUser" + }, + "firstName": { + "type": "string", + "example": "John" + }, + "lastName": { + "type": "string", + "example": "James" + }, + "email": { + "type": "string", + "example": "john@email.com" + }, + "password": { + "type": "string", + "example": "12345" + }, + "phone": { + "type": "string", + "example": "12345" + }, + "userStatus": { + "type": "integer", + "description": "User Status", + "format": "int32", + "example": 1 + } + }, + "xml": { + "name": "user" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "tag" + } + }, + "Pet": { + "required": [ + "name", + "photoUrls" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "name": { + "type": "string", + "example": "doggie" + }, + "category": { + "$ref": "#/components/schemas/Category" + }, + "photoUrls": { + "type": "array", + "xml": { + "wrapped": true + }, + "items": { + "type": "string", + "xml": { + "name": "photoUrl" + } + } + }, + "tags": { + "type": "array", + "xml": { + "wrapped": true + }, + "items": { + "$ref": "#/components/schemas/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "xml": { + "name": "##default" + } + } + }, + "requestBodies": { + "Pet": { + "description": "Pet object that needs to be added to the store", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "UserArray": { + "description": "List of user object", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + } + } + } + }, + "securitySchemes": { + "petstore_auth": { + "type": "oauth2", + "flows": { + "implicit": { + "authorizationUrl": "https://petstore3.swagger.io/oauth/authorize", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + } + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + } + } +} \ No newline at end of file diff --git a/scripts/example.sh b/scripts/example.sh index f6abe1f0d..28fe01b14 100755 --- a/scripts/example.sh +++ b/scripts/example.sh @@ -4,16 +4,21 @@ dir="$(dirname -- "$0")" # publish and build gradle example ./gradlew src:plugin:gradle:publishToMavenLocal && - (cd "$dir"/../examples/spring-boot-gradle-plugin && ./gradlew wirespec build) && +(cd "$dir"/../examples/spring-boot-gradle-plugin && ./gradlew wirespec build) && # publish and build maven example ./gradlew src:plugin:maven:publishToMavenLocal && - (cd "$dir"/../examples/spring-boot-maven-plugin && ./mvnw package) && +(cd "$dir"/../examples/spring-boot-maven-plugin && ./mvnw package) && # publish and build maven example ./gradlew src:plugin:maven:publishToMavenLocal && - (cd "$dir"/../examples/spring-boot-custom-maven-plugin && ./mvnw package) && +(cd "$dir"/../examples/spring-boot-custom-maven-plugin && ./mvnw package) && # publish and build maven example ./gradlew src:plugin:maven:publishToMavenLocal && - (cd "$dir"/../examples/spring-boot-openapi-maven-plugin && ./mvnw package) + (cd "$dir"/../examples/spring-boot-openapi-maven-plugin && ./mvnw package) && + +# publish and build maven example +./gradlew src:plugin:maven:publishToMavenLocal && +./gradlew src:integration:spring:publishToMavenLocal && +(cd "$dir"/../examples/spring-boot-integration && ./mvnw package) \ No newline at end of file diff --git a/src/compiler/core/src/commonMain/kotlin/community/flock/wirespec/compiler/core/emit/JavaEmitter.kt b/src/compiler/core/src/commonMain/kotlin/community/flock/wirespec/compiler/core/emit/JavaEmitter.kt index f13f33460..77f77d447 100644 --- a/src/compiler/core/src/commonMain/kotlin/community/flock/wirespec/compiler/core/emit/JavaEmitter.kt +++ b/src/compiler/core/src/commonMain/kotlin/community/flock/wirespec/compiler/core/emit/JavaEmitter.kt @@ -116,6 +116,7 @@ class JavaEmitter( override fun Endpoint.emit() = withLogging(logger) { """public interface $name { |${SPACER}static String PATH = "${path.emitSegment()}"; + |${SPACER}static Wirespec.Method METHOD = Wirespec.Method.${method}; |${responses.emitResponseMapper()} |${SPACER}interface Request extends Wirespec.Request {} |${requests.joinToString("\n") { it.emit(this) }} diff --git a/src/integration/spring/build.gradle.kts b/src/integration/spring/build.gradle.kts index 9a04db580..ae29cf25a 100644 --- a/src/integration/spring/build.gradle.kts +++ b/src/integration/spring/build.gradle.kts @@ -36,6 +36,7 @@ kotlin { tasks.getByName("jar") { enabled = true + exclude("community/flock/wirespec/Wirespec.class") } val jvmMain by getting { @@ -52,6 +53,7 @@ kotlin { implementation("org.springframework.boot:spring-boot-starter-test") } } + } } diff --git a/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/kotlin/Wirespec.kt b/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/Wirespec.kt similarity index 50% rename from src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/kotlin/Wirespec.kt rename to src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/Wirespec.kt index cc89cd6fb..d6d9ede59 100644 --- a/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/kotlin/Wirespec.kt +++ b/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/Wirespec.kt @@ -1,11 +1,25 @@ -package community.flock.wirespec.kotlin +package community.flock.wirespec import java.lang.reflect.Type +import java.lang.reflect.ParameterizedType interface Wirespec { enum class Method { GET, PUT, POST, DELETE, OPTIONS, HEAD, PATCH, TRACE } - data class Content (val type:String, val body:T ) + @JvmRecord data class Content (val type:String, val body:T ) interface Request { val path:String; val method: Method; val query: Map>; val headers: Map>; val content:Content? } interface Response { val status:Int; val headers: Map>; val content:Content? } interface ContentMapper { fun read(content: Content, valueType: Type): Content fun write(content: Content): Content } + companion object { + @JvmStatic fun getType(type: Class<*>, isIterable: Boolean): Type { + return if (isIterable) { + object : ParameterizedType { + override fun getRawType() = MutableList::class.java + override fun getActualTypeArguments() = arrayOf(type) + override fun getOwnerType() = null + } + } else { + type + } + } + } } \ No newline at end of file diff --git a/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/Util.kt b/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/Util.kt new file mode 100644 index 000000000..90b597e50 --- /dev/null +++ b/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/Util.kt @@ -0,0 +1,25 @@ +package community.flock.wirespec.integration.spring.annotations + +import java.lang.reflect.Field +import java.lang.reflect.Method +import kotlin.reflect.full.companionObjectInstance + +object Util { + fun Class<*>.isKotlinClass(): Boolean = declaredAnnotations.any { + it.annotationClass.qualifiedName == "kotlin.Metadata" + } + + fun Class<*>.getStaticClass() = + if (isKotlinClass()) { + kotlin.companionObjectInstance?.javaClass ?: error("not found") + } else { + this + } + + fun Class<*>.getStaticField(name: String) = this.getStaticClass().getDeclaredField(name).apply { setAccessible(true) } + + fun Method.invokeStatic(obj:Any, vararg args:Any): T { + return this.invoke(obj, args) as T + } + +} \ No newline at end of file diff --git a/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/WirespecConfiguration.kt b/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/WirespecConfiguration.kt index 5cd8d8f86..ac003e911 100644 --- a/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/WirespecConfiguration.kt +++ b/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/WirespecConfiguration.kt @@ -1,7 +1,8 @@ package community.flock.wirespec.integration.spring.annotations import com.fasterxml.jackson.databind.ObjectMapper -import community.flock.wirespec.kotlin.Wirespec +import community.flock.wirespec.Wirespec +import community.flock.wirespec.integration.spring.annotations.Util.getStaticField import org.springframework.context.ApplicationContext import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @@ -14,30 +15,32 @@ import java.io.BufferedReader import java.lang.reflect.Type import kotlin.reflect.full.companionObjectInstance +class JacksonContentMapper(val objectMapper: ObjectMapper) : Wirespec.ContentMapper { + override fun read( + content: Wirespec.Content, + valueType: Type, + ) = content.let { + val type = objectMapper.constructType(valueType) + val obj: T = objectMapper.readValue(it.body, type) + Wirespec.Content(it.type, obj) + } + + override fun write( + content: Wirespec.Content, + ) = content.let { + val bytes: ByteArray = objectMapper.writeValueAsBytes(content.body) + Wirespec.Content(it.type, bytes.inputStream().bufferedReader()) + } +} + @Configuration @Import(WirespecResponseBodyAdvice::class, WirespecWebMvcConfigurer::class) open class WirespecConfiguration { - @Bean - open fun contentMapper(objectMapper: ObjectMapper) = - object : Wirespec.ContentMapper { - override fun read( - content: Wirespec.Content, - valueType: Type, - ) = content.let { - val type = objectMapper.constructType(valueType) - val obj: T = objectMapper.readValue(it.body, type) - Wirespec.Content(it.type, obj) - } - override fun write( - content: Wirespec.Content, - ) = content.let { - val bytes: ByteArray = objectMapper.writeValueAsBytes(content.body) - Wirespec.Content(it.type, bytes.inputStream().bufferedReader()) - } - } + @Bean + open fun contentMapper(objectMapper: ObjectMapper) = JacksonContentMapper(objectMapper) @Bean open fun registerWirespecController( @@ -52,9 +55,8 @@ open class WirespecConfiguration { applicationContext.getBeansWithAnnotation(WirespecController::class.java) .forEach { controller -> controller.value.javaClass.interfaces.toList().forEach { endpoint -> - val path = endpoint.getDeclaredField("PATH").get(endpoint) as String - val method = endpoint.kotlin.companionObjectInstance?.javaClass?.getDeclaredField("METHOD") - ?.apply { setAccessible(true) }?.get(endpoint) as Wirespec.Method + val path = endpoint.getStaticField("PATH").get(endpoint) as String + val method = endpoint.getStaticField("METHOD").get(endpoint) as Wirespec.Method val requestMappingWirespec = RequestMappingInfo .paths(path) .methods(RequestMethod.valueOf(method.name)) @@ -72,4 +74,5 @@ open class WirespecConfiguration { return "Hello" } + } diff --git a/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/WirespecMethodArgumentResolver.kt b/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/WirespecMethodArgumentResolver.kt index 4d0598473..0d872a50e 100644 --- a/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/WirespecMethodArgumentResolver.kt +++ b/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/WirespecMethodArgumentResolver.kt @@ -1,7 +1,8 @@ package community.flock.wirespec.integration.spring.annotations -import community.flock.wirespec.kotlin.Wirespec +import community.flock.wirespec.Wirespec +import community.flock.wirespec.integration.spring.annotations.Util.invokeStatic import jakarta.servlet.http.HttpServletRequest import org.springframework.core.MethodParameter import org.springframework.http.server.PathContainer @@ -11,11 +12,10 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver import org.springframework.web.method.support.ModelAndViewContainer import org.springframework.web.util.pattern.PathPatternParser import java.io.BufferedReader -import java.io.Reader import kotlin.reflect.full.companionObjectInstance typealias RequestMapper = (path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) -> Wirespec.Request<*> -class WirespecMethodArgumentResolver(private val contentMapper: Wirespec.ContentMapper): HandlerMethodArgumentResolver { +class WirespecMethodArgumentResolver(private val contentMapper: JacksonContentMapper): HandlerMethodArgumentResolver { override fun supportsParameter(parameter: MethodParameter): Boolean { return Wirespec.Request::class.java.isAssignableFrom(parameter.parameterType) } @@ -27,17 +27,20 @@ class WirespecMethodArgumentResolver(private val contentMapper: Wirespec.Content binderFactory: WebDataBinderFactory? ): Wirespec.Request<*> { val request = webRequest.nativeRequest as HttpServletRequest - val companionObjectInstance = parameter.parameterType.declaringClass.kotlin.companionObjectInstance - val requestMapper = companionObjectInstance?.javaClass?.declaredMethods?.get(0) - val requestMapperFunc = requestMapper?.invoke(companionObjectInstance, contentMapper) as RequestMapper + + val static = parameter.parameterType.declaringClass + val requestMapper = static.javaClass.methods.find { it.name == "REQUEST_MAPPER" } val content = request.contentType?.let { Wirespec.Content(it, request.reader) } - return requestMapperFunc( + + return requestMapper?.invokeStatic( + static, + contentMapper, request.requestURI, Wirespec.Method.valueOf(request.method), request.parameterMap.mapValues { it.value.toList() }, request.headerNames.toList().associateWith { request.getHeaders(it).toList() }, - content - ) + content as Any + )?: error("") } } diff --git a/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/WirespecResponseBodyAdvice.kt b/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/WirespecResponseBodyAdvice.kt index 5b0a6317d..cb0171f78 100644 --- a/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/WirespecResponseBodyAdvice.kt +++ b/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/WirespecResponseBodyAdvice.kt @@ -1,6 +1,6 @@ package community.flock.wirespec.integration.spring.annotations -import community.flock.wirespec.kotlin.Wirespec +import community.flock.wirespec.Wirespec import org.springframework.core.MethodParameter import org.springframework.http.HttpStatusCode import org.springframework.http.MediaType diff --git a/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/WirespecWebMvcConfigurer.kt b/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/WirespecWebMvcConfigurer.kt index 20434362e..08f749422 100644 --- a/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/WirespecWebMvcConfigurer.kt +++ b/src/integration/spring/src/jvmMain/kotlin/community/flock/wirespec/integration/spring/annotations/WirespecWebMvcConfigurer.kt @@ -1,6 +1,6 @@ package community.flock.wirespec.integration.spring.annotations -import community.flock.wirespec.kotlin.Wirespec +import community.flock.wirespec.Wirespec import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Configuration import org.springframework.web.method.support.HandlerMethodArgumentResolver @@ -12,7 +12,7 @@ import java.io.BufferedReader open class WirespecWebMvcConfigurer : WebMvcConfigurer { @Autowired - lateinit var contentMapper: Wirespec.ContentMapper + lateinit var contentMapper: JacksonContentMapper override fun addArgumentResolvers(argumentResolvers: MutableList) { argumentResolvers.add(WirespecMethodArgumentResolver(contentMapper)) diff --git a/src/integration/spring/src/jvmTest/kotlin/community/flock/wirespec/integration/spring/generated/Petstorev3.kt b/src/integration/spring/src/jvmTest/kotlin/community/flock/wirespec/integration/spring/generated/Petstorev3.kt index 2c91c314a..f68e39c22 100644 --- a/src/integration/spring/src/jvmTest/kotlin/community/flock/wirespec/integration/spring/generated/Petstorev3.kt +++ b/src/integration/spring/src/jvmTest/kotlin/community/flock/wirespec/integration/spring/generated/Petstorev3.kt @@ -1,1531 +1,722 @@ package community.flock.wirespec.integration.spring.generated -import community.flock.wirespec.kotlin.Wirespec -import kotlin.reflect.jvm.javaType -import kotlin.reflect.typeOf +import community.flock.wirespec.Wirespec interface AddPet { - sealed interface Request : Wirespec.Request - class RequestApplicationJson( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(body: Pet) : this( - path = "/pet", - method = Wirespec.Method.POST, - query = mapOf>(), - headers = mapOf>(), - content = Wirespec.Content("application/json", body) - ) - } - - class RequestApplicationXml( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(body: Pet) : this( - path = "/pet", - method = Wirespec.Method.POST, - query = mapOf>(), - headers = mapOf>(), - content = Wirespec.Content("application/xml", body) - ) - } - - class RequestApplicationXWwwFormUrlencoded( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(body: Pet) : this( - path = "/pet", - method = Wirespec.Method.POST, - query = mapOf>(), - headers = mapOf>(), - content = Wirespec.Content("application/x-www-form-urlencoded", body) - ) - } - - sealed interface Response : Wirespec.Response - sealed interface Response2XX : Response - sealed interface Response4XX : Response - sealed interface Response200 : Response2XX - sealed interface Response405 : Response4XX - class Response200ApplicationXml(override val headers: Map>, body: Pet) : Response200 { - override val status = 200; - override val content = Wirespec.Content("application/xml", body) - } - - class Response200ApplicationJson(override val headers: Map>, body: Pet) : Response200 { - override val status = 200; - override val content = Wirespec.Content("application/json", body) - } - - class Response405Unit(override val headers: Map>) : Response405 { - override val status = 405; - override val content = null - } + sealed interface Request: Wirespec.Request + class RequestApplicationJson(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(body: Pet): this(path = "/pet", method = Wirespec.Method.POST, query = mapOf>(), headers = mapOf>(), content = Wirespec.Content("application/json", body))} + class RequestApplicationXml(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(body: Pet): this(path = "/pet", method = Wirespec.Method.POST, query = mapOf>(), headers = mapOf>(), content = Wirespec.Content("application/xml", body))} + class RequestApplicationXWwwFormUrlencoded(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(body: Pet): this(path = "/pet", method = Wirespec.Method.POST, query = mapOf>(), headers = mapOf>(), content = Wirespec.Content("application/x-www-form-urlencoded", body))} + sealed interface Response: Wirespec.Response + sealed interface Response2XX: Response + sealed interface Response4XX: Response + sealed interface Response200: Response2XX + sealed interface Response405: Response4XX + class Response200ApplicationXml (override val headers: Map>, body: Pet): Response200 { override val status = 200; override val content = Wirespec.Content("application/xml", body)} + class Response200ApplicationJson (override val headers: Map>, body: Pet): Response200 { override val status = 200; override val content = Wirespec.Content("application/json", body)} + class Response405Unit (override val headers: Map> ): Response405 { override val status = 405; override val content = null} suspend fun addPet(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.POST const val PATH = "/pet" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content?.type == "application/json" -> contentMapper - .read(content, typeOf().javaType) - .let { RequestApplicationJson(path, method, query, headers, it) } - - content?.type == "application/xml" -> contentMapper - .read(content, typeOf().javaType) - .let { RequestApplicationXml(path, method, query, headers, it) } - - content?.type == "application/x-www-form-urlencoded" -> contentMapper - .read(content, typeOf().javaType) - .let { RequestApplicationXWwwFormUrlencoded(path, method, query, headers, it) } - - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - status == 200 && content?.type == "application/xml" -> contentMapper - .read(content, typeOf().javaType) - .let { Response200ApplicationXml(headers, it.body) } - - status == 200 && content?.type == "application/json" -> contentMapper - .read(content, typeOf().javaType) - .let { Response200ApplicationJson(headers, it.body) } - - status == 405 && content == null -> Response405Unit(headers) - - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content?.type == "application/json" -> contentMapper + .read(content, Wirespec.getType(Pet::class.java, false)) + .let{ RequestApplicationJson(path, method, query, headers, it) } + content?.type == "application/xml" -> contentMapper + .read(content, Wirespec.getType(Pet::class.java, false)) + .let{ RequestApplicationXml(path, method, query, headers, it) } + content?.type == "application/x-www-form-urlencoded" -> contentMapper + .read(content, Wirespec.getType(Pet::class.java, false)) + .let{ RequestApplicationXWwwFormUrlencoded(path, method, query, headers, it) } + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + status == 200 && content?.type == "application/xml" -> contentMapper + .read(content, Wirespec.getType(Pet::class.java, false)) + .let{ Response200ApplicationXml(headers, it.body) } + status == 200 && content?.type == "application/json" -> contentMapper + .read(content, Wirespec.getType(Pet::class.java, false)) + .let{ Response200ApplicationJson(headers, it.body) } + status == 405 && content == null -> Response405Unit(headers) + + else -> error("Cannot map response with status $status") + } } } interface UpdatePet { - sealed interface Request : Wirespec.Request - class RequestApplicationJson( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(body: Pet) : this( - path = "/pet", - method = Wirespec.Method.PUT, - query = mapOf>(), - headers = mapOf>(), - content = Wirespec.Content("application/json", body) - ) - } - - class RequestApplicationXml( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(body: Pet) : this( - path = "/pet", - method = Wirespec.Method.PUT, - query = mapOf>(), - headers = mapOf>(), - content = Wirespec.Content("application/xml", body) - ) - } - - class RequestApplicationXWwwFormUrlencoded( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(body: Pet) : this( - path = "/pet", - method = Wirespec.Method.PUT, - query = mapOf>(), - headers = mapOf>(), - content = Wirespec.Content("application/x-www-form-urlencoded", body) - ) - } - - sealed interface Response : Wirespec.Response - sealed interface Response2XX : Response - sealed interface Response4XX : Response - sealed interface Response200 : Response2XX - sealed interface Response400 : Response4XX - sealed interface Response404 : Response4XX - sealed interface Response405 : Response4XX - class Response200ApplicationXml(override val headers: Map>, body: Pet) : Response200 { - override val status = 200; - override val content = Wirespec.Content("application/xml", body) - } - - class Response200ApplicationJson(override val headers: Map>, body: Pet) : Response200 { - override val status = 200; - override val content = Wirespec.Content("application/json", body) - } - - class Response400Unit(override val headers: Map>) : Response400 { - override val status = 400; - override val content = null - } - - class Response404Unit(override val headers: Map>) : Response404 { - override val status = 404; - override val content = null - } - - class Response405Unit(override val headers: Map>) : Response405 { - override val status = 405; - override val content = null - } + sealed interface Request: Wirespec.Request + class RequestApplicationJson(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(body: Pet): this(path = "/pet", method = Wirespec.Method.PUT, query = mapOf>(), headers = mapOf>(), content = Wirespec.Content("application/json", body))} + class RequestApplicationXml(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(body: Pet): this(path = "/pet", method = Wirespec.Method.PUT, query = mapOf>(), headers = mapOf>(), content = Wirespec.Content("application/xml", body))} + class RequestApplicationXWwwFormUrlencoded(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(body: Pet): this(path = "/pet", method = Wirespec.Method.PUT, query = mapOf>(), headers = mapOf>(), content = Wirespec.Content("application/x-www-form-urlencoded", body))} + sealed interface Response: Wirespec.Response + sealed interface Response2XX: Response + sealed interface Response4XX: Response + sealed interface Response200: Response2XX + sealed interface Response400: Response4XX + sealed interface Response404: Response4XX + sealed interface Response405: Response4XX + class Response200ApplicationXml (override val headers: Map>, body: Pet): Response200 { override val status = 200; override val content = Wirespec.Content("application/xml", body)} + class Response200ApplicationJson (override val headers: Map>, body: Pet): Response200 { override val status = 200; override val content = Wirespec.Content("application/json", body)} + class Response400Unit (override val headers: Map> ): Response400 { override val status = 400; override val content = null} + class Response404Unit (override val headers: Map> ): Response404 { override val status = 404; override val content = null} + class Response405Unit (override val headers: Map> ): Response405 { override val status = 405; override val content = null} suspend fun updatePet(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.PUT const val PATH = "/pet" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content?.type == "application/json" -> contentMapper - .read(content, typeOf().javaType) - .let { RequestApplicationJson(path, method, query, headers, it) } - - content?.type == "application/xml" -> contentMapper - .read(content, typeOf().javaType) - .let { RequestApplicationXml(path, method, query, headers, it) } - - content?.type == "application/x-www-form-urlencoded" -> contentMapper - .read(content, typeOf().javaType) - .let { RequestApplicationXWwwFormUrlencoded(path, method, query, headers, it) } - - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - status == 200 && content?.type == "application/xml" -> contentMapper - .read(content, typeOf().javaType) - .let { Response200ApplicationXml(headers, it.body) } - - status == 200 && content?.type == "application/json" -> contentMapper - .read(content, typeOf().javaType) - .let { Response200ApplicationJson(headers, it.body) } - - status == 400 && content == null -> Response400Unit(headers) - status == 404 && content == null -> Response404Unit(headers) - status == 405 && content == null -> Response405Unit(headers) - - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content?.type == "application/json" -> contentMapper + .read(content, Wirespec.getType(Pet::class.java, false)) + .let{ RequestApplicationJson(path, method, query, headers, it) } + content?.type == "application/xml" -> contentMapper + .read(content, Wirespec.getType(Pet::class.java, false)) + .let{ RequestApplicationXml(path, method, query, headers, it) } + content?.type == "application/x-www-form-urlencoded" -> contentMapper + .read(content, Wirespec.getType(Pet::class.java, false)) + .let{ RequestApplicationXWwwFormUrlencoded(path, method, query, headers, it) } + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + status == 200 && content?.type == "application/xml" -> contentMapper + .read(content, Wirespec.getType(Pet::class.java, false)) + .let{ Response200ApplicationXml(headers, it.body) } + status == 200 && content?.type == "application/json" -> contentMapper + .read(content, Wirespec.getType(Pet::class.java, false)) + .let{ Response200ApplicationJson(headers, it.body) } + status == 400 && content == null -> Response400Unit(headers) + status == 404 && content == null -> Response404Unit(headers) + status == 405 && content == null -> Response405Unit(headers) + + else -> error("Cannot map response with status $status") + } } } interface FindPetsByStatus { - sealed interface Request : Wirespec.Request - class RequestUnit( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(status: FindPetsByStatusParameterStatus? = null) : this( - path = "/pet/findByStatus", - method = Wirespec.Method.GET, - query = mapOf>("status" to listOf(status)), - headers = mapOf>(), - content = null - ) - } - - sealed interface Response : Wirespec.Response - sealed interface Response2XX : Response - sealed interface Response4XX : Response - sealed interface Response200 : Response2XX - sealed interface Response400 : Response4XX - class Response200ApplicationXml(override val headers: Map>, body: List) : - Response200> { - override val status = 200; - override val content = Wirespec.Content("application/xml", body) - } - - class Response200ApplicationJson(override val headers: Map>, body: List) : - Response200> { - override val status = 200; - override val content = Wirespec.Content("application/json", body) - } - - class Response400Unit(override val headers: Map>) : Response400 { - override val status = 400; - override val content = null - } + sealed interface Request: Wirespec.Request + class RequestUnit(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(status: FindPetsByStatusParameterStatus? = null): this(path = "/pet/findByStatus", method = Wirespec.Method.GET, query = mapOf>("status" to listOf(status)), headers = mapOf>(), content = null)} + sealed interface Response: Wirespec.Response + sealed interface Response2XX: Response + sealed interface Response4XX: Response + sealed interface Response200: Response2XX + sealed interface Response400: Response4XX + class Response200ApplicationXml (override val headers: Map>, body: List ): + Response200> { override val status = 200; override val content = Wirespec.Content("application/xml", body)} + class Response200ApplicationJson (override val headers: Map>, body: List ): + Response200> { override val status = 200; override val content = Wirespec.Content("application/json", body)} + class Response400Unit (override val headers: Map> ): Response400 { override val status = 400; override val content = null} suspend fun findPetsByStatus(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.GET const val PATH = "/pet/findByStatus" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content == null -> RequestUnit(path, method, query, headers, null) - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - status == 200 && content?.type == "application/xml" -> contentMapper - .read>(content, typeOf>().javaType) - .let { Response200ApplicationXml(headers, it.body) } - - status == 200 && content?.type == "application/json" -> contentMapper - .read>(content, typeOf>().javaType) - .let { Response200ApplicationJson(headers, it.body) } - - status == 400 && content == null -> Response400Unit(headers) - - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content == null -> RequestUnit(path, method, query, headers, null) + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + status == 200 && content?.type == "application/xml" -> contentMapper + .read>(content, Wirespec.getType(Pet::class.java, true)) + .let{ Response200ApplicationXml(headers, it.body) } + status == 200 && content?.type == "application/json" -> contentMapper + .read>(content, Wirespec.getType(Pet::class.java, true)) + .let{ Response200ApplicationJson(headers, it.body) } + status == 400 && content == null -> Response400Unit(headers) + + else -> error("Cannot map response with status $status") + } } } interface FindPetsByTags { - sealed interface Request : Wirespec.Request - class RequestUnit( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(tags: List? = null) : this( - path = "/pet/findByTags", - method = Wirespec.Method.GET, - query = mapOf>("tags" to listOf(tags)), - headers = mapOf>(), - content = null - ) - } - - sealed interface Response : Wirespec.Response - sealed interface Response2XX : Response - sealed interface Response4XX : Response - sealed interface Response200 : Response2XX - sealed interface Response400 : Response4XX - class Response200ApplicationXml(override val headers: Map>, body: List) : - Response200> { - override val status = 200; - override val content = Wirespec.Content("application/xml", body) - } - - class Response200ApplicationJson(override val headers: Map>, body: List) : - Response200> { - override val status = 200; - override val content = Wirespec.Content("application/json", body) - } - - class Response400Unit(override val headers: Map>) : Response400 { - override val status = 400; - override val content = null - } + sealed interface Request: Wirespec.Request + class RequestUnit(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(tags: List? = null): this(path = "/pet/findByTags", method = Wirespec.Method.GET, query = mapOf>("tags" to listOf(tags)), headers = mapOf>(), content = null)} + sealed interface Response: Wirespec.Response + sealed interface Response2XX: Response + sealed interface Response4XX: Response + sealed interface Response200: Response2XX + sealed interface Response400: Response4XX + class Response200ApplicationXml (override val headers: Map>, body: List ): + Response200> { override val status = 200; override val content = Wirespec.Content("application/xml", body)} + class Response200ApplicationJson (override val headers: Map>, body: List ): + Response200> { override val status = 200; override val content = Wirespec.Content("application/json", body)} + class Response400Unit (override val headers: Map> ): Response400 { override val status = 400; override val content = null} suspend fun findPetsByTags(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.GET const val PATH = "/pet/findByTags" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content == null -> RequestUnit(path, method, query, headers, null) - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - status == 200 && content?.type == "application/xml" -> contentMapper - .read>(content, typeOf>().javaType) - .let { Response200ApplicationXml(headers, it.body) } - - status == 200 && content?.type == "application/json" -> contentMapper - .read>(content, typeOf>().javaType) - .let { Response200ApplicationJson(headers, it.body) } - - status == 400 && content == null -> Response400Unit(headers) - - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content == null -> RequestUnit(path, method, query, headers, null) + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + status == 200 && content?.type == "application/xml" -> contentMapper + .read>(content, Wirespec.getType(Pet::class.java, true)) + .let{ Response200ApplicationXml(headers, it.body) } + status == 200 && content?.type == "application/json" -> contentMapper + .read>(content, Wirespec.getType(Pet::class.java, true)) + .let{ Response200ApplicationJson(headers, it.body) } + status == 400 && content == null -> Response400Unit(headers) + + else -> error("Cannot map response with status $status") + } } } interface GetPetById { - sealed interface Request : Wirespec.Request - class RequestUnit( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(petId: Int) : this( - path = "/pet/${petId}", - method = Wirespec.Method.GET, - query = mapOf>(), - headers = mapOf>(), - content = null - ) - } - - sealed interface Response : Wirespec.Response - sealed interface Response2XX : Response - sealed interface Response4XX : Response - sealed interface Response200 : Response2XX - sealed interface Response400 : Response4XX - sealed interface Response404 : Response4XX - class Response200ApplicationXml(override val headers: Map>, body: Pet) : Response200 { - override val status = 200; - override val content = Wirespec.Content("application/xml", body) - } - - class Response200ApplicationJson(override val headers: Map>, body: Pet) : Response200 { - override val status = 200; - override val content = Wirespec.Content("application/json", body) - } - - class Response400Unit(override val headers: Map>) : Response400 { - override val status = 400; - override val content = null - } - - class Response404Unit(override val headers: Map>) : Response404 { - override val status = 404; - override val content = null - } + sealed interface Request: Wirespec.Request + class RequestUnit(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(petId: Int): this(path = "/pet/${petId}", method = Wirespec.Method.GET, query = mapOf>(), headers = mapOf>(), content = null)} + sealed interface Response: Wirespec.Response + sealed interface Response2XX: Response + sealed interface Response4XX: Response + sealed interface Response200: Response2XX + sealed interface Response400: Response4XX + sealed interface Response404: Response4XX + class Response200ApplicationXml (override val headers: Map>, body: Pet): Response200 { override val status = 200; override val content = Wirespec.Content("application/xml", body)} + class Response200ApplicationJson (override val headers: Map>, body: Pet): Response200 { override val status = 200; override val content = Wirespec.Content("application/json", body)} + class Response400Unit (override val headers: Map> ): Response400 { override val status = 400; override val content = null} + class Response404Unit (override val headers: Map> ): Response404 { override val status = 404; override val content = null} suspend fun getPetById(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.GET const val PATH = "/pet/{petId}" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content == null -> RequestUnit(path, method, query, headers, null) - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - status == 200 && content?.type == "application/xml" -> contentMapper - .read(content, typeOf().javaType) - .let { Response200ApplicationXml(headers, it.body) } - - status == 200 && content?.type == "application/json" -> contentMapper - .read(content, typeOf().javaType) - .let { Response200ApplicationJson(headers, it.body) } - - status == 400 && content == null -> Response400Unit(headers) - status == 404 && content == null -> Response404Unit(headers) - - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content == null -> RequestUnit(path, method, query, headers, null) + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + status == 200 && content?.type == "application/xml" -> contentMapper + .read(content, Wirespec.getType(Pet::class.java, false)) + .let{ Response200ApplicationXml(headers, it.body) } + status == 200 && content?.type == "application/json" -> contentMapper + .read(content, Wirespec.getType(Pet::class.java, false)) + .let{ Response200ApplicationJson(headers, it.body) } + status == 400 && content == null -> Response400Unit(headers) + status == 404 && content == null -> Response404Unit(headers) + + else -> error("Cannot map response with status $status") + } } } interface UpdatePetWithForm { - sealed interface Request : Wirespec.Request - class RequestUnit( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(petId: Int, name: String? = null, status: String? = null) : this( - path = "/pet/${petId}", - method = Wirespec.Method.POST, - query = mapOf>("name" to listOf(name), "status" to listOf(status)), - headers = mapOf>(), - content = null - ) - } - - sealed interface Response : Wirespec.Response - sealed interface Response4XX : Response - sealed interface Response405 : Response4XX - class Response405Unit(override val headers: Map>) : Response405 { - override val status = 405; - override val content = null - } + sealed interface Request: Wirespec.Request + class RequestUnit(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(petId: Int, name: String? = null, status: String? = null): this(path = "/pet/${petId}", method = Wirespec.Method.POST, query = mapOf>("name" to listOf(name), "status" to listOf(status)), headers = mapOf>(), content = null)} + sealed interface Response: Wirespec.Response + sealed interface Response4XX: Response + sealed interface Response405: Response4XX + class Response405Unit (override val headers: Map> ): Response405 { override val status = 405; override val content = null} suspend fun updatePetWithForm(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.POST const val PATH = "/pet/{petId}" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content == null -> RequestUnit(path, method, query, headers, null) - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - status == 405 && content == null -> Response405Unit(headers) - - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content == null -> RequestUnit(path, method, query, headers, null) + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + status == 405 && content == null -> Response405Unit(headers) + + else -> error("Cannot map response with status $status") + } } } interface DeletePet { - sealed interface Request : Wirespec.Request - class RequestUnit( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(petId: Int, api_key: String? = null) : this( - path = "/pet/${petId}", - method = Wirespec.Method.DELETE, - query = mapOf>(), - headers = mapOf>("api_key" to listOf(api_key)), - content = null - ) - } - - sealed interface Response : Wirespec.Response - sealed interface Response4XX : Response - sealed interface Response400 : Response4XX - class Response400Unit(override val headers: Map>) : Response400 { - override val status = 400; - override val content = null - } + sealed interface Request: Wirespec.Request + class RequestUnit(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(petId: Int, api_key: String? = null): this(path = "/pet/${petId}", method = Wirespec.Method.DELETE, query = mapOf>(), headers = mapOf>("api_key" to listOf(api_key)), content = null)} + sealed interface Response: Wirespec.Response + sealed interface Response4XX: Response + sealed interface Response400: Response4XX + class Response400Unit (override val headers: Map> ): Response400 { override val status = 400; override val content = null} suspend fun deletePet(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.DELETE const val PATH = "/pet/{petId}" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content == null -> RequestUnit(path, method, query, headers, null) - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - status == 400 && content == null -> Response400Unit(headers) - - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content == null -> RequestUnit(path, method, query, headers, null) + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + status == 400 && content == null -> Response400Unit(headers) + + else -> error("Cannot map response with status $status") + } } } interface UploadFile { - sealed interface Request : Wirespec.Request - class RequestApplicationOctetStream( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor( - petId: Int, - additionalMetadata: String? = null, - body: String - ) : this( - path = "/pet/${petId}/uploadImage", - method = Wirespec.Method.POST, - query = mapOf>("additionalMetadata" to listOf(additionalMetadata)), - headers = mapOf>(), - content = Wirespec.Content("application/octet-stream", body) - ) - } - - sealed interface Response : Wirespec.Response - sealed interface Response2XX : Response - sealed interface Response200 : Response2XX - class Response200ApplicationJson(override val headers: Map>, body: ApiResponse) : - Response200 { - override val status = 200; - override val content = Wirespec.Content("application/json", body) - } + sealed interface Request: Wirespec.Request + class RequestApplicationOctetStream(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(petId: Int, additionalMetadata: String? = null, body: String): this(path = "/pet/${petId}/uploadImage", method = Wirespec.Method.POST, query = mapOf>("additionalMetadata" to listOf(additionalMetadata)), headers = mapOf>(), content = Wirespec.Content("application/octet-stream", body))} + sealed interface Response: Wirespec.Response + sealed interface Response2XX: Response + sealed interface Response200: Response2XX + class Response200ApplicationJson (override val headers: Map>, body: ApiResponse): + Response200 { override val status = 200; override val content = Wirespec.Content("application/json", body)} suspend fun uploadFile(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.POST const val PATH = "/pet/{petId}/uploadImage" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content?.type == "application/octet-stream" -> contentMapper - .read(content, typeOf().javaType) - .let { RequestApplicationOctetStream(path, method, query, headers, it) } - - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - status == 200 && content?.type == "application/json" -> contentMapper - .read(content, typeOf().javaType) - .let { Response200ApplicationJson(headers, it.body) } - - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content?.type == "application/octet-stream" -> contentMapper + .read(content, Wirespec.getType(String::class.java, false)) + .let{ RequestApplicationOctetStream(path, method, query, headers, it) } + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + status == 200 && content?.type == "application/json" -> contentMapper + .read(content, Wirespec.getType(ApiResponse::class.java, false)) + .let{ Response200ApplicationJson(headers, it.body) } + + else -> error("Cannot map response with status $status") + } } } interface GetInventory { - sealed interface Request : Wirespec.Request - class RequestUnit( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor() : this( - path = "/store/inventory", - method = Wirespec.Method.GET, - query = mapOf>(), - headers = mapOf>(), - content = null - ) - } - - sealed interface Response : Wirespec.Response - sealed interface Response2XX : Response - sealed interface Response200 : Response2XX - class Response200ApplicationJson(override val headers: Map>, body: Map) : - Response200> { - override val status = 200; - override val content = Wirespec.Content("application/json", body) - } + sealed interface Request: Wirespec.Request + class RequestUnit(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(): this(path = "/store/inventory", method = Wirespec.Method.GET, query = mapOf>(), headers = mapOf>(), content = null)} + sealed interface Response: Wirespec.Response + sealed interface Response2XX: Response + sealed interface Response200: Response2XX + class Response200ApplicationJson (override val headers: Map>, body: Map ): + Response200> { override val status = 200; override val content = Wirespec.Content("application/json", body)} suspend fun getInventory(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.GET const val PATH = "/store/inventory" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content == null -> RequestUnit(path, method, query, headers, null) - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - status == 200 && content?.type == "application/json" -> contentMapper - .read>(content, typeOf>().javaType) - .let { Response200ApplicationJson(headers, it.body) } - - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content == null -> RequestUnit(path, method, query, headers, null) + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + status == 200 && content?.type == "application/json" -> contentMapper + .read>(content, Wirespec.getType(Int::class.java, false)) + .let{ Response200ApplicationJson(headers, it.body) } + + else -> error("Cannot map response with status $status") + } } } interface PlaceOrder { - sealed interface Request : Wirespec.Request - class RequestApplicationJson( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(body: Order) : this( - path = "/store/order", - method = Wirespec.Method.POST, - query = mapOf>(), - headers = mapOf>(), - content = Wirespec.Content("application/json", body) - ) - } - - class RequestApplicationXml( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(body: Order) : this( - path = "/store/order", - method = Wirespec.Method.POST, - query = mapOf>(), - headers = mapOf>(), - content = Wirespec.Content("application/xml", body) - ) - } - - class RequestApplicationXWwwFormUrlencoded( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(body: Order) : this( - path = "/store/order", - method = Wirespec.Method.POST, - query = mapOf>(), - headers = mapOf>(), - content = Wirespec.Content("application/x-www-form-urlencoded", body) - ) - } - - sealed interface Response : Wirespec.Response - sealed interface Response2XX : Response - sealed interface Response4XX : Response - sealed interface Response200 : Response2XX - sealed interface Response405 : Response4XX - class Response200ApplicationJson(override val headers: Map>, body: Order) : Response200 { - override val status = 200; - override val content = Wirespec.Content("application/json", body) - } - - class Response405Unit(override val headers: Map>) : Response405 { - override val status = 405; - override val content = null - } + sealed interface Request: Wirespec.Request + class RequestApplicationJson(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(body: Order): this(path = "/store/order", method = Wirespec.Method.POST, query = mapOf>(), headers = mapOf>(), content = Wirespec.Content("application/json", body))} + class RequestApplicationXml(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(body: Order): this(path = "/store/order", method = Wirespec.Method.POST, query = mapOf>(), headers = mapOf>(), content = Wirespec.Content("application/xml", body))} + class RequestApplicationXWwwFormUrlencoded(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(body: Order): this(path = "/store/order", method = Wirespec.Method.POST, query = mapOf>(), headers = mapOf>(), content = Wirespec.Content("application/x-www-form-urlencoded", body))} + sealed interface Response: Wirespec.Response + sealed interface Response2XX: Response + sealed interface Response4XX: Response + sealed interface Response200: Response2XX + sealed interface Response405: Response4XX + class Response200ApplicationJson (override val headers: Map>, body: Order): Response200 { override val status = 200; override val content = Wirespec.Content("application/json", body)} + class Response405Unit (override val headers: Map> ): Response405 { override val status = 405; override val content = null} suspend fun placeOrder(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.POST const val PATH = "/store/order" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content?.type == "application/json" -> contentMapper - .read(content, typeOf().javaType) - .let { RequestApplicationJson(path, method, query, headers, it) } - - content?.type == "application/xml" -> contentMapper - .read(content, typeOf().javaType) - .let { RequestApplicationXml(path, method, query, headers, it) } - - content?.type == "application/x-www-form-urlencoded" -> contentMapper - .read(content, typeOf().javaType) - .let { RequestApplicationXWwwFormUrlencoded(path, method, query, headers, it) } - - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - status == 200 && content?.type == "application/json" -> contentMapper - .read(content, typeOf().javaType) - .let { Response200ApplicationJson(headers, it.body) } - - status == 405 && content == null -> Response405Unit(headers) - - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content?.type == "application/json" -> contentMapper + .read(content, Wirespec.getType(Order::class.java, false)) + .let{ RequestApplicationJson(path, method, query, headers, it) } + content?.type == "application/xml" -> contentMapper + .read(content, Wirespec.getType(Order::class.java, false)) + .let{ RequestApplicationXml(path, method, query, headers, it) } + content?.type == "application/x-www-form-urlencoded" -> contentMapper + .read(content, Wirespec.getType(Order::class.java, false)) + .let{ RequestApplicationXWwwFormUrlencoded(path, method, query, headers, it) } + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + status == 200 && content?.type == "application/json" -> contentMapper + .read(content, Wirespec.getType(Order::class.java, false)) + .let{ Response200ApplicationJson(headers, it.body) } + status == 405 && content == null -> Response405Unit(headers) + + else -> error("Cannot map response with status $status") + } } } interface GetOrderById { - sealed interface Request : Wirespec.Request - class RequestUnit( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(orderId: Int) : this( - path = "/store/order/${orderId}", - method = Wirespec.Method.GET, - query = mapOf>(), - headers = mapOf>(), - content = null - ) - } - - sealed interface Response : Wirespec.Response - sealed interface Response2XX : Response - sealed interface Response4XX : Response - sealed interface Response200 : Response2XX - sealed interface Response400 : Response4XX - sealed interface Response404 : Response4XX - class Response200ApplicationXml(override val headers: Map>, body: Order) : Response200 { - override val status = 200; - override val content = Wirespec.Content("application/xml", body) - } - - class Response200ApplicationJson(override val headers: Map>, body: Order) : Response200 { - override val status = 200; - override val content = Wirespec.Content("application/json", body) - } - - class Response400Unit(override val headers: Map>) : Response400 { - override val status = 400; - override val content = null - } - - class Response404Unit(override val headers: Map>) : Response404 { - override val status = 404; - override val content = null - } + sealed interface Request: Wirespec.Request + class RequestUnit(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(orderId: Int): this(path = "/store/order/${orderId}", method = Wirespec.Method.GET, query = mapOf>(), headers = mapOf>(), content = null)} + sealed interface Response: Wirespec.Response + sealed interface Response2XX: Response + sealed interface Response4XX: Response + sealed interface Response200: Response2XX + sealed interface Response400: Response4XX + sealed interface Response404: Response4XX + class Response200ApplicationXml (override val headers: Map>, body: Order): Response200 { override val status = 200; override val content = Wirespec.Content("application/xml", body)} + class Response200ApplicationJson (override val headers: Map>, body: Order): Response200 { override val status = 200; override val content = Wirespec.Content("application/json", body)} + class Response400Unit (override val headers: Map> ): Response400 { override val status = 400; override val content = null} + class Response404Unit (override val headers: Map> ): Response404 { override val status = 404; override val content = null} suspend fun getOrderById(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.GET const val PATH = "/store/order/{orderId}" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content == null -> RequestUnit(path, method, query, headers, null) - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - status == 200 && content?.type == "application/xml" -> contentMapper - .read(content, typeOf().javaType) - .let { Response200ApplicationXml(headers, it.body) } - - status == 200 && content?.type == "application/json" -> contentMapper - .read(content, typeOf().javaType) - .let { Response200ApplicationJson(headers, it.body) } - - status == 400 && content == null -> Response400Unit(headers) - status == 404 && content == null -> Response404Unit(headers) - - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content == null -> RequestUnit(path, method, query, headers, null) + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + status == 200 && content?.type == "application/xml" -> contentMapper + .read(content, Wirespec.getType(Order::class.java, false)) + .let{ Response200ApplicationXml(headers, it.body) } + status == 200 && content?.type == "application/json" -> contentMapper + .read(content, Wirespec.getType(Order::class.java, false)) + .let{ Response200ApplicationJson(headers, it.body) } + status == 400 && content == null -> Response400Unit(headers) + status == 404 && content == null -> Response404Unit(headers) + + else -> error("Cannot map response with status $status") + } } } interface DeleteOrder { - sealed interface Request : Wirespec.Request - class RequestUnit( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(orderId: Int) : this( - path = "/store/order/${orderId}", - method = Wirespec.Method.DELETE, - query = mapOf>(), - headers = mapOf>(), - content = null - ) - } - - sealed interface Response : Wirespec.Response - sealed interface Response4XX : Response - sealed interface Response400 : Response4XX - sealed interface Response404 : Response4XX - class Response400Unit(override val headers: Map>) : Response400 { - override val status = 400; - override val content = null - } - - class Response404Unit(override val headers: Map>) : Response404 { - override val status = 404; - override val content = null - } + sealed interface Request: Wirespec.Request + class RequestUnit(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(orderId: Int): this(path = "/store/order/${orderId}", method = Wirespec.Method.DELETE, query = mapOf>(), headers = mapOf>(), content = null)} + sealed interface Response: Wirespec.Response + sealed interface Response4XX: Response + sealed interface Response400: Response4XX + sealed interface Response404: Response4XX + class Response400Unit (override val headers: Map> ): Response400 { override val status = 400; override val content = null} + class Response404Unit (override val headers: Map> ): Response404 { override val status = 404; override val content = null} suspend fun deleteOrder(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.DELETE const val PATH = "/store/order/{orderId}" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content == null -> RequestUnit(path, method, query, headers, null) - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - status == 400 && content == null -> Response400Unit(headers) - status == 404 && content == null -> Response404Unit(headers) - - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content == null -> RequestUnit(path, method, query, headers, null) + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + status == 400 && content == null -> Response400Unit(headers) + status == 404 && content == null -> Response404Unit(headers) + + else -> error("Cannot map response with status $status") + } } } interface CreateUser { - sealed interface Request : Wirespec.Request - class RequestApplicationJson( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(body: User) : this( - path = "/user", - method = Wirespec.Method.POST, - query = mapOf>(), - headers = mapOf>(), - content = Wirespec.Content("application/json", body) - ) - } - - class RequestApplicationXml( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(body: User) : this( - path = "/user", - method = Wirespec.Method.POST, - query = mapOf>(), - headers = mapOf>(), - content = Wirespec.Content("application/xml", body) - ) - } - - class RequestApplicationXWwwFormUrlencoded( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(body: User) : this( - path = "/user", - method = Wirespec.Method.POST, - query = mapOf>(), - headers = mapOf>(), - content = Wirespec.Content("application/x-www-form-urlencoded", body) - ) - } - - sealed interface Response : Wirespec.Response - sealed interface ResponseDefault : Response - - - class ResponseDefaultApplicationJson( - override val status: Int, - override val headers: Map>, - body: User - ) : ResponseDefault { - override val content = Wirespec.Content("application/json", body) - } - - class ResponseDefaultApplicationXml( - override val status: Int, - override val headers: Map>, - body: User - ) : ResponseDefault { - override val content = Wirespec.Content("application/xml", body) - } - + sealed interface Request: Wirespec.Request + class RequestApplicationJson(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(body: User): this(path = "/user", method = Wirespec.Method.POST, query = mapOf>(), headers = mapOf>(), content = Wirespec.Content("application/json", body))} + class RequestApplicationXml(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(body: User): this(path = "/user", method = Wirespec.Method.POST, query = mapOf>(), headers = mapOf>(), content = Wirespec.Content("application/xml", body))} + class RequestApplicationXWwwFormUrlencoded(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(body: User): this(path = "/user", method = Wirespec.Method.POST, query = mapOf>(), headers = mapOf>(), content = Wirespec.Content("application/x-www-form-urlencoded", body))} + sealed interface Response: Wirespec.Response + sealed interface ResponseDefault: Response + + + class ResponseDefaultApplicationJson (override val status: Int, override val headers: Map>, body: User): + ResponseDefault { override val content = Wirespec.Content("application/json", body)} + class ResponseDefaultApplicationXml (override val status: Int, override val headers: Map>, body: User): + ResponseDefault { override val content = Wirespec.Content("application/xml", body)} suspend fun createUser(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.POST const val PATH = "/user" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content?.type == "application/json" -> contentMapper - .read(content, typeOf().javaType) - .let { RequestApplicationJson(path, method, query, headers, it) } - - content?.type == "application/xml" -> contentMapper - .read(content, typeOf().javaType) - .let { RequestApplicationXml(path, method, query, headers, it) } - - content?.type == "application/x-www-form-urlencoded" -> contentMapper - .read(content, typeOf().javaType) - .let { RequestApplicationXWwwFormUrlencoded(path, method, query, headers, it) } - - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - - content?.type == "application/json" -> contentMapper - .read(content, typeOf().javaType) - .let { ResponseDefaultApplicationJson(status, headers, it.body) } - - content?.type == "application/xml" -> contentMapper - .read(content, typeOf().javaType) - .let { ResponseDefaultApplicationXml(status, headers, it.body) } - - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content?.type == "application/json" -> contentMapper + .read(content, Wirespec.getType(User::class.java, false)) + .let{ RequestApplicationJson(path, method, query, headers, it) } + content?.type == "application/xml" -> contentMapper + .read(content, Wirespec.getType(User::class.java, false)) + .let{ RequestApplicationXml(path, method, query, headers, it) } + content?.type == "application/x-www-form-urlencoded" -> contentMapper + .read(content, Wirespec.getType(User::class.java, false)) + .let{ RequestApplicationXWwwFormUrlencoded(path, method, query, headers, it) } + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + + content?.type == "application/json" -> contentMapper + .read(content, Wirespec.getType(User::class.java, false)) + .let{ ResponseDefaultApplicationJson(status, headers, it.body) } + content?.type == "application/xml" -> contentMapper + .read(content, Wirespec.getType(User::class.java, false)) + .let{ ResponseDefaultApplicationXml(status, headers, it.body) } + else -> error("Cannot map response with status $status") + } } } interface CreateUsersWithListInput { - sealed interface Request : Wirespec.Request - class RequestApplicationJson( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content>? - ) : Request> { - constructor(body: List) : this( - path = "/user/createWithList", - method = Wirespec.Method.POST, - query = mapOf>(), - headers = mapOf>(), - content = Wirespec.Content("application/json", body) - ) - } - - sealed interface Response : Wirespec.Response - sealed interface Response2XX : Response - sealed interface ResponseDefault : Response - sealed interface Response200 : Response2XX - class Response200ApplicationXml(override val headers: Map>, body: User) : Response200 { - override val status = 200; - override val content = Wirespec.Content("application/xml", body) - } - - class Response200ApplicationJson(override val headers: Map>, body: User) : Response200 { - override val status = 200; - override val content = Wirespec.Content("application/json", body) - } - - class ResponseDefaultUnit(override val status: Int, override val headers: Map>) : - ResponseDefault { - override val content = null - } - + sealed interface Request: Wirespec.Request + class RequestApplicationJson(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content>?) : + Request> { constructor(body: List): this(path = "/user/createWithList", method = Wirespec.Method.POST, query = mapOf>(), headers = mapOf>(), content = Wirespec.Content("application/json", body))} + sealed interface Response: Wirespec.Response + sealed interface Response2XX: Response + sealed interface ResponseDefault: Response + sealed interface Response200: Response2XX + class Response200ApplicationXml (override val headers: Map>, body: User): Response200 { override val status = 200; override val content = Wirespec.Content("application/xml", body)} + class Response200ApplicationJson (override val headers: Map>, body: User): Response200 { override val status = 200; override val content = Wirespec.Content("application/json", body)} + class ResponseDefaultUnit (override val status: Int, override val headers: Map> ): + ResponseDefault { override val content = null} suspend fun createUsersWithListInput(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.POST const val PATH = "/user/createWithList" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content?.type == "application/json" -> contentMapper - .read>(content, typeOf>().javaType) - .let { RequestApplicationJson(path, method, query, headers, it) } - - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - status == 200 && content?.type == "application/xml" -> contentMapper - .read(content, typeOf().javaType) - .let { Response200ApplicationXml(headers, it.body) } - - status == 200 && content?.type == "application/json" -> contentMapper - .read(content, typeOf().javaType) - .let { Response200ApplicationJson(headers, it.body) } - - content == null -> ResponseDefaultUnit(status, headers) - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content?.type == "application/json" -> contentMapper + .read>(content, Wirespec.getType(User::class.java, true)) + .let{ RequestApplicationJson(path, method, query, headers, it) } + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + status == 200 && content?.type == "application/xml" -> contentMapper + .read(content, Wirespec.getType(User::class.java, false)) + .let{ Response200ApplicationXml(headers, it.body) } + status == 200 && content?.type == "application/json" -> contentMapper + .read(content, Wirespec.getType(User::class.java, false)) + .let{ Response200ApplicationJson(headers, it.body) } + content == null -> ResponseDefaultUnit(status, headers) + else -> error("Cannot map response with status $status") + } } } interface LoginUser { - sealed interface Request : Wirespec.Request - class RequestUnit( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(username: String? = null, password: String? = null) : this( - path = "/user/login", - method = Wirespec.Method.GET, - query = mapOf>("username" to listOf(username), "password" to listOf(password)), - headers = mapOf>(), - content = null - ) - } - - sealed interface Response : Wirespec.Response - sealed interface Response2XX : Response - sealed interface Response4XX : Response - sealed interface Response200 : Response2XX - sealed interface Response400 : Response4XX - class Response200ApplicationXml(override val headers: Map>, body: String) : Response200 { - override val status = 200; - override val content = Wirespec.Content("application/xml", body) - } - - class Response200ApplicationJson(override val headers: Map>, body: String) : - Response200 { - override val status = 200; - override val content = Wirespec.Content("application/json", body) - } - - class Response400Unit(override val headers: Map>) : Response400 { - override val status = 400; - override val content = null - } + sealed interface Request: Wirespec.Request + class RequestUnit(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(username: String? = null, password: String? = null): this(path = "/user/login", method = Wirespec.Method.GET, query = mapOf>("username" to listOf(username), "password" to listOf(password)), headers = mapOf>(), content = null)} + sealed interface Response: Wirespec.Response + sealed interface Response2XX: Response + sealed interface Response4XX: Response + sealed interface Response200: Response2XX + sealed interface Response400: Response4XX + class Response200ApplicationXml (override val headers: Map>, body: String ): + Response200 { override val status = 200; override val content = Wirespec.Content("application/xml", body)} + class Response200ApplicationJson (override val headers: Map>, body: String ): + Response200 { override val status = 200; override val content = Wirespec.Content("application/json", body)} + class Response400Unit (override val headers: Map> ): Response400 { override val status = 400; override val content = null} suspend fun loginUser(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.GET const val PATH = "/user/login" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content == null -> RequestUnit(path, method, query, headers, null) - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - status == 200 && content?.type == "application/xml" -> contentMapper - .read(content, typeOf().javaType) - .let { Response200ApplicationXml(headers, it.body) } - - status == 200 && content?.type == "application/json" -> contentMapper - .read(content, typeOf().javaType) - .let { Response200ApplicationJson(headers, it.body) } - - status == 400 && content == null -> Response400Unit(headers) - - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content == null -> RequestUnit(path, method, query, headers, null) + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + status == 200 && content?.type == "application/xml" -> contentMapper + .read(content, Wirespec.getType(String::class.java, false)) + .let{ Response200ApplicationXml(headers, it.body) } + status == 200 && content?.type == "application/json" -> contentMapper + .read(content, Wirespec.getType(String::class.java, false)) + .let{ Response200ApplicationJson(headers, it.body) } + status == 400 && content == null -> Response400Unit(headers) + + else -> error("Cannot map response with status $status") + } } } interface LogoutUser { - sealed interface Request : Wirespec.Request - class RequestUnit( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor() : this( - path = "/user/logout", - method = Wirespec.Method.GET, - query = mapOf>(), - headers = mapOf>(), - content = null - ) - } - - sealed interface Response : Wirespec.Response - sealed interface ResponseDefault : Response - + sealed interface Request: Wirespec.Request + class RequestUnit(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(): this(path = "/user/logout", method = Wirespec.Method.GET, query = mapOf>(), headers = mapOf>(), content = null)} + sealed interface Response: Wirespec.Response + sealed interface ResponseDefault: Response - class ResponseDefaultUnit(override val status: Int, override val headers: Map>) : - ResponseDefault { - override val content = null - } + class ResponseDefaultUnit (override val status: Int, override val headers: Map> ): + ResponseDefault { override val content = null} suspend fun logoutUser(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.GET const val PATH = "/user/logout" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content == null -> RequestUnit(path, method, query, headers, null) - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - - content == null -> ResponseDefaultUnit(status, headers) - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content == null -> RequestUnit(path, method, query, headers, null) + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + + content == null -> ResponseDefaultUnit(status, headers) + else -> error("Cannot map response with status $status") + } } } interface GetUserByName { - sealed interface Request : Wirespec.Request - class RequestUnit( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(username: String) : this( - path = "/user/${username}", - method = Wirespec.Method.GET, - query = mapOf>(), - headers = mapOf>(), - content = null - ) - } - - sealed interface Response : Wirespec.Response - sealed interface Response2XX : Response - sealed interface Response4XX : Response - sealed interface Response200 : Response2XX - sealed interface Response400 : Response4XX - sealed interface Response404 : Response4XX - class Response200ApplicationXml(override val headers: Map>, body: User) : Response200 { - override val status = 200; - override val content = Wirespec.Content("application/xml", body) - } - - class Response200ApplicationJson(override val headers: Map>, body: User) : Response200 { - override val status = 200; - override val content = Wirespec.Content("application/json", body) - } - - class Response400Unit(override val headers: Map>) : Response400 { - override val status = 400; - override val content = null - } - - class Response404Unit(override val headers: Map>) : Response404 { - override val status = 404; - override val content = null - } + sealed interface Request: Wirespec.Request + class RequestUnit(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(username: String): this(path = "/user/${username}", method = Wirespec.Method.GET, query = mapOf>(), headers = mapOf>(), content = null)} + sealed interface Response: Wirespec.Response + sealed interface Response2XX: Response + sealed interface Response4XX: Response + sealed interface Response200: Response2XX + sealed interface Response400: Response4XX + sealed interface Response404: Response4XX + class Response200ApplicationXml (override val headers: Map>, body: User): Response200 { override val status = 200; override val content = Wirespec.Content("application/xml", body)} + class Response200ApplicationJson (override val headers: Map>, body: User): Response200 { override val status = 200; override val content = Wirespec.Content("application/json", body)} + class Response400Unit (override val headers: Map> ): Response400 { override val status = 400; override val content = null} + class Response404Unit (override val headers: Map> ): Response404 { override val status = 404; override val content = null} suspend fun getUserByName(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.GET const val PATH = "/user/{username}" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content == null -> RequestUnit(path, method, query, headers, null) - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - status == 200 && content?.type == "application/xml" -> contentMapper - .read(content, typeOf().javaType) - .let { Response200ApplicationXml(headers, it.body) } - - status == 200 && content?.type == "application/json" -> contentMapper - .read(content, typeOf().javaType) - .let { Response200ApplicationJson(headers, it.body) } - - status == 400 && content == null -> Response400Unit(headers) - status == 404 && content == null -> Response404Unit(headers) - - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content == null -> RequestUnit(path, method, query, headers, null) + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + status == 200 && content?.type == "application/xml" -> contentMapper + .read(content, Wirespec.getType(User::class.java, false)) + .let{ Response200ApplicationXml(headers, it.body) } + status == 200 && content?.type == "application/json" -> contentMapper + .read(content, Wirespec.getType(User::class.java, false)) + .let{ Response200ApplicationJson(headers, it.body) } + status == 400 && content == null -> Response400Unit(headers) + status == 404 && content == null -> Response404Unit(headers) + + else -> error("Cannot map response with status $status") + } } } interface UpdateUser { - sealed interface Request : Wirespec.Request - class RequestApplicationJson( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(username: String, body: User) : this( - path = "/user/${username}", - method = Wirespec.Method.PUT, - query = mapOf>(), - headers = mapOf>(), - content = Wirespec.Content("application/json", body) - ) - } - - class RequestApplicationXml( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(username: String, body: User) : this( - path = "/user/${username}", - method = Wirespec.Method.PUT, - query = mapOf>(), - headers = mapOf>(), - content = Wirespec.Content("application/xml", body) - ) - } - - class RequestApplicationXWwwFormUrlencoded( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(username: String, body: User) : this( - path = "/user/${username}", - method = Wirespec.Method.PUT, - query = mapOf>(), - headers = mapOf>(), - content = Wirespec.Content("application/x-www-form-urlencoded", body) - ) - } - - sealed interface Response : Wirespec.Response - sealed interface ResponseDefault : Response - - - class ResponseDefaultUnit(override val status: Int, override val headers: Map>) : - ResponseDefault { - override val content = null - } - + sealed interface Request: Wirespec.Request + class RequestApplicationJson(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(username: String, body: User): this(path = "/user/${username}", method = Wirespec.Method.PUT, query = mapOf>(), headers = mapOf>(), content = Wirespec.Content("application/json", body))} + class RequestApplicationXml(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(username: String, body: User): this(path = "/user/${username}", method = Wirespec.Method.PUT, query = mapOf>(), headers = mapOf>(), content = Wirespec.Content("application/xml", body))} + class RequestApplicationXWwwFormUrlencoded(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(username: String, body: User): this(path = "/user/${username}", method = Wirespec.Method.PUT, query = mapOf>(), headers = mapOf>(), content = Wirespec.Content("application/x-www-form-urlencoded", body))} + sealed interface Response: Wirespec.Response + sealed interface ResponseDefault: Response + + + class ResponseDefaultUnit (override val status: Int, override val headers: Map> ): + ResponseDefault { override val content = null} suspend fun updateUser(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.PUT const val PATH = "/user/{username}" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content?.type == "application/json" -> contentMapper - .read(content, typeOf().javaType) - .let { RequestApplicationJson(path, method, query, headers, it) } - - content?.type == "application/xml" -> contentMapper - .read(content, typeOf().javaType) - .let { RequestApplicationXml(path, method, query, headers, it) } - - content?.type == "application/x-www-form-urlencoded" -> contentMapper - .read(content, typeOf().javaType) - .let { RequestApplicationXWwwFormUrlencoded(path, method, query, headers, it) } - - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - - content == null -> ResponseDefaultUnit(status, headers) - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content?.type == "application/json" -> contentMapper + .read(content, Wirespec.getType(User::class.java, false)) + .let{ RequestApplicationJson(path, method, query, headers, it) } + content?.type == "application/xml" -> contentMapper + .read(content, Wirespec.getType(User::class.java, false)) + .let{ RequestApplicationXml(path, method, query, headers, it) } + content?.type == "application/x-www-form-urlencoded" -> contentMapper + .read(content, Wirespec.getType(User::class.java, false)) + .let{ RequestApplicationXWwwFormUrlencoded(path, method, query, headers, it) } + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + + content == null -> ResponseDefaultUnit(status, headers) + else -> error("Cannot map response with status $status") + } } } interface DeleteUser { - sealed interface Request : Wirespec.Request - class RequestUnit( - override val path: String, - override val method: Wirespec.Method, - override val query: Map>, - override val headers: Map>, - override val content: Wirespec.Content? - ) : Request { - constructor(username: String) : this( - path = "/user/${username}", - method = Wirespec.Method.DELETE, - query = mapOf>(), - headers = mapOf>(), - content = null - ) - } - - sealed interface Response : Wirespec.Response - sealed interface Response4XX : Response - sealed interface Response400 : Response4XX - sealed interface Response404 : Response4XX - class Response400Unit(override val headers: Map>) : Response400 { - override val status = 400; - override val content = null - } - - class Response404Unit(override val headers: Map>) : Response404 { - override val status = 404; - override val content = null - } + sealed interface Request: Wirespec.Request + class RequestUnit(override val path:String, override val method: Wirespec.Method, override val query: Map>, override val headers: Map>, override val content:Wirespec.Content?) : + Request { constructor(username: String): this(path = "/user/${username}", method = Wirespec.Method.DELETE, query = mapOf>(), headers = mapOf>(), content = null)} + sealed interface Response: Wirespec.Response + sealed interface Response4XX: Response + sealed interface Response400: Response4XX + sealed interface Response404: Response4XX + class Response400Unit (override val headers: Map> ): Response400 { override val status = 400; override val content = null} + class Response404Unit (override val headers: Map> ): Response404 { override val status = 404; override val content = null} suspend fun deleteUser(request: Request<*>): Response<*> - - companion object { + companion object{ val METHOD = Wirespec.Method.DELETE const val PATH = "/user/{username}" - fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun( - path: String, - method: Wirespec.Method, - query: Map>, - headers: Map>, - content: Wirespec.Content? - ) = - when { - content == null -> RequestUnit(path, method, query, headers, null) - else -> error("Cannot map request") - } - - fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) = - fun(status: Int, headers: Map>, content: Wirespec.Content?) = - when { - status == 400 && content == null -> Response400Unit(headers) - status == 404 && content == null -> Response404Unit(headers) - - else -> error("Cannot map response with status $status") - } + fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) = + when { + content == null -> RequestUnit(path, method, query, headers, null) + else -> error("Cannot map request") + } + fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) = + when { + status == 400 && content == null -> Response400Unit(headers) + status == 404 && content == null -> Response404Unit(headers) + + else -> error("Cannot map response with status $status") + } } } -enum class FindPetsByStatusParameterStatus(val label: String) { +enum class FindPetsByStatusParameterStatus (val label: String){ available("available"), pending("pending"), sold("sold"); @@ -1544,7 +735,7 @@ data class Order( val complete: Boolean? = null ) -enum class OrderStatus(val label: String) { +enum class OrderStatus (val label: String){ placed("placed"), approved("approved"), delivered("delivered"); @@ -1597,7 +788,7 @@ data class Pet( val status: PetStatus? = null ) -enum class PetStatus(val label: String) { +enum class PetStatus (val label: String){ available("available"), pending("pending"), sold("sold"); diff --git a/src/plugin/maven/src/main/kotlin/GenerateMojo.kt b/src/plugin/maven/src/main/kotlin/GenerateMojo.kt index fec82148b..6b55ab3ff 100644 --- a/src/plugin/maven/src/main/kotlin/GenerateMojo.kt +++ b/src/plugin/maven/src/main/kotlin/GenerateMojo.kt @@ -54,7 +54,7 @@ class GenerateMojo : BaseMojo() { fun executeKotlin() { val emitter = KotlinEmitter(packageName, logger) - if(shared == true) { + if(shared == null || shared == true) { JvmUtil.emitJvm("community.flock.wirespec", output, "Wirespec", "kt").writeText(emitter.shared) } if (openapi != null) {