Skip to content

Commit 42b5470

Browse files
committed
Add swagger-ui and paging
1 parent df2f1d6 commit 42b5470

File tree

9 files changed

+145
-56
lines changed

9 files changed

+145
-56
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
.gradle/
2+
.idea/
23
build/

README.md

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,20 @@ A demo Java application leveraging Spring JPA with SAP HANA database.
66
$ gradle build
77
$ java -jar build/libs/springboot-jpa-hana-0.1.0.jar
88
```
9-
Then open browser and visit http://localhost:8080
9+
Then open browser and visit http://localhost:8080/swagger-ui.html
1010

1111
## How to run directly?
1212
```bash
1313
$ gradle bootRun
1414
```
15-
Then open browser and visit http://localhost:8080
15+
Then open browser and visit http://localhost:8080/swagger-ui.html
1616

1717
## How to run with Docker?
1818
```bash
1919
$ docker build -t springboot-jpa-hana .
2020
$ docker run -p 8080:8080 springboot-jpa-hana
2121
```
22-
Then open browser and visit http://localhost:8080
23-
24-
## How to generate some test data?
25-
```bash
26-
$ curl -i localhost:8080/users/ -F name=John_Doe -F email=foo@bar.com -F locale=en
27-
```
22+
Then open browser and visit http://localhost:8080/swagger-ui.html
2823

2924
## Caveats
3025
- The solution only works with HANA 2.0 because it requires `select hibernate_sequence.nextval from sys.dummy` when auto generates Id.

build.gradle

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ apply plugin: 'org.springframework.boot'
1414
apply plugin: 'io.spring.dependency-management'
1515

1616
bootJar {
17-
baseName = 'springboot-jpa-hana'
17+
archiveBaseName = 'springboot-jpa-hana'
1818
version = '0.1.0'
1919
}
2020

@@ -34,6 +34,12 @@ dependencies {
3434
// tag::actuator[]
3535
compile("org.springframework.boot:spring-boot-starter-actuator")
3636
// end::actuator[]
37+
// tag::swagger[]
38+
compile("io.springfox:springfox-swagger2:2.9.2")
39+
compile("io.springfox:springfox-swagger-ui:2.9.2")
40+
// https://github.com/swagger-api/swagger-core/issues/2783
41+
compile("io.swagger:swagger-models:1.6.0")
42+
// end::swagger[]
3743
// tag::tests[]
3844
testCompile("org.springframework.boot:spring-boot-starter-test")
3945
testCompile("com.h2database:h2")

src/main/java/com/sap/demo/HelloController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class HelloController {
1010
@Autowired
1111
Environment env;
1212

13-
@RequestMapping("/")
13+
@GetMapping("/")
1414
public String index() {
1515
return "Greetings from Spring Boot!";
1616
}

src/main/java/com/sap/demo/NotFoundError.java

Lines changed: 0 additions & 20 deletions
This file was deleted.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.sap.demo;
2+
3+
import org.springframework.context.annotation.Bean;
4+
import org.springframework.context.annotation.Configuration;
5+
import springfox.documentation.builders.RequestHandlerSelectors;
6+
import springfox.documentation.service.ApiInfo;
7+
import springfox.documentation.service.Contact;
8+
import springfox.documentation.spi.DocumentationType;
9+
import springfox.documentation.spring.web.plugins.Docket;
10+
import springfox.documentation.swagger2.annotations.EnableSwagger2;
11+
12+
import java.util.ArrayList;
13+
14+
@Configuration
15+
@EnableSwagger2
16+
public class SwaggerConfig {
17+
/**
18+
* Every Docket bean is picked up by the swagger-mvc framework - allowing for multiple
19+
* swagger groups i.e. same code base multiple swagger resource listings.
20+
*/
21+
@Bean
22+
public Docket customDocket() {
23+
return new Docket(DocumentationType.SWAGGER_2)
24+
.select()
25+
// .apis(RequestHandlerSelectors.any())
26+
.apis(RequestHandlerSelectors.basePackage("com.sap.demo"))
27+
// .paths(PathSelectors.regex("/v1/.*"))
28+
.build().apiInfo(apiInfo());
29+
}
30+
31+
private ApiInfo apiInfo() {
32+
return new ApiInfo(
33+
"SpringBoot JPA with SAP HANA",
34+
"A demo Java application leveraging Spring JPA with SAP HANA database",
35+
"0.1.0",
36+
"Terms of service",
37+
new Contact("John Doe", "", "foo@bar.com"),
38+
"License of API",
39+
"https://swagger.io/docs/",
40+
new ArrayList<>());
41+
}
42+
}
Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,67 @@
11
package com.sap.demo;
22

3+
import io.swagger.annotations.ApiOperation;
4+
import io.swagger.annotations.ApiParam;
35
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.data.domain.Page;
7+
import org.springframework.data.domain.PageRequest;
8+
import org.springframework.data.domain.Pageable;
9+
import org.springframework.data.domain.Sort;
10+
import org.springframework.http.ResponseEntity;
411
import org.springframework.web.bind.annotation.*;
512

13+
import javax.validation.Valid;
14+
import java.util.Optional;
15+
616
@RestController()
7-
@RequestMapping(path = "/users")
17+
@RequestMapping(path = "/v1/users")
818
public class UserController {
919

10-
@Autowired // This means to get the bean called userRepository
11-
// Which is auto-generated by Spring, we will use it to handle the data
12-
private UserRepository userRepository;
20+
private static final String DEFAULT_PAGE_SIZE = "10";
21+
private static final String DEFAULT_PAGE_INDEX = "0";
22+
private static final String DEFAULT_PAGE_SORT_DIRECTION = "ASC";
23+
24+
25+
@Autowired
26+
private UserService userService;
27+
1328

14-
@PostMapping(path = "/") // Map ONLY GET Requests
29+
@PostMapping()
30+
@ApiOperation(
31+
value = "Add a new user",
32+
notes = "Returns the same user added.",
33+
response = User.class)
1534
public @ResponseBody
16-
User addUser(@RequestParam String name
17-
, @RequestParam String email
18-
, @RequestParam String locale) {
19-
// @ResponseBody means the returned String is the response, not a view name
20-
// @RequestParam means it is a parameter from the GET or POST request
21-
22-
User n = new User();
23-
n.setName(name);
24-
n.setEmail(email);
25-
n.setLocale(locale);
26-
userRepository.save(n);
27-
return n;
35+
ResponseEntity<User> addUser(@Valid @RequestBody User user) {
36+
userService.save(user);
37+
return ResponseEntity.ok().body(user);
2838
}
2939

30-
@GetMapping(path = "/")
40+
@GetMapping()
41+
@ApiOperation(
42+
value = "Get all users",
43+
notes = "Returns first N users specified by the size parameter with page offset specified by page parameter.",
44+
response = Page.class)
3145
public @ResponseBody
32-
Iterable<User> getAllUsers() {
33-
// This returns a JSON or XML with the users
34-
return userRepository.findAll();
46+
Page<User> getAllUsers(
47+
@ApiParam("The size of the page to be returned") @RequestParam(required = false, defaultValue = DEFAULT_PAGE_SIZE) Integer pageSize,
48+
@ApiParam("Zero-based page index") @RequestParam(required = false, defaultValue = DEFAULT_PAGE_INDEX) Integer pageNo,
49+
@ApiParam("Field name to sort by") @RequestParam(required = false, defaultValue = "name") String sortBy,
50+
@ApiParam("Direction to sort by") @RequestParam(required = false, defaultValue = DEFAULT_PAGE_SORT_DIRECTION) Sort.Direction direction
51+
) {
52+
Pageable paging = PageRequest.of(pageNo, pageSize, direction, sortBy);
53+
return userService.findAll(paging);
3554
}
3655

3756
@GetMapping(path = "/{email}")
57+
@ApiOperation(
58+
value = "Find user by email address",
59+
notes = "Returns the user matched by the given email.",
60+
response = User.class)
3861
public @ResponseBody
39-
User getUserByEmail(@PathVariable("email") String email) {
40-
return userRepository.findByEmail(email).orElseThrow(() -> new NotFoundError());
62+
ResponseEntity<User> getUserByEmail(@PathVariable("email") String email) {
63+
Optional<User> user = userService.findByEmail(email);
64+
return ResponseEntity.of(user);
4165
}
4266

4367
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
package com.sap.demo;
22

3-
import org.springframework.data.repository.CrudRepository;
3+
import org.springframework.data.jpa.repository.JpaRepository;
44

55
import java.util.Optional;
66

77

88
// This will be AUTO IMPLEMENTED by Spring into a Bean called userRepository
99
// CRUD refers Create, Read, Update, Delete
1010

11-
public interface UserRepository extends CrudRepository<User, Integer> {
11+
public interface UserRepository extends JpaRepository<User, Integer> {
1212

13-
public Optional<User> findByEmail(String email);
13+
Optional<User> findByEmail(String email);
1414
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.sap.demo;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.data.domain.Page;
5+
import org.springframework.data.domain.Pageable;
6+
import org.springframework.stereotype.Service;
7+
import org.springframework.transaction.annotation.Transactional;
8+
9+
import java.util.Optional;
10+
11+
12+
@Service
13+
@Transactional
14+
public class UserService {
15+
16+
@Autowired
17+
private UserRepository repository;
18+
19+
@Transactional(readOnly = true)
20+
Page<User> findAll(Pageable pageable) {
21+
22+
return repository.findAll(pageable);
23+
}
24+
25+
@Transactional(readOnly = true)
26+
Optional<User> findById(Integer id) {
27+
return repository.findById(id);
28+
}
29+
30+
@Transactional()
31+
User save(User user) {
32+
return repository.saveAndFlush(user);
33+
}
34+
35+
@Transactional(readOnly = true)
36+
Optional<User> findByEmail(String email) {
37+
return repository.findByEmail(email);
38+
}
39+
40+
41+
}

0 commit comments

Comments
 (0)