Skip to content

Commit a89ee6a

Browse files
authored
Devadv 1645 improve and fix current java springboot tutorial (#5)
* Change tests from unit tests to integration tests. This prevents the mvn package from crashing when not running Couchbase Server locally. * Code cleanup: removal of unused code/imports etc. * Various code improvements. Adapt Swagger codes and exception catching to match actual behaviour. Documentation fixes. Change bucket name to not conflict with other Couchbase tutorials. Remove unneeded imports. * Add MIT license.
1 parent ce91938 commit a89ee6a

File tree

13 files changed

+99
-80
lines changed

13 files changed

+99
-80
lines changed

LICENSE.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Released under MIT License
2+
3+
Copyright (c) 2021 Couchbase, Inc
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6+
7+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8+
9+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ Full documentation can be found on the [Couchbase Developer Portal](https://deve
99
## Prerequisites
1010
To run this prebuilt project, you will need:
1111

12-
- Couchbase 7 Installed (version 7.0.0-5247 or higher)
12+
- Couchbase Server 7 Installed (version 7.0.0-5247 or higher)
1313
- Java SDK v1.8 or higher installed
1414
- Code Editor installed (IntelliJ IDEA, Eclipse, or Visual Studio Code)
15+
- Maven command line
1516

1617
## Install Dependencies
1718

@@ -23,7 +24,7 @@ mvn package
2324
2425
### Database Server Configuration
2526

26-
All configuration for communication with the database is stored in the `/src/main/java/resources/application.properties` file. This includes the connection string, username, and password. The default username is assumed to be `Administrator` and the default password is assumed to be `password`. If these are different in your environment you will need to change them before running the application.
27+
All configuration for communication with the database is stored in the `/src/main/resources/application.properties` file. This includes the connection string, username, and password. The default username is assumed to be `Administrator` and the default password is assumed to be `password`. If these are different in your environment you will need to change them before running the application.
2728

2829
### Dependency Injection via DBSetupRunner class
2930

@@ -41,19 +42,17 @@ You can launch your browser and go to the [Swagger start page](https://localhost
4142

4243
## Running The Tests
4344

44-
To run the standard integration tests, use the following commands:
45-
46-
To run the standard integration tests, use the following commands:
45+
To run the standard integration tests (which requires a running Couchbase Server), use the following command:
4746

4847
```sh
49-
mvn test
48+
mvn verify
5049
```
5150

5251
## Project Setup Notes
5352

54-
This project was based on the standard [Spring Boot project](https://spring.io/guides/gs/rest-service/). The HealthCheckController is provided as a santity check and is used in unit tests.
53+
This project was based on the standard [Spring Boot project](https://spring.io/guides/gs/rest-service/). The HealthCheckController is provided as a sanity check and is used in integration tests.
5554

56-
A fully list of packages are referenced in the pom.xml file
55+
A full list of packages are referenced in the pom.xml file
5756

5857
## Conclusion
5958

pom.xml

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
<dependency>
8080
<groupId>org.mindrot</groupId>
8181
<artifactId>jbcrypt</artifactId>
82-
<version>0.3m</version>
82+
<version>0.4</version>
8383
</dependency>
8484
<dependency>
8585
<groupId>org.junit.jupiter</groupId>
@@ -101,9 +101,57 @@
101101

102102
<build>
103103
<plugins>
104+
<plugin>
105+
<groupId>org.apache.maven.plugins</groupId>
106+
<artifactId>maven-surefire-plugin</artifactId>
107+
<version>2.22.2</version>
108+
<configuration>
109+
<excludes>
110+
<exclude>**/*IntegrationTest.java</exclude>
111+
</excludes>
112+
</configuration>
113+
</plugin>
104114
<plugin>
105115
<groupId>org.springframework.boot</groupId>
106116
<artifactId>spring-boot-maven-plugin</artifactId>
117+
<executions>
118+
<execution>
119+
<id>pre-integration-test</id>
120+
<goals>
121+
<goal>start</goal>
122+
</goals>
123+
</execution>
124+
<execution>
125+
<id>post-integration-test</id>
126+
<goals>
127+
<goal>stop</goal>
128+
</goals>
129+
</execution>
130+
</executions>
131+
</plugin>
132+
<plugin>
133+
<groupId>org.apache.maven.plugins</groupId>
134+
<artifactId>maven-failsafe-plugin</artifactId>
135+
<version>3.0.0-M5</version>
136+
<configuration>
137+
<includes>
138+
<include>**/*IntegrationTest.java</include>
139+
</includes>
140+
</configuration>
141+
<executions>
142+
<execution>
143+
<id>integration-tests</id>
144+
<goals>
145+
<goal>integration-test</goal>
146+
</goals>
147+
</execution>
148+
<execution>
149+
<id>verify</id>
150+
<goals>
151+
<goal>verify</goal>
152+
</goals>
153+
</execution>
154+
</executions>
107155
</plugin>
108156
</plugins>
109157
</build>

src/main/java/org/couchbase/quickstart/configs/CouchbaseConfig.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import org.springframework.beans.factory.annotation.Autowired;
1010
import org.springframework.context.annotation.Bean;
1111
import org.springframework.context.annotation.Configuration;
12-
import org.springframework.stereotype.Component;
1312

1413
@Configuration
1514
public class CouchbaseConfig {
@@ -25,8 +24,8 @@ public Cluster getCouchbaseCluster(){
2524
@Bean
2625
public Bucket getCouchbaseBucket(Cluster cluster){
2726

28-
//Creates the cluster if it does not exist yet
29-
if( !cluster.buckets().getAllBuckets().containsKey(dbProp.getBucketName())) {
27+
// Creates the cluster if it does not exist yet
28+
if (!cluster.buckets().getAllBuckets().containsKey(dbProp.getBucketName())) {
3029
cluster.buckets().createBucket(
3130
BucketSettings.create(dbProp.getBucketName())
3231
.bucketType(BucketType.COUCHBASE)

src/main/java/org/couchbase/quickstart/configs/Swagger.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.couchbase.quickstart.configs;
22

3-
import org.couchbase.quickstart.Application;
43
import org.couchbase.quickstart.controllers.ProfileController;
54
import org.springframework.context.annotation.Bean;
65
import org.springframework.context.annotation.Configuration;
@@ -11,7 +10,6 @@
1110
import springfox.documentation.builders.RequestHandlerSelectors;
1211

1312
@Configuration
14-
1513
public class Swagger {
1614
@Bean
1715
public Docket api() {

src/main/java/org/couchbase/quickstart/controllers/IndexController.java

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/main/java/org/couchbase/quickstart/controllers/ProfileController.java

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,27 @@
11
package org.couchbase.quickstart.controllers;
22

3+
import com.couchbase.client.core.error.DocumentNotFoundException;
34
import com.couchbase.client.java.Bucket;
45
import com.couchbase.client.java.Cluster;
56
import com.couchbase.client.java.Collection;
6-
import com.couchbase.client.java.json.JsonObject;
77
import com.couchbase.client.java.query.QueryScanConsistency;
88
import io.swagger.annotations.ApiOperation;
99
import io.swagger.annotations.ApiResponse;
1010
import io.swagger.annotations.ApiResponses;
11-
import static org.couchbase.quickstart.configs.CollectionNames.PROFILE;
12-
13-
import org.couchbase.quickstart.configs.CollectionNames;
1411
import org.couchbase.quickstart.configs.DBProperties;
1512
import org.couchbase.quickstart.models.Profile;
1613
import org.couchbase.quickstart.models.ProfileRequest;
1714
import org.springframework.http.HttpStatus;
1815
import org.springframework.http.MediaType;
1916
import org.springframework.http.ResponseEntity;
2017
import org.springframework.web.bind.annotation.*;
21-
import static com.couchbase.client.java.query.QueryOptions.queryOptions;
22-
2318

24-
import java.util.ArrayList;
2519
import java.util.List;
2620
import java.util.UUID;
2721

22+
import static com.couchbase.client.java.query.QueryOptions.queryOptions;
23+
import static org.couchbase.quickstart.configs.CollectionNames.PROFILE;
24+
2825
@RestController
2926
@RequestMapping("/api/v1/profile")
3027
public class ProfileController {
@@ -46,52 +43,58 @@ public ProfileController(Cluster cluster, Bucket bucket, DBProperties dbProperti
4643
@ApiOperation(value = "Create a user profile from the request")
4744
@ApiResponses({
4845
@ApiResponse(code = 201, message = "Created", response = Profile.class),
46+
@ApiResponse(code = 400, message = "Bad request", response = Error.class),
4947
@ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
5048
})
5149
public ResponseEntity<Profile> save(@RequestBody final ProfileRequest userProfile) {
5250
//generates an id and save the user
5351
Profile profile = userProfile.getProfile();
54-
profileCol.insert(profile.getPid(), profile);
55-
return ResponseEntity.status(HttpStatus.CREATED).body(profile);
52+
53+
try {
54+
profileCol.insert(profile.getPid(), profile);
55+
return ResponseEntity.status(HttpStatus.CREATED).body(profile);
56+
} catch (Exception e) {
57+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
58+
}
5659
}
5760

5861
@CrossOrigin(value="*")
5962
@GetMapping(path = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
60-
@ApiOperation(value = "Get a user profile by Id", produces = MediaType.APPLICATION_JSON_VALUE)
63+
@ApiOperation(value = "Get a user profile by id", produces = MediaType.APPLICATION_JSON_VALUE)
6164
@ApiResponses(
6265
value = {
6366
@ApiResponse(code = 200, message = "OK"),
6467
@ApiResponse(code = 500, message = "Error occurred in getting user profiles", response = Error.class)
6568
})
66-
public ResponseEntity<Profile> getProfile(@RequestParam String pid) {
67-
Profile profile = profileCol.get(pid).contentAs(Profile.class);
69+
public ResponseEntity<Profile> getProfile(@PathVariable("id") UUID id) {
70+
Profile profile = profileCol.get(id.toString()).contentAs(Profile.class);
6871
return ResponseEntity.status(HttpStatus.OK).body(profile);
6972
}
7073

7174
@CrossOrigin(value="*")
7275
@PutMapping(path = "/{id}")
7376
@ApiOperation(value = "Update a user profile", response = Profile.class)
7477
@ApiResponses({
75-
@ApiResponse(code = 200, message = "Updated the user profile", response = Profile.class),
76-
@ApiResponse(code = 404, message = "user profile not found", response = Error.class),
77-
@ApiResponse(code = 500, message = "returns internal server error", response = Error.class)
78+
@ApiResponse(code = 201, message = "Updated the user profile", response = Profile.class),
79+
@ApiResponse(code = 404, message = "User profile not found", response = Error.class),
80+
@ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
7881
})
79-
public ResponseEntity<Profile> update( @PathVariable("id") String id, @RequestBody Profile profile) {
80-
82+
public ResponseEntity<Profile> update(@PathVariable("id") UUID id, @RequestBody Profile profile) {
8183
try {
82-
profileCol.upsert(id, profile);
84+
profileCol.replace(id.toString(), profile);
8385
return ResponseEntity.status(HttpStatus.CREATED).body(profile);
86+
} catch (DocumentNotFoundException dnfe) {
87+
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
8488
} catch (Exception e){
85-
return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(null);
89+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
8690
}
8791
}
8892

8993
@CrossOrigin(value="*")
9094
@DeleteMapping(path = "/{id}")
91-
@ApiOperation(value = "Delete a Users Profile")
95+
@ApiOperation(value = "Delete a user profile")
9296
@ApiResponses({
9397
@ApiResponse(code = 200, message = "OK"),
94-
@ApiResponse(code = 401, message = "Unauthorized", response = Error.class),
9598
@ApiResponse(code = 404, message = "Not Found", response = Error.class),
9699
@ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
97100
})
@@ -100,8 +103,10 @@ public ResponseEntity delete(@PathVariable UUID id){
100103
try {
101104
profileCol.remove(id.toString());
102105
return ResponseEntity.status(HttpStatus.OK).body(null);
106+
} catch (DocumentNotFoundException dnfe) {
107+
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
103108
} catch (Exception e){
104-
return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(null);
109+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
105110
}
106111
}
107112

src/main/java/org/couchbase/quickstart/models/ProfileList.java

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/main/java/org/couchbase/quickstart/runners/DBSetupRunner.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616

1717
/**
18-
* This class run after the application startup. It automatically setup all indexes needed
18+
* This class run after the application startup. It automatically sets up all needed indexes
1919
*/
2020
@Component
2121
public class DBSetupRunner implements CommandLineRunner {
@@ -34,7 +34,6 @@ public void run(String... args) {
3434
System.out.println("Created primary index" + props.getBucketName());
3535
} catch (Exception e) {
3636
System.out.println("Primary index already exists on bucket "+props.getBucketName());
37-
e.printStackTrace();
3837
}
3938

4039
CollectionManager collectionManager = bucket.collections();
@@ -71,8 +70,4 @@ public void run(String... args) {
7170
System.out.println(String.format("Failed to create secondary index on profile.firstName: %s", e.getMessage()));
7271
}
7372
}
74-
//
75-
// private QueryResult createCollectionIndex() throws Exception {
76-
// return cluster.query("CREATE PRIMARY INDEX default_profile_index ON "+props.getBucketName()+"._default."+ CollectionNames.PROFILE);
77-
// }
7873
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
spring.couchbase.bootstrap-hosts=localhost
2-
spring.couchbase.bucket.name=user_profile
2+
spring.couchbase.bucket.name=user_profile_spring
33
spring.couchbase.bucket.user=Administrator
44
spring.couchbase.bucket.password=password

0 commit comments

Comments
 (0)