1818import com .contentgrid .appserver .rest .links .ContentGridLinkRelations ;
1919import com .contentgrid .hateoas .spring .pagination .SlicedResourcesAssembler ;
2020import com .contentgrid .hateoas .spring .server .RepresentationModelContextAssembler ;
21+ import java .util .Collection ;
22+ import java .util .List ;
2123import java .util .Map ;
2224import java .util .Optional ;
2325import lombok .RequiredArgsConstructor ;
2426import lombok .With ;
2527import org .springframework .hateoas .CollectionModel ;
2628import org .springframework .hateoas .IanaLinkRelations ;
2729import org .springframework .hateoas .Link ;
30+ import org .springframework .hateoas .LinkRelation ;
2831import org .springframework .hateoas .PagedModel ;
2932import org .springframework .hateoas .PagedModel .PageMetadata ;
3033import org .springframework .hateoas .server .RepresentationModelAssembler ;
34+ import org .springframework .hateoas .server .core .EmbeddedWrapper ;
3135import org .springframework .http .HttpMethod ;
3236import org .springframework .lang .NonNull ;
3337import org .springframework .stereotype .Component ;
@@ -76,7 +80,13 @@ public CollectionModel<EntityDataRepresentationModel> toSlicedModel(ResultSlice
7680 var pageMetadata = getPageMetadata (slice );
7781
7882 // Add pageMetadata to slicedModel by wrapping it in a PagedModel
79- return PagedModel .of (slicedModel .getContent (), pageMetadata , slicedModel .getLinks ());
83+ return PagedModel .of (wrap (slicedModel .getContent ()), pageMetadata , slicedModel .getLinks ());
84+ }
85+
86+ private Collection <EntityDataRepresentationModel > wrap (Collection <EntityDataRepresentationModel > content ) {
87+ // Yes, this is casting to a type that it really isn't, but that's the only way to make spring hateoas
88+ // properly make this object always present and have an 'item' linkrel
89+ return (Collection )List .of (new EntityDataCollectionRepresentationModelEmbeddedWrapper (content ));
8090 }
8191
8292 @ Override
@@ -85,7 +95,8 @@ public CollectionModel<EntityDataRepresentationModel> toCollectionModel(Iterable
8595 if (entities instanceof ResultSlice slice ) {
8696 return toSlicedModel (slice , context );
8797 }
88- var result = RepresentationModelContextAssembler .super .toCollectionModel (entities , context );
98+ var result = RepresentationModelContextAssembler .super .toCollectionModel (entities , context )
99+ .withFallbackType (EntityDataRepresentationModel .class );
89100 result .add (getCollectionSelfLink (context ))
90101 .add (getEntityProfileLink (context ));
91102 return result ;
@@ -152,4 +163,35 @@ HalFormsTemplateGenerator templateGenerator() {
152163 return new HalFormsTemplateGenerator (application , userLocales , linkFactoryProvider );
153164 }
154165 }
166+
167+ @ RequiredArgsConstructor
168+ private static class EntityDataCollectionRepresentationModelEmbeddedWrapper implements EmbeddedWrapper {
169+
170+ private final Collection <EntityDataRepresentationModel > contents ;
171+
172+ @ Override
173+ public Optional <LinkRelation > getRel () {
174+ return Optional .of (IanaLinkRelations .ITEM );
175+ }
176+
177+ @ Override
178+ public boolean hasRel (LinkRelation rel ) {
179+ return IanaLinkRelations .ITEM .isSameAs (rel );
180+ }
181+
182+ @ Override
183+ public boolean isCollectionValue () {
184+ return true ;
185+ }
186+
187+ @ Override
188+ public Object getValue () {
189+ return contents ;
190+ }
191+
192+ @ Override
193+ public Class <?> getRelTargetType () {
194+ return EntityDataRepresentationModel .class ;
195+ }
196+ }
155197}
0 commit comments