# (DD02) CurrencyRateAPI — Class Detailed Design [75 LOC]

| Field | Value |
|-------|-------|
| Fully Qualified Name | `com.github.blaxk3.api.CurrencyRateAPI` |
| Layer | Controller |
| Module | `com.github.blaxk3.api` |

## Class Overview

`CurrencyRateAPI` is a REST client class that wraps the ExchangeRate-API (v6.exchangerate-api.com) to fetch real-time foreign exchange rates. It loads its API key from a `config.properties` file, builds authenticated URLs, and performs HTTP GET requests to retrieve JSON responses containing currency conversion data. The class uses `HttpURLConnection` for raw HTTP communication and Gson's `JsonParser` for JSON parsing, making it a lightweight, dependency-minimal API wrapper.

---

## Method: getJsonObject()

### 1. Role

Performs an HTTP GET request against a provided URL and returns the JSON response body as a `JsonObject`. This is the core network I/O method — all other public methods ultimately delegate to this one.

### 2. Processing Pattern

```mermaid
flowchart TD
    A["Start"] --> B["Open HttpURLConnection from URL"]
    B --> C["Set HTTP method to GET"]
    C --> D["Execute request - get response code"]
    D --> E{Response code is 200?}
    E -->|Yes| F["Parse JSON from response InputStream"]
    F --> G["Return JsonObject"]
    E -->|No| H["Log error with response code"]
    H --> I["Return null"]
    D --> J["Exception caught"]
    J --> K["Log error message"]
    K --> I
```

### 3. Parameter Analysis

| Parameter | Type | Direction | Description |
|-----------|------|-----------|-------------|
| `url` | `java.net.URL` | In | The full API endpoint URL. Must include the API key (typically produced by `getURL()`). Caller must not pass `null`. |

### 4. CRUD Operations / Called Service

| Called Method / Service | Class | I/O | Description |
|------------------------|-------|-----|-------------|
| `getApiKeyService()` | `CurrencyRateAPI` | Read | Not directly called here; called by callers to compose the URL. |
| `HttpURLConnection.openConnection()` | `java.net` | IO | Opens a network connection and sends the GET request. |
| `request.getContent()` | `java.net` | Read | Returns the response body as an `InputStream`. |
| `JsonParser.parseReader()` | `com.google.gson` | Parse | Converts the `InputStream` stream to a `JsonElement`. |
| `JsonElement.getAsJsonObject()` | `com.google.gson` | Cast | Extracts the root as a `JsonObject`. |

### 5. Dependency Trace

| Caller | File | Usage |
|--------|------|-------|
| `getCurrencyCode()` | `CurrencyRateAPI.java` | Fetches conversion rates JSON |
| `convert()` | `CurrencyRateAPI.java` | Fetches pair conversion result JSON |

### 6. Per-Branch Detail Blocks

**Branch: HttpURLConnection throws IOException (or other Exception)**

- Triggered when the URL is malformed, the host is unreachable, or a network I/O error occurs.
- `catch (Exception e)` catches **any** `Exception` subclass (broad catch-all).
- Logs: `ERROR "An error occurred during the API request"` with the full stack trace.
- Returns: `null`.

**Branch: HTTP response is 200 (OK)**

- Executes: `JsonParser.parseReader(new InputStreamReader((InputStream) request.getContent()))` — reads the response body character-by-character into a `JsonElement`.
- Casts via `getAsJsonObject()` to obtain a `JsonObject`.
- Returns: the parsed `JsonObject` directly.
- Note: no explicit validation of JSON structure beyond the cast. If the response body is not a JSON object, `getAsJsonObject()` will throw (caught by the outer catch).

**Branch: HTTP response is NOT 200**

- Logs: `ERROR "GET request failed with response code: {responseCode}"` where `{responseCode}` is the actual HTTP status code (e.g., 401, 404, 500).
- Falls through to `return null` at the end of the method.

---

## Method: getApiKeyService()

### 1. Role

Loads the API key from the application's `config.properties` resource file. The returned key is embedded into the base URL for all ExchangeRate-API calls. This method isolates configuration loading so the property file can be swapped or updated without code changes.

### 2. Processing Pattern

```mermaid
flowchart TD
    A["Start"] --> B["Create new Properties instance"]
    B --> C["Load 'config.properties' from classpath"]
    C --> D{Input stream is null?}
    D -->|Yes| E["Log error: Unable to find config.properties"]
    E --> F["Return null"]
    D -->|No| G["Load properties from stream"]
    G --> H["Extract property 'API_KEY'"]
    H --> I["Return API_KEY value"]
    G --> J["IOException caught"]
    J --> K["Log error: Error loading config.properties"]
    K --> F
```

### 3. Parameter Analysis

| Parameter | Type | Direction | Description |
|-----------|------|-----------|-------------|
| *(none)* | — | — | Takes no parameters. Reads from classpath resource. |

### 4. CRUD Operations / Called Service

