Skip to content

Commit 37dc83a

Browse files
author
bnasslahsen
committed
HAL representation (_embedded and _links) in definitions when application returning plain json. Fixes #870.
1 parent 0df5f2c commit 37dc83a

File tree

8 files changed

+246
-6
lines changed

8 files changed

+246
-6
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import io.swagger.v3.core.util.Json;
3131
import org.springdoc.hateoas.HateoasHalProvider;
3232

33+
import org.springframework.boot.autoconfigure.hateoas.HateoasProperties;
3334
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
3435
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule;
3536

@@ -49,7 +50,8 @@ public class DataRestHalProvider extends HateoasHalProvider {
4950
*
5051
* @param repositoryRestConfigurationOptional the repository rest configuration optional
5152
*/
52-
public DataRestHalProvider(Optional<RepositoryRestConfiguration> repositoryRestConfigurationOptional) {
53+
public DataRestHalProvider(Optional<RepositoryRestConfiguration> repositoryRestConfigurationOptional,Optional<HateoasProperties> hateoasPropertiesOptional) {
54+
super(hateoasPropertiesOptional);
5355
this.repositoryRestConfigurationOptional = repositoryRestConfigurationOptional;
5456
}
5557

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
4949
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
5050
import org.springframework.boot.autoconfigure.data.web.SpringDataWebProperties;
51+
import org.springframework.boot.autoconfigure.hateoas.HateoasProperties;
5152
import org.springframework.context.annotation.Bean;
5253
import org.springframework.context.annotation.Configuration;
5354
import org.springframework.context.annotation.Lazy;
@@ -101,14 +102,15 @@ DelegatingMethodParameterCustomizer delegatingMethodParameterCustomizer(Optional
101102
* Hal provider data rest hal provider.
102103
*
103104
* @param repositoryRestConfiguration the repository rest configuration
105+
* @param hateoasPropertiesOptional the hateoas properties optional
104106
* @return the data rest hal provider
105107
*/
106108
@Bean
107109
@ConditionalOnMissingBean
108110
@Primary
109111
@Lazy(false)
110-
DataRestHalProvider halProvider(Optional<RepositoryRestConfiguration> repositoryRestConfiguration) {
111-
return new DataRestHalProvider(repositoryRestConfiguration);
112+
DataRestHalProvider halProvider(Optional<RepositoryRestConfiguration> repositoryRestConfiguration,Optional<HateoasProperties> hateoasPropertiesOptional) {
113+
return new DataRestHalProvider(repositoryRestConfiguration,hateoasPropertiesOptional);
112114
}
113115

114116

springdoc-openapi-hateoas/src/main/java/org/springdoc/hateoas/HateoasHalProvider.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@
2020

2121
package org.springdoc.hateoas;
2222

23+
import java.util.Optional;
24+
2325
import javax.annotation.PostConstruct;
2426

2527
import io.swagger.v3.core.util.Json;
2628

29+
import org.springframework.boot.autoconfigure.hateoas.HateoasProperties;
2730
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule;
2831

2932
/**
@@ -32,6 +35,20 @@
3235
*/
3336
public class HateoasHalProvider {
3437

38+
/**
39+
* The Hateoas properties optional.
40+
*/
41+
private final Optional<HateoasProperties> hateoasPropertiesOptional;
42+
43+
/**
44+
* Instantiates a new Hateoas hal provider.
45+
*
46+
* @param hateoasPropertiesOptional the hateoas properties optional
47+
*/
48+
public HateoasHalProvider(Optional<HateoasProperties> hateoasPropertiesOptional) {
49+
this.hateoasPropertiesOptional = hateoasPropertiesOptional;
50+
}
51+
3552
/**
3653
* Init.
3754
*/
@@ -49,7 +66,9 @@ protected void init() {
4966
* @return the boolean
5067
*/
5168
public boolean isHalEnabled() {
52-
return true;
69+
return hateoasPropertiesOptional
70+
.map(HateoasProperties::getUseHalAsDefaultJsonMediaType)
71+
.orElse(true);
5372
}
5473

5574
}

springdoc-openapi-hateoas/src/main/java/org/springdoc/hateoas/SpringDocHateoasConfiguration.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
package org.springdoc.hateoas;
2222

23+
import java.util.Optional;
24+
2325
import com.fasterxml.jackson.core.JsonGenerator;
2426
import com.fasterxml.jackson.databind.SerializerProvider;
2527
import io.swagger.v3.core.util.Json;
@@ -32,6 +34,7 @@
3234
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
3335
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3436
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
37+
import org.springframework.boot.autoconfigure.hateoas.HateoasProperties;
3538
import org.springframework.context.annotation.Bean;
3639
import org.springframework.context.annotation.Configuration;
3740
import org.springframework.context.annotation.Lazy;
@@ -53,13 +56,14 @@ public class SpringDocHateoasConfiguration {
5356
/**
5457
* Hateoas hal provider hateoas hal provider.
5558
*
59+
* @param hateoasPropertiesOptional the hateoas properties optional
5660
* @return the hateoas hal provider
5761
*/
5862
@Bean
5963
@ConditionalOnMissingBean
6064
@Lazy(false)
61-
HateoasHalProvider hateoasHalProvider() {
62-
return new HateoasHalProvider();
65+
HateoasHalProvider hateoasHalProvider(Optional<HateoasProperties> hateoasPropertiesOptional) {
66+
return new HateoasHalProvider(hateoasPropertiesOptional);
6367
}
6468

6569
/**
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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.app6;
20+
21+
import test.org.springdoc.api.AbstractSpringDocTest;
22+
23+
import org.springframework.boot.autoconfigure.SpringBootApplication;
24+
import org.springframework.test.context.TestPropertySource;
25+
26+
@TestPropertySource(properties = "spring.hateoas.use-hal-as-default-json-media-type= false")
27+
public class SpringDocApp6Test extends AbstractSpringDocTest {
28+
29+
@SpringBootApplication
30+
static class SpringDocTestApp {
31+
}
32+
33+
34+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package test.org.springdoc.api.app6.controller;
2+
3+
import java.util.List;
4+
import java.util.stream.Collectors;
5+
import java.util.stream.Stream;
6+
7+
import io.swagger.v3.oas.annotations.Operation;
8+
import io.swagger.v3.oas.annotations.tags.Tag;
9+
import test.org.springdoc.api.app6.model.Item;
10+
11+
import org.springframework.hateoas.CollectionModel;
12+
import org.springframework.http.HttpStatus;
13+
import org.springframework.http.MediaType;
14+
import org.springframework.http.ResponseEntity;
15+
import org.springframework.web.bind.annotation.GetMapping;
16+
import org.springframework.web.bind.annotation.RequestMapping;
17+
import org.springframework.web.bind.annotation.RestController;
18+
19+
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
20+
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
21+
22+
@RestController
23+
@RequestMapping(value = "/v1/items", produces = MediaType.APPLICATION_JSON_VALUE)
24+
@Tag(name = "Item", description = "The Item API")
25+
public class ItemController {
26+
27+
List<Item> items = Stream.of(new Item("foo"), new Item("bar")).collect(Collectors.toList());
28+
29+
@Operation(summary = "Get all items")
30+
@GetMapping(produces = {MediaType.APPLICATION_JSON_VALUE})
31+
public ResponseEntity<CollectionModel<Item>> getAllItems() {
32+
33+
CollectionModel<Item> collection = CollectionModel.of(items);
34+
collection.add(linkTo(methodOn(ItemController.class).getAllItems()).withSelfRel().expand());
35+
36+
return new ResponseEntity<>(collection, HttpStatus.OK);
37+
}
38+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package test.org.springdoc.api.app6.model;
2+
3+
import org.springframework.hateoas.Link;
4+
import org.springframework.hateoas.RepresentationModel;
5+
6+
7+
public class Item extends RepresentationModel<Item> {
8+
9+
private String description;
10+
11+
public Item(String description) {
12+
this.description = description;
13+
}
14+
15+
public Item(Link initialLink, String description) {
16+
super(initialLink);
17+
this.description = description;
18+
}
19+
20+
public Item(Iterable<Link> initialLinks, String description) {
21+
super(initialLinks);
22+
this.description = description;
23+
}
24+
25+
public String getDescription() {
26+
return description;
27+
}
28+
29+
public void setDescription(String description) {
30+
this.description = description;
31+
}
32+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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+
"tags": [
14+
{
15+
"name": "Item",
16+
"description": "The Item API"
17+
}
18+
],
19+
"paths": {
20+
"/v1/items": {
21+
"get": {
22+
"tags": [
23+
"Item"
24+
],
25+
"summary": "Get all items",
26+
"operationId": "getAllItems",
27+
"responses": {
28+
"200": {
29+
"description": "OK",
30+
"content": {
31+
"application/json": {
32+
"schema": {
33+
"$ref": "#/components/schemas/CollectionModelItem"
34+
}
35+
}
36+
}
37+
}
38+
}
39+
}
40+
}
41+
},
42+
"components": {
43+
"schemas": {
44+
"CollectionModelItem": {
45+
"type": "object",
46+
"properties": {
47+
"links": {
48+
"type": "array",
49+
"items": {
50+
"$ref": "#/components/schemas/Link"
51+
}
52+
},
53+
"content": {
54+
"type": "array",
55+
"items": {
56+
"$ref": "#/components/schemas/Item"
57+
}
58+
}
59+
}
60+
},
61+
"Item": {
62+
"type": "object",
63+
"properties": {
64+
"description": {
65+
"type": "string"
66+
},
67+
"links": {
68+
"type": "array",
69+
"items": {
70+
"$ref": "#/components/schemas/Link"
71+
}
72+
}
73+
}
74+
},
75+
"Link": {
76+
"type": "object",
77+
"properties": {
78+
"rel": {
79+
"type": "string"
80+
},
81+
"href": {
82+
"type": "string"
83+
},
84+
"hreflang": {
85+
"type": "string"
86+
},
87+
"media": {
88+
"type": "string"
89+
},
90+
"title": {
91+
"type": "string"
92+
},
93+
"type": {
94+
"type": "string"
95+
},
96+
"deprecation": {
97+
"type": "string"
98+
},
99+
"profile": {
100+
"type": "string"
101+
},
102+
"name": {
103+
"type": "string"
104+
}
105+
}
106+
}
107+
}
108+
}
109+
}

0 commit comments

Comments
 (0)