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

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

## Class Overview

`CurrencyRateAPI` is a small utility-style API client that reads an ExchangeRate API key from `config.properties`, builds request URLs, and fetches currency conversion data as JSON. It centralizes HTTP access, JSON parsing, and currency-code discovery for the rest of the application. The class is stateless apart from its logger, and its methods rely on the runtime environment for configuration and remote API availability.

---

## Method: getApiKeyService()

### 1. Role

Loads the ExchangeRate API key from `config.properties` on the application classpath. This method is the configuration entry point for every downstream API call made by this class.

### 2. Processing Pattern

```mermaid
flowchart TD
    A["Start getApiKeyService"] --> B["Create Properties instance"]
    B --> C["Open config.properties from classpath"]
    C --> D{"Resource found"}
    D -->|No| E["Log error Unable to find config.properties"]
    E --> F["Return null"]
    D -->|Yes| G["Load properties from input stream"]
    G --> H{"IOException thrown"}
    H -->|Yes| I["Log error Error loading config.properties and exception"]
    I --> F
    H -->|No| J["Read property API_KEY"]
    J --> K["Return API_KEY value"]
```

### 3. Parameter Analysis

| Parameter | Type | Required | Validation / Notes |
|-----------|------|----------|--------------------|
| None | - | - | This method takes no arguments. It depends on the presence of `config.properties` in the classpath and on the `API_KEY` property being defined. |

### 4. CRUD Operations / Called Services

| Category | Target | Operation | Notes |
|----------|--------|-----------|-------|
| Read | `config.properties` | Load resource stream | Reads configuration from the classpath using the class loader. |
| Read | `Properties` | `load`, `getProperty` | Parses the properties file and extracts `API_KEY`. |
| Log | `logger` | `error` | Records missing resource or I/O failure. |

### 5. Dependency Trace

| Direction | Member / Service | Evidence | Impact |
|-----------|-------------------|----------|--------|
| Internal caller | `getURL()` | `getURL()` concatenates the return value of this method into the base ExchangeRate API endpoint. | If this method returns `null`, every URL built from `getURL()` becomes invalid. |
| External dependency | `config.properties` | Loaded through `getClass().getClassLoader().getResourceAsStream("config.properties")`. | Missing or malformed configuration prevents API calls. |
| External dependency | SLF4J logger | Used to report missing config and I/O errors. | Operational visibility for configuration failures. |

### 6. Per-Branch Detail Blocks

- **Branch 1: `config.properties` resource is missing**
  - The class loader returns `null` for the input stream.
  - The method logs `Unable to find config.properties`.
  - The method returns `null` immediately.
  - Any caller that depends on a non-null API key will fail later when constructing URLs.

- **Branch 2: `config.properties` is present and loads successfully**
  - The properties object is populated from the stream.
  - The method reads the `API_KEY` property.
  - The returned string is the only value that the rest of the class needs to build API endpoints.
  - If `API_KEY` is absent, `getProperty("API_KEY")` returns `null` and the failure is deferred to URL construction.

- **Branch 3: I/O failure while reading the properties file**
  - Any `IOException` during `properties.load(input)` is caught.
  - The method logs `Error loading config.properties` together with the exception.
  - The method returns `null`.
  - This is a soft failure path; the class does not throw, so callers must handle `null` defensively.

---

## Method: getJsonObject(URL url)

### 1. Role

Sends a synchronous HTTP GET request to the supplied URL and converts the response body into a Gson `JsonObject`. This method is the main transport-and-parse primitive used by the class for both currency discovery and conversion.

### 2. Processing Pattern

```mermaid
flowchart TD
    A["Start getJsonObject"] --> B["Open HttpURLConnection from URL"]
    B --> C["Set request method to GET"]
    C --> D["Read HTTP response code"]
    D --> E{"Response code equals HTTP_OK"}
    E -->|Yes| F["Read content stream"]
    F --> G["Parse JSON using JsonParser"]
    G --> H["Convert root element to JsonObject"]
    H --> I["Return JsonObject"]
    E -->|No| J["Log error GET request failed with response code"]
    J --> K["Return null"]
    C --> L{"Exception thrown during connection or parsing"}
    L -->|Yes| M["Log error An error occurred during the API request and exception"]
    M --> K
```

### 3. Parameter Analysis

| Parameter | Type | Required | Validation / Notes |
|-----------|------|----------|--------------------|
| `url` | `URL` | Yes | Must point to a reachable ExchangeRate API endpoint. The method does not validate host, scheme, or path. |

### 4. CRUD Operations / Called Services

| Category | Target | Operation | Notes |
|----------|--------|-----------|-------|
| Read | `URL` | `openConnection` | Opens a live HTTP connection to the remote service. |
| Update | `HttpURLConnection` | `setRequestMethod("GET")` | Configures the request as an HTTP GET. |
| Read | `HttpURLConnection` | `getResponseCode`, `getContent` | Uses the response code to gate parsing and reads the response body on success. |
| Read | Gson `JsonParser` | `parseReader` | Converts the response stream into a JSON tree. |
| Log | `logger` | `error` | Reports non-200 responses and unexpected exceptions. |

### 5. Dependency Trace

| Direction | Member / Service | Evidence | Impact |
|-----------|-------------------|----------|--------|
| Internal caller | `getCurrencyCode()` | Calls `getJsonObject(new java.net.URI(getURL() + "/latest/" + "USD").toURL())`. | Currency-code discovery depends on successful JSON retrieval. |
| Internal caller | `convert(...)` | Calls `getJsonObject(new java.net.URI(getURL() + "/pair/...`).toURL())`. | Currency conversion depends on this method returning a populated JSON object. |
| External dependency | `HttpURLConnection` | Used for request execution and response handling. | Remote network, HTTP status, and stream availability determine behavior. |
| External dependency | Gson parser | Used to convert the response body into a JSON object. | Invalid JSON or non-JSON payloads cause parsing failure and a null return. |
| External dependency | SLF4J logger | Used for error reporting. | Provides observability for transport failures. |

### 6. Per-Branch Detail Blocks

- **Branch 1: HTTP 200 OK response**
  - The method opens a connection and sets the verb to GET.
  - It reads the response code and detects `HTTP_OK`.
  - It consumes the content stream with `InputStreamReader`.
  - `JsonParser.parseReader(...)` converts the payload into a `JsonElement`.
  - The root JSON element is converted to `JsonObject` and returned.
  - This is the only successful exit path.

- **Branch 2: Non-200 response code**
  - The connection succeeds, but the remote service does not return `HTTP_OK`.
  - The method logs `GET request failed with response code: {code}`.
  - No JSON is parsed because the status gate fails.
  - The method returns `null`.
  - Downstream callers must tolerate a missing response object.

- **Branch 3: Exception during request or parsing**
  - Any exception in connection setup, response access, stream handling, or parsing is caught by the broad `catch (Exception e)` block.
  - The method logs `An error occurred during the API request` with the exception.
  - The method returns `null`.
  - This includes network errors, malformed URLs, and unexpected runtime failures.

- **Branch 4: Response body is not valid JSON**
  - The code path is still inside the success branch until parsing occurs.
  - If the payload cannot be parsed, the exception is caught by the same broad handler.
  - The method logs the API request error and returns `null`.
  - The method does not attempt partial recovery or schema validation.

---

## Method: getCurrencyCode()

### 1. Role

Discovers the list of available currency codes by calling the ExchangeRate API latest-rates endpoint for USD and extracting the keys from the `conversion_rates` object. The method acts as a lightweight metadata lookup rather than a full conversion operation.

### 2. Processing Pattern

```mermaid
flowchart TD
    A["Start getCurrencyCode"] --> B["Build latest rates URL for USD"]
    B --> C["Convert URI to URL"]
    C --> D["Call getJsonObject"]
    D --> E{"JsonObject is not null"}
    E -->|No| F["Return null"]
    E -->|Yes| G{"Has conversion_rates field"}
    G -->|No| F
    G -->|Yes| H["Read conversion_rates as JsonObject"]
    H --> I{"conversion_rates exists and is not empty"}
    I -->|No| F
    I -->|Yes| J["Extract key set"]
    J --> K["Convert key set to String array"]
    K --> L["Return String array"]
```

### 3. Parameter Analysis

| Parameter | Type | Required | Validation / Notes |
|-----------|------|----------|--------------------|
| None | - | - | This method takes no arguments. It hardcodes the base currency as `USD` and relies on `getURL()` and `getJsonObject(...)`. |

### 4. CRUD Operations / Called Services

| Category | Target | Operation | Notes |
|----------|--------|-----------|-------|
| Read | ExchangeRate API latest endpoint | HTTP GET via `getJsonObject` | Retrieves latest currency conversion data for USD. |
| Read | JSON payload | `has`, `getAsJsonObject`, `keySet` | Navigates the `conversion_rates` object and extracts currency codes. |
| Transform | `Set<String>` | `toArray(new String[0])` | Converts the JSON key set into a string array. |

### 5. Dependency Trace

| Direction | Member / Service | Evidence | Impact |
|-----------|-------------------|----------|--------|
| Internal dependency | `getURL()` | Used to build `getURL() + "/latest/" + "USD"`. | Invalid API key or base URL makes discovery fail. |
| Internal dependency | `getJsonObject(URL)` | Used to fetch the remote JSON document. | All parsing depends on the transport method returning a `JsonObject`. |
| External dependency | ExchangeRate API schema | Requires a top-level `conversion_rates` object. | Schema changes or missing field cause a null return. |

### 6. Per-Branch Detail Blocks

- **Branch 1: Remote JSON is null**
  - `getJsonObject(...)` returns `null` because the request failed or the response was invalid.
  - The first `jsonObject != null` guard fails.
  - The method returns `null` without further processing.
  - This is the most basic failure case and prevents all downstream extraction.

- **Branch 2: JSON exists but does not contain `conversion_rates`**
  - The method checks `jsonObject.has("conversion_rates")`.
  - If the field is absent, it returns `null`.
  - No exception is thrown; the method treats the response as semantically incomplete.
  - This guards against unexpected API payloads.

- **Branch 3: `conversion_rates` is present but empty**
  - The method reads the field into `code` as a `JsonObject`.
  - If the object is `null` or its key set is empty, the method returns `null`.
  - The method does not return an empty array, so callers must handle `null` as the failure signal.
  - This design makes empty data indistinguishable from a missing response.

- **Branch 4: `conversion_rates` contains at least one entry**
  - The method collects all keys from the JSON object.
  - It converts the key set to a `String[]` using `toArray(new String[0])`.
  - The array is returned to the caller.
  - The returned values represent ISO-style currency codes supported by the API response.
