Skip to content

Commit 6d7a301

Browse files
committed
Hacking
1 parent 9797df4 commit 6d7a301

13 files changed

Lines changed: 746 additions & 32 deletions

File tree

server/pom.xml

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,97 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/ma
290290

291291
</profile>
292292

293+
<profile>
294+
295+
<id>documentation</id>
296+
297+
<activation>
298+
<activeByDefault>true</activeByDefault>
299+
</activation>
300+
301+
<properties>
302+
<snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
303+
</properties>
304+
305+
<dependencyManagement>
306+
<dependencies>
307+
<dependency>
308+
<groupId>org.springframework.cloud</groupId>
309+
<artifactId>spring-cloud-dependencies</artifactId>
310+
<version>2025.1.0</version>
311+
<type>pom</type>
312+
<scope>import</scope>
313+
</dependency>
314+
</dependencies>
315+
</dependencyManagement>
316+
317+
<dependencies>
318+
319+
<!-- Spring RESTDocs -->
320+
321+
<dependency>
322+
<groupId>org.springframework.restdocs</groupId>
323+
<artifactId>spring-restdocs-mockmvc</artifactId>
324+
<scope>test</scope>
325+
</dependency>
326+
327+
<dependency>
328+
<groupId>org.springframework.cloud</groupId>
329+
<artifactId>spring-cloud-contract-wiremock</artifactId>
330+
<scope>test</scope>
331+
</dependency>
332+
333+
</dependencies>
334+
335+
<build>
336+
<plugins>
337+
<plugin>
338+
<groupId>org.asciidoctor</groupId>
339+
<artifactId>asciidoctor-maven-plugin</artifactId>
340+
<version>3.2.0</version>
341+
<executions>
342+
<execution>
343+
<id>generate-docs</id>
344+
<phase>prepare-package</phase>
345+
<goals>
346+
<goal>process-asciidoc</goal>
347+
</goals>
348+
<configuration>
349+
<backend>html</backend>
350+
<doctype>book</doctype>
351+
<sourceDocumentName>index.adoc</sourceDocumentName>
352+
<attributes>
353+
<snippets>${snippetsDirectory}</snippets>
354+
</attributes>
355+
</configuration>
356+
</execution>
357+
</executions>
358+
</plugin>
359+
<plugin>
360+
<artifactId>maven-resources-plugin</artifactId>
361+
<executions>
362+
<execution>
363+
<id>copy-resources</id>
364+
<phase>prepare-package</phase>
365+
<goals>
366+
<goal>copy-resources</goal>
367+
</goals>
368+
<configuration>
369+
<outputDirectory>${project.build.outputDirectory}/static/docs</outputDirectory>
370+
<resources>
371+
<resource>
372+
<directory>${project.build.directory}/generated-docs</directory>
373+
</resource>
374+
</resources>
375+
</configuration>
376+
377+
</execution>
378+
</executions>
379+
</plugin>
380+
</plugins>
381+
</build>
382+
</profile>
383+
293384
<!-- General AOT support -->
294385

