# CurrencyRateAPI -- Class Detailed Design [41 LOC]

| Field | Value |
|-------|-------|
| Fully Qualified Name | `com.github.blaxk3.api.CurrencyRateAPI` |
| Layer | Controller / Service (API client) |
| Module | `com.github.blaxk3.api` |

## Class Overview

`CurrencyRateAPI` is an API client wrapper for the ExchangeRate API (v6, hosted at exchangerate-api.com). It reads an API key from a local `config.properties` file, constructs authenticated URLs, performs HTTP GET requests to the exchange rate service, and returns parsed JSON responses. The class supports querying available currency codes and converting between currency pairs via the provider's `/latest/` and `/pair/` endpoints. It uses Gson for JSON parsing and SLF4J for structured logging.

---

## Method: getJsonObject(URL url)

### 1. Role

Sends an HTTP GET request to the given URL and returns the JSON response body as a Gson `JsonObject`. This is the core network I/O method that all other API interactions route through.

### 2. Processing Pattern

```mermaid
flowchart TD
    Start["Start: getJsonObject"] --> OpenConn["Open HttpURLConnection"]
    OpenConn --> SetMethod["Set request method to GET"]
    SetMethod --> GetResp["Get response code"]
    GetResp --> CheckCode{responseCode == HTTP_OK?}
    CheckCode -->|"Yes"| ParseJSON["Parse JSON from InputStream"]
    ParseJSON --> ReturnObject["Return JsonObject"]
    CheckCode -->|"No"| LogFail["Log error: GET request failed w/ code"]
    LogFail --> ReturnNull["Return null"]
```

### 3. Parameter Analysis

| Parameter | Type | Description |
|-----------|------|-------------|
| `url` | `java.net.URL` | The full API endpoint URL to call (e.g., the URL returned by `getURL()` plus a path segment like `/latest/USD`) |

### 4. CRUD Operations / Called Services

| Operation | Detail |
|-----------|--------|
| HTTP GET | Calls `HttpURLConnection.openConnection()`, sets method to GET, reads response |
| JSON Parsing | Uses `com.google.gson.JsonParser.parseReader(...)` to parse the HTTP response body |
| Logging | Uses SLF4J `logger.error()` for both HTTP error codes and exceptions |

### 5. Dependency Trace

| Caller | File |
|--------|------|
| `getCurrencyCode()` | `CurrencyRateAPI.java` (line ~63) |
| `convert()` | `CurrencyRateAPI.java` (line ~72) |

### 6. Per-Branch Detail Blocks

**Path A -- Successful HTTP 200 response**
1. Opens a `HttpURLConnection` from the provided URL.
2. Sets the request method to `"GET"`.
3. Calls `request.getResponseCode()` to check the HTTP status.
4. If status equals `HttpURLConnection.HTTP_OK` (200), it retrieves the response content via `request.getContent()`, wraps it in an `InputStreamReader`, and parses the JSON with `JsonParser.parseReader(...)`.
5. The parsed `JsonElement` is cast to `JsonObject` and returned.

**Path B -- Non-200 HTTP response**
1. After obtaining the response code, if it differs from `HTTP_OK`, the method logs an error message `"GET request failed with response code: {responseCode}"` including the actual status code.
2. Falls through to the end and returns `null`.

**Path C -- Exception during connection**
1. If any exception is thrown (e.g., `IOException`, `RuntimeException`), the catch block logs `"An error occurred during the API request"` with the exception as a context parameter.
2. Falls through to return `null`.

**Null-safety**: The method does not validate the `url` parameter for null -- passing `null` will throw a `NullPointerException` at `url.openConnection()`.

---

## Method: getApiKeyService()

### 1. Role

Reads the API key from the application's embedded `config.properties` resource file and returns it as a `String`. This key is used to authenticate requests to the ExchangeRate API. The original Japanese comment reads: "APIキーを取得する" (Retrieve the API key).

### 2. Processing Pattern

```mermaid
flowchart TD
    Start["Start: getApiKeyService"] --> LoadProps["Load config.properties via getClassLoader"]
    LoadProps --> CheckNull{input == null?}
    CheckNull -->|"Yes"| LogError1["Log: Unable to find config.properties"]
    CheckNull -->|"No"| LoadIntoProps["properties.load(input)"]
    LogError1 --> ReturnNull1["Return null"]
    LoadIntoProps --> GetProp["Return properties.getProperty(API_KEY)"]
    GetProp --> ReturnKey["Return API_KEY value"]
```

### 3. Parameter Analysis

| Parameter | Type | Description |
|-----------|------|-------------|
| *(none)* | -- | Reads from classpath resource `config.properties` |

### 4. CRUD Operations / Called Services

| Operation | Detail |
|-----------|--------|
| File I/O | Reads classpath resource `config.properties` via `getClassLoader().getResourceAsStream("config.properties")` |
| Properties | Uses `java.util.Properties.load(InputStream)` to parse key-value pairs |
| Logging | Uses SLF4J `logger.error()` for missing file or IO exception |

