Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ public EndpointTransferMapper() {
registerMapper(new PageableMapper());
registerMapper(new UUIDMapper());
registerMapper(new PageMapper());
registerMapper(new SliceMapper());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,56 @@
*/
package com.vaadin.hilla.endpointransfermapper;

import java.util.List;
import java.util.ArrayList;

import com.vaadin.hilla.endpointransfermapper.EndpointTransferMapper.Mapper;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import com.vaadin.hilla.mappedtypes.Page;

/**
* A mapper between {@link Page} and {@link List}.
* A mapper between {@link org.springframework.data.domain.Page} and
* {@link Page}.
*/
public class PageMapper implements Mapper<Page<?>, List<?>> {
public class PageMapper
implements Mapper<org.springframework.data.domain.Page<?>, Page<?>> {

private SortMapper sortMapper = new SortMapper();

@Override
public Class<? extends Page<?>> getEndpointType() {
return (Class) Page.class;
@SuppressWarnings("unchecked")
public Class<? extends org.springframework.data.domain.Page<?>> getEndpointType() {
return (Class<? extends org.springframework.data.domain.Page<?>>) (Class<?>) org.springframework.data.domain.Page.class;
}

@Override
public Class<? extends List<?>> getTransferType() {
return (Class) List.class;
@SuppressWarnings("unchecked")
public Class<? extends Page<?>> getTransferType() {
return (Class<? extends Page<?>>) (Class<?>) Page.class;
}

@Override
public List<?> toTransferType(Page<?> page) {
return page.getContent();
public Page<?> toTransferType(
org.springframework.data.domain.Page<?> page) {
Page<Object> transferPage = new Page<>();
transferPage.setContent(new ArrayList<>(page.getContent()));
transferPage.setSort(sortMapper.toTransferType(page.getSort()));
transferPage.setLast(page.isLast());
transferPage.setTotalPages(page.getTotalPages());
transferPage.setTotalElements(page.getTotalElements());
transferPage.setFirst(page.isFirst());
transferPage.setNumberOfElements(page.getNumberOfElements());
transferPage.setSize(page.getSize());
transferPage.setNumber(page.getNumber());
transferPage.setHasContent(page.hasContent());
transferPage.setHasNext(page.hasNext());
transferPage.setHasPrevious(page.hasPrevious());
transferPage.setEmpty(page.isEmpty());
return transferPage;
}

@Override
public Page<?> toEndpointType(List<?> list) {
return new PageImpl<>(list);
public org.springframework.data.domain.Page<?> toEndpointType(
Page<?> transferPage) {
throw new UnsupportedOperationException(
"Cannot create a Page from a transfer Page. Please create endpoints that accept Pageable as a parameter instead of returning a Page");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 2000-2025 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* 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.
*/
package com.vaadin.hilla.endpointransfermapper;

import java.util.ArrayList;

import com.vaadin.hilla.endpointransfermapper.EndpointTransferMapper.Mapper;
import com.vaadin.hilla.mappedtypes.Slice;

/**
* A mapper between {@link org.springframework.data.domain.Slice} and
* {@link Slice}.
*/
public class SliceMapper
implements Mapper<org.springframework.data.domain.Slice<?>, Slice<?>> {

private SortMapper sortMapper = new SortMapper();

@Override
@SuppressWarnings("unchecked")
public Class<? extends org.springframework.data.domain.Slice<?>> getEndpointType() {
return (Class<? extends org.springframework.data.domain.Slice<?>>) (Class<?>) org.springframework.data.domain.Slice.class;
}

@Override
@SuppressWarnings("unchecked")
public Class<? extends Slice<?>> getTransferType() {
return (Class<? extends Slice<?>>) (Class<?>) Slice.class;
}

@Override
public Slice<?> toTransferType(
org.springframework.data.domain.Slice<?> slice) {
Slice<Object> transferSlice = new Slice<>();
transferSlice.setContent(new ArrayList<>(slice.getContent()));
transferSlice.setSort(sortMapper.toTransferType(slice.getSort()));
transferSlice.setLast(slice.isLast());
transferSlice.setFirst(slice.isFirst());
transferSlice.setNumberOfElements(slice.getNumberOfElements());
transferSlice.setSize(slice.getSize());
transferSlice.setNumber(slice.getNumber());
transferSlice.setHasContent(slice.hasContent());
transferSlice.setHasNext(slice.hasNext());
transferSlice.setHasPrevious(slice.hasPrevious());
transferSlice.setEmpty(slice.isEmpty());
return transferSlice;
}

@Override
public org.springframework.data.domain.Slice<?> toEndpointType(
Slice<?> transferSlice) {
throw new UnsupportedOperationException(
"Cannot create a Slice from a transfer Slice. Please create endpoints that accept Pageable as a parameter instead of returning a Slice");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.NullHandling;

Expand All @@ -29,8 +31,10 @@ public void getTransferTypeClass_works() {
.getTransferType(AbstractPageRequest.class));
Assert.assertEquals(Pageable.class,
endpointTransferMapper.getTransferType(PageRequest.class));
Assert.assertEquals(List.class,
Assert.assertEquals(com.vaadin.hilla.mappedtypes.Page.class,
endpointTransferMapper.getTransferType(Page.class));
Assert.assertEquals(com.vaadin.hilla.mappedtypes.Slice.class,
endpointTransferMapper.getTransferType(Slice.class));
Assert.assertEquals(String.class,
endpointTransferMapper.getTransferType(UUID.class));
Assert.assertNull(
Expand All @@ -46,8 +50,10 @@ public void getTransferTypeString_works() {
// When defining the methods, the defined classes are used to we do not
// need to
// support e.g. AbstractPageRequest and PageRequest here
Assert.assertEquals(List.class.getName(),
Assert.assertEquals(com.vaadin.hilla.mappedtypes.Page.class.getName(),
endpointTransferMapper.getTransferType(Page.class.getName()));
Assert.assertEquals(com.vaadin.hilla.mappedtypes.Slice.class.getName(),
endpointTransferMapper.getTransferType(Slice.class.getName()));
Assert.assertEquals(String.class.getName(),
endpointTransferMapper.getTransferType(UUID.class.getName()));
Assert.assertNull(endpointTransferMapper
Expand Down Expand Up @@ -144,22 +150,93 @@ public void uuid_fromTransferType() {
}

@Test
public void page_toTransferType() {
public void page_toTransferType_returnsMappedPage() {
List<String> content = new ArrayList<>();
content.add("First");
content.add("Second");
content.add("Third");

org.springframework.data.domain.Pageable pageable = PageRequest.of(1,
3);
Page<String> p = new PageImpl<>(content, pageable, 10);

com.vaadin.hilla.mappedtypes.Page<?> mappedPage = (com.vaadin.hilla.mappedtypes.Page<?>) endpointTransferMapper
.toTransferType(p);

Assert.assertEquals(content, mappedPage.getContent());
Assert.assertEquals(3, mappedPage.getNumberOfElements());
Assert.assertEquals(1, mappedPage.getNumber());
Assert.assertEquals(3, mappedPage.getSize());
Assert.assertFalse(mappedPage.isFirst());
Assert.assertFalse(mappedPage.isLast());
Assert.assertTrue(mappedPage.isHasNext());
Assert.assertTrue(mappedPage.isHasPrevious());
Assert.assertTrue(mappedPage.isHasContent());
Assert.assertFalse(mappedPage.isEmpty());
Assert.assertEquals(10, mappedPage.getTotalElements());
Assert.assertEquals(4, mappedPage.getTotalPages());
}

@Test
public void page_fromTransferType_throwsUnsupportedOperation() {
com.vaadin.hilla.mappedtypes.Page<String> mappedPage = new com.vaadin.hilla.mappedtypes.Page<>();
List<String> content = new ArrayList<>();
content.add("First");
content.add("Second");
mappedPage.setContent(content);

try {
endpointTransferMapper.toEndpointType(mappedPage, Page.class);
Assert.fail("Expected UnsupportedOperationException");
} catch (UnsupportedOperationException e) {
// Page is unidirectional
Assert.assertTrue(e.getMessage()
.contains("Cannot create a Page from a transfer Page"));
}
}

Page p = new PageImpl<>(content);
List l = (List) endpointTransferMapper.toTransferType(p);
Assert.assertEquals(content, l);
@Test
public void slice_toTransferType_returnsMappedSlice() {
List<String> content = new ArrayList<>();
content.add("First");
content.add("Second");
content.add("Third");

// Create a middle slice that has next but not previous
org.springframework.data.domain.Pageable pageable = PageRequest.of(1,
3);
Slice<String> s = new SliceImpl<>(content, pageable, true);

com.vaadin.hilla.mappedtypes.Slice<?> mappedSlice = (com.vaadin.hilla.mappedtypes.Slice<?>) endpointTransferMapper
.toTransferType(s);

Assert.assertEquals(content, mappedSlice.getContent());
Assert.assertEquals(3, mappedSlice.getNumberOfElements());
Assert.assertEquals(1, mappedSlice.getNumber());
Assert.assertEquals(3, mappedSlice.getSize());
Assert.assertFalse(mappedSlice.isFirst());
Assert.assertFalse(mappedSlice.isLast());
Assert.assertTrue(mappedSlice.isHasNext());
Assert.assertTrue(mappedSlice.isHasPrevious());
Assert.assertTrue(mappedSlice.isHasContent());
Assert.assertFalse(mappedSlice.isEmpty());
}

@Test
public void page_fromTransferType() {
List<String> incoming = new ArrayList<>();
incoming.add("First");
incoming.add("Second");
Page p = endpointTransferMapper.toEndpointType(incoming, Page.class);
Assert.assertEquals(incoming, p.getContent());
public void slice_fromTransferType_throwsUnsupportedOperation() {
com.vaadin.hilla.mappedtypes.Slice<String> mappedSlice = new com.vaadin.hilla.mappedtypes.Slice<>();
List<String> content = new ArrayList<>();
content.add("First");
content.add("Second");
mappedSlice.setContent(content);

try {
endpointTransferMapper.toEndpointType(mappedSlice, Slice.class);
Assert.fail("Expected UnsupportedOperationException");
} catch (UnsupportedOperationException e) {
// Slice is unidirectional
Assert.assertTrue(e.getMessage()
.contains("Cannot create a Slice from a transfer Slice"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.jspecify.annotations.NonNull;

import com.vaadin.hilla.mappedtypes.Order;
import com.vaadin.hilla.mappedtypes.Page;
import com.vaadin.hilla.mappedtypes.Pageable;
import com.vaadin.hilla.mappedtypes.Sort;
import com.vaadin.hilla.parser.core.AbstractPlugin;
Expand All @@ -33,13 +34,14 @@
import com.vaadin.hilla.runtime.transfertypes.Signal;
import com.vaadin.hilla.runtime.transfertypes.ValueSignal;
import com.vaadin.hilla.transfertypes.annotations.FromModule;
import com.vaadin.hilla.transfertypes.annotations.ModelFromModule;

public final class TransferTypesPlugin
extends AbstractPlugin<PluginConfiguration> {
private static final Map<String, Class<?>> classMap = new HashMap<>();

static {
classMap.put("org.springframework.data.domain.Page", List.class);
classMap.put("org.springframework.data.domain.Page", Page.class);
classMap.put("org.springframework.data.domain.Pageable",
Pageable.class);
classMap.put("org.springframework.data.domain.Sort$Order", Order.class);
Expand Down Expand Up @@ -100,6 +102,31 @@ public void exit(NodePath<?> nodePath) {

schema.addExtension("x-from-module", fromModule);
});

cls.getAnnotations().stream()
.filter((model) -> model.getName()
.equals(ModelFromModule.class.getName()))
.findFirst().ifPresent((annotationModel) -> {
var annotation = (ModelFromModule) annotationModel
.get();
var namedSpecifier = annotation.namedSpecifier();
var defaultSpecifier = annotation
.defaultSpecifier();

var modelMap = new HashMap<String, Object>();
modelMap.put("module", annotation.module());

if (!namedSpecifier.isBlank()) {
modelMap.put("named", namedSpecifier);
}

if (!defaultSpecifier.isBlank()) {
modelMap.put("default", defaultSpecifier);
}

schema.addExtension("x-model-from-module",
modelMap);
});
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;

@Endpoint
Expand All @@ -14,11 +15,23 @@ public Page<String> getPage() {
return null;
}

public Slice<String> getSlice() {
return null;
}

public Pageable getPageable() {
return null;
}

public Sort getSort() {
return null;
}

public Custom getCustom() {
return null;
}

public static class Custom {
public Page page;
}
}
Loading
Loading