# Com / Github / Blaxk3 / Api

## Overview

This module provides a Java client for fetching live foreign exchange rates from the [exchangerate-api.com](https://www.exchangerate-api.com/) REST service. It is a single-class utility (`CurrencyRateAPI`) that handles configuration loading, HTTP communication, and JSON parsing — exposing two high-level operations: listing available currency codes and converting an amount between two currencies.

## Key Classes

### `CurrencyRateAPI`

**Source:** [src/main/java/com/github/blaxk3/api/CurrencyRateAPI.java](src/main/java/com/github/blaxk3/api/CurrencyRateAPI.java)

This is the only class in the package and serves as the facade for all interaction with the exchange rate service. It encapsulates API key management, HTTP request setup, and JSON response parsing using [Gson](https://github.com/google/gson).

#### Methods

**`getApiKeyService()` — lines 23–36**

Reads the `API_KEY` property from `config.properties` on the classpath. Returns `null` if the file is missing or unreadable (logged at `ERROR`).

- **Parameters:** None
- **Returns:** `String` — the raw API key, or `null` on failure
- **Side effects:** Logs errors but never throws; the `InputStream` is closed via try-with-resources

This method is called internally by `getURL()`. If `config.properties` is absent, the entire API becomes non-functional because the URL will contain `null` in the path.

**`getURL()` — lines 38–40**

Assembles the base URL for the exchangerate-api endpoint:

```
https://v6.exchangerate-api.com/v6/{API_KEY}
```

- **Returns:** `String` — the full base URL

This is a simple template that delegates to `getApiKeyService()`. Any failure in that call propagates as a `null`-containing URL string.

**`getJsonObject(URL url)` — lines 42–58**

Sends an HTTP `GET` request to the supplied URL and parses the response body into a Gson `JsonObject`.

- **Parameters:** `java.net.URL` — the endpoint to call
- **Returns:** `JsonObject` on success, `null` on failure
- **Failure modes:**
  - Non-`200 OK` response → logs the response code, returns `null`
  - Any exception during connection/response → logs and returns `null`

The method uses `HttpURLConnection` directly (no connection pooling, no timeout configuration). The response is read via `request.getContent()`, which wraps it in an `InputStreamReader` and feeds it to `JsonParser.parseReader()`.

**`getCurrencyCode()` — lines 61–70**

Fetches the list of all supported ISO 4217 currency codes from the `/latest/USD` endpoint.

- **Parameters:** None
- **Returns:** `String[]` of currency code strings (e.g. `["USD", "EUR", "GBP", ...]`), or `null` on failure
- **Throws:** `MalformedURLException`, `URISyntaxException`

The flow is: build the URL → call `getJsonObject()` → extract the `conversion_rates` object → return its key set as a String array. This gives callers a complete inventory of the currencies they can use in conversion requests.

**`convert(String foreignCurrency1, String foreignCurrency2, BigDecimal amount)` — lines 72–74**

Performs a currency conversion. Internally it calls the `/pair/{from}/{to}/{amount}` endpoint and extracts the `conversion_result` field.

- **Parameters:**
  - `foreignCurrency1` — source currency code (e.g. `"USD"`)
  - `foreignCurrency2` — target currency code (e.g. `"JPY"`)
  - `amount` — amount as `BigDecimal` (preserves precision)
- **Returns:** `String` — the converted amount as a string representation of a number
- **Throws:** `MalformedURLException`, `URISyntaxException`

**Important:** This method does not null-check the `JsonObject` or the `conversion_result` field. If the API returns an error response (e.g. invalid currency codes, rate limit exceeded), this will throw a `NullPointerException` or `IllegalStateException` from Gson. Callers should handle exceptions defensively.

## How It Works

### End-to-end conversion flow

```mermaid
flowchart LR
    Caller["Caller"] --> convert["convert(from, to, amount)"]
    convert --> buildURL["getURL() + /pair/{from}/{to}/{amount}"]
    buildURL --> req["getJsonObject(url)"]
    req --> http["HttpURLConnection GET"]
    http --> resp["200 OK"]
    resp --> json["JsonParser.parseReader()"]
    json --> result["conversion_result extracted"]
    result --> Caller
```

1. **Configuration:** `convert()` or `getCurrencyCode()` triggers `getURL()`, which calls `getApiKeyService()` to load the API key from `config.properties`.
2. **URL assembly:** The method appends the specific endpoint segment (`/latest/USD` or `/pair/...`) to the base URL.
3. **HTTP request:** `getJsonObject()` opens an `HttpURLConnection`, sends a `GET`, and checks for a `200 OK` response.
4. **JSON parsing:** The response stream is parsed into a Gson `JsonObject`.
5. **Extraction:** For `getCurrencyCode()`, the `conversion_rates` keys are returned. For `convert()`, the `conversion_result` field is returned as a string.

### Architecture diagram

```mermaid
flowchart TD
    A["CurrencyRateAPI"] --> B["getApiKeyService()
Loads API_KEY from config.properties"]
    A --> C["getURL()
Builds base URL with API key"]
    A --> D["getJsonObject(URL)
Sends GET request, returns JsonObject"]
    A --> E["getCurrencyCode()
Fetches all ISO currency codes
from /latest/USD endpoint"]
    A --> F["convert()
Converts amount between currencies
via /pair endpoint"]
```

### Design decisions and trade-offs

- **Thin facade:** The entire module is one class with five methods. There are no interfaces, builders, or dependency injection — it is designed for straightforward, imperative use.
- **No connection pooling:** Each call creates a fresh `HttpURLConnection`. Under high concurrency this will open many sockets and may exhaust resources.
- **No timeouts:** Neither connect timeout nor read timeout is configured on the `HttpURLConnection`. A slow or unresponsive server will block the calling thread indefinitely.
- **Error returns are silent:** Most methods return `null` on failure rather than throwing. This avoids checked exceptions for callers but makes it easy to accidentally dereference null. The `convert()` method is the exception — it does not null-check and will throw on bad input or response.

## Data Model

This module does not define any custom entity classes. All data flows through standard Java types:

| Type | Role |
|------|------|
| `String` | Currency codes (ISO 4217, e.g. `"USD"`, `"EUR"`) |
| `String[]` | Array of currency codes returned by `getCurrencyCode()` |
| `BigDecimal` | Monetary amount — preserves precision across conversions |
| `JsonObject` | Intermediate parsed representation of the API JSON response (Gson type) |
| `String` (return of `convert`) | The numeric result of the conversion, returned as a string |

### Expected API response shape (abridged)

The `/pair/USD/JPY/100` endpoint returns JSON roughly equivalent to:

```json
{
  "conversion_result": "14832.50",
  "conversion_code": "USD",
  "target_code": "JPY",
  "conversion_amount": 100,
  "time_of_last_updated": "..."
}
```

The `convert()` method only cares about `conversion_result`. The `getCurrencyCode()` method consumes the `conversion_rates` object from the `/latest/USD` endpoint, which looks like:

```json
{
  "conversion_rates": {
    "USD": 1,
    "EUR": 0.92,
    "GBP": 0.79,
    ...
  }
}
```

## Dependencies and Integration

### External dependencies

| Dependency | Usage |
|------------|-------|
| `com.google.gson:JsonObject, JsonParser` | Parsing the API JSON responses |
| `org.slf4j:Logger, LoggerFactory` | Logging errors for missing config and failed requests |
| `java.net.HttpURLConnection` | Making HTTP GET requests to the exchangerate-api |
| `java.util.Properties` | Loading `config.properties` from the classpath |

### Configuration

This module expects a `config.properties` file on the classpath containing at least:

```properties
API_KEY=your_exchangerate_api_key_here
```

The file is loaded relative to the classloader that loaded `CurrencyRateAPI`. In a standard Maven project layout, place it at `src/main/resources/config.properties`.

### Integration points

This class is designed to be instantiated and called directly from other parts of the application. It has no dependencies on Spring, CDI, or other frameworks. To use it:

```java
CurrencyRateAPI api = new CurrencyRateAPI();

// Get available currencies
String[] currencies = api.getCurrencyCode();

// Convert 100 USD to JPY
String result = api.convert("USD", "JPY", new BigDecimal("100"));
```

## Notes for Developers

- **Thread safety:** `CurrencyRateAPI` is effectively immutable after construction (no mutable instance state), so it can be safely shared across threads. However, the underlying `HttpURLConnection` is not thread-safe — do not share connections.
- **Exception handling on `convert()`:** As noted above, `convert()` does not null-check. Before using the result in production, consider wrapping it in a try/catch or using `getJsonObject()` directly to inspect the response.
- **API rate limits:** The free tier of exchangerate-api.com has rate limits. Caching results from `getCurrencyCode()` is recommended since currency codes change infrequently.
- **Missing timeouts:** If this module is called from a web endpoint or user-facing thread, consider adding connection and read timeouts to prevent thread starvation. Wrap the call in a `CompletableFuture` with a timeout, or switch to a library like OkHttp or Apache HttpClient.
- **No authentication on the caller side:** The API key is embedded in the URL path. There is no separate authentication header. Be careful not to log the full URL containing the key in production.
- **Extending the module:** To add new endpoints (e.g. historical rates, time-series), follow the existing pattern — add a public method that builds a URL and calls `getJsonObject()`. Consider introducing a return type class to make the data easier to work with than raw `String` or `JsonObject`.
