# Java SDK

Official Java SDK for KeyEnv.

Source: https://keyenv.dev/docs/sdks/java/

Official Java SDK for KeyEnv with Java 11+ support and async/await patterns.

> **Warning**
>
>   **Beta Release**: This SDK is currently in development. APIs may change before the stable 1.0 release.

## Installation

### Maven

Add the JitPack repository and dependency to your `pom.xml`:

```xml
<repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>

<dependency>
    <groupId>com.github.keyenv</groupId>
    <artifactId>java-sdk</artifactId>
    <version>v1.1.0</version>
</dependency>
```

### Gradle

Add the JitPack repository and dependency to your `build.gradle`:

```groovy
repositories {
    maven { url 'https://jitpack.io' }
}

dependencies {
    implementation 'com.github.keyenv:java-sdk:v1.1.0'
}
```

Or with Kotlin DSL (`build.gradle.kts`):

```kotlin
repositories {
    maven("https://jitpack.io")
}

dependencies {
    implementation("com.github.keyenv:java-sdk:v1.1.0")
}
```

## Quick Start

```java
import dev.keyenv.KeyEnv;

public class Application {
    public static void main(String[] args) {
        KeyEnv client = KeyEnv.create(System.getenv("KEYENV_TOKEN"));

        // Load secrets into system properties
        int count = client.loadEnv("your-project-id", "production");
        System.out.println("Loaded " + count + " secrets");

        // Access secrets via System.getProperty
        System.out.println(System.getProperty("DATABASE_URL"));
    }
}
```

## Initialize the Client

### Simple Initialization

```java
import dev.keyenv.KeyEnv;

KeyEnv client = KeyEnv.create(System.getenv("KEYENV_TOKEN"));
```

### Builder Pattern

For more control, use the builder:

```java
import dev.keyenv.KeyEnv;
import java.time.Duration;

KeyEnv client = KeyEnv.builder()
    .token(System.getenv("KEYENV_TOKEN"))
    .timeout(Duration.ofSeconds(60))
    .cacheTtl(Duration.ofMinutes(5))
    .build();
```

## Loading Secrets

### Load into System Properties

The simplest way to use secrets in your application:

```java
int count = client.loadEnv("project-id", "production");
System.out.println("Loaded " + count + " secrets");

// Access via System.getProperty
String dbUrl = System.getProperty("DATABASE_URL");
String apiKey = System.getProperty("API_KEY");
```

### Get Secrets as Map

Get secrets as a key-value map:

```java
import java.util.Map;

Map<String, String> secrets = client.exportSecretsAsMap("project-id", "production");
System.out.println(secrets.get("DATABASE_URL"));
System.out.println(secrets.get("API_KEY"));
```

### Export as List

Get secrets with metadata:

```java
import dev.keyenv.types.SecretWithValueAndInheritance;
import java.util.List;

List<SecretWithValueAndInheritance> secrets = client.exportSecrets("project-id", "production");
for (SecretWithValueAndInheritance secret : secrets) {
    System.out.println(secret.getKey() + "=" + secret.getValue());
}
```

## Managing Secrets

### Get a Single Secret

```java
import dev.keyenv.types.SecretWithValue;

SecretWithValue secret = client.getSecret("project-id", "production", "DATABASE_URL");
System.out.println(secret.getValue());
System.out.println(secret.getDescription());
```

### Set a Secret

Creates or updates a secret:

```java
client.setSecret("project-id", "production", "API_KEY", "sk_live_...");

// With description
client.setSecret("project-id", "production", "API_KEY", "sk_live_...", "Production API key");
```

### Delete a Secret

```java
client.deleteSecret("project-id", "production", "OLD_KEY");
```

## Bulk Operations

### Bulk Import

Import multiple secrets at once:

```java
import dev.keyenv.types.SecretInput;
import dev.keyenv.types.BulkImportOptions;
import dev.keyenv.types.BulkImportResult;
import java.util.List;

List<SecretInput> secrets = List.of(
    new SecretInput("DATABASE_URL", "postgres://localhost/mydb", null),
    new SecretInput("REDIS_URL", "redis://localhost:6379", null),
    new SecretInput("API_KEY", "sk_test_...", "Test API key")
);

BulkImportResult result = client.bulkImport(
    "project-id",
    "development",
    secrets,
    new BulkImportOptions(true) // overwrite existing
);

System.out.println("Created: " + result.getCreated() + ", Updated: " + result.getUpdated());
```

### Generate .env File

```java
import java.nio.file.Files;
import java.nio.file.Paths;

String envContent = client.generateEnvFile("project-id", "production");
Files.writeString(Paths.get(".env"), envContent);
```

## Projects & Environments

### List Projects

```java
import dev.keyenv.types.Project;
import java.util.List;

List<Project> projects = client.listProjects();
for (Project project : projects) {
    System.out.println(project.getName() + " (" + project.getId() + ")");
}
```

### Get Project Details

```java
import dev.keyenv.types.Project;

Project project = client.getProject("project-id");
System.out.println("Project: " + project.getName());
for (var env : project.getEnvironments()) {
    System.out.println("  - " + env.getName());
}
```