### 5. Dependency Trace

| Caller | File |
|--------|------|
| `getURL()` | `CurrencyRateAPI.java` (line ~40) |
| `getCurrencyCode()` | `CurrencyRateAPI.java` (line ~63, indirect via `getURL()`) |
| `convert()` | `CurrencyRateAPI.java` (line ~72, indirect via `getURL()`) |

### 6. Per-Branch Detail Blocks

**Path A -- config.properties not found**
1. `getClassLoader().getResourceAsStream("config.properties")` returns `null`.
2. Logs error `"Unable to find config.properties"` via SLF4J.
3. Returns `null`.

**Path B -- config.properties found and loaded successfully**
1. Creates a new `Properties` object.
2. Opens the `config.properties` resource as an `InputStream` wrapped in a try-with-resources block.
3. Calls `properties.load(input)` to parse the key-value pairs.
4. Returns `properties.getProperty("API_KEY")` -- the value associated with the `"API_KEY"` key.

**Path C -- IOException during load**
1. If `properties.load(input)` throws an `IOException`, the catch block logs `"Error loading config.properties"` with the exception as a context parameter.
2. Returns `null`.

**Properties file format** (`src/main/resources/config.properties`):
```
# You can get free key from -> (https://www.exchangerate-api.com/)
API_KEY = YOUR_API_KEY
```

---

## Method: getCurrencyCode()

### 1. Role

Calls the ExchangeRate API's `/latest/USD` endpoint to retrieve the list of supported currency codes. The method returns an array of currency code strings (e.g., `["USD", "EUR", "JPY", ...]`) extracted from the `conversion_rates` JSON object. The original comment reads: "為替通貨コードの配列を取得する" (Retrieve an array of exchange currency codes).

### 2. Processing Pattern

```mermaid
flowchart TD
    Start["Start: getCurrencyCode"] --> BuildURL["Build URL: getURL() + /latest/USD"]
    BuildURL --> CallAPI["Call getJsonObject(url)"]
    CallAPI --> CheckNull{jsonObject != null?}
    CheckNull -->|"No"| ReturnNull["Return null"]
    CheckNull -->|"Yes"| CheckRates{has conversion_rates?}
    CheckRates -->|"No"| ReturnNull
    CheckRates -->|"Yes"| GetRates["Get conversion_rates JsonObject"]
    GetRates --> CheckEmpty{keySet not empty?}
    CheckEmpty -->|"No"| ReturnNull
    CheckEmpty -->|"Yes"| ToArray["Return keySet().toArray(new String[0])"]
```

### 3. Parameter Analysis

| Parameter | Type | Description |
|-----------|------|-------------|
| *(none)* | -- | Constructs internal URL from `getURL()` + `"/latest/USD"` |

| Checked Value | Source | Description |
|---------------|--------|-------------|
| `jsonObject` | `getJsonObject(...)` | Parsed response from the ExchangeRate API |
| `"conversion_rates"` key | `jsonObject.has(...)` | Boolean flag indicating if currency data is present |
| `conversion_rates` object | `jsonObject.getAsJsonObject("conversion_rates")` | Inner object whose keys are ISO 4217 currency codes |

### 4. CRUD Operations / Called Services

| Operation | Detail |
|-----------|--------|
| HTTP GET | Calls `getJsonObject()` which performs HTTP GET to `/latest/USD` |
| JSON traversal | Accesses `conversion_rates` nested `JsonObject` |
| Array conversion | Uses `JsonObject.keySet().toArray(new String[0])` |

### 5. Dependency Trace

| Caller | File |
|--------|------|
| (No callers found) | Standalone utility method -- likely called from UI layer or test code |

### 6. Per-Branch Detail Blocks

**Path A -- No API response or API returned null**
1. `getJsonObject(new URI(getURL() + "/latest/USD").toURL())` returns `null` (network error, non-200 response, or config error).
2. The null guard `jsonObject != null` fails.
3. Returns `null`.

**Path B -- No `conversion_rates` key in response**
1. The `jsonObject` is valid but does not contain the `"conversion_rates"` key.
2. The guard `jsonObject.has("conversion_rates")` fails.
3. Returns `null`.

**Path C -- `conversion_rates` object is null or empty**
1. The `conversion_rates` key exists but `getAsJsonObject("conversion_rates")` returns `null` or an empty `JsonObject`.
2. The guard `code != null && !code.keySet().isEmpty()` fails.
3. Returns `null`.

**Path D -- Successful retrieval**
1. Builds the full URL: `"https://v6.exchangerate-api.com/v6/{API_KEY}/latest/USD"`.
2. Sends the request via `getJsonObject()`.
3. Extracts the `conversion_rates` `JsonObject` from the response.
4. Converts the `keySet()` (ISO 4217 currency codes like `"USD"`, `"EUR"`, `"JPY"`) to a `String[]` and returns it.

---
