Files
ngrok-api-java/README.md
T

251 lines
8.8 KiB
Markdown

# ngrok API client library for Java
This library wraps the [ngrok HTTP API](https://ngrok.com/docs/api) to
make it easier to consume in Java.
## Usage
This library is published on [Maven
Central](https://search.maven.org/artifact/com.ngrok/ngrok-api-java).
In your Maven `pom.xml` file, add:
```xml
<dependencies>
<dependency>
<groupId>com.ngrok</groupId>
<artifactId>ngrok-api-java</artifactId>
<version>${ngrok-api-java.version}</version>
</dependency>
</dependencies>
```
See the above URL for the latest version of the API client.
## Documentation
All objects, methods and properties are documented with Javadoc for
integration with an IDE like IntelliJ IDEA or Eclipse. You can also
[view the documentation online](https://java-api.docs.ngrok.com/).
Beyond that, this readme is the best source of documentation for the
library.
### Versioning
This class library is published to Maven Central using semantic
versioning. Breaking changes to the API will only be released with a
bump of the major version number. Each released commit is tagged in
this repository.
No compatibility promises are made for versions < 1.0.0.
### Quickstart
First, use the ngrok dashboard to generate an API key. Store that in a
safe place. Inject it into your application using the environment
variable `NGROK_API_KEY`. The `Ngrok.createDefault()` method will pull
from that environment variable. If you prefer, you can also pass the API
key explicitly.
#### Create an IP Policy that allows traffic from some subnets
```java
import com.ngrok.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
public class Example {
public static void main(final String[] args) {
final var ngrok = Ngrok.createDefault();
ngrok.ipPolicies().create("allow").call().thenCompose(policy ->
CompletableFuture.allOf(
Stream.of("24.0.0.0/8", "12.0.0.0/8")
.map(cidr ->
ngrok.ipPolicyRules()
.create(cidr, policy.getId())
.call()
.toCompletableFuture()
)
.toArray(CompletableFuture[]::new)
)
).toCompletableFuture().join();
}
}
```
#### List all online tunnels
```java
import com.ngrok.*;
import com.ngrok.definitions.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
public class Example {
public static void main(final String[] args) {
final var ngrok = Ngrok.createDefault();
System.out.println("Tunnels:");
ngrok.tunnels().list().call()
.thenCompose(tunnelPage -> printRecursively(ngrok, tunnelPage))
.toCompletableFuture().join();
}
private static CompletionStage<Void> printRecursively(final Ngrok ngrok, final Page<TunnelList> currentPage) {
currentPage.getPage().getTunnels.forEach(System.out::println);
return currentPage.next().thenCompose(maybeTunnelPage -> maybeTunnelPage
.map(tunnelPage -> printRecursively(ngrok, tunnelPage))
.orElseGet(() -> CompletableFuture.<Void>completedFuture(null))
);
}
}
```
### Conventions
Conventional usage of this package is to construct a root `Ngrok` object
using the static `createDefault()` method. You can then access API
resources using that object. Do not construct the individual API
resource client classes in your application code.
You can also customize low-level behavior by instantiating the
`DefaultNgrokApiClient` yourself, and then using it to construct the
`Ngrok` instance. If you'd like to use a different HTTP library
entirely, you can even implement the `NgrokApiClient` interface
yourself.
```java
import com.ngrok.*;
public class Example {
public static void main(final String[] args) {
// Create the root api client using an API key from the environment variable NGROK_API_KEY
final var defaultNgrok = Ngrok.createDefault();
// ... or create the root api client using an API key provided directly
final var defaultNgrokWithApiKey = Ngrok.createDefault("my secret api key");
// ... or create the root api client by customizing the low-level networking details
final var customApiClient = DefaultNgrokApiClient
.newBuilder(System.getenv("NGROK_API_KEY"))
.ojectMapper(new ObjectMapper().registerModule(new JavaTimeModule()).registerModule(new Jdk8Module()))
.baseUri(URI.create("https://some-other-server.com"))
.build();
final var ngrokWithCustomApiClient = new Ngrok(customApiClient);
// Clients for all api resources (like ip policies) are acccessible via methods on the root client
final var policy = defaultNgrok.ipPolicies()
.get(policyId)
.call().toCompletableFuture().join();
// Some api resources are 'namespaced' through another method
final var circuitBreaker = defaultNgrok.pointcfgModule()
.circuitBreaker()
.get(endpointConfigId).call()
.toCompletableFuture().join();
}
}
```
### Paging
All list responses from the ngrok API are paged. All list response
objects implement the `Pageable` interface, and are wrapped in a `Page`
class, which has a `next()` method. Calling `next()` will asyncronously
request the next page. If no next page is available, an empty `Optional`
will be returned inside the `CompletionStage`.
```java
import com.ngrok.*;
import com.ngrok.definitions.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
public class Example {
public static void main(final String[] args) {
final var ngrok = Ngrok.createDefault();
ngrok.credentials().list().call()
.thenCompose(credentialPage -> printRecursively(ngrok, credentiasPage))
.toCompletableFuture().join();
}
private static CompletionStage<Void> printRecursively(final Ngrok ngrok, final Page<CredentialList> currentPage) {
currentPage.getPage().getTunnels.forEach(System.out::println);
return currentPage.next().thenCompose(maybeCredentialPage -> maybeCredentialPage
.map(credentialPage -> printRecursively(ngrok, credentialPage))
.orElseGet(() -> CompletableFuture.<Void>completedFuture(null))
);
}
}
```
### Error Handling
All errors returned by the ngrok API are serialized as structured
payloads for easy error handling. If a structured error is returned by
the ngrok API, this library will return a failed `CompletionStage`
containing a `NgrokApiError`.
This object will allow you to check the unique ngrok error code and the
http status code of a failed response. Use the `getErrorCode()` method
to check for unique ngrok error codes returned by the API. All error
codes are documented at
[https://ngrok.com/docs/errors](https://ngrok.com/docs/errors). There is
also an `isErrorCode()` method on the exception object to check against
multiple error codes. The `getHttpStatusCode()` method can be used to
check not found errors.
Other non-structured errors encountered while making an API call from
e.g. networking or serialization failures are not wrapped in any way and
will bubble up as normal.
```java
import com.ngrok.*;
import com.ngrok.definitions.*;
public class Example {
public static void main(final String[] args) {
final var ngrok = Ngrok.createDefault();
ngrok.reservedDomains().create(System.getenv("NGROK_DOMAIN"))
.description("example domain")
.call()
.whenComplete((domain, error) -> {
if (error != null) {
if (error instanceof NgrokApiError ngrokError) {
if (ngrokError.isErrorCode("NGROK_ERR_402", "NGROK_ERR_403")) {
System.out.println("Ignoring invalid wildcard domain.");
} else {
System.out.printf("API Error (%s): %s%n", ngrokError.getErrorCode().orElse(""), ngrokError.getMessage());
}
} else {
System.out.println("Other error: " + error.getMessage());
}
} else {
System.out.println("Successfully reserved domain");
}
})
.toCompletableFuture().join();
}
}
```
### Datatype Overrides
All datatype objects in the ngrok API library properly override
`equals()` and `hashCode()` so that the objects can be compared.
Similarly, they override `toString()` for more helpful pretty printing
of ngrok domain objects.
### Sync / Async Interfaces
The `call()` method on each API client operation is asynchronous and
returns a `CompletionStage`. If you require a synchronous call, you can
use `blockingCall()` instead.