Skip to content

Commit 3662e62

Browse files
author
bnasslahsen
committed
Merge branch 'fredrik-w-hal-collectionmodel-content-map'
2 parents 25303f8 + 1d448db commit 3662e62

File tree

5 files changed

+93
-35
lines changed

5 files changed

+93
-35
lines changed

springdoc-openapi-data-rest/src/main/java/org/springdoc/core/HalProvider.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020

2121
import javax.annotation.PostConstruct;
2222

23+
import io.swagger.v3.core.converter.ModelConverters;
2324
import io.swagger.v3.core.util.Json;
25+
import org.springdoc.core.hal.CollectionModelContentConverter;
2426

2527
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
2628
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule;
@@ -35,7 +37,10 @@ public HalProvider(RepositoryRestConfiguration repositoryRestConfiguration) {
3537

3638
@PostConstruct
3739
private void init(){
38-
if(repositoryRestConfiguration.useHalAsDefaultJsonMediaType())
40+
if(repositoryRestConfiguration.useHalAsDefaultJsonMediaType()) {
3941
Json.mapper().registerModule(new Jackson2HalModule());
42+
ModelConverters.getInstance()
43+
.addConverter(new CollectionModelContentConverter());
44+
}
4045
}
4146
}

springdoc-openapi-data-rest/src/main/java/org/springdoc/core/SpringDocDataRestConfiguration.java

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,21 @@
2424
import io.swagger.v3.core.converter.ModelConverters;
2525
import io.swagger.v3.core.converter.ResolvedSchema;
2626
import io.swagger.v3.core.util.Json;
27-
import io.swagger.v3.oas.annotations.media.Schema;
2827
import io.swagger.v3.oas.models.media.MapSchema;
2928
import io.swagger.v3.oas.models.media.ObjectSchema;
3029
import io.swagger.v3.oas.models.media.StringSchema;
3130
import org.springdoc.core.converters.Pageable;
3231
import org.springdoc.core.customizers.OpenApiCustomiser;
32+
import org.springdoc.core.hal.RepresentationModelLinksOASMixin;
3333

34+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
3435
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
3536
import org.springframework.context.annotation.Bean;
3637
import org.springframework.context.annotation.Configuration;
3738
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
3839
import org.springframework.hateoas.Link;
3940
import org.springframework.hateoas.Links;
4041
import org.springframework.hateoas.RepresentationModel;
41-
import org.springframework.hateoas.mediatype.hal.RepresentationModelMixin;
4242

4343
import static org.springdoc.core.Constants.SPRINGDOC_ENABLED;
4444
import static org.springdoc.core.SpringDocUtils.getConfig;
@@ -52,35 +52,36 @@ public class SpringDocDataRestConfiguration {
5252
.replaceWithClass(org.springframework.data.domain.PageRequest.class,Pageable.class);
5353
}
5454

