Skip to content

Commit b53b76b

Browse files
committed
final commit for blog post
1 parent 5432eec commit b53b76b

File tree

13 files changed

+415
-275
lines changed

13 files changed

+415
-275
lines changed

README.md

+25-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
1-
# API first Spring Boot Service (WIP)
21

3-
This Spring Boot Service will be created by the API first approach and tested with [Karate](https://intuit.github.io/karate/). Due to missing capability of OpenAPI Generator in Version 5.0.0 the direct download of the API spec is not possible at the moment. So we will rely on using [Gradle Download Task](https://github.com/michel-kraemer/gradle-download-task). There is also already a [bug report](https://github.com/OpenAPITools/openapi-generator/issues/8255) written by [joschi](https://github.com/joschi). And there is an open [feature request](https://github.com/OpenAPITools/openapi-generator/issues/9083) for OpenAPI Generator to support OpenAPI Spec 3.1
2+
# OpenAPI generated API stub
43

5-
At the moment the blog post can be finalized with the workaround in mind. Also a good idea to test the "update feature" on the codecentric blog next year.
4+
Spring Framework stub
5+
6+
7+
## Overview
8+
This code was generated by the [OpenAPI Generator](https://openapi-generator.tech) project.
9+
By using the [OpenAPI-Spec](https://openapis.org), you can easily generate an API stub.
10+
This is an example of building API stub interfaces in Java using the Spring framework.
11+
12+
The stubs generated can be used in your existing Spring-MVC or Spring-Boot application to create controller endpoints
13+
by adding ```@Controller``` classes that implement the interface. Eg:
14+
```java
15+
@Controller
16+
public class PetController implements PetApi {
17+
// implement all PetApi methods
18+
}
19+
```
20+
21+
You can also use the interface to create [Spring-Cloud Feign clients](http://projects.spring.io/spring-cloud/spring-cloud.html#spring-cloud-feign-inheritance).Eg:
22+
```java
23+
@FeignClient(name="pet", url="http://petstore.swagger.io/v2")
24+
public interface PetClient extends PetApi {
25+
26+
}
27+
```

build.gradle

+18-48
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
plugins {
22
id 'org.springframework.boot' version '2.3.5.RELEASE'
33
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
4-
id 'org.openapi.generator' version '5.1.1'
54
id 'de.undercouch.download' version '4.1.1'
6-
id 'org.asciidoctor.convert' version '1.5.8'
75
id 'org.jetbrains.gradle.plugin.idea-ext' version '0.5'
6+
id 'io.openapiprocessor.openapi-processor' version '2021.3'
87
id 'java'
98
}
109

@@ -26,28 +25,19 @@ repositories {
2625
mavenCentral()
2726
}
2827

29-
ext {
30-
set('snippetsDir', file("build/generated-snippets"))
31-
}
3228

3329
dependencies {
3430
implementation 'org.springframework.boot:spring-boot-starter-actuator'
3531
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
3632
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
3733
implementation 'org.springframework.boot:spring-boot-starter-web'
38-
implementation 'io.springfox:springfox-boot-starter:3.0.0'
39-
40-
compile 'io.springfox:springfox-swagger-ui:3.0.0'
41-
//needed for OpenAPI code generation
42-
compile "io.springfox:springfox-swagger2:3.0.0"
43-
compile "org.openapitools:jackson-databind-nullable:0.1.0"
4434

35+
compileOnly "org.projectlombok:lombok"
36+
annotationProcessor "org.projectlombok:lombok"
4537
developmentOnly 'org.springframework.boot:spring-boot-devtools'
4638
runtimeOnly 'com.h2database:h2'
4739

48-
testImplementation('org.springframework.boot:spring-boot-starter-test') {
49-
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
50-
}
40+
testImplementation('org.springframework.boot:spring-boot-starter-test')
5141
testImplementation('org.junit.jupiter:junit-jupiter-api:5.4.2')
5242
testRuntime('org.junit.jupiter:junit-jupiter-engine:5.4.2')
5343
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
@@ -59,46 +49,33 @@ dependencies {
5949

6050
test {
6151
useJUnitPlatform()
62-
systemProperty('karate.options', System.properties.getProperty('karate.options'))
63-
systemProperty('karate.env', System.properties.getProperty('karate.env'))
64-
outputs.upToDateWhen { false }
6552
}
6653

6754
task downloadFile(type: Download) {
6855
src "https://raw.githubusercontent.com/codecentric/api-showcases/main/specs/news.yaml"
69-
dest buildDir
56+
dest "${projectDir}/src/api"
7057
onlyIfModified true
7158
useETag true
7259
}
7360

74-
openApiValidate {
75-
inputSpec = "$buildDir/news.yaml"
61+
openapiProcessor {
62+
spring {
63+
processor 'io.openapiprocessor:openapi-processor-spring:2021.4'
64+
apiPath "$projectDir/src/api/news.yaml"
65+
targetDir "$projectDir/build/openapi"
66+
mapping "$projectDir/mapping.yaml"
67+
showWarnings true
68+
}
7669
}
7770

78-
openApiGenerate {
79-
generatorName = "spring"
80-
library = "spring-boot"
81-
inputSpec = "$buildDir/news.yaml"
82-
configFile = "${projectDir}/openapi-generator-config.json"
83-
outputDir = "${buildDir}/generated/open-api"
84-
templateDir = "${projectDir}/src/main/resources/openapi-templates"
85-
}
86-
87-
tasks.openApiGenerate.dependsOn tasks.downloadFile
88-
89-
idea.project.settings {
90-
taskTriggers {
91-
beforeBuild tasks.openApiGenerate
92-
beforeRebuild tasks.openApiGenerate
93-
afterSync tasks.openApiGenerate
94-
beforeSync tasks.openApiGenerate
95-
}
71+
afterEvaluate {
72+
tasks.processSpring.dependsOn(tasks.downloadFile)
9673
}
9774

9875
sourceSets {
9976
main {
10077
java {
101-
srcDir "${buildDir.absolutePath}/generated/open-api/src/main/java"
78+
srcDir "build/openapi"
10279
}
10380
}
10481

@@ -110,16 +87,9 @@ sourceSets {
11087
}
11188
}
11289

113-
compileJava {
114-
dependsOn tasks.openApiGenerate
115-
source "${buildDir}/generated/open-api/src/main/java"
116-
}
90+
91+
compileJava.dependsOn('processSpring')
11792

11893
springBoot {
11994
mainClassName = "de.codecentric.apifirstspringboot.ApifirstSpringbootApplication"
12095
}
121-
122-
asciidoctor {
123-
inputs.dir snippetsDir
124-
dependsOn test
125-
}

mapping.yaml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
openapi-processor-mapping: v2
2+
3+
options:
4+
package-name: de.codecentric.generated.news
5+
6+
map:
7+
result: org.springframework.http.ResponseEntity
8+
9+
types:
10+
- type: array => java.util.List
11+

openapi-generator-config.json

-17
This file was deleted.

src/api/news.yaml

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
openapi: 3.0.1
2+
servers:
3+
- url: 'http://localhost:8080/api'
4+
info:
5+
version: 1.0.0
6+
title: News API
7+
contact:
8+
name: Daniel Kocot
9+
url: 'http://www.codecentric.de'
10+
11+
license:
12+
name: MIT
13+
url: https://www.tldrelgal.com/mit
14+
description: An API to provide news
15+
tags:
16+
- name: news
17+
paths:
18+
/news:
19+
get:
20+
description: gets latest news
21+
operationId: getNews
22+
tags:
23+
- news
24+
responses:
25+
'200':
26+
description: Expected response to a valid request
27+
content:
28+
application/json:
29+
schema:
30+
$ref: '#/components/schemas/ArticleList'
31+
examples: {}
32+
components:
33+
schemas:
34+
ArticleList:
35+
title: ArticleList
36+
type: array
37+
items:
38+
$ref: '#/components/schemas/Article'
39+
Article:
40+
x-java-class-annotation:
41+
- "@javax.persistence.Entity"
42+
title: Article
43+
description: A article is a part of a news.
44+
type: object
45+
properties:
46+
id:
47+
x-java-field-annotation:
48+
- "@javax.persistence.Id"
49+
- "@javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.IDENTITY)"
50+
type: integer
51+
title:
52+
type: string
53+
date:
54+
type: string
55+
format: date
56+
description:
57+
type: string
58+
imageUrl:
59+
type: string
60+
required:
61+
- id
62+
- title
63+
- date
64+
- description
65+
- imageUrl
66+
securitySchemes: {}
67+
examples:
68+
news:
69+
value:
70+
description: Example shared example
71+
type: object
72+
properties:
73+
id:
74+
type: string
75+
required:
76+
- id
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,28 @@
11
package de.codecentric.apifirstspringboot.controller;
22

3+
import de.codecentric.apifirstspringboot.repository.NewsRepository;
34
import de.codecentric.apifirstspringboot.service.NewsService;
4-
import de.codecentric.news.api.model.Article;
5-
import org.springframework.http.HttpStatus;
5+
import de.codecentric.generated.news.api.NewsApi;
6+
import de.codecentric.generated.news.model.Article;
7+
import org.springframework.beans.factory.annotation.Autowired;
68
import org.springframework.http.ResponseEntity;
79
import org.springframework.stereotype.Controller;
810
import org.springframework.web.bind.annotation.GetMapping;
911
import org.springframework.web.bind.annotation.RequestMapping;
10-
import springfox.documentation.oas.annotations.EnableOpenApi;
1112

1213
import java.util.List;
1314

14-
@EnableOpenApi
15-
@RequestMapping("/api")
16-
@Controller
17-
public class NewsController {
15+
import static de.codecentric.apifirstspringboot.mapper.ArticleModelMapper.toApi;
1816

19-
private final NewsService newsService;
17+
@Controller
18+
public class NewsController implements NewsApi {
2019

21-
public NewsController(NewsService newsService) {
22-
this.newsService = newsService;
23-
}
20+
@Autowired
21+
private NewsService newsService;
2422

25-
@GetMapping("/news")
23+
@Override
2624
public ResponseEntity<List<Article>> getNews() {
27-
return new ResponseEntity<>(this.newsService.getNews(), HttpStatus.OK);
25+
List<de.codecentric.apifirstspringboot.entities.Article> allArticles = newsService.retrieveAllArticles();
26+
return ResponseEntity.ok().body(toApi(allArticles));
2827
}
2928
}

0 commit comments

Comments
 (0)