Skip to content

Commit

Permalink
Added test for POST endpoint, fix for DataRecordAttribute
Browse files Browse the repository at this point in the history
- Metamodel properties cannot be used here when entity-manager not present
- Added explicit dependency on spring-boot validation (error when missing)
- Docu update
  • Loading branch information
p3t committed May 13, 2024
1 parent 6817a5b commit c0b68d8
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 25 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/publish-githubpkg.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ jobs:
publish:

runs-on: ubuntu-latest

permissions:
contents: read
packages: write

outputs:
BUILD_VERSION: ${{ steps.release_version.outputs.BUILD_VERSION }}

steps:
- name: "Release Version"
id: release_version
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,13 @@ There are two dependencies:
</dependencies>
```

Note: Currently the library is only available on gitHub-packages: https://maven.pkg.github.com/p3t/spring-cursorpaging
Note: Currently the library is only available on gitHub-packages:

- Repository-URL: https://maven.pkg.github.com/p3t/spring-cursorpaging
- You need an personal-access token (classic) to read from any github-package
repo [see docu](https://docs.github.com/en/packages/learn-github-packages/introduction-to-github-packages#authenticating-to-github-packages)
- Add the token to your `~/.m2/settings.xml` or `~/.gradle/gradle.properties` file as password
- username is you github-user

## Generate the JPA meta-model

Expand Down
1 change: 1 addition & 0 deletions cursorpaging-jpa-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies {
implementation(project(":cursorpaging-jpa"))
implementation("com.google.protobuf:protobuf-java:4.26.1")
implementation("org.springframework:spring-core")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("jakarta.validation:jakarta.validation-api")
implementation("jakarta.persistence:jakarta.persistence-api:3.1.0")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,22 @@ public class DtoPageRequest {

@Min( 1 )
@Max( 100 )
private final int pageSize = 100;
private int pageSize = 100; // Must not be final!

public DtoPageRequest withPageSize( final int pageSize ) {
this.pageSize = pageSize;
return this;
}

public DtoPageRequest withOrderBy( final String name, final Order order ) {
orderBy.put( name, order );
return this;
}

public DtoPageRequest withFilterBy( final String name, final String... values ) {
filterBy.put( name, List.of( values ) );
return this;
}

public <T> PageRequest<T> toPageRequest( final Function<String, Attribute> attributeProvider ) {
return PageRequest.create( b -> {
Expand All @@ -43,7 +58,7 @@ public <T> PageRequest<T> toPageRequest( final Function<String, Attribute> attri
} );
}

public void addOrderByIfNotPresent( final String name, final Order order ) {
public void addOrderByIfAbsent( final String name, final Order order ) {
if ( !orderBy.containsKey( name ) ) {
orderBy.put( name, order );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ public static Attribute path( final SingleAttribute... path ) {
return new Attribute( Arrays.asList( path ) );
}


public static Attribute path( final String name1, final Class<?> type1, final String name2, final Class<?> type2 ) {
return new Attribute( List.of( SingleAttribute.of( name1, type1 ), SingleAttribute.of( name2, type2 ) ) );
}

public static Attribute path( final String name1, final Class<?> type1, final String name2, final Class<?> type2,
final String name3, final Class<?> type3 ) {
return new Attribute( List.of( SingleAttribute.of( name1, type1 ), SingleAttribute.of( name2, type2 ),
SingleAttribute.of( name3, type3 ) ) );
}

Comparable<?> valueOf( final Object entity ) {
Object result = entity;
for ( final SingleAttribute a : attributes ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,8 @@ public RepresentationModel<?> getCursor(
"pageSize": 100
}""" ) ) ) //
@RequestBody final DtoPageRequest request ) {
request.addOrderByIfNotPresent( DataRecord_.ID, Order.ASC );
final PageRequest<DataRecord> pageRequest = request.toPageRequest(
name -> DataRecordAttribute.valueOf( name.toUpperCase() ).getAttribute() );
request.addOrderByIfAbsent( DataRecord_.ID, Order.ASC );
final PageRequest<DataRecord> pageRequest = request.toPageRequest( DataRecordAttribute::forName );
return RepresentationModel.of( request )
.add( getLink( Optional.of( request.getPageSize() ), pageRequest, IanaLinkRelations.FIRST ) );
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
package io.vigier.cursorpaging.testapp.api.model;

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;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Getter
public enum DataRecordAttribute {
ID( Attribute.of( DataRecord_.id ) ),
NAME( Attribute.of( DataRecord_.name ) ),
CREATED_AT( Attribute.path( DataRecord_.auditInfo, AuditInfo_.createdAt ) ),
MODIFIED_AT( Attribute.path( DataRecord_.auditInfo, AuditInfo_.modifiedAt ) );
private final Attribute attribute;
// Not using the "lowercase" properties here, because there are tests where no EntityManager is instantiated
// (i.e. the MockMVC test), but this one is needed to populate them :-(

DataRecordAttribute( final Attribute attribute ) {
this.attribute = attribute;
}
ID( Attribute.of( DataRecord_.NAME, UUID.class ) ),
NAME( Attribute.of( DataRecord_.NAME, String.class ) ),
CREATED_AT( Attribute.path( DataRecord_.AUDIT_INFO, AuditInfo.class, AuditInfo_.CREATED_AT, Instant.class ) ),
MODIFIED_AT( Attribute.path( DataRecord_.AUDIT_INFO, AuditInfo.class, AuditInfo_.MODIFIED_AT, Instant.class ) );

private final Attribute attribute;

public Attribute getAttribute() {
return attribute;
public static Attribute forName( final String name ) {
return valueOf( name.toUpperCase() ).getAttribute();
}
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,43 @@
package io.vigier.cursorpaging.testapp.api.controller;

import static io.vigier.cursorpaging.testapp.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.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.fasterxml.jackson.databind.ObjectMapper;
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 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.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.testapp.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;

Expand Down Expand Up @@ -78,4 +86,28 @@ void shouldReturnCountUsingCursor() throws Exception {
.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 ) ) );
}
}

0 comments on commit c0b68d8

Please sign in to comment.