Skip to content

Commit 4fc7e91

Browse files
author
bnasslahsen
committed
Spring HATEOAS support. fixes springdoc#549
1 parent 2993578 commit 4fc7e91

File tree

7 files changed

+241
-32
lines changed

7 files changed

+241
-32
lines changed

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

+6-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
package org.springdoc.data.rest;
2020

21+
import java.util.Optional;
22+
2123
import javax.annotation.PostConstruct;
2224

2325
import io.swagger.v3.core.converter.ModelConverters;
@@ -29,15 +31,15 @@
2931

3032
public class HalProvider {
3133

32-
private final RepositoryRestConfiguration repositoryRestConfiguration;
34+
private RepositoryRestConfiguration repositoryRestConfiguration;
3335

34-
public HalProvider(RepositoryRestConfiguration repositoryRestConfiguration) {
35-
this.repositoryRestConfiguration = repositoryRestConfiguration;
36+
public HalProvider(Optional<RepositoryRestConfiguration> optionalRepositoryRestConfiguration) {
37+
optionalRepositoryRestConfiguration.ifPresent(repositoryRestConfiguration -> this.repositoryRestConfiguration =repositoryRestConfiguration);
3638
}
3739

3840
@PostConstruct
3941
private void init() {
40-
if (repositoryRestConfiguration.useHalAsDefaultJsonMediaType()) {
42+
if (repositoryRestConfiguration == null || repositoryRestConfiguration.useHalAsDefaultJsonMediaType()) {
4143
if (!Jackson2HalModule.isAlreadyRegisteredIn(Json.mapper()))
4244
Json.mapper().registerModule(new Jackson2HalModule());
4345
ModelConverters.getInstance()

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

+25-28
Original file line numberDiff line numberDiff line change
@@ -72,37 +72,34 @@ QuerydslPredicateOperationCustomizer queryDslQuerydslPredicateOperationCustomize
7272
}
7373
}
7474

75-
@ConditionalOnClass(RepositoryRestConfiguration.class)
76-
class HalProviderConfiguration {
7775

78-
@Bean
79-
@ConditionalOnMissingBean
80-
HalProvider halProvider(Optional<RepositoryRestConfiguration> repositoryRestConfiguration) {
81-
return repositoryRestConfiguration.isPresent() ? new HalProvider(repositoryRestConfiguration.get()) : null;
82-
}
76+
@Bean
77+
@ConditionalOnMissingBean
78+
HalProvider halProvider(Optional<RepositoryRestConfiguration> repositoryRestConfiguration) {
79+
return new HalProvider(repositoryRestConfiguration);
80+
}
8381

84-
/**
85-
* Registers an OpenApiCustomiser and a jackson mixin to ensure the definition of `Links` matches the serialized
86-
* output. This is done because the customer serializer converts the data to a map before serializing it.
87-
*
88-
* @see org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider)
89-
*/
90-
@Bean
91-
OpenApiCustomiser linksSchemaCustomiser(Optional<RepositoryRestConfiguration> repositoryRestConfiguration) {
92-
if (!repositoryRestConfiguration.isPresent() || !repositoryRestConfiguration.get().useHalAsDefaultJsonMediaType()) {
93-
return openApi -> {
94-
};
95-
}
96-
Json.mapper().addMixIn(RepresentationModel.class, RepresentationModelLinksOASMixin.class);
82+
/**
83+
* Registers an OpenApiCustomiser and a jackson mixin to ensure the definition of `Links` matches the serialized
84+
* output. This is done because the customer serializer converts the data to a map before serializing it.
85+
*
86+
* @see org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize(Links, JsonGenerator, SerializerProvider)
87+
*/
88+
@Bean
89+
OpenApiCustomiser linksSchemaCustomiser(Optional<RepositoryRestConfiguration> repositoryRestConfiguration) {
90+
if (!repositoryRestConfiguration.isPresent() || !repositoryRestConfiguration.get().useHalAsDefaultJsonMediaType()) {
91+
return openApi -> {
92+
};
93+
}
94+
Json.mapper().addMixIn(RepresentationModel.class, RepresentationModelLinksOASMixin.class);
9795

98-
ResolvedSchema resolvedLinkSchema = ModelConverters.getInstance()
99-
.resolveAsResolvedSchema(new AnnotatedType(Link.class).resolveAsRef(true));
96+
ResolvedSchema resolvedLinkSchema = ModelConverters.getInstance()
97+
.resolveAsResolvedSchema(new AnnotatedType(Link.class).resolveAsRef(true));
10098

101-
return openApi -> openApi
102-
.schema("Link", resolvedLinkSchema.schema)
103-
.schema("Links", new MapSchema()
104-
.additionalProperties(new StringSchema())
105-
.additionalProperties(new ObjectSchema().$ref("#/components/schemas/Link")));
106-
}
99+
return openApi -> openApi
100+
.schema("Link", resolvedLinkSchema.schema)
101+
.schema("Links", new MapSchema()
102+
.additionalProperties(new StringSchema())
103+
.additionalProperties(new ObjectSchema().$ref("#/components/schemas/Link")));
107104
}
108105
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package test.org.springdoc.api.app8;
2+
3+
public class Album {
4+
5+
private String title;
6+
private String description;
7+
private String releaseDate;
8+
9+
public Album(String title, String description, String releaseDate) {
10+
this.title = title;
11+
this.description = description;
12+
this.releaseDate = releaseDate;
13+
}
14+
15+
public String getTitle() {
16+
return title;
17+
}
18+
19+
public String getDescription() {
20+
return description;
21+
}
22+
23+
public String getReleaseDate() {
24+
return releaseDate;
25+
}
26+
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package test.org.springdoc.api.app8;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
import org.springframework.hateoas.EntityModel;
7+
import org.springframework.hateoas.Link;
8+
import org.springframework.hateoas.server.RepresentationModelAssembler;
9+
import org.springframework.stereotype.Component;
10+
11+
@Component
12+
public class AlbumModelAssembler implements RepresentationModelAssembler<Album, EntityModel<Album>> {
13+
14+
@Override
15+
public EntityModel<Album> toModel(Album entity) {
16+
List<Link> links = new ArrayList<>();
17+
return new EntityModel<>(entity, links);
18+
}
19+
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
*
3+
* * Copyright 2019-2020 the original author or authors.
4+
* *
5+
* * Licensed under the Apache License, Version 2.0 (the "License");
6+
* * you may not use this file except in compliance with the License.
7+
* * You may obtain a copy of the License at
8+
* *
9+
* * https://www.apache.org/licenses/LICENSE-2.0
10+
* *
11+
* * Unless required by applicable law or agreed to in writing, software
12+
* * distributed under the License is distributed on an "AS IS" BASIS,
13+
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* * See the License for the specific language governing permissions and
15+
* * limitations under the License.
16+
*
17+
*/
18+
19+
package test.org.springdoc.api.app8;
20+
21+
import test.org.springdoc.api.AbstractSpringDocTest;
22+
23+
import org.springframework.boot.autoconfigure.SpringBootApplication;
24+
25+
public class SpringDocApp8Test extends AbstractSpringDocTest {
26+
27+
@SpringBootApplication
28+
static class SpringDocTestApp {}
29+
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package test.org.springdoc.api.app8;
2+
3+
import java.util.Arrays;
4+
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.data.domain.Page;
7+
import org.springframework.data.domain.PageImpl;
8+
import org.springframework.data.web.PagedResourcesAssembler;
9+
import org.springframework.hateoas.PagedModel;
10+
import org.springframework.web.bind.annotation.GetMapping;
11+
import org.springframework.web.bind.annotation.RestController;
12+
13+
@RestController
14+
public class WebController {
15+
16+
@Autowired
17+
private AlbumModelAssembler albumModelAssembler;
18+
@Autowired
19+
private PagedResourcesAssembler pagedResourcesAssembler;
20+
21+
@GetMapping("/api/albums")
22+
public PagedModel<Album> getAllAlbums() {
23+
Album album1 = new Album("album-title-1", "album-description-1", "album-release-date-1");
24+
Album album2 = new Album("album-title-2", "album-description-2", "album-release-date-2");
25+
Page<Album> albumPage = new PageImpl<>(Arrays.asList(album1, album2));
26+
27+
return pagedResourcesAssembler.toModel(albumPage, albumModelAssembler);
28+
}
29+
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
{
2+
"openapi": "3.0.1",
3+
"info": {
4+
"title": "OpenAPI definition",
5+
"version": "v0"
6+
},
7+
"servers": [
8+
{
9+
"url": "http://localhost",
10+
"description": "Generated server url"
11+
}
12+
],
13+
"paths": {
14+
"/api/albums": {
15+
"get": {
16+
"tags": [
17+
"web-controller"
18+
],
19+
"operationId": "getAllAlbums",
20+
"responses": {
21+
"200": {
22+
"description": "default response",
23+
"content": {
24+
"*/*": {
25+
"schema": {
26+
"$ref": "#/components/schemas/PagedModelAlbum"
27+
}
28+
}
29+
}
30+
}
31+
}
32+
}
33+
}
34+
},
35+
"components": {
36+
"schemas": {
37+
"Album": {
38+
"type": "object",
39+
"properties": {
40+
"title": {
41+
"type": "string"
42+
},
43+
"description": {
44+
"type": "string"
45+
},
46+
"releaseDate": {
47+
"type": "string"
48+
}
49+
}
50+
},
51+
"Links": {
52+
"type": "object",
53+
"additionalProperties": {
54+
"$ref": "#/components/schemas/Link"
55+
}
56+
},
57+
"PageMetadata": {
58+
"type": "object",
59+
"properties": {
60+
"size": {
61+
"type": "integer",
62+
"format": "int64"
63+
},
64+
"totalElements": {
65+
"type": "integer",
66+
"format": "int64"
67+
},
68+
"totalPages": {
69+
"type": "integer",
70+
"format": "int64"
71+
},
72+
"number": {
73+
"type": "integer",
74+
"format": "int64"
75+
}
76+
}
77+
},
78+
"PagedModelAlbum": {
79+
"type": "object",
80+
"properties": {
81+
"_embedded": {
82+
"type": "object",
83+
"additionalProperties": {
84+
"type": "array",
85+
"items": {
86+
"$ref": "#/components/schemas/Album"
87+
}
88+
}
89+
},
90+
"_links": {
91+
"$ref": "#/components/schemas/Links"
92+
},
93+
"page": {
94+
"$ref": "#/components/schemas/PageMetadata"
95+
}
96+
}
97+
},
98+
"Link": {
99+
"$ref": "#/components/schemas/Link"
100+
}
101+
}
102+
}
103+
}

0 commit comments

Comments
 (0)