| Called Method / Service | Class | I/O | Description |
|------------------------|-------|-----|-------------|
| `getClass().getClassLoader().getResourceAsStream("config.properties")` | `java.lang.Class` | IO | Loads `config.properties` from the classpath root. |
| `Properties.load(InputStream)` | `java.util` | Read | Parses the `InputStream` into key-value pairs. |
| `Properties.getProperty("API_KEY")` | `java.util` | Read | Extracts the value associated with key `"API_KEY"`. |
| `getResourceAsStream` returns `null` | — | Check | Indicates the file is missing from the classpath. |

### 5. Dependency Trace

| Caller | File | Usage |
|--------|------|-------|
| `getURL()` | `CurrencyRateAPI.java` | Appends the API key to the base URL |

### 6. Per-Branch Detail Blocks

**Branch: `config.properties` not found on classpath**

- Triggered when `getResourceAsStream("config.properties")` returns `null`.
- Logs: `ERROR "Unable to find config.properties"` (no stack trace, as this is expected if the file is absent).
- Returns: `null`.
- Consequence: downstream `getURL()` will produce a malformed URL with `null` interpolated.

**Branch: Properties file found and loaded successfully**

- Executes `properties.load(input)` inside a try-with-resources block (auto-closes `InputStream`).
- Reads the `"API_KEY"` property value via `properties.getProperty("API_KEY")`.
- Returns: the raw `String` value of the API key.

**Branch: IOException during property loading**

- Triggered by I/O errors while reading the properties file (e.g., permission denied, corrupted file).
- Logs: `ERROR "Error loading config.properties"` with the full exception stack trace.
- Returns: `null`.

---

## Method: getCurrencyCode()

### 1. Role

Fetches the list of all supported currency codes from the ExchangeRate-API. It calls the `/latest/USD` endpoint, extracts the `conversion_rates` object, and returns its keys as a `String[]` array. This is typically used to populate currency-selection dropdowns or validate currency codes before a conversion request.

### 2. Processing Pattern

```mermaid
flowchart TD
    A["Start"] --> B["Compose URL: getURL() + /latest/USD"]
    B --> C["Call getJsonObject(url)"]
    C --> D{jsonObject is not null?}
    D -->|No| J["Return null"]
    D -->|Yes| E{"Has key 'conversion_rates'?"}
    E -->|No| J
    E -->|Yes| F["Extract JsonObject from 'conversion_rates'"]
    F --> G{"code is not null and keySet not empty?"}
    G -->|No| J
    G -->|Yes| H["Convert keySet to String[]"]
    H --> I["Return String[]"]
```

### 3. Parameter Analysis

| Parameter | Type | Direction | Description |
|-----------|------|-----------|-------------|
| *(none)* | — | — | Takes no parameters. The endpoint is fully composed internally. |

**Declares throws:**
| Exception | Reason |
|-----------|--------|
| `MalformedURLException` | URL construction from composed string may fail |
| `URISyntaxException` | URI conversion may fail |

### 4. CRUD Operations / Called Service

| Called Method / Service | Class | I/O | Description |
|------------------------|-------|-----|-------------|
| `getURL()` | `CurrencyRateAPI` | Read | Produces the base API URL with API key |
| `getJsonObject(URL)` | `CurrencyRateAPI` | IO | Executes HTTP GET and returns parsed JSON |
| `JsonObject.has("conversion_rates")` | `com.google.gson` | Read | Checks if the response contains a conversion_rates field |
| `JsonObject.getAsJsonObject("conversion_rates")` | `com.google.gson` | Read | Extracts the conversion rates object |
| `JsonObject.keySet().toArray(new String[0])` | `com.google.gson` | Transform | Converts currency code keys to a string array |

### 5. Dependency Trace

| Caller | File | Usage |
|--------|------|-------|
| *(none found)* | — | No callers discovered in the codebase. This method is likely called from application entry points or a UI layer outside the current project scope. |

### 6. Per-Branch Detail Blocks

**Branch: getJsonObject() returns null (network error or non-200 response)**

- The `if (jsonObject != null && ...)` short-circuits.
- Falls through to the method's end.
- Returns: `null`.

**Branch: JsonObject does not contain "conversion_rates" key**

- The `jsonObject.has("conversion_rates")` check returns `false`.
- Falls through to the method's end.
- Returns: `null`.

**Branch: conversion_rates object is null or has empty keySet**

- `code != null && !code.keySet().isEmpty()` fails.
- Falls through to the method's end.
- Returns: `null`.

**Branch: All checks pass (successful extraction)**

- Extracts the `conversion_rates` JsonObject via `getAsJsonObject("conversion_rates")`.
- Calls `code.keySet().toArray(new String[0])` — the Gson JsonObject stores currency codes as keys (e.g., `"USD"`, `"EUR"`, `"JPY"`).
- Returns: a `String[]` containing all supported currency codes. The order is determined by the internal iteration order of `JsonObject.keySet()` (not guaranteed to be alphabetical).
