diff --git a/.gitignore b/.gitignore index 4b53cab..0761877 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -HELP.md + .gradle build/ !gradle/wrapper/gradle-wrapper.jar @@ -36,3 +36,5 @@ out/ ### VS Code ### .vscode/ /lib/ + +**/generated_tests/* diff --git a/cursorpaging-examples/build.gradle.kts b/cursorpaging-examples/build.gradle.kts index 3d8e9fc..76ec3c2 100644 --- a/cursorpaging-examples/build.gradle.kts +++ b/cursorpaging-examples/build.gradle.kts @@ -1,65 +1,2 @@ -plugins { - java - id("org.springframework.boot") - id("io.vigier.java-library-conventions") -} - -group = "io.vigier.cursorpaging" -version = "0-SNAPSHOT" - -dependencies { - val mapstructVersion: String by extra("1.5.5.Final") - - implementation(project(":cursorpaging-jpa")) - implementation(project(":cursorpaging-jpa-api")) - - implementation("org.springframework.boot:spring-boot-starter-data-jpa") - implementation("org.springframework.boot:spring-boot-starter-web") - implementation("org.springframework.boot:spring-boot-starter-validation") - implementation("org.springframework.hateoas:spring-hateoas") - implementation("org.springframework.retry:spring-retry") - implementation("org.springframework:spring-aspects") - implementation("org.apache.commons:commons-lang3") -// compileOnly("javax.xml.bind:jaxb-api") - - implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0") - implementation("org.mapstruct:mapstruct:${mapstructVersion}") - - annotationProcessor("org.mapstruct:mapstruct-processor:${mapstructVersion}") - annotationProcessor("org.hibernate:hibernate-jpamodelgen:6.5.0.Final") - -// implementation("org.springframework.boot:spring-boot-starter-data-mongodb") -// implementation("org.springframework.boot:spring-boot-starter-data-mongodb-reactive") -// implementation("org.springframework.boot:spring-boot-starter-data-r2dbc") -// implementation("org.liquibase:liquibase-core") - - - runtimeOnly("org.postgresql:postgresql") -// runtimeOnly("org.postgresql:r2dbc-postgresql") - -// annotationProcessor("org.hibernate:hibernate-jpamodelgen") -// annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") - - developmentOnly("org.springframework.boot:spring-boot-devtools") - developmentOnly("org.springframework.boot:spring-boot-docker-compose") - - testImplementation("org.springframework.boot:spring-boot-starter-test") - testImplementation("org.springframework.boot:spring-boot-testcontainers") - testImplementation("org.testcontainers:junit-jupiter") - testImplementation("org.testcontainers:postgresql") -// testImplementation("io.projectreactor:reactor-test") -// testImplementation("org.testcontainers:mongodb") -// testImplementation("org.testcontainers:r2dbc") -} - -tasks { - javadoc { - options { - (this as CoreJavadocOptions).addStringOption("Xdoclint:none", "-quiet") - } - } - test { - useJUnitPlatform() - } - -} +group = "io.vigier.cursorpaging.examples" +version = findProperty("version") ?: System.getenv("BUILD_VERSION") ?: "0-SNAPSHOT" \ No newline at end of file diff --git a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/ScpReleaseTestApplication.java b/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/ScpReleaseTestApplication.java deleted file mode 100644 index 24b4066..0000000 --- a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/ScpReleaseTestApplication.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.vigier.sbcpreleasetest; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class ScpReleaseTestApplication { - - public static void main(String[] args) { - SpringApplication.run( ScpReleaseTestApplication.class, args); - } - -} diff --git a/cursorpaging-examples/maven-setup/src/test/java/io/vigier/sbcpreleasetest/ScpReleaseTestApplicationTests.java b/cursorpaging-examples/maven-setup/src/test/java/io/vigier/sbcpreleasetest/ScpReleaseTestApplicationTests.java deleted file mode 100644 index 0dac3f3..0000000 --- a/cursorpaging-examples/maven-setup/src/test/java/io/vigier/sbcpreleasetest/ScpReleaseTestApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.vigier.sbcpreleasetest; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class ScpReleaseTestApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/cursorpaging-examples/maven-setup/.gitignore b/cursorpaging-examples/webapp-with-maven/.gitignore similarity index 89% rename from cursorpaging-examples/maven-setup/.gitignore rename to cursorpaging-examples/webapp-with-maven/.gitignore index fa2eafe..5b9785b 100644 --- a/cursorpaging-examples/maven-setup/.gitignore +++ b/cursorpaging-examples/webapp-with-maven/.gitignore @@ -1,8 +1,11 @@ HELP.md target/ +build/ !.mvn/wrapper/maven-wrapper.jar !**/src/main/**/target/ !**/src/test/**/target/ +!**/src/main/**/build/ +!**/src/test/**/build/ .mvn/settings.xml .mvn/maven.config diff --git a/cursorpaging-examples/maven-setup/.mvn/wrapper/maven-wrapper.jar b/cursorpaging-examples/webapp-with-maven/.mvn/wrapper/maven-wrapper.jar similarity index 100% rename from cursorpaging-examples/maven-setup/.mvn/wrapper/maven-wrapper.jar rename to cursorpaging-examples/webapp-with-maven/.mvn/wrapper/maven-wrapper.jar diff --git a/cursorpaging-examples/maven-setup/.mvn/wrapper/maven-wrapper.properties b/cursorpaging-examples/webapp-with-maven/.mvn/wrapper/maven-wrapper.properties similarity index 100% rename from cursorpaging-examples/maven-setup/.mvn/wrapper/maven-wrapper.properties rename to cursorpaging-examples/webapp-with-maven/.mvn/wrapper/maven-wrapper.properties diff --git a/cursorpaging-examples/maven-setup/README.md b/cursorpaging-examples/webapp-with-maven/README.md similarity index 84% rename from cursorpaging-examples/maven-setup/README.md rename to cursorpaging-examples/webapp-with-maven/README.md index 5de9813..80c6cc7 100644 --- a/cursorpaging-examples/maven-setup/README.md +++ b/cursorpaging-examples/webapp-with-maven/README.md @@ -5,9 +5,15 @@ This should be a half-way realistic application implementation used to demonstrate cursor-based pagination. The application is a simple Spring Boot web application that exposes a REST API to manage a list of data-records. +## Build-notice (gradle vs. maven)! + +The project build with gradle is integrated in the cursorpaging overall continuous build - and should work all the time. +The maven build is using the latest released version of the libraries and is only updated in case of an new release. +Therefor it is possible, that it will fail in case new features are used which are not yet released! + ## Required infrastructure -For running the application, you need a PostgreSQL ( s. the [compose.yml](../compose.yml) ) and a Java 17 runtime. +For running the application, you need a PostgreSQL ( s. the [docker-compose.yaml](docker-compose.yaml) ) and a Java 17 runtime. ## Components @@ -75,20 +81,20 @@ problematic. Here some tries: public CollectionModel getDataRecords( // All of this variants will result in a "no matching editors or conversion strategy found" error - @Parameter() @RequestParam( required = false ) final List order - @Parameter( style = ParameterStyle.DEEPOBJECT ) @RequestParam( required = false ) final List order - @Parameter( style = ParameterStyle.DEEPOBJECT ) @RequestParam( required = false ) final AttributeOrder[] order - @Parameter( style = ParameterStyle.FORM ) @RequestParam( required = false ) final AttributeOrder order + @Parameter() @RequestParam( required = false ) final List order1, + @Parameter( style = ParameterStyle.DEEPOBJECT ) @RequestParam( required = false ) final List order2, + @Parameter( style = ParameterStyle.DEEPOBJECT ) @RequestParam( required = false ) final AttributeOrder[] order3, + @Parameter( style = ParameterStyle.FORM ) @RequestParam( required = false ) final AttributeOrder order4, // order = null, just not mapped - @Parameter( style = ParameterStyle.DEEPOBJECT ) @RequestParam( required = false ) final AttributeOrder order + @Parameter( style = ParameterStyle.DEEPOBJECT ) @RequestParam( required = false ) final AttributeOrder order5, // works but the field must be required, to be shown in swagger @Parameter( description = "Define the order of the records", example = """ { "NAME": "DESC", "ID": "ASC" - }""" ) @RequestParam( defaultValue = "{}" ) final Map orderBy + }""" ) @RequestParam( defaultValue = "{}" ) final Map orderBy, // still there is the glitch, that when adding multiple parameters, all of them contain all request parameter. // We can use this to have a good representation in Swagger, but we need to parse the request parameters by hand @@ -116,6 +122,7 @@ Other considered alternatives: - Use simple parameters, and name them as the model properties: `@RequestParam( required = false ) final Order orderByName, @RequestParam( required = false ) final Order orderByCreatedAt` - Encode all in the attribute enum: "ORDER_BY_NAME_ASC", "ORDER_BY_CREATED_AT_DESC, etc." +- Just use a string and decode the contained json in the controller method. As there is a significant difference between creating a cursor for the first page and requesting the data for all subsequent page requests, personally I liked the POST approach most. diff --git a/cursorpaging-examples/webapp-with-maven/build.gradle.kts b/cursorpaging-examples/webapp-with-maven/build.gradle.kts new file mode 100644 index 0000000..972ae42 --- /dev/null +++ b/cursorpaging-examples/webapp-with-maven/build.gradle.kts @@ -0,0 +1,68 @@ +// Gradle build to include the example in the project build +// While the maven pom.xml es executed separately + +plugins { + java + id("org.springframework.boot") + id("io.vigier.java-library-conventions") +} + +group = "io.vigier.cursorpaging.examples.webapp" +version = findProperty("version") ?: System.getenv("BUILD_VERSION") ?: "0-SNAPSHOT" + +dependencies { + val mapstructVersion: String by extra("1.5.5.Final") + + implementation(project(":cursorpaging-jpa")) + implementation(project(":cursorpaging-jpa-api")) + + implementation("org.springframework.boot:spring-boot-starter-data-jpa") + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-validation") + implementation("org.springframework.hateoas:spring-hateoas") + implementation("org.springframework.retry:spring-retry") + implementation("org.springframework:spring-aspects") + implementation("org.apache.commons:commons-lang3") +// compileOnly("javax.xml.bind:jaxb-api") + + implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0") + implementation("org.mapstruct:mapstruct:${mapstructVersion}") + + annotationProcessor("org.mapstruct:mapstruct-processor:${mapstructVersion}") + annotationProcessor("org.hibernate:hibernate-jpamodelgen:6.5.0.Final") + +// implementation("org.springframework.boot:spring-boot-starter-data-mongodb") +// implementation("org.springframework.boot:spring-boot-starter-data-mongodb-reactive") +// implementation("org.springframework.boot:spring-boot-starter-data-r2dbc") +// implementation("org.liquibase:liquibase-core") + + + runtimeOnly("org.postgresql:postgresql") +// runtimeOnly("org.postgresql:r2dbc-postgresql") + +// annotationProcessor("org.hibernate:hibernate-jpamodelgen") +// annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") + + developmentOnly("org.springframework.boot:spring-boot-devtools") + developmentOnly("org.springframework.boot:spring-boot-docker-compose") + + testImplementation("org.springframework.boot:spring-boot-starter-test") + testImplementation("org.springframework.boot:spring-boot-testcontainers") + testImplementation("org.testcontainers:junit-jupiter") + testImplementation("org.testcontainers:postgresql") +// testImplementation("io.projectreactor:reactor-test") +// testImplementation("org.testcontainers:mongodb") +// testImplementation("org.testcontainers:r2dbc") +} + +tasks { + javadoc { + options { + (this as CoreJavadocOptions).addStringOption("Xdoclint:none", "-quiet") + } + } + test { + useJUnitPlatform() + } + +} diff --git a/cursorpaging-examples/maven-setup/docker-compose.yaml b/cursorpaging-examples/webapp-with-maven/docker-compose.yaml similarity index 100% rename from cursorpaging-examples/maven-setup/docker-compose.yaml rename to cursorpaging-examples/webapp-with-maven/docker-compose.yaml diff --git a/cursorpaging-examples/maven-setup/mvnw b/cursorpaging-examples/webapp-with-maven/mvnw similarity index 100% rename from cursorpaging-examples/maven-setup/mvnw rename to cursorpaging-examples/webapp-with-maven/mvnw diff --git a/cursorpaging-examples/maven-setup/mvnw.cmd b/cursorpaging-examples/webapp-with-maven/mvnw.cmd similarity index 100% rename from cursorpaging-examples/maven-setup/mvnw.cmd rename to cursorpaging-examples/webapp-with-maven/mvnw.cmd diff --git a/cursorpaging-examples/maven-setup/pom.xml b/cursorpaging-examples/webapp-with-maven/pom.xml similarity index 71% rename from cursorpaging-examples/maven-setup/pom.xml rename to cursorpaging-examples/webapp-with-maven/pom.xml index 173da02..3db8608 100644 --- a/cursorpaging-examples/maven-setup/pom.xml +++ b/cursorpaging-examples/webapp-with-maven/pom.xml @@ -5,30 +5,44 @@ org.springframework.boot spring-boot-starter-parent - 3.2.5 + 3.3.1 io.vigier - cursorpaging-example-maven + cursorpaging-example-webapp-with-maven 0-SNAPSHOT - cursorpaging :: example :: maven + cursorpaging :: example :: webapp-maven An maven build setup, using a released version of the cursorpaging libraries. 17 1.5.5.Final 1.18.32 - 0.0.1-RC4 + 0.8.0-RC1 + 1.19.8 - - - p3t-github-packages - https://maven.pkg.github.com/p3t/spring-cursorpaging - - + + + githubPackages + + + githubPackages + + + + + p3t-github-packages + https://maven.pkg.github.com/p3t/spring-cursorpaging + + + + 0.8.0-RC1 + + + @@ -90,8 +104,35 @@ spring-boot-starter-test test + + org.springframework.boot + spring-boot-testcontainers + test + + + org.testcontainers + postgresql + test + + + org.testcontainers + junit-jupiter + test + + + + + + org.testcontainers + testcontainers-bom + ${testcontainers.version} + pom + import + + + diff --git a/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/ExampleWebApplication.java b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/ExampleWebApplication.java new file mode 100644 index 0000000..6d73f31 --- /dev/null +++ b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/ExampleWebApplication.java @@ -0,0 +1,13 @@ +package io.vigier.cursorpaging.example.webapp; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ExampleWebApplication { + + public static void main( final String[] args ) { + SpringApplication.run( ExampleWebApplication.class, args ); + } + +} diff --git a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/api/controller/ControllerExceptionHandler.java b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/api/controller/ControllerExceptionHandler.java similarity index 93% rename from cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/api/controller/ControllerExceptionHandler.java rename to cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/api/controller/ControllerExceptionHandler.java index c241094..23a7e7d 100644 --- a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/api/controller/ControllerExceptionHandler.java +++ b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/api/controller/ControllerExceptionHandler.java @@ -1,4 +1,4 @@ -package io.vigier.cursorpaging.testapp.api.controller; +package io.vigier.cursorpaging.example.webapp.api.controller; import jakarta.validation.ValidationException; import org.springframework.http.ResponseEntity; diff --git a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/api/controller/DataRecordController.java b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/api/controller/DataRecordController.java similarity index 93% rename from cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/api/controller/DataRecordController.java rename to cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/api/controller/DataRecordController.java index 2c97f3f..6f2b5a4 100644 --- a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/api/controller/DataRecordController.java +++ b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/api/controller/DataRecordController.java @@ -1,4 +1,4 @@ -package io.vigier.cursorpaging.testapp.api.controller; +package io.vigier.cursorpaging.example.webapp.api.controller; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -6,18 +6,18 @@ import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; +import io.vigier.cursorpaging.example.webapp.api.model.DataRecordAttribute; +import io.vigier.cursorpaging.example.webapp.api.model.DtoDataRecord; +import io.vigier.cursorpaging.example.webapp.api.model.mapper.DtoDataRecordMapper; +import io.vigier.cursorpaging.example.webapp.model.DataRecord; +import io.vigier.cursorpaging.example.webapp.model.DataRecord_; +import io.vigier.cursorpaging.example.webapp.repository.DataRecordRepository; import io.vigier.cursorpaging.jpa.Order; import io.vigier.cursorpaging.jpa.PageRequest; import io.vigier.cursorpaging.jpa.api.DtoPageRequest; import io.vigier.cursorpaging.jpa.serializer.Base64String; import io.vigier.cursorpaging.jpa.serializer.EntitySerializer; import io.vigier.cursorpaging.jpa.validation.MaxSize; -import io.vigier.cursorpaging.testapp.api.model.DataRecordAttribute; -import io.vigier.cursorpaging.testapp.api.model.DtoDataRecord; -import io.vigier.cursorpaging.testapp.api.model.mapper.DtoDataRecordMapper; -import io.vigier.cursorpaging.testapp.model.DataRecord; -import io.vigier.cursorpaging.testapp.model.DataRecord_; -import io.vigier.cursorpaging.testapp.repository.DataRecordRepository; import java.util.List; import java.util.Map; import java.util.Optional; diff --git a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/api/model/DataRecordAttribute.java b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/api/model/DataRecordAttribute.java similarity index 79% rename from cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/api/model/DataRecordAttribute.java rename to cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/api/model/DataRecordAttribute.java index cb3380e..14bc3bb 100644 --- a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/api/model/DataRecordAttribute.java +++ b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/api/model/DataRecordAttribute.java @@ -1,9 +1,9 @@ -package io.vigier.cursorpaging.testapp.api.model; +package io.vigier.cursorpaging.example.webapp.api.model; +import io.vigier.cursorpaging.example.webapp.model.AuditInfo; +import io.vigier.cursorpaging.example.webapp.model.AuditInfo_; +import io.vigier.cursorpaging.example.webapp.model.DataRecord_; import io.vigier.cursorpaging.jpa.Attribute; -import io.vigier.cursorpaging.testapp.model.AuditInfo; -import io.vigier.cursorpaging.testapp.model.AuditInfo_; -import io.vigier.cursorpaging.testapp.model.DataRecord_; import java.time.Instant; import java.util.UUID; import lombok.Getter; diff --git a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/api/model/DtoDataRecord.java b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/api/model/DtoDataRecord.java similarity index 79% rename from cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/api/model/DtoDataRecord.java rename to cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/api/model/DtoDataRecord.java index 2a1d369..3d4df1d 100644 --- a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/api/model/DtoDataRecord.java +++ b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/api/model/DtoDataRecord.java @@ -1,4 +1,4 @@ -package io.vigier.cursorpaging.testapp.api.model; +package io.vigier.cursorpaging.example.webapp.api.model; import java.time.OffsetDateTime; import lombok.Data; diff --git a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/api/model/mapper/DtoDataRecordMapper.java b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/api/model/mapper/DtoDataRecordMapper.java similarity index 79% rename from cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/api/model/mapper/DtoDataRecordMapper.java rename to cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/api/model/mapper/DtoDataRecordMapper.java index 52923ae..eabe251 100644 --- a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/api/model/mapper/DtoDataRecordMapper.java +++ b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/api/model/mapper/DtoDataRecordMapper.java @@ -1,8 +1,8 @@ -package io.vigier.cursorpaging.testapp.api.model.mapper; +package io.vigier.cursorpaging.example.webapp.api.model.mapper; -import io.vigier.cursorpaging.testapp.api.model.DtoDataRecord; -import io.vigier.cursorpaging.testapp.config.MapstructConfig; -import io.vigier.cursorpaging.testapp.model.DataRecord; +import io.vigier.cursorpaging.example.webapp.api.model.DtoDataRecord; +import io.vigier.cursorpaging.example.webapp.config.MapstructConfig; +import io.vigier.cursorpaging.example.webapp.model.DataRecord; import java.time.Instant; import java.time.OffsetDateTime; import java.time.ZoneOffset; diff --git a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/config/EntitySerializerConfig.java b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/config/EntitySerializerConfig.java similarity index 90% rename from cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/config/EntitySerializerConfig.java rename to cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/config/EntitySerializerConfig.java index b7b606e..c33f506 100644 --- a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/config/EntitySerializerConfig.java +++ b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/config/EntitySerializerConfig.java @@ -1,9 +1,9 @@ -package io.vigier.cursorpaging.testapp.config; +package io.vigier.cursorpaging.example.webapp.config; +import io.vigier.cursorpaging.example.webapp.model.DataRecord; import io.vigier.cursorpaging.jpa.serializer.Encrypter; import io.vigier.cursorpaging.jpa.serializer.EntitySerializer; import io.vigier.cursorpaging.jpa.serializer.EntitySerializerFactory; -import io.vigier.cursorpaging.testapp.model.DataRecord; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/config/JpaConfig.java b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/config/JpaConfig.java similarity index 61% rename from cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/config/JpaConfig.java rename to cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/config/JpaConfig.java index c5d993d..c84b105 100644 --- a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/config/JpaConfig.java +++ b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/config/JpaConfig.java @@ -1,7 +1,7 @@ -package io.vigier.cursorpaging.testapp.config; +package io.vigier.cursorpaging.example.webapp.config; +import io.vigier.cursorpaging.example.webapp.ExampleWebApplication; import io.vigier.cursorpaging.jpa.bootstrap.CursorPageRepositoryFactoryBean; -import io.vigier.cursorpaging.testapp.TestApplication; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @@ -10,7 +10,8 @@ * would require an entity manager factory (bean). */ @Configuration -@EnableJpaRepositories( basePackageClasses = TestApplication.class, repositoryFactoryBeanClass = CursorPageRepositoryFactoryBean.class ) +@EnableJpaRepositories( basePackageClasses = ExampleWebApplication.class, + repositoryFactoryBeanClass = CursorPageRepositoryFactoryBean.class ) public class JpaConfig { } diff --git a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/config/MapstructConfig.java b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/config/MapstructConfig.java similarity index 67% rename from cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/config/MapstructConfig.java rename to cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/config/MapstructConfig.java index 4607ab6..0ffe52f 100644 --- a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/config/MapstructConfig.java +++ b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/config/MapstructConfig.java @@ -1,4 +1,4 @@ -package io.vigier.cursorpaging.testapp.config; +package io.vigier.cursorpaging.example.webapp.config; import org.mapstruct.MapperConfig; diff --git a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/config/WebConfig.java b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/config/WebConfig.java similarity index 93% rename from cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/config/WebConfig.java rename to cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/config/WebConfig.java index 50d601b..50b0366 100644 --- a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/config/WebConfig.java +++ b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/config/WebConfig.java @@ -1,4 +1,4 @@ -package io.vigier.cursorpaging.testapp.config; +package io.vigier.cursorpaging.example.webapp.config; import io.vigier.cursorpaging.jpa.api.StringToBase64StringConverter; import org.springframework.context.annotation.Bean; diff --git a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/model/AuditInfo.java b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/model/AuditInfo.java similarity index 97% rename from cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/model/AuditInfo.java rename to cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/model/AuditInfo.java index 4169d2f..443030a 100644 --- a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/model/AuditInfo.java +++ b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/model/AuditInfo.java @@ -1,4 +1,4 @@ -package io.vigier.cursorpaging.testapp.model; +package io.vigier.cursorpaging.example.webapp.model; import jakarta.persistence.Embeddable; import java.io.Serial; diff --git a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/model/DataRecord.java b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/model/DataRecord.java similarity index 95% rename from cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/model/DataRecord.java rename to cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/model/DataRecord.java index ee39d05..6458b30 100644 --- a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/model/DataRecord.java +++ b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/model/DataRecord.java @@ -1,4 +1,4 @@ -package io.vigier.cursorpaging.testapp.model; +package io.vigier.cursorpaging.example.webapp.model; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/repository/DataRecordRepository.java b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/repository/DataRecordRepository.java similarity index 73% rename from cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/repository/DataRecordRepository.java rename to cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/repository/DataRecordRepository.java index a99705e..0d7b53c 100644 --- a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/repository/DataRecordRepository.java +++ b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/repository/DataRecordRepository.java @@ -1,7 +1,7 @@ -package io.vigier.cursorpaging.testapp.repository; +package io.vigier.cursorpaging.example.webapp.repository; +import io.vigier.cursorpaging.example.webapp.model.DataRecord; import io.vigier.cursorpaging.jpa.repository.CursorPageRepository; -import io.vigier.cursorpaging.testapp.model.DataRecord; import java.util.UUID; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/service/TestDataService.java b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/service/TestDataService.java similarity index 84% rename from cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/service/TestDataService.java rename to cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/service/TestDataService.java index c671dfc..0a180cb 100644 --- a/cursorpaging-examples/maven-setup/src/main/java/io/vigier/sbcpreleasetest/service/TestDataService.java +++ b/cursorpaging-examples/webapp-with-maven/src/main/java/io/vigier/cursorpaging/example/webapp/service/TestDataService.java @@ -1,8 +1,8 @@ -package io.vigier.cursorpaging.testapp.service; +package io.vigier.cursorpaging.example.webapp.service; -import io.vigier.cursorpaging.testapp.model.AuditInfo; -import io.vigier.cursorpaging.testapp.model.DataRecord; -import io.vigier.cursorpaging.testapp.repository.DataRecordRepository; +import io.vigier.cursorpaging.example.webapp.model.AuditInfo; +import io.vigier.cursorpaging.example.webapp.model.DataRecord; +import io.vigier.cursorpaging.example.webapp.repository.DataRecordRepository; import jakarta.annotation.PostConstruct; import java.time.Instant; import java.time.temporal.ChronoUnit; diff --git a/cursorpaging-examples/maven-setup/src/main/resources/application.yaml b/cursorpaging-examples/webapp-with-maven/src/main/resources/application.yaml similarity index 86% rename from cursorpaging-examples/maven-setup/src/main/resources/application.yaml rename to cursorpaging-examples/webapp-with-maven/src/main/resources/application.yaml index b6a273c..380c911 100644 --- a/cursorpaging-examples/maven-setup/src/main/resources/application.yaml +++ b/cursorpaging-examples/webapp-with-maven/src/main/resources/application.yaml @@ -1,5 +1,5 @@ spring: - application.name: "scp-release-testapp" + application.name: "Spring Cursor Paging Example App" jpa: hibernate.ddl-auto: create database-platform: "org.hibernate.dialect.PostgreSQLDialect" diff --git a/cursorpaging-examples/webapp-with-maven/src/main/resources/static/index.html b/cursorpaging-examples/webapp-with-maven/src/main/resources/static/index.html new file mode 100644 index 0000000..fae61c5 --- /dev/null +++ b/cursorpaging-examples/webapp-with-maven/src/main/resources/static/index.html @@ -0,0 +1,7 @@ + + +

