Skip to content

Commit f6195d9

Browse files
authored
add a mongodb api sample (#831)
Signed-off-by: Mark Nelson <mark.x.nelson@oracle.com>
1 parent 34ef114 commit f6195d9

File tree

12 files changed

+787
-0
lines changed

12 files changed

+787
-0
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
## Example of "customer32" reimplemented to use the Oracle Database API for MongoDB
2+
3+
This directory provides an example of the [customer32](../customer32/) service reimplmented using [Oracle Database API for MongoDB](https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/mongo-using-oracle-database-api-mongodb.html) and [Spring Data MongoDB](https://docs.spring.io/spring-data/mongodb/reference/index.html).
4+
5+
To run this application, you need to perform some initial setup:
6+
7+
- Create an instance of Oracle Autonomous Database in Oracle Cloud Infrastructure
8+
9+
- [Configure access for MongoDB](https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/mongo-using-oracle-database-api-mongodb.html#GUID-49018B09-9712-44DC-A950-B8129E7DA0D2)
10+
11+
- [Enable MongoDB API](https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/mongo-using-oracle-database-api-mongodb.html#ADBSB-GUID-2D3D792B-416A-4B3B-A8B6-F888AFA7B9CC)
12+
13+
- Create a user with the necessary permissions to use the MongoDB API as described [here](https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/mongo-using-oracle-database-api-mongodb.html#ADBSB-GUID-613DD3CE-6E84-4D8E-B614-2CFC18A41784) and [here](https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/mongo-using-oracle-database-api-mongodb.html#ADBSB-GUID-C161A1F8-12CD-4F74-9F7C-423ABF892D8C)
14+
15+
- Navigate to the Autonomous Database details page for your database in the OCI Console and copy the MongoDB API "Access URL" from the "Tool Configuration" page.
16+
17+
- Update the [Spring application configuration](./src/main/resources/application.yaml) to set the correct connection string. Note that you will have to set the USER, PASSWORD, hostname and region. You must also set the database name in that file to match the USER.
18+
19+
Once that configuration is done, you can run the application using `mvn spring-boot:run` and try the various APIs, for example:
20+
21+
- Create a customer
22+
23+
```
24+
curl -i -X POST -H 'Content-Type: application/json' \
25+
-d '{"name":"Bob Jones","email":"bob@job.com"}' \
26+
http://localhost:8080/api/v1mongo/customer
27+
```
28+
29+
- List customers
30+
31+
```
32+
curl http://localhost:8080/api/v1mongo/customer|jq .
33+
```
34+
35+
- Update a customer - make sure you use the correct ID from the output of the previous example
36+
37+
```
38+
curl -i -X PUT -H 'Content-Type: application/json' \
39+
-d '{"name":"Bob Jackson","email":"bob@jackson.com"}' \
40+
http://localhost:8080/api/v1mongo/customer/65cb9ad3d0538471e9fb5f27
41+
```
42+
43+
- Find a customer by email
44+
45+
```
46+
curl -i http://localhost:8080/api/v1mongo/customer/email/bob@jackson.com
47+
```
48+
49+
### Notes on the MongoDB connection
50+
51+
Please refer to the [Spring Data MongoDB documentation](https://docs.spring.io/spring-data/mongodb/reference/mongodb/configuration.html) for details of how to configure the connection and database.
52+
53+
This example follows the examples provided in that documentation, in particular, the [AppConfig class](./src/main/java/com/example/customer32mongo/config/AppConfig.java) creates a `MongoClient` bean and a `MongoDatabaseFactory` bean as described in the Spring Data MongoDB documentation.

cloudbank-v32/customer32-mongo/checkstyle/checkstyle.xml

Lines changed: 329 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0"?>
2+
3+
<!DOCTYPE suppressions PUBLIC
4+
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
5+
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
6+
7+
<suppressions>
8+
<!-- There's nothing wrong with these - ignore them b/c they generate noise -->
9+
<suppress checks="AbbreviationAsWordInName" files=".*"/>
10+
<suppress checks="MemberNameCheck" files=".*"/>
11+
<suppress checks="VariableDeclarationUsageDistanceCheck" files=".*"/>
12+
<suppress checks="AbbreviationAsWordInNameCheck" files=".*"/>
13+
</suppressions>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">
3+
</suppressions>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>com.example</groupId>
7+
<artifactId>cloudbank-apps</artifactId>
8+
<version>0.0.1-SNAPSHOT</version>
9+
</parent>
10+
11+
<groupId>com.example</groupId>
12+
<artifactId>customer32-mongo</artifactId>
13+
<version>0.0.1-SNAPSHOT</version>
14+
<name>customer32-mongo</name>
15+
<description>Customer32 service implemented with Oracle Database MongoDB API</description>
16+
17+
<properties>
18+
<java.version>17</java.version>
19+
</properties>
20+
21+
<dependencies>
22+
<dependency>
23+
<groupId>org.springframework.boot</groupId>
24+
<artifactId>spring-boot-starter-data-mongodb</artifactId>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.springframework.boot</groupId>
28+
<artifactId>spring-boot-starter-web</artifactId>
29+
</dependency>
30+
</dependencies>
31+
32+
<build>
33+
<plugins>
34+
<plugin>
35+
<groupId>org.springframework.boot</groupId>
36+
<artifactId>spring-boot-maven-plugin</artifactId>
37+
</plugin>
38+
</plugins>
39+
</build>
40+
41+
</project>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) 2024, Oracle and/or its affiliates.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
3+
4+
package com.example.customer32mongo;
5+
6+
import org.springframework.boot.SpringApplication;
7+
import org.springframework.boot.autoconfigure.SpringBootApplication;
8+
9+
@SpringBootApplication
10+
public class CustomerMongoApplication {
11+
12+
public static void main(String[] args) {
13+
SpringApplication.run(CustomerMongoApplication.class, args);
14+
}
15+
16+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright (c) 2024, Oracle and/or its affiliates.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
3+
4+
package com.example.customer32mongo.config;
5+
6+
import com.mongodb.ConnectionString;
7+
import com.mongodb.MongoClientSettings;
8+
import com.mongodb.ServerApi;
9+
import com.mongodb.ServerApiVersion;
10+
import com.mongodb.client.MongoClient;
11+
import com.mongodb.client.MongoClients;
12+
import org.springframework.beans.factory.annotation.Value;
13+
import org.springframework.context.annotation.Bean;
14+
import org.springframework.context.annotation.Configuration;
15+
import org.springframework.data.mongodb.MongoDatabaseFactory;
16+
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
17+
18+
@Configuration
19+
public class AppConfig {
20+
21+
@Value("${spring.data.mongodb.uri}")
22+
private String uri;
23+
24+
@Value("${spring.data.mongodb.database}")
25+
private String db;
26+
27+
// for information on how mongo is configured in Spring Data Mongo, please see these references:
28+
//
29+
// https://docs.spring.io/spring-data/mongodb/reference/mongodb/configuration.html
30+
// https://www.mongodb.com/docs/drivers/java/sync/current/fundamentals/connection/connect/#atlas-connection-example
31+
32+
/**
33+
* Configure the Mongo Client.
34+
*
35+
* In particular, use TLS and ignore host name validation. This is equivalent to invoking mongosh
36+
* with the following arguments:
37+
*
38+
* `mongosh --tls --tlsAllowInvalidCertificates 'mongodb://...'`
39+
*
40+
* @return the MongoClient configured for Oracle.
41+
*/
42+
@Bean
43+
public MongoClient mongoClient() {
44+
45+
ServerApi serverApi = ServerApi.builder()
46+
.version(ServerApiVersion.V1)
47+
.build();
48+
49+
MongoClientSettings settings = MongoClientSettings.builder()
50+
.applyConnectionString(new ConnectionString(uri))
51+
.serverApi(serverApi)
52+
.applyToSslSettings(builder -> {
53+
builder.enabled(true);
54+
builder.invalidHostNameAllowed(true);
55+
})
56+
.build();
57+
58+
return MongoClients.create(settings);
59+
}
60+
61+
@Bean
62+
public MongoDatabaseFactory mongoDatabaseFactory() {
63+
return new SimpleMongoClientDatabaseFactory(mongoClient(), db);
64+
}
65+
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// Copyright (c) 2024, Oracle and/or its affiliates.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
3+
4+
package com.example.customer32mongo.controller;
5+
6+
import java.net.URI;
7+
import java.util.List;
8+
import java.util.Optional;
9+
10+
import com.example.customer32mongo.model.Customer;
11+
import com.example.customer32mongo.service.CustomerService;
12+
import org.springframework.http.HttpHeaders;
13+
import org.springframework.http.HttpStatus;
14+
import org.springframework.http.ResponseEntity;
15+
import org.springframework.web.bind.annotation.DeleteMapping;
16+
import org.springframework.web.bind.annotation.GetMapping;
17+
import org.springframework.web.bind.annotation.PathVariable;
18+
import org.springframework.web.bind.annotation.PostMapping;
19+
import org.springframework.web.bind.annotation.PutMapping;
20+
import org.springframework.web.bind.annotation.RequestBody;
21+
import org.springframework.web.bind.annotation.RequestMapping;
22+
import org.springframework.web.bind.annotation.ResponseStatus;
23+
import org.springframework.web.bind.annotation.RestController;
24+
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
25+
26+
@RestController
27+
@RequestMapping("/api/v1mongo")
28+
public class CustomerController {
29+
30+
private final CustomerService customerService;
31+
32+
public CustomerController(CustomerService customerService) {
33+
this.customerService = customerService;
34+
}
35+
36+
/**
37+
* Find all customers.
38+
*
39+
* @return A list of all customers in the collection.
40+
*/
41+
@GetMapping("/customer")
42+
@ResponseStatus(HttpStatus.OK)
43+
List<Customer> findAll() {
44+
return customerService.findAll();
45+
}
46+
47+
/**
48+
* Find a customer by name.
49+
*
50+
* @param name The name to search for.
51+
* @return Customer with the specified name, if found, and status 200. If not, 204.
52+
*/
53+
@GetMapping("/customer/name/{name}")
54+
public ResponseEntity<Customer> findCustomerByName(@PathVariable String name) {
55+
Optional<Customer> customer = customerService.findByCustomerName(name);
56+
try {
57+
return customer.map(customers -> new ResponseEntity<>(customers, HttpStatus.OK))
58+
.orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
59+
} catch (Exception e) {
60+
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
61+
}
62+
}
63+
64+
/**
65+
* Find customer by id.
66+
*
67+
* @param id The customer ID to search for.
68+
* @return Customer with the specified ID, if found, and status 200. If not, 204.
69+
*/
70+
@GetMapping("/customer/{id}")
71+
ResponseEntity<Customer> findCustomerById(@PathVariable String id) {
72+
Optional<Customer> customer = customerService.findByCustomerId(id);
73+
try {
74+
return customer.map(customers -> new ResponseEntity<>(customers, HttpStatus.OK))
75+
.orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
76+
} catch (Exception e) {
77+
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
78+
}
79+
}
80+
81+
/**
82+
* Find a customer by email.
83+
*
84+
* @param email The name to search for.
85+
* @return Customer with the specified email, if found, and status 200. If not, 204.
86+
*/
87+
@GetMapping("/customer/email/{email}")
88+
public ResponseEntity<Customer> findCustomerByEmail(@PathVariable String email) {
89+
Optional<Customer> customer = customerService.findByCustomerEmail(email);
90+
try {
91+
return customer.map(customers -> new ResponseEntity<>(customers, HttpStatus.OK))
92+
.orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
93+
} catch (Exception e) {
94+
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
95+
}
96+
}
97+
98+
/**
99+
* Create a customer.
100+
*
101+
* @param customer The customer object
102+
* @return The created customer and location header with 201, else 204.
103+
*/
104+
@PostMapping("/customer")
105+
ResponseEntity<Customer> createCustomer(@RequestBody Customer customer) {
106+
var result = customerService.createCustomer(customer);
107+
if (result != null) {
108+
URI location = ServletUriComponentsBuilder
109+
.fromCurrentRequest()
110+
.path("/{id}")
111+
.buildAndExpand(customer.id)
112+
.toUri();
113+
var headers = new HttpHeaders();
114+
headers.setLocation(location);
115+
return new ResponseEntity<>(result, headers, HttpStatus.CREATED);
116+
} else {
117+
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
118+
}
119+
}
120+
121+
/**
122+
* Update a customer.
123+
*
124+
* @param customer The customer to update.
125+
* @return The udpated customer with 200, else 204.
126+
*/
127+
@PutMapping("/customer/{id}")
128+
ResponseEntity<Customer> updateCustomer(@RequestBody Customer customer, @PathVariable String id) {
129+
var result = customerService.updateCustomer(customer, id);
130+
if (result != null) {
131+
return new ResponseEntity<>(result, HttpStatus.OK);
132+
} else {
133+
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
134+
}
135+
}
136+
137+
/**
138+
* Delete a customer.
139+
*
140+
* Note that Mongo's delete API silently ignores if a document
141+
* with the specified ID is not found, so we just return OK no
142+
* matter what happened.
143+
*
144+
* @param id The ID of the customer to delete.
145+
* @return 200 OK.
146+
*/
147+
@DeleteMapping("/customer/{id}")
148+
ResponseEntity<Customer> deleteCustomer(@PathVariable String id) {
149+
customerService.deleteCustomer(id);
150+
return new ResponseEntity<>(HttpStatus.OK);
151+
}
152+
153+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) 2024, Oracle and/or its affiliates.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
3+
4+
package com.example.customer32mongo.model;
5+
6+
import org.springframework.data.annotation.Id;
7+
8+
public class Customer {
9+
10+
@Id
11+
public String id;
12+
13+
public String name;
14+
public String email;
15+
16+
public Customer() {
17+
}
18+
19+
public Customer(String name, String email) {
20+
this.name = name;
21+
this.email = email;
22+
}
23+
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) 2024, Oracle and/or its affiliates.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
3+
4+
package com.example.customer32mongo.repository;
5+
6+
import java.util.Optional;
7+
8+
import com.example.customer32mongo.model.Customer;
9+
import org.springframework.data.mongodb.repository.MongoRepository;
10+
import org.springframework.lang.NonNull;
11+
12+
public interface CustomerRepository extends MongoRepository<Customer, String> {
13+
public Optional<Customer> findByName(String name);
14+
15+
public Optional<Customer> findByEmail(String email);
16+
17+
public @NonNull Optional<Customer> findById(@NonNull String id);
18+
}

0 commit comments

Comments
 (0)