55-
@Bean
56-
public HalProvider halProvider(RepositoryRestConfiguration repositoryRestConfiguration) {
57-
return new HalProvider(repositoryRestConfiguration);
58-
}
55+
@Configuration
56+
@ConditionalOnClass(RepositoryRestConfiguration.class)
57+
class HalProviderConfiguration {
5958

60-
/**
61-
* Registers an OpenApiCustomiser and a jackson mixin to ensure the definition of `Links` matches the serialized
62-
* output. This is done because the customer serializer converts the data to a map before serializing it.
63-
*
64-
* @see org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider)
65-
*/
66-
@Bean
67-
public OpenApiCustomiser linksSchemaCustomiser(RepositoryRestConfiguration repositoryRestConfiguration) {
68-
if (!repositoryRestConfiguration.useHalAsDefaultJsonMediaType()) {
69-
return openApi -> {};
70-
}
71-
Json.mapper().addMixIn(RepresentationModel.class, RepresentationModelLinksOASMixin.class);
72-
ResolvedSchema resolvedLinkSchema = ModelConverters.getInstance()
73-
.resolveAsResolvedSchema(new AnnotatedType(Link.class));
74-
return openApi -> openApi
75-
.schema("Link", resolvedLinkSchema.schema)
76-
.schema("Links", new MapSchema()
77-
.additionalProperties(new StringSchema())
78-
.additionalProperties(new ObjectSchema().$ref("#/components/schemas/Link")));
79-
}
59+
@Bean
60+
public HalProvider halProvider(RepositoryRestConfiguration repositoryRestConfiguration) {
61+
return new HalProvider(repositoryRestConfiguration);
62+
}
8063

81-
abstract static class RepresentationModelLinksOASMixin extends RepresentationModelMixin {
82-
@Override
83-
@Schema(ref = "#/components/schemas/Links")
84-
public abstract Links getLinks();
85-
}
64+
/**
65+
* Registers an OpenApiCustomiser and a jackson mixin to ensure the definition of `Links` matches the serialized
66+
* output. This is done because the customer serializer converts the data to a map before serializing it.
67+
*
68+
* @see org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider)
69+
*/
70+
@Bean
71+
public OpenApiCustomiser linksSchemaCustomiser(RepositoryRestConfiguration repositoryRestConfiguration) {
72+
if (!repositoryRestConfiguration.useHalAsDefaultJsonMediaType()) {
73+
return openApi -> {};
74+
}
75+
Json.mapper().addMixIn(RepresentationModel.class, RepresentationModelLinksOASMixin.class);
76+
77+
ResolvedSchema resolvedLinkSchema = ModelConverters.getInstance()
78+
.resolveAsResolvedSchema(new AnnotatedType(Link.class));
79+
80+
return openApi -> openApi
81+
.schema("Link", resolvedLinkSchema.schema)
82+
.schema("Links", new MapSchema()
83+
.additionalProperties(new StringSchema())
84+
.additionalProperties(new ObjectSchema().$ref("#/components/schemas/Link")));
85+
}
86+
}
8687
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.springdoc.core.hal;
2+
3+
import com.fasterxml.jackson.core.JsonGenerator;
4+
import com.fasterxml.jackson.databind.SerializerProvider;
5+
import com.fasterxml.jackson.databind.type.CollectionType;
6+
import io.swagger.v3.core.converter.AnnotatedType;
7+
import io.swagger.v3.core.converter.ModelConverter;
8+
import io.swagger.v3.core.converter.ModelConverterContext;
9+
import io.swagger.v3.oas.models.media.ArraySchema;
10+
import io.swagger.v3.oas.models.media.MapSchema;
11+
import io.swagger.v3.oas.models.media.Schema;
12+
import io.swagger.v3.oas.models.media.StringSchema;
13+
14+
import java.util.Collection;
15+
import java.util.Iterator;
16+
17+
/**
18+
* Override resolved schema as there is a custom serializer that converts the data to a map before serializing it.
19+
*
20+
* @see org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalResourcesSerializer
21+
* @see org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalResourcesSerializer#serialize(Collection, JsonGenerator, SerializerProvider)
22+
*/
23+
public class CollectionModelContentConverter implements ModelConverter {
24+
@Override
25+
public Schema<?> resolve(AnnotatedType type, ModelConverterContext context, Iterator<ModelConverter> chain) {
26+
if (chain.hasNext() && type != null && type.getType() instanceof CollectionType
27+
&& "_embedded".equalsIgnoreCase(type.getPropertyName())) {
28+
Schema<?> schema = chain.next().resolve(type, context, chain);
29+
if (schema instanceof ArraySchema) {
30+
return new MapSchema()
31+
.name("_embedded")
32+
.additionalProperties(new StringSchema())
33+
.additionalProperties(schema);
34+
}
35+
}
36+
return chain.hasNext() ? chain.next().resolve(type, context, chain) : null;
37+
}
38+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.springdoc.core.hal;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import org.springframework.hateoas.Links;
5+
import org.springframework.hateoas.mediatype.hal.RepresentationModelMixin;
6+
7+
public abstract class RepresentationModelLinksOASMixin extends RepresentationModelMixin {
8+
@Override
9+
@Schema(ref = "#/components/schemas/Links")
10+
public abstract Links getLinks();
11+
}

springdoc-openapi-data-rest/src/test/resources/results/app2.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,12 @@
172172
"type": "object",
173173
"properties": {
174174
"_embedded": {
175-
"type": "array",
176-
"items": {
177-
"$ref": "#/components/schemas/EntityModelEmployee"
175+
"type": "object",
176+
"additionalProperties": {
177+
"type": "array",
178+
"items": {
179+
"$ref": "#/components/schemas/EntityModelEmployee"
180+
}
178181
}
179182
},
180183
"_links": {

0 commit comments

Comments
 (0)