Cursor Paging Test App

+

API

+swagger-ui + + \ No newline at end of file diff --git a/cursorpaging-examples/webapp-with-maven/src/test/java/io/vigier/cursorpaging/example/webapp/ExampleWebApplicationTests.java b/cursorpaging-examples/webapp-with-maven/src/test/java/io/vigier/cursorpaging/example/webapp/ExampleWebApplicationTests.java new file mode 100644 index 0000000..fb74113 --- /dev/null +++ b/cursorpaging-examples/webapp-with-maven/src/test/java/io/vigier/cursorpaging/example/webapp/ExampleWebApplicationTests.java @@ -0,0 +1,53 @@ +package io.vigier.cursorpaging.example.webapp; + +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.test.web.servlet.MockMvc; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@Testcontainers +@AutoConfigureMockMvc +@SpringBootTest +@Slf4j +class ExampleWebApplicationTests { + + @Container + @ServiceConnection + static PostgreSQLContainer postgresqlContainer = postgresContainer(); + + @Autowired + private MockMvc mockMvc; + + @SneakyThrows + private static PostgreSQLContainer postgresContainer() { + final PostgreSQLContainer container = new PostgreSQLContainer<>( + DockerImageName.parse( "postgres:latest" ) ); + container.start(); + log.info( "PostgreSQL exposed ports: {}", container.getExposedPorts() ); +// waitForConnectionReady( container ); + + return container; + } + + @Test + void contextLoads() { + + } + + @Test + void shouldAcceptEmptyCursor() throws Exception { + mockMvc.perform( get( "/api/v1/datarecord" ) ).andExpect( status().isOk() ); + } + +} diff --git a/cursorpaging-examples/webapp-with-maven/src/test/java/io/vigier/cursorpaging/example/webapp/api/controller/DataRecordControllerTest.java b/cursorpaging-examples/webapp-with-maven/src/test/java/io/vigier/cursorpaging/example/webapp/api/controller/DataRecordControllerTest.java new file mode 100644 index 0000000..6e0ef70 --- /dev/null +++ b/cursorpaging-examples/webapp-with-maven/src/test/java/io/vigier/cursorpaging/example/webapp/api/controller/DataRecordControllerTest.java @@ -0,0 +1,113 @@ +package io.vigier.cursorpaging.example.webapp.api.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.vigier.cursorpaging.example.webapp.api.model.mapper.DtoDataRecordMapper; +import io.vigier.cursorpaging.example.webapp.model.DataRecord; +import io.vigier.cursorpaging.example.webapp.model.DataRecord_; +import io.vigier.cursorpaging.example.webapp.repository.DataRecordRepository; +import io.vigier.cursorpaging.jpa.Attribute; +import io.vigier.cursorpaging.jpa.Order; +import io.vigier.cursorpaging.jpa.PageRequest; +import io.vigier.cursorpaging.jpa.api.DtoPageRequest; +import io.vigier.cursorpaging.jpa.serializer.Base64String; +import io.vigier.cursorpaging.jpa.serializer.EntitySerializer; +import java.util.Base64; +import java.util.UUID; +import lombok.extern.slf4j.Slf4j; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import static io.vigier.cursorpaging.example.webapp.api.controller.DataRecordController.PATH; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest( DataRecordController.class ) +//@ExtendWith( MockitoExtension.class ) +@Slf4j +class DataRecordControllerTest { + + private static final String PATH_COUNT = PATH + DataRecordController.COUNT; + private static final String CURSOR = "OW2T2rDudgONjtP04KHOUguDTrUGTCA7edByRquqlqus1TaSdcr1JwMLSwiDcW88hp7zSMqJrn9Q-W94P1GFGMuAQNeWfMZ5vfK6Mf712w"; + @Autowired + private MockMvc mockMvc; + + @MockBean + private DataRecordRepository dataRecordRepository; + + @MockBean + private DtoDataRecordMapper dtoDataRecordMapper; + + @MockBean + private EntitySerializer serializer; + + @Test + void shouldValidateMaxPageSize() throws Exception { + mockMvc.perform( get( PATH ).param( "pageSize", "1000" ) ) + .andExpect( status().isBadRequest() ) + .andExpect( jsonPath( "$.detail" ).value( "Validation failure" ) ); + } + + @Test + void shouldValidateCursor() throws Exception { + mockMvc.perform( get( PATH ) // + .param( "pageSize", "10" ) // + .param( "cursor", "%&/$RT5" ) ) + .andExpect( status().isBadRequest() ) + .andExpect( jsonPath( "$.detail" ).value( Matchers.containsString( "Failed to convert 'cursor'" ) ) ); + } + + @Test + void shouldReturnTotalCountWhenNoCursorProvided() throws Exception { + when( dataRecordRepository.count() ).thenReturn( 4711L ); + mockMvc.perform( get( PATH_COUNT ) ) + .andExpect( status().isOk() ) + .andExpect( jsonPath( "$.totalElements" ).value( 4711 ) ); + } + + @Test + void shouldReturnCountUsingCursor() throws Exception { + final var pageRequest = PageRequest.create( b -> b.asc( Attribute.of( "id", UUID.class ) ) ); + final var encoded = Base64.getUrlEncoder().encode( "TEST".getBytes( UTF_8 ) ); + when( serializer.toPageRequest( any( Base64String.class ) ) ).thenReturn( pageRequest ); + when( dataRecordRepository.count( pageRequest ) ).thenReturn( 4711L ); + + mockMvc.perform( get( PATH_COUNT ) // + .param( "cursor", new String( encoded, UTF_8 ) ) ) + .andExpect( status().isOk() ) + .andExpect( jsonPath( "$.totalElements" ).value( 4711 ) ); + } + + @Test + void shouldCreateNewCursorOnPost() throws Exception { + final var request = new DtoPageRequest().withFilterBy( DataRecord_.NAME, "Tango", "Bravo" ) + .withOrderBy( DataRecord_.NAME, Order.ASC ) + .withPageSize( 10 ); + final String json = new ObjectMapper().writeValueAsString( request ); + log.debug( "Json:, {}", json ); + + when( serializer.toBase64( any() ) ).thenReturn( new Base64String( CURSOR ) ); + + mockMvc.perform( post( PATH + "/page" ) // + .contentType( MediaType.APPLICATION_JSON ) // + .content( json ) ) // + .andExpect( status().isCreated() ) // + .andExpect( jsonPath( "$.orderBy.name" ).value( "ASC" ) ) + .andExpect( jsonPath( "$.orderBy.id" ).value( "ASC" ) ) + .andExpect( jsonPath( "$.filterBy.name" ).isArray() ) + .andExpect( jsonPath( "$.filterBy.name[0]" ).value( "Tango" ) ) + .andExpect( jsonPath( "$.filterBy.name[1]" ).value( "Bravo" ) ) + .andExpect( jsonPath( "$.pageSize" ).value( 10 ) ) // + .andExpect( jsonPath( "$._links.first.href" ).exists() ) + .andExpect( jsonPath( "$._links.first.href" ).value( Matchers.containsString( CURSOR ) ) ); + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index acf4d56..ef3965c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -26,6 +26,9 @@ dependencyResolutionManagement { rootProject.name = "spring-cursorpaging" include("cursorpaging-jpa", "cursorpaging-jpa-api") +include("cursorpaging-examples:webapp-with-maven") +//project("cursorpaging-example-webapp") +// .projectDir = file("cursorpaging-examples/webapp-with-maven") develocity { buildScan {