Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

# Maven
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar

# IDE
.idea/
*.iml
.vscode/
.settings/
.project
.classpath

# OS
.DS_Store
Thumbs.db

# Spring Boot
*.tmp
*.bak
143 changes: 142 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,142 @@
# coding-agent-output-comparison
# User API - REST API for User Management

A Spring Boot REST API implementation based on detailed specifications for user management with JWT authentication.

## Features

- **GET /api/users/{id}** - Retrieve user by ID
- JWT-based authentication and authorization
- Input validation for path parameters
- Comprehensive error handling (400, 401, 403, 404)
- H2 in-memory database with JPA/Hibernate
- Complete test coverage

## API Specification

### Endpoint
- **URL**: `GET /api/users/{id}`
- **Method**: GET
- **Path Parameter**: `id` (Long, 0以上, required)
- **Authentication**: JWT Bearer token required

### Request Example
```bash
GET /api/users/123
Authorization: Bearer {JWT}
```

### Response Examples

#### Success (200 OK)
```json
{
"id": 123,
"name": "田中 太郎",
"email": "tanaka@example.com",
"role": "admin"
}
```

#### Error Responses
| Status | Response | Description |
|--------|----------|-------------|
| 404 | `{"error": "User not found"}` | User does not exist |
| 401 | `{"error": "Unauthorized"}` | Authentication failed |
| 403 | `{"error": "Forbidden"}` | Insufficient permissions |
| 400 | `{"error": "Invalid ID"}` | Invalid ID format or negative value |

## Database Schema

```sql
CREATE TABLE users (
id BIGINT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(255),
role VARCHAR(20)
);
```

## Running the Application

### Prerequisites
- Java 17+
- Maven 3.6+

### Start the Application
```bash
mvn spring-boot:run
```

The application will start on `http://localhost:8080`

### Generate JWT Token (for testing)
```bash
curl -X POST "http://localhost:8080/auth/token" \
-H "Content-Type: application/json" \
-d '{"username": "testuser", "role": "admin"}'
```

### Test the API
```bash
# Get JWT token
TOKEN=$(curl -s -X POST "http://localhost:8080/auth/token" \
-H "Content-Type: application/json" \
-d '{"username": "testuser", "role": "admin"}' | jq -r '.token')

# Test successful request
curl -X GET "http://localhost:8080/api/users/1" \
-H "Authorization: Bearer $TOKEN"

# Test user not found
curl -X GET "http://localhost:8080/api/users/999" \
-H "Authorization: Bearer $TOKEN"

# Test unauthorized access
curl -X GET "http://localhost:8080/api/users/1"

# Test invalid ID
curl -X GET "http://localhost:8080/api/users/abc" \
-H "Authorization: Bearer $TOKEN"
```

## Running Tests

```bash
mvn test
```

All tests validate the API behavior including:
- Successful user retrieval
- User not found scenarios
- Authentication and authorization
- Input validation
- Error handling

## Technology Stack

- **Spring Boot 3.2.0** - Application framework
- **Spring Security** - Authentication and authorization
- **Spring Data JPA** - Database access layer
- **H2 Database** - In-memory database
- **JWT (JJWT)** - JSON Web Token implementation
- **JUnit 5** - Testing framework

## Project Structure

```
src/
├── main/
│ ├── java/com/example/userapi/
│ │ ├── controller/ # REST controllers
│ │ ├── entity/ # JPA entities
│ │ ├── exception/ # Custom exceptions and handlers
│ │ ├── repository/ # Data repositories
│ │ ├── security/ # Security configuration and JWT utilities
│ │ └── UserApiApplication.java
│ └── resources/
│ ├── application.properties
│ └── data.sql # Sample data
└── test/
└── java/com/example/userapi/
└── controller/ # API tests
```
80 changes: 80 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>user-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>user-api</name>
<description>REST API for user management with JWT authentication</description>

<properties>
<java.version>17</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
13 changes: 13 additions & 0 deletions src/main/java/com/example/userapi/UserApiApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.userapi;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class UserApiApplication {

public static void main(String[] args) {
SpringApplication.run(UserApiApplication.class, args);
}

}
24 changes: 24 additions & 0 deletions src/main/java/com/example/userapi/controller/AuthController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.example.userapi.controller;

import com.example.userapi.security.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@RequestMapping("/auth")
public class AuthController {

@Autowired
private JwtUtil jwtUtil;

@PostMapping("/token")
public Map<String, String> generateToken(@RequestBody Map<String, String> credentials) {
String username = credentials.get("username");
String role = credentials.getOrDefault("role", "user");

String token = jwtUtil.generateToken(username, role);
return Map.of("token", token);
}
}
28 changes: 28 additions & 0 deletions src/main/java/com/example/userapi/controller/UserController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.example.userapi.controller;

import com.example.userapi.entity.User;
import com.example.userapi.exception.InvalidIdException;
import com.example.userapi.exception.UserNotFoundException;
import com.example.userapi.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/users")
public class UserController {

@Autowired
private UserRepository userRepository;

@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
// Validate ID (must be 0 or greater)
if (id < 0) {
throw new InvalidIdException("Invalid ID");
}

// Find user in database
return userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException("User not found"));
}
}
Loading