295386
<profile>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
= Spring RESTBucks API Guide
2+
Oliver Gieke;
3+
:doctype: book
4+
:icons: font
5+
:source-highlighter: highlightjs
6+
:toc: left
7+
:toclevels: 4
8+
:sectlinks:
9+
:snippets: ../../../target/generated-snippets/
10+
11+
[[overview]]
12+
= Overview
13+
14+
[[overview-http-verbs]]
15+
== HTTP verbs
16+
17+
RESTful notes tries to adhere as closely as possible to standard HTTP and REST conventions in its
18+
use of HTTP verbs.
19+
20+
|===
21+
| Verb | Usage
22+
23+
| `GET`
24+
| Used to retrieve a resource
25+
26+
| `POST`
27+
| Used to create a new resource
28+
29+
| `PATCH`
30+
| Used to update an existing resource, including partial updates
31+
32+
| `DELETE`
33+
| Used to delete an existing resource
34+
|===
35+
36+
[[overview-http-status-codes]]
37+
== HTTP status codes
38+
39+
RESTful notes tries to adhere as closely as possible to standard HTTP and REST conventions in its
40+
use of HTTP status codes.
41+
42+
|===
43+
| Status code | Usage
44+
45+
| `200 OK`
46+
| The request completed successfully
47+
48+
| `201 Created`
49+
| A new resource has been created successfully. The resource's URI is available from the response's
50+
`Location` header
51+
52+
| `204 No Content`
53+
| An update to an existing resource has been applied successfully
54+
55+
| `400 Bad Request`
56+
| The request was malformed. The response body will include an error providing further information
57+
58+
| `404 Not Found`
59+
| The requested resource did not exist
60+
|===
61+
62+
[[overview-hypermedia]]
63+
== Hypermedia
64+
65+
RESTful Notes uses hypermedia and resources include links to other resources in their
66+
responses. Responses are in http://stateless.co/hal_specification.html[Hypertext Application
67+
from resource to resource.
68+
Language (HAL)] format. Links can be found beneath the `_links` key. Users of the API should
69+
not create URIs themselves, instead they should use the above-described links to navigate
70+
71+
[[link-relations]]
72+
= Link relations
73+
74+
|===
75+
|Relation|Description
76+
77+
|`restbucks:payment`
78+
|A link indicating an order can be paid. Find more information about that in <<usecases.payorder>>.
79+
80+
|===
81+
82+
include::usecases.adoc[]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
= The orders resource
2+
3+
Lists all orders.
4+
5+
include::{snippets}/orders/1/curl-request.adoc[]
6+
include::{snippets}/orders/1/http-request.adoc[]
7+
include::{snippets}/orders/1/http-response.adoc[]
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
[[usecases]]
2+
= Use cases
3+
4+
[[usecases.payorder]]
5+
== Submitting a payment
6+
7+
If an order can be paid, a `payment` link will be included in the representation of it.
8+
9+
=== Detecting payability
10+
11+
include::{snippets}/pay-order/access-order/curl-request.adoc[]
12+
include::{snippets}/pay-order/access-order/http-request.adoc[]
13+
include::{snippets}/pay-order/access-order/http-response.adoc[]
14+
include::{snippets}/pay-order/access-order/links.adoc[]
15+
16+
[[payment]]
17+
=== Issuing the payment
18+
19+
We can now follow the link submitting a PUT request with the credit card number as payload.
20+
21+
include::{snippets}/pay-order/trigger-payment/curl-request.adoc[]
22+
include::{snippets}/pay-order/trigger-payment/http-request.adoc[]
23+
24+
The service will answer with detailed payment information.
25+
26+
include::{snippets}/pay-order/trigger-payment/http-response.adoc[]
27+
include::{snippets}/pay-order/trigger-payment/response-fields.adoc[]
28+
include::{snippets}/pay-order/trigger-payment/links.adoc[]
29+
30+
[[receipt]]
31+
=== Concluding the order
32+
33+
If an order has been completed, a `receipt` link will be present in its representation.
34+
35+
include::{snippets}/pay-order/order-prepared/http-response.adoc[]
36+
include::{snippets}/pay-order/order-prepared/links.adoc[]
37+
38+
The receipt can then be accessed by issuing a GET request:
39+
40+
include::{snippets}/pay-order/access-receipt/curl-request.adoc[]
41+
include::{snippets}/pay-order/access-receipt/http-request.adoc[]
42+
include::{snippets}/pay-order/access-receipt/http-response.adoc[]
43+
44+
Issuing a DELETE request will conclude the order.
45+
46+
include::{snippets}/pay-order/take-receipt/curl-request.adoc[]
47+
include::{snippets}/pay-order/take-receipt/http-request.adoc[]
48+
include::{snippets}/pay-order/take-receipt/http-response.adoc[]

server/src/main/java/de/odrotbohm/restbucks/Restbucks.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
2121
import org.springframework.context.annotation.Bean;
2222
import org.springframework.hateoas.UriTemplate;
23+
import org.springframework.hateoas.config.EnableHypermediaSupport;
24+
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
2325
import org.springframework.hateoas.mediatype.hal.CurieProvider;
2426
import org.springframework.hateoas.mediatype.hal.DefaultCurieProvider;
2527
import org.springframework.modulith.Modulithic;
@@ -37,13 +39,14 @@
3739
@ConfigurationPropertiesScan
3840
@Modulithic(sharedModules = "core")
3941
@SpringBootApplication
42+
@EnableHypermediaSupport(type = { HypermediaType.HAL, HypermediaType.HAL_FORMS })
4043
public class Restbucks {
4144

4245
public static String CURIE_NAMESPACE = "restbucks";
4346

4447
@Bean
4548
CurieProvider curieProvider() {
46-
return new DefaultCurieProvider(CURIE_NAMESPACE, UriTemplate.of("/docs/{rel}.html"));
49+
return new DefaultCurieProvider(CURIE_NAMESPACE, UriTemplate.of("/docs/{rel}"));
4750
}
4851

4952
/**
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package de.odrotbohm.restbucks.core.web;
2+
3+
import org.springframework.http.MediaType;
4+
import org.springframework.stereotype.Controller;
5+
import org.springframework.web.bind.annotation.GetMapping;
6+
import org.springframework.web.bind.annotation.PathVariable;
7+
8+
@Controller
9+
class DocsController {
10+
11+
@GetMapping(path = "/docs/{rel:^\\w+$}", produces = MediaType.TEXT_HTML_VALUE)
12+
String resolveDocs(@PathVariable String rel) {
13+
return "redirect:index.html#".concat(rel);
14+
}
15+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package de.odrotbohm.restbucks.payment.web;
2+
3+
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
4+
5+
import org.springframework.hateoas.Link;
6+
import org.springframework.hateoas.MediaTypes;
7+
import org.springframework.hateoas.RepresentationModel;
8+
import org.springframework.http.ResponseEntity;
9+
import org.springframework.web.bind.annotation.GetMapping;
10+
11+
// @Controller
12+
class PaymentMetadataController {
13+
14+
@GetMapping(path = "/docs/payment", produces = MediaTypes.HAL_FORMS_JSON_VALUE)
15+
ResponseEntity<RepresentationModel<?>> getPaymentMetadata() {
16+
17+
Link selfLink = linkTo(methodOn(PaymentMetadataController.class).getPaymentMetadata()).withSelfRel()
18+
.andAffordance(afford(methodOn(PaymentController.class).submitPayment(null, null)));
19+
20+
return ResponseEntity.ok(new RepresentationModel<>(selfLink));
21+
}
22+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package de.odrotbohm.restbucks;
17+
18+
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*;
19+
import static org.springframework.restdocs.operation.preprocess.Preprocessors.*;
20+
21+
import org.junit.jupiter.api.BeforeEach;
22+
import org.junit.jupiter.api.extension.ExtendWith;
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.boot.test.context.SpringBootTest;
25+
import org.springframework.restdocs.RestDocumentationContextProvider;
26+
import org.springframework.restdocs.RestDocumentationExtension;
27+
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
28+
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
29+
import org.springframework.restdocs.snippet.Snippet;
30+
import org.springframework.test.web.servlet.MockMvc;
31+
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
32+
import org.springframework.transaction.annotation.Transactional;
33+
import org.springframework.web.context.WebApplicationContext;
34+
35+
/**
36+
* @author Oliver Gierke
37+
*/
38+
@ExtendWith(RestDocumentationExtension.class)
39+
@Transactional
40+
@SpringBootTest
41+
public abstract class AbstractDocumentation {
42+
43+
@Autowired WebApplicationContext context;
44+
45+
protected MockMvc mockMvc;
46+
47+
@BeforeEach
48+
protected void setUp(RestDocumentationContextProvider restDocumentation) {
49+
50+
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) //
51+
.apply(documentationConfiguration(restDocumentation)) //
52+
.build();
53+
}
54+
55+
protected RestDocumentationResultHandler document(String name, Snippet... snippets) {
56+
return MockMvcRestDocumentation.document(name.concat("/{step}"), preprocessResponse(maskLinks("…")), snippets);
57+
}
58+
}

0 commit comments

Comments
 (0)