Skip to content

Commit 937bc24

Browse files
committed
Add Java orders service
1 parent 56717ee commit 937bc24

File tree

6 files changed

+242
-0
lines changed

6 files changed

+242
-0
lines changed

services/orders-java/Dockerfile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FROM maven:3.9-eclipse-temurin-17 AS build
2+
WORKDIR /app
3+
COPY . .
4+
RUN mvn test
5+
RUN mvn clean package -DskipTests
6+
7+
FROM eclipse-temurin:17-jdk
8+
WORKDIR /app
9+
COPY --from=build /app/target/*.jar app.jar
10+
ENTRYPOINT ["java", "-jar", "app.jar"]

services/orders-java/pom.xml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
4+
http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>org.springframework.boot</groupId>
9+
<artifactId>spring-boot-starter-parent</artifactId>
10+
<version>3.1.4</version>
11+
<relativePath/>
12+
</parent>
13+
14+
<groupId>com.example</groupId>
15+
<artifactId>orders-java</artifactId>
16+
<version>1.0.0</version>
17+
18+
<dependencies>
19+
<dependency>
20+
<groupId>org.springframework.boot</groupId>
21+
<artifactId>spring-boot-starter-web</artifactId>
22+
</dependency>
23+
<dependency>
24+
<groupId>org.json</groupId>
25+
<artifactId>json</artifactId>
26+
<version>20231013</version>
27+
</dependency>
28+
<dependency>
29+
<groupId>org.springdoc</groupId>
30+
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
31+
<version>2.1.0</version>
32+
</dependency>
33+
<dependency>
34+
<groupId>org.junit.jupiter</groupId>
35+
<artifactId>junit-jupiter</artifactId>
36+
<version>5.10.0</version>
37+
<scope>test</scope>
38+
</dependency>
39+
</dependencies>
40+
41+
<build>
42+
<plugins>
43+
<plugin>
44+
<groupId>org.springframework.boot</groupId>
45+
<artifactId>spring-boot-maven-plugin</artifactId>
46+
</plugin>
47+
<plugin>
48+
<groupId>org.apache.maven.plugins</groupId>
49+
<artifactId>maven-surefire-plugin</artifactId>
50+
<version>3.0.0-M9</version>
51+
</plugin>
52+
</plugins>
53+
</build>
54+
</project>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.example.orders;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class OrdersApplication {
8+
public static void main(String[] args) {
9+
SpringApplication.run(OrdersApplication.class, args);
10+
}
11+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.example.orders.controller;
2+
3+
import org.springframework.beans.factory.annotation.Value;
4+
import com.example.orders.model.OrderRequest;
5+
import org.json.JSONObject;
6+
import org.springframework.http.*;
7+
import org.springframework.web.bind.annotation.*;
8+
import org.springframework.web.client.RestTemplate;
9+
10+
@RestController
11+
@RequestMapping("/orders")
12+
public class OrderController {
13+
14+
private final RestTemplate restTemplate = new RestTemplate();
15+
16+
@Value("${AUTH_SERVICE_URL:http://auth-python:8000}")
17+
private String authServiceUrl;
18+
19+
@Value("${BILLING_SERVICE_URL:http://billing-csharp:80}")
20+
private String billingServiceUrl;
21+
22+
@PostMapping
23+
public ResponseEntity<String> placeOrder(@RequestBody OrderRequest request,
24+
@RequestHeader("Authorization") String authHeader) {
25+
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
26+
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Missing token");
27+
}
28+
29+
String token = authHeader.substring(7);
30+
String username = validateToken(token);
31+
if (username == null) {
32+
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid session");
33+
}
34+
35+
boolean billed = charge(username, request.productId, request.quantity);
36+
if (!billed) {
37+
return ResponseEntity.status(HttpStatus.PAYMENT_REQUIRED).body("Billing failed");
38+
}
39+
40+
return ResponseEntity.ok("Order placed successfully by " + username);
41+
}
42+
43+
protected String validateToken(String token) {
44+
String authUrl = authServiceUrl + "/auth/validate?token=" + token;
45+
try {
46+
ResponseEntity<String> authResponse = restTemplate.getForEntity(authUrl, String.class);
47+
if (!authResponse.getStatusCode().is2xxSuccessful()) return null;
48+
49+
String body = authResponse.getBody();
50+
return body.contains("username") ? body.split(":")[1].replaceAll("[\"{} ]", "") : null;
51+
} catch (Exception e) {
52+
return null;
53+
}
54+
}
55+
56+
protected boolean charge(String username, String productId, int quantity) {
57+
HttpHeaders headers = new HttpHeaders();
58+
headers.setContentType(MediaType.APPLICATION_JSON);
59+
headers.set("X-Service-Secret", System.getenv("BILLING_SECRET"));
60+
61+
JSONObject payload = new JSONObject();
62+
payload.put("username", username);
63+
payload.put("productId", productId);
64+
payload.put("quantity", quantity);
65+
66+
HttpEntity<String> entity = new HttpEntity<>(payload.toString(), headers);
67+
try {
68+
ResponseEntity<String> billingResponse = restTemplate.postForEntity(
69+
billingServiceUrl + "/billing/charge", entity, String.class);
70+
return billingResponse.getStatusCode().is2xxSuccessful();
71+
} catch (Exception e) {
72+
return false;
73+
}
74+
}
75+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.example.orders.model;
2+
3+
public class OrderRequest {
4+
public String productId;
5+
public int quantity;
6+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package com.example.orders;
2+
3+
import com.example.orders.controller.OrderController;
4+
import com.example.orders.model.OrderRequest;
5+
import org.junit.jupiter.api.Test;
6+
import org.springframework.http.ResponseEntity;
7+
8+
import static org.junit.jupiter.api.Assertions.*;
9+
10+
class OrderControllerTests {
11+
12+
static class MockedOrderController extends OrderController {
13+
private final boolean validToken;
14+
private final boolean billingSuccess;
15+
private final String mockUser;
16+
17+
public MockedOrderController(boolean validToken, boolean billingSuccess, String mockUser) {
18+
this.validToken = validToken;
19+
this.billingSuccess = billingSuccess;
20+
this.mockUser = mockUser;
21+
}
22+
23+
@Override
24+
protected String validateToken(String token) {
25+
return validToken ? mockUser : null;
26+
}
27+
28+
@Override
29+
protected boolean charge(String username, String productId, int quantity) {
30+
return billingSuccess;
31+
}
32+
}
33+
34+
@Test
35+
void testOrderSuccess() {
36+
MockedOrderController controller = new MockedOrderController(true, true, "alice");
37+
38+
OrderRequest req = new OrderRequest();
39+
req.productId = "widget-123";
40+
req.quantity = 2;
41+
42+
ResponseEntity<String> response = controller.placeOrder(req, "Bearer faketoken");
43+
44+
assertEquals(200, response.getStatusCodeValue());
45+
assertTrue(response.getBody().contains("alice"));
46+
}
47+
48+
@Test
49+
void testInvalidToken() {
50+
MockedOrderController controller = new MockedOrderController(false, true, "alice");
51+
52+
OrderRequest req = new OrderRequest();
53+
req.productId = "widget-123";
54+
req.quantity = 2;
55+
56+
ResponseEntity<String> response = controller.placeOrder(req, "Bearer faketoken");
57+
58+
assertEquals(401, response.getStatusCodeValue());
59+
}
60+
61+
@Test
62+
void testBillingFailure() {
63+
MockedOrderController controller = new MockedOrderController(true, false, "alice");
64+
65+
OrderRequest req = new OrderRequest();
66+
req.productId = "widget-123";
67+
req.quantity = 2;
68+
69+
ResponseEntity<String> response = controller.placeOrder(req, "Bearer faketoken");
70+
71+
assertEquals(402, response.getStatusCodeValue());
72+
}
73+
74+
@Test
75+
void testMissingAuthHeader() {
76+
MockedOrderController controller = new MockedOrderController(true, true, "alice");
77+
78+
OrderRequest req = new OrderRequest();
79+
req.productId = "widget-123";
80+
req.quantity = 2;
81+
82+
ResponseEntity<String> response = controller.placeOrder(req, null);
83+
84+
assertEquals(401, response.getStatusCodeValue());
85+
}
86+
}

0 commit comments

Comments
 (0)