### List Environments

```java
import dev.keyenv.types.Environment;
import java.util.List;

List<Environment> environments = client.listEnvironments("project-id");
for (Environment env : environments) {
    System.out.println(env.getName());
}
```

## Async Operations

All methods have async variants that return `CompletableFuture`:

```java
import java.util.concurrent.CompletableFuture;

// Async secret loading
CompletableFuture<Integer> future = client.loadEnvAsync("project-id", "production");
future.thenAccept(count -> {
    System.out.println("Loaded " + count + " secrets");
});

// Async secrets retrieval
client.exportSecretsAsMapAsync("project-id", "production")
    .thenAccept(secrets -> {
        System.out.println(secrets.get("DATABASE_URL"));
    });
```

## Caching

Enable caching for better performance in high-throughput applications:

```java
import java.time.Duration;

KeyEnv client = KeyEnv.builder()
    .token(System.getenv("KEYENV_TOKEN"))
    .cacheTtl(Duration.ofMinutes(5))
    .build();

// First call fetches from API
client.exportSecrets("project-id", "production");

// Subsequent calls within TTL use cache
client.exportSecrets("project-id", "production");

// Clear cache when needed
client.clearCache("project-id", "production");
client.clearAllCache();
```

## Error Handling

```java
import dev.keyenv.KeyEnv;
import dev.keyenv.KeyEnvException;

try {
    var secret = client.getSecret("project-id", "production", "MISSING_KEY");
} catch (KeyEnvException e) {
    System.err.println("Error " + e.getStatus() + ": " + e.getMessage());

    if (e.getStatus() == 401) {
        System.err.println("Invalid or expired token");
    } else if (e.getStatus() == 403) {
        System.err.println("Access denied");
    } else if (e.getStatus() == 404) {
        System.err.println("Secret not found");
    }
}
```

## API Reference

### Constructor Options (Builder)

| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| `token` | `String` | Yes | - | Service token |
| `timeout` | `Duration` | No | `30s` | Request timeout |
| `cacheTtl` | `Duration` | No | `0` | Cache TTL (0 disables) |
| `baseUrl` | `String` | No | `https://api.keyenv.dev` | API base URL |

### Methods

| Method | Description |
|--------|-------------|
| `getCurrentUser()` | Get current user/token info |
| `listProjects()` | List all accessible projects |
| `getProject(id)` | Get project with environments |
| `listEnvironments(projectId)` | List environments in a project |
| `listSecrets(projectId, env)` | List secret keys (no values) |
| `exportSecrets(projectId, env)` | Export secrets with values |
| `exportSecretsAsMap(projectId, env)` | Export as key-value map |
| `getSecret(projectId, env, key)` | Get single secret |
| `setSecret(projectId, env, key, value)` | Create or update secret |
| `deleteSecret(projectId, env, key)` | Delete secret |
| `bulkImport(projectId, env, secrets, options)` | Bulk import secrets |
| `loadEnv(projectId, env)` | Load secrets into System properties |
| `generateEnvFile(projectId, env)` | Generate .env file content |
| `listPermissions(projectId, env)` | List permissions for an environment |
| `setPermission(projectId, env, userId, role)` | Set user's permission |
| `deletePermission(projectId, env, userId)` | Delete user's permission |
| `getMyPermissions(projectId)` | Get current user's permissions |
| `getProjectDefaults(projectId)` | Get default permissions |
| `setProjectDefaults(projectId, defaults)` | Set default permissions |

## Examples

### Spring Boot Application

```java
import dev.keyenv.KeyEnv;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import jakarta.annotation.PostConstruct;

@SpringBootApplication
public class Application {

    @PostConstruct
    public void loadSecrets() {
        String token = System.getenv("KEYENV_TOKEN");
        if (token != null) {
            KeyEnv client = KeyEnv.create(token);
            client.loadEnv(System.getenv("KEYENV_PROJECT"), "production");
        }
    }

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

### Micronaut Application

```java
import dev.keyenv.KeyEnv;
import io.micronaut.context.event.ApplicationEventListener;
import io.micronaut.context.event.StartupEvent;
import jakarta.inject.Singleton;

@Singleton
public class SecretsLoader implements ApplicationEventListener<StartupEvent> {

    @Override
    public void onApplicationEvent(StartupEvent event) {
        String token = System.getenv("KEYENV_TOKEN");
        if (token != null) {
            KeyEnv client = KeyEnv.create(token);
            int count = client.loadEnv(System.getenv("KEYENV_PROJECT"), "production");
            System.out.println("Loaded " + count + " secrets from KeyEnv");
        }
    }
}
```

### Quarkus Application

```java
import dev.keyenv.KeyEnv;
import io.quarkus.runtime.Startup;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
@Startup
public class SecretsConfig {

    @PostConstruct
    void loadSecrets() {
        String token = System.getenv("KEYENV_TOKEN");
        if (token != null) {
            KeyEnv client = KeyEnv.create(token);
            client.loadEnv(System.getenv("KEYENV_PROJECT"), "production");
        }
    }
}
```
