diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml
index 87d7796..74d2b4e 100644
--- a/.github/workflows/CI.yaml
+++ b/.github/workflows/CI.yaml
@@ -28,21 +28,15 @@ jobs:
- name: Build and test
run: ./gradlew build
- - name: Read version
- id: version
- run: |
- VERSION=$(grep '^VERSION_NAME=' gradle.properties | cut -d= -f2)
- echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
- name: Publish SNAPSHOT (main only)
- if: github.event_name == 'push' && github.ref == 'refs/heads/main' && endsWith(steps.version.outputs.VERSION, 'SNAPSHOT')
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
env:
- ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_USERNAME }}
- ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }}
- ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_IN_MEMORY_KEY }}
- ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.SIGNING_KEY_ID }}
- ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }}
- run: ./gradlew publishAllPublicationsToMavenCentralRepository -x test
+ SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
+ SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
+ GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
+ GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
+ run: ./gradlew publishAllPublicationsToCentralPortalSnapshots -x test
- name: Update dependency graph
uses: gradle/actions/dependency-submission@v5
diff --git a/.github/workflows/Release.yaml b/.github/workflows/Release.yaml
index c5896a1..64165ad 100644
--- a/.github/workflows/Release.yaml
+++ b/.github/workflows/Release.yaml
@@ -11,11 +11,10 @@ jobs:
release:
runs-on: ubuntu-latest
env:
- ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_USERNAME }}
- ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }}
- ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_IN_MEMORY_KEY }}
- ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.SIGNING_KEY_ID }}
- ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }}
+ SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
+ SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
+ GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
+ GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
steps:
- uses: actions/checkout@v6
@@ -46,4 +45,4 @@ jobs:
run: ./gradlew build -Pversion=${{ steps.version.outputs.VERSION }}
- name: Deploy to Maven Central
- run: ./gradlew publishAllPublicationsToMavenCentralRepository -Pversion=${{ steps.version.outputs.VERSION }}
+ run: ./gradlew publishAllPublicationsToCentralPortal -Pversion=${{ steps.version.outputs.VERSION }}
diff --git a/README.md b/README.md
index 33816e0..a83554f 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ A lightweight Java SDK for the Apple Maps Server API, with automatic access-toke
## Installation
-Replace `0.1.2` with the latest release.
+Replace `0.1.3` with the latest release.
Note: this repo’s build uses a Gradle Java toolchain (Java 17). If you don’t have JDK 17 installed locally, Gradle will download it automatically.
@@ -18,7 +18,7 @@ Note: this repo’s build uses a Gradle Java toolchain (Java 17). If you don’t
```groovy
dependencies {
- implementation("com.williamcallahan:apple-maps-java:0.1.2")
+ implementation("com.williamcallahan:apple-maps-java:0.1.3")
}
```
@@ -28,7 +28,7 @@ dependencies {
com.williamcallahan
apple-maps-java
- 0.1.2
+ 0.1.3
```
diff --git a/build.gradle.kts b/build.gradle.kts
index 7a3214c..79dd5e6 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -14,8 +14,10 @@ import java.util.Properties
import java.io.FileInputStream
plugins {
- id("com.vanniktech.maven.publish") version "0.35.0"
`java-library`
+ `maven-publish`
+ signing
+ id("com.gradleup.nmcp") version "1.2.1"
}
// Load .env file if it exists
@@ -45,7 +47,7 @@ val javaTargetVersion = 17
group = providers.gradleProperty("GROUP").orNull ?: "com.williamcallahan"
version = providers.gradleProperty("version").orNull
?: providers.gradleProperty("VERSION_NAME").orNull
- ?: "0.1.2"
+ ?: "0.1.4-SNAPSHOT"
repositories {
mavenCentral()
@@ -160,12 +162,69 @@ tasks.withType().configureEach {
(options as StandardJavadocDocletOptions).addStringOption("-release", javaTargetVersion.toString())
}
-mavenPublishing {
- publishToMavenCentral(automaticRelease = true)
- signAllPublications()
+publishing {
+ publications {
+ create("mavenJava") {
+ from(components["java"])
+ artifactId = providers.gradleProperty("POM_ARTIFACT_ID").orNull ?: "apple-maps-java"
+
+ pom {
+ name.set(providers.gradleProperty("POM_NAME").orNull ?: "Apple Maps Java")
+ description.set(providers.gradleProperty("POM_DESCRIPTION").orNull ?: "Apple Maps Java implements the Apple Maps Server API for use in JVMs.")
+ url.set(providers.gradleProperty("POM_URL").orNull ?: "https://github.com/WilliamAGH/apple-maps-java")
+
+ licenses {
+ license {
+ name.set(providers.gradleProperty("POM_LICENSE_NAME").orNull ?: "MIT License")
+ url.set(providers.gradleProperty("POM_LICENSE_URL").orNull ?: "https://opensource.org/license/mit")
+ }
+ }
+
+ developers {
+ developer {
+ id.set(providers.gradleProperty("POM_DEVELOPER_ID").orNull ?: "WilliamAGH")
+ name.set(providers.gradleProperty("POM_DEVELOPER_NAME").orNull ?: "William Callahan")
+ url.set(providers.gradleProperty("POM_DEVELOPER_URL").orNull ?: "https://github.com/WilliamAGH/")
+ }
+ }
+
+ scm {
+ url.set(providers.gradleProperty("POM_SCM_URL").orNull ?: "https://github.com/WilliamAGH/apple-maps-java")
+ connection.set(providers.gradleProperty("POM_SCM_CONNECTION").orNull ?: "scm:git:git://github.com/WilliamAGH/apple-maps-java.git")
+ developerConnection.set(providers.gradleProperty("POM_SCM_DEV_CONNECTION").orNull ?: "scm:git:ssh://git@github.com/WilliamAGH/apple-maps-java.git")
+ }
+ }
+ }
+ }
+
+ repositories {
+ maven {
+ val releasesUrl = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
+ val snapshotsUrl = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
+ url = if (version.toString().endsWith("SNAPSHOT")) snapshotsUrl else releasesUrl
+
+ credentials {
+ username = providers.environmentVariable("SONATYPE_USERNAME").orNull
+ password = providers.environmentVariable("SONATYPE_PASSWORD").orNull
+ }
+ }
+ }
+}
+
+signing {
+ val signingKey = providers.environmentVariable("GPG_PRIVATE_KEY").orNull
+ val signingPassword = providers.environmentVariable("GPG_PASSPHRASE").orNull
+
+ if (signingKey != null && signingPassword != null) {
+ useInMemoryPgpKeys(signingKey, signingPassword)
+ sign(publishing.publications)
+ }
}
-// Fix task dependency issue with Gradle 9.x and vanniktech plugin
-tasks.matching { it.name == "generateMetadataFileForMavenPublication" }.configureEach {
- dependsOn(tasks.matching { it.name == "plainJavadocJar" })
+nmcp {
+ publishAllPublicationsToCentralPortal {
+ username.set(providers.environmentVariable("SONATYPE_USERNAME").orNull)
+ password.set(providers.environmentVariable("SONATYPE_PASSWORD").orNull)
+ publishingType.set("USER_MANAGED")
+ }
}
diff --git a/docs/authorization.md b/docs/authorization.md
index bd5109f..c49f2e5 100644
--- a/docs/authorization.md
+++ b/docs/authorization.md
@@ -18,6 +18,7 @@ Option A (recommended): environment variable
```bash
export APPLE_MAPS_TOKEN="your-token"
+export APPLE_MAPS_ORIGIN="https://api.example.com" # Required if your JWT has a specific origin
```
Option B (local dev for this repo): `.env` fallback
@@ -31,7 +32,7 @@ cp .env-example .env
`.env` is ignored by git (so you don’t accidentally commit secrets).
This project’s Gradle build loads `.env` into **system properties**, which is mainly convenient for running tests locally.
-In CI, prefer setting `APPLE_MAPS_TOKEN` as an environment variable.
+In CI, set `APPLE_MAPS_TOKEN` as an environment variable. Optionally set `APPLE_MAPS_ORIGIN` if your token requires it (e.g., `https://api.example.com` matching your JWT's `origin` claim).
## Supplying the token to the SDK
@@ -40,9 +41,13 @@ For example:
```java
String token = System.getenv("APPLE_MAPS_TOKEN");
+String origin = System.getenv("APPLE_MAPS_ORIGIN");
if (token == null || token.isBlank()) {
token = System.getProperty("APPLE_MAPS_TOKEN");
}
+if (origin == null || origin.isBlank()) {
+ origin = System.getProperty("APPLE_MAPS_ORIGIN");
+}
-AppleMaps api = new AppleMaps(token);
+AppleMaps api = new AppleMaps(token, origin);
```
diff --git a/docs/cli.md b/docs/cli.md
index eb08b4b..57e4dd1 100644
--- a/docs/cli.md
+++ b/docs/cli.md
@@ -4,7 +4,7 @@ This repo includes a small CLI for running Apple Maps Server queries from your t
## Prerequisites
-Set `APPLE_MAPS_TOKEN` (env var recommended). See `README.md` / `docs/authorization.md`.
+Set `APPLE_MAPS_TOKEN` (env var recommended). Optionally set `APPLE_MAPS_ORIGIN` if your token requires it (e.g. `https://api.example.com` matching your JWT's `origin` claim). See `README.md` / `docs/authorization.md`.
## Run
diff --git a/gradle.properties b/gradle.properties
index b8dc14d..5ffe9e4 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,6 +1,6 @@
GROUP=com.williamcallahan
POM_ARTIFACT_ID=apple-maps-java
-VERSION_NAME=0.1.2-SNAPSHOT
+VERSION_NAME=0.1.4-SNAPSHOT
POM_NAME=Apple Maps Java
POM_DESCRIPTION=Apple Maps Java implements the Apple Maps Server API for use in JVMs.
diff --git a/src/main/java/com/williamcallahan/applemaps/AppleMaps.java b/src/main/java/com/williamcallahan/applemaps/AppleMaps.java
index 30c18a7..b0f5b14 100644
--- a/src/main/java/com/williamcallahan/applemaps/AppleMaps.java
+++ b/src/main/java/com/williamcallahan/applemaps/AppleMaps.java
@@ -1,5 +1,10 @@
package com.williamcallahan.applemaps;
+import java.time.Duration;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+
import com.williamcallahan.applemaps.adapters.mapsserver.AppleMapsClientException;
import com.williamcallahan.applemaps.adapters.mapsserver.HttpAppleMapsGateway;
import com.williamcallahan.applemaps.domain.model.AlternateIdsResponse;
@@ -19,10 +24,6 @@
import com.williamcallahan.applemaps.domain.request.PlaceLookupInput;
import com.williamcallahan.applemaps.domain.request.SearchAutocompleteInput;
import com.williamcallahan.applemaps.domain.request.SearchInput;
-import java.time.Duration;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.CompletableFuture;
/**
* Entry point for Apple Maps Server API operations.
@@ -33,41 +34,114 @@ public final class AppleMaps implements AutoCloseable {
private final AppleMapsGateway gateway;
+ /**
+ * Creates an {@link AppleMaps} client using the provided authorization token and a default timeout.
+ *
+ * @param authToken the Apple Maps Server API authorization token
+ */
public AppleMaps(String authToken) {
- this(authToken, DEFAULT_TIMEOUT);
+ this(authToken, DEFAULT_TIMEOUT, null);
+ }
+
+ /**
+ * Creates an {@link AppleMaps} client using the provided authorization token and origin.
+ *
+ * @param authToken the Apple Maps Server API authorization token
+ * @param origin value for the Origin header (required for some JWT configurations)
+ */
+ public AppleMaps(String authToken, String origin) {
+ this(authToken, DEFAULT_TIMEOUT, origin);
}
+ /**
+ * Creates an {@link AppleMaps} client using the provided authorization token and timeout.
+ *
+ * @param authToken the Apple Maps Server API authorization token
+ * @param timeout request timeout
+ */
public AppleMaps(String authToken, Duration timeout) {
+ this(authToken, timeout, null);
+ }
+
+ /**
+ * Creates an {@link AppleMaps} client using the provided authorization token, timeout, and origin.
+ *
+ * @param authToken the Apple Maps Server API authorization token
+ * @param timeout request timeout
+ * @param origin value for the Origin header (required for some JWT configurations)
+ */
+ public AppleMaps(String authToken, Duration timeout, String origin) {
Objects.requireNonNull(authToken, "authToken");
Objects.requireNonNull(timeout, "timeout");
- this.gateway = new HttpAppleMapsGateway(authToken, timeout);
+ this.gateway = new HttpAppleMapsGateway(authToken, timeout, origin);
}
+ /**
+ * Creates an {@link AppleMaps} client backed by a custom gateway implementation.
+ *
+ * @param gateway the gateway to use for API operations
+ */
public AppleMaps(AppleMapsGateway gateway) {
this.gateway = Objects.requireNonNull(gateway, "gateway");
}
+ /**
+ * Performs a geocode request.
+ *
+ * @param input geocode request parameters
+ * @return geocode results
+ */
public PlaceResults geocode(GeocodeInput input) {
return gateway.geocode(input);
}
+ /**
+ * Performs a search request.
+ *
+ * @param input search request parameters
+ * @return search results
+ */
public SearchResponse search(SearchInput input) {
return gateway.search(input);
}
+ /**
+ * Performs an autocomplete request.
+ *
+ * @param input autocomplete request parameters
+ * @return autocomplete results
+ */
public SearchAutocompleteResponse autocomplete(SearchAutocompleteInput input) {
return gateway.autocomplete(input);
}
+ /**
+ * Resolves a completion URL returned from an autocomplete response.
+ *
+ * @param completionUrl completion URL to resolve
+ * @return search results for the completion URL
+ */
public SearchResponse resolveCompletionUrl(String completionUrl) {
return gateway.resolveCompletionUrl(completionUrl);
}
+ /**
+ * Resolves all completion URLs from an autocomplete response.
+ *
+ * @param response an autocomplete response
+ * @return resolved search responses in the same order as the results
+ */
public List resolveCompletionUrls(SearchAutocompleteResponse response) {
Objects.requireNonNull(response, "response");
return resolveCompletionUrls(response.results());
}
+ /**
+ * Resolves all completion URLs from an autocomplete result list.
+ *
+ * @param results autocomplete results
+ * @return resolved search responses in the same order as the results
+ */
public List resolveCompletionUrls(List results) {
Objects.requireNonNull(results, "results");
if (results.isEmpty()) {
@@ -92,36 +166,88 @@ public List resolveCompletionUrls(List resul
}
}
+ /**
+ * Performs a reverse geocode request using the default language ({@code en-US}).
+ *
+ * @param latitude latitude in decimal degrees
+ * @param longitude longitude in decimal degrees
+ * @return reverse geocode results
+ */
public PlaceResults reverseGeocode(double latitude, double longitude) {
return gateway.reverseGeocode(latitude, longitude, DEFAULT_LANGUAGE);
}
+ /**
+ * Performs a reverse geocode request.
+ *
+ * @param latitude latitude in decimal degrees
+ * @param longitude longitude in decimal degrees
+ * @param language response language (BCP 47); if {@code null} or blank, defaults to {@code en-US}
+ * @return reverse geocode results
+ */
public PlaceResults reverseGeocode(double latitude, double longitude, String language) {
String resolvedLanguage = language == null || language.isBlank() ? DEFAULT_LANGUAGE : language;
return gateway.reverseGeocode(latitude, longitude, resolvedLanguage);
}
+ /**
+ * Performs a directions request.
+ *
+ * @param input directions request parameters
+ * @return directions results
+ */
public DirectionsResponse directions(DirectionsInput input) {
return gateway.directions(input);
}
+ /**
+ * Performs an ETA request.
+ *
+ * @param input ETA request parameters
+ * @return ETA results
+ */
public EtaResponse etas(EtaInput input) {
return gateway.etas(input);
}
+ /**
+ * Looks up a place by ID using the default language ({@code en-US}).
+ *
+ * @param placeId place identifier
+ * @return a place
+ */
public Place lookupPlace(String placeId) {
return lookupPlace(placeId, DEFAULT_LANGUAGE);
}
+ /**
+ * Looks up a place by ID.
+ *
+ * @param placeId place identifier
+ * @param language response language (BCP 47); if {@code null} or blank, defaults to {@code en-US}
+ * @return a place
+ */
public Place lookupPlace(String placeId, String language) {
String resolvedLanguage = language == null || language.isBlank() ? DEFAULT_LANGUAGE : language;
return gateway.lookupPlace(placeId, resolvedLanguage);
}
+ /**
+ * Looks up places using a place lookup input.
+ *
+ * @param input place lookup request parameters
+ * @return places results
+ */
public PlacesResponse lookupPlaces(PlaceLookupInput input) {
return gateway.lookupPlaces(input);
}
+ /**
+ * Looks up alternate IDs for one or more places.
+ *
+ * @param input alternate IDs request parameters
+ * @return alternate IDs results
+ */
public AlternateIdsResponse lookupAlternateIds(AlternateIdsInput input) {
return gateway.lookupAlternateIds(input);
}
diff --git a/src/main/java/com/williamcallahan/applemaps/adapters/jackson/AppleMapsObjectMapperFactory.java b/src/main/java/com/williamcallahan/applemaps/adapters/jackson/AppleMapsObjectMapperFactory.java
index 0ad8071..c5a98d3 100644
--- a/src/main/java/com/williamcallahan/applemaps/adapters/jackson/AppleMapsObjectMapperFactory.java
+++ b/src/main/java/com/williamcallahan/applemaps/adapters/jackson/AppleMapsObjectMapperFactory.java
@@ -15,6 +15,11 @@ public final class AppleMapsObjectMapperFactory {
private AppleMapsObjectMapperFactory() {}
+ /**
+ * Creates a new {@link ObjectMapper} configured for Apple Maps Server API models.
+ *
+ * @return an object mapper instance
+ */
public static ObjectMapper create() {
return JsonMapper.builder()
.addMixIn(Location.class, LocationMixin.class)
diff --git a/src/main/java/com/williamcallahan/applemaps/adapters/mapsserver/AppleMapsApiException.java b/src/main/java/com/williamcallahan/applemaps/adapters/mapsserver/AppleMapsApiException.java
index 78bbc7c..bba953c 100644
--- a/src/main/java/com/williamcallahan/applemaps/adapters/mapsserver/AppleMapsApiException.java
+++ b/src/main/java/com/williamcallahan/applemaps/adapters/mapsserver/AppleMapsApiException.java
@@ -6,19 +6,42 @@
* Indicates a non-successful response from the Apple Maps API.
*/
public final class AppleMapsApiException extends RuntimeException {
+ /**
+ * HTTP status code returned by the Apple Maps Server API.
+ */
private final int statusCode;
+ /**
+ * Response body returned by the Apple Maps Server API (may be empty).
+ */
private final String responseBody;
+ /**
+ * Creates an exception for a non-successful Apple Maps Server API response.
+ *
+ * @param operation a short operation name (for example, {@code "search"})
+ * @param statusCode the HTTP status code
+ * @param responseBody the response body, if available
+ */
public AppleMapsApiException(String operation, int statusCode, String responseBody) {
super("Apple Maps API request failed for " + operation + " (status " + statusCode + ")");
this.statusCode = statusCode;
this.responseBody = Objects.requireNonNullElse(responseBody, "");
}
+ /**
+ * Returns the HTTP status code from the API response.
+ *
+ * @return the status code
+ */
public int statusCode() {
return statusCode;
}
+ /**
+ * Returns the API response body (may be empty).
+ *
+ * @return the response body
+ */
public String responseBody() {
return responseBody;
}
diff --git a/src/main/java/com/williamcallahan/applemaps/adapters/mapsserver/AppleMapsAuthorizationService.java b/src/main/java/com/williamcallahan/applemaps/adapters/mapsserver/AppleMapsAuthorizationService.java
index 8ae7578..deb4ece 100644
--- a/src/main/java/com/williamcallahan/applemaps/adapters/mapsserver/AppleMapsAuthorizationService.java
+++ b/src/main/java/com/williamcallahan/applemaps/adapters/mapsserver/AppleMapsAuthorizationService.java
@@ -1,7 +1,5 @@
package com.williamcallahan.applemaps.adapters.mapsserver;
-import com.williamcallahan.applemaps.adapters.jackson.AppleMapsObjectMapperFactory;
-import com.williamcallahan.applemaps.domain.model.TokenResponse;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
@@ -13,6 +11,10 @@
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
+
+import com.williamcallahan.applemaps.adapters.jackson.AppleMapsObjectMapperFactory;
+import com.williamcallahan.applemaps.domain.model.TokenResponse;
+
import tools.jackson.databind.ObjectMapper;
/**
@@ -27,12 +29,19 @@ public final class AppleMapsAuthorizationService {
private final URI tokenUri;
private final Duration timeout;
private final String authToken;
+ private final String origin;
private final Clock clock;
private final ReentrantLock refreshLock = new ReentrantLock();
private final AtomicReference accessToken = new AtomicReference<>();
- public AppleMapsAuthorizationService(String authToken, Duration timeout) {
- this(new Dependencies(authToken, timeout));
+ /**
+ * Creates a service that exchanges an authorization token for access tokens.
+ *
+ * @param authToken the Apple Maps Server API authorization token
+ * @param timeout request timeout for token exchange
+ */
+ public AppleMapsAuthorizationService(String authToken, Duration timeout, String origin) {
+ this(new Dependencies(authToken, timeout, origin));
}
AppleMapsAuthorizationService(Dependencies dependencies) {
@@ -41,9 +50,19 @@ public AppleMapsAuthorizationService(String authToken, Duration timeout) {
this.tokenUri = dependencies.tokenUri();
this.timeout = dependencies.timeout();
this.authToken = dependencies.authToken();
+ this.origin = dependencies.origin();
this.clock = dependencies.clock();
}
+
+ public String getOrigin() {
+ return origin;
+ }
+ /**
+ * Returns a cached access token, refreshing it when needed.
+ *
+ * @return the access token string
+ */
public String getAccessToken() {
AccessToken cachedToken = accessToken.get();
if (cachedToken != null && !isExpiring(cachedToken)) {
@@ -63,12 +82,17 @@ public String getAccessToken() {
}
private AccessToken refreshAccessToken() {
- HttpRequest httpRequest = HttpRequest.newBuilder()
+ HttpRequest.Builder builder = HttpRequest.newBuilder()
.GET()
.timeout(timeout)
.uri(tokenUri)
- .setHeader("Authorization", "Bearer " + authToken)
- .build();
+ .setHeader("Authorization", "Bearer " + authToken);
+
+ if (origin != null) {
+ builder.setHeader("Origin", origin);
+ }
+
+ HttpRequest httpRequest = builder.build();
try {
HttpResponse response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() != 200) {
@@ -119,15 +143,17 @@ static final class Dependencies {
private final URI tokenUri;
private final Duration timeout;
private final String authToken;
+ private final String origin;
private final Clock clock;
- Dependencies(String authToken, Duration timeout) {
+ Dependencies(String authToken, Duration timeout, String origin) {
this(new DependenciesConfig(
AppleMapsObjectMapperFactory.create(),
HttpClient.newHttpClient(),
URI.create("https://maps-api.apple.com" + TOKEN_PATH),
timeout,
authToken,
+ origin,
Clock.systemUTC()
));
}
@@ -138,6 +164,7 @@ static final class Dependencies {
this.tokenUri = Objects.requireNonNull(config.tokenUri(), "tokenUri");
this.timeout = Objects.requireNonNull(config.timeout(), "timeout");
this.authToken = Objects.requireNonNull(config.authToken(), "authToken");
+ this.origin = config.origin();
this.clock = Objects.requireNonNull(config.clock(), "clock");
}
@@ -147,6 +174,7 @@ record DependenciesConfig(
URI tokenUri,
Duration timeout,
String authToken,
+ String origin,
Clock clock
) {
}
@@ -171,6 +199,10 @@ String authToken() {
return authToken;
}
+ String origin() {
+ return origin;
+ }
+
Clock clock() {
return clock;
}
diff --git a/src/main/java/com/williamcallahan/applemaps/adapters/mapsserver/AppleMapsClientException.java b/src/main/java/com/williamcallahan/applemaps/adapters/mapsserver/AppleMapsClientException.java
index e90f11d..a418188 100644
--- a/src/main/java/com/williamcallahan/applemaps/adapters/mapsserver/AppleMapsClientException.java
+++ b/src/main/java/com/williamcallahan/applemaps/adapters/mapsserver/AppleMapsClientException.java
@@ -6,6 +6,12 @@
* Indicates a client-side failure when calling the Apple Maps API.
*/
public final class AppleMapsClientException extends RuntimeException {
+ /**
+ * Creates an exception for a client-side failure.
+ *
+ * @param operation a short operation name (for example, {@code "search"})
+ * @param cause the underlying exception
+ */
public AppleMapsClientException(String operation, Throwable cause) {
super("Apple Maps request failed for " + Objects.requireNonNull(operation, "operation"), cause);
}
diff --git a/src/main/java/com/williamcallahan/applemaps/adapters/mapsserver/HttpAppleMapsGateway.java b/src/main/java/com/williamcallahan/applemaps/adapters/mapsserver/HttpAppleMapsGateway.java
index 598e33b..d421b85 100644
--- a/src/main/java/com/williamcallahan/applemaps/adapters/mapsserver/HttpAppleMapsGateway.java
+++ b/src/main/java/com/williamcallahan/applemaps/adapters/mapsserver/HttpAppleMapsGateway.java
@@ -1,5 +1,17 @@
package com.williamcallahan.applemaps.adapters.mapsserver;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
import com.williamcallahan.applemaps.adapters.jackson.AppleMapsObjectMapperFactory;
import com.williamcallahan.applemaps.domain.model.AlternateIdsResponse;
import com.williamcallahan.applemaps.domain.model.DirectionsResponse;
@@ -17,17 +29,7 @@
import com.williamcallahan.applemaps.domain.request.PlaceLookupInput;
import com.williamcallahan.applemaps.domain.request.SearchAutocompleteInput;
import com.williamcallahan.applemaps.domain.request.SearchInput;
-import java.net.URI;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
-import java.time.Duration;
-import java.util.Objects;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
+
import tools.jackson.databind.ObjectMapper;
/**
@@ -56,8 +58,18 @@ public final class HttpAppleMapsGateway implements AppleMapsGateway {
private final Duration timeout;
private final ExecutorService executorService;
+ /**
+ * Creates an HTTP gateway that calls the Apple Maps Server API.
+ *
+ * @param authToken the Apple Maps Server API authorization token
+ * @param timeout request timeout
+ */
public HttpAppleMapsGateway(String authToken, Duration timeout) {
- this(new Dependencies(authToken, timeout));
+ this(new Dependencies(authToken, timeout, null));
+ }
+
+ public HttpAppleMapsGateway(String authToken, Duration timeout, String origin) {
+ this(new Dependencies(authToken, timeout, origin));
}
HttpAppleMapsGateway(Dependencies dependencies) {
@@ -158,12 +170,18 @@ private URI buildUri(String path, String queryString) {
}
private T invokeApi(String operation, URI uri, Class responseType) {
- HttpRequest httpRequest = HttpRequest.newBuilder()
+
+ HttpRequest.Builder builder = HttpRequest.newBuilder()
.GET()
.uri(uri)
.timeout(timeout)
- .setHeader("Authorization", "Bearer " + authorizationService.getAccessToken())
- .build();
+ .setHeader("Authorization", "Bearer " + authorizationService.getAccessToken());
+
+ if (authorizationService.getOrigin() != null) {
+ builder.setHeader("Origin", authorizationService.getOrigin());
+ }
+
+ HttpRequest httpRequest = builder.build();
try {
HttpResponse response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() != 200) {
@@ -184,8 +202,8 @@ static final class Dependencies {
private final Duration timeout;
private final ExecutorService executorService;
- Dependencies(String authToken, Duration timeout) {
- this(createDefaultDependenciesConfig(authToken, timeout));
+ Dependencies(String authToken, Duration timeout, String origin) {
+ this(createDefaultDependenciesConfig(authToken, timeout, origin));
}
Dependencies(DependenciesConfig config) {
@@ -205,7 +223,7 @@ record DependenciesConfig(
) {
}
- private static DependenciesConfig createDefaultDependenciesConfig(String authToken, Duration timeout) {
+ private static DependenciesConfig createDefaultDependenciesConfig(String authToken, Duration timeout, String origin) {
ThreadFactory httpClientThreadFactory = new ThreadFactory() {
private final AtomicInteger httpClientThreadSequence = new AtomicInteger(1);
@@ -221,7 +239,7 @@ public Thread newThread(Runnable runnable) {
HttpClient httpClient = HttpClient.newBuilder().executor(httpClientExecutorService).build();
return new DependenciesConfig(
- new AppleMapsAuthorizationService(authToken, timeout),
+ new AppleMapsAuthorizationService(authToken, timeout, origin),
AppleMapsObjectMapperFactory.create(),
httpClient,
timeout,
diff --git a/src/main/java/com/williamcallahan/applemaps/cli/AppleMapsCli.java b/src/main/java/com/williamcallahan/applemaps/cli/AppleMapsCli.java
index cd3adb9..d45f955 100644
--- a/src/main/java/com/williamcallahan/applemaps/cli/AppleMapsCli.java
+++ b/src/main/java/com/williamcallahan/applemaps/cli/AppleMapsCli.java
@@ -1,5 +1,11 @@
package com.williamcallahan.applemaps.cli;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+
import com.williamcallahan.applemaps.AppleMaps;
import com.williamcallahan.applemaps.adapters.jackson.AppleMapsObjectMapperFactory;
import com.williamcallahan.applemaps.adapters.mapsserver.AppleMapsApiException;
@@ -13,12 +19,10 @@
import com.williamcallahan.applemaps.domain.request.GeocodeInput;
import com.williamcallahan.applemaps.domain.request.SearchAutocompleteInput;
import com.williamcallahan.applemaps.domain.request.SearchInput;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
+/**
+ * Command line interface for exercising Apple Maps Server API operations.
+ */
public final class AppleMapsCli {
private static final int EXIT_USAGE = 2;
@@ -55,6 +59,11 @@ public final class AppleMapsCli {
private AppleMapsCli() {}
+ /**
+ * CLI entry point.
+ *
+ * @param args command line arguments
+ */
public static void main(String[] args) {
try {
run(args);
@@ -104,8 +113,9 @@ private static void run(String[] args) {
Arrays.copyOfRange(args, 1, args.length)
);
String token = resolveToken();
+ String origin = resolveOrigin();
- try (AppleMaps api = new AppleMaps(token)) {
+ try (AppleMaps api = new AppleMaps(token, origin)) {
ParsedOptions resolvedOptions = commandUsesLocationHints(command)
? options.resolveUserLocation(api)
: options;
@@ -247,6 +257,10 @@ private static String resolveToken() {
);
}
+ private static String resolveOrigin() {
+ return readSettingText("APPLE_MAPS_ORIGIN").orElse(null);
+ }
+
private static java.util.Optional readSettingText(
String settingName
) {
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/AddressCategory.java b/src/main/java/com/williamcallahan/applemaps/domain/model/AddressCategory.java
index e6fb3dd..6c7bcd3 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/AddressCategory.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/AddressCategory.java
@@ -4,11 +4,29 @@
* Enumerated values that represent political geographical boundaries.
*/
public enum AddressCategory {
+ /**
+ * Country-level address component.
+ */
COUNTRY("Country"),
+ /**
+ * Top-level administrative area (for example, a state or province).
+ */
ADMINISTRATIVE_AREA("AdministrativeArea"),
+ /**
+ * Sub-level administrative area (for example, a county or district).
+ */
SUB_ADMINISTRATIVE_AREA("SubAdministrativeArea"),
+ /**
+ * Locality address component (for example, a city).
+ */
LOCALITY("Locality"),
+ /**
+ * Sub-locality address component (for example, a neighborhood).
+ */
SUB_LOCALITY("SubLocality"),
+ /**
+ * Postal code address component.
+ */
POSTAL_CODE("PostalCode");
private final String apiValue;
@@ -17,6 +35,11 @@ public enum AddressCategory {
this.apiValue = apiValue;
}
+ /**
+ * Returns the Apple Maps Server API value for this category.
+ *
+ * @return the value used by the API
+ */
public String apiValue() {
return apiValue;
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/AlternateIdsEntry.java b/src/main/java/com/williamcallahan/applemaps/domain/model/AlternateIdsEntry.java
index 386d3a1..13332bf 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/AlternateIdsEntry.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/AlternateIdsEntry.java
@@ -8,6 +8,12 @@
* Alternate Place identifiers associated with a primary Place ID.
*/
public record AlternateIdsEntry(Optional id, List alternateIds) {
+ /**
+ * Canonical constructor that normalizes potentially-null inputs to non-null values.
+ *
+ * @param id the primary place ID (optional)
+ * @param alternateIds alternate place IDs associated with {@code id}
+ */
public AlternateIdsEntry {
id = normalizeOptional(id);
alternateIds = normalizeList(alternateIds);
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/AlternateIdsResponse.java b/src/main/java/com/williamcallahan/applemaps/domain/model/AlternateIdsResponse.java
index fe6566a..00af8b4 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/AlternateIdsResponse.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/AlternateIdsResponse.java
@@ -7,6 +7,12 @@
* A list of alternate Place ID results and lookup errors.
*/
public record AlternateIdsResponse(List results, List errors) {
+ /**
+ * Canonical constructor that normalizes potentially-null lists to immutable lists.
+ *
+ * @param results alternate ID results
+ * @param errors lookup errors
+ */
public AlternateIdsResponse {
results = normalizeList(results);
errors = normalizeList(errors);
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/AutocompleteResult.java b/src/main/java/com/williamcallahan/applemaps/domain/model/AutocompleteResult.java
index 810cf23..5286e44 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/AutocompleteResult.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/AutocompleteResult.java
@@ -13,6 +13,14 @@ public record AutocompleteResult(
Optional location,
Optional structuredAddress
) {
+ /**
+ * Canonical constructor that normalizes potentially-null optionals and lists.
+ *
+ * @param completionUrl completion URL used to resolve this result
+ * @param displayLines display lines suitable for presentation
+ * @param location optional location associated with the completion
+ * @param structuredAddress optional structured address associated with the completion
+ */
public AutocompleteResult {
completionUrl = Objects.requireNonNull(completionUrl, "completionUrl");
displayLines = normalizeList(displayLines);
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsAvoid.java b/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsAvoid.java
index c50f329..a2c892e 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsAvoid.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsAvoid.java
@@ -4,6 +4,9 @@
* Route features that can be avoided during direction requests.
*/
public enum DirectionsAvoid {
+ /**
+ * Avoid toll roads where possible.
+ */
TOLLS("Tolls");
private final String apiValue;
@@ -12,6 +15,11 @@ public enum DirectionsAvoid {
this.apiValue = apiValue;
}
+ /**
+ * Returns the Apple Maps Server API value for this avoid option.
+ *
+ * @return the value used by the API
+ */
public String apiValue() {
return apiValue;
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsEndpoint.java b/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsEndpoint.java
index 5c52a2b..e6fcac5 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsEndpoint.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsEndpoint.java
@@ -8,18 +8,41 @@
public record DirectionsEndpoint(String formattedLocation) {
private static final String COORDINATE_SEPARATOR = ",";
+ /**
+ * Canonical constructor that validates the formatted location is non-null.
+ *
+ * @param formattedLocation formatted endpoint string (address or coordinate pair)
+ */
public DirectionsEndpoint {
formattedLocation = Objects.requireNonNull(formattedLocation, "formattedLocation");
}
+ /**
+ * Creates an endpoint from a free-form address string.
+ *
+ * @param address the address text
+ * @return a directions endpoint
+ */
public static DirectionsEndpoint fromAddress(String address) {
return new DirectionsEndpoint(address);
}
+ /**
+ * Creates an endpoint from latitude/longitude coordinates.
+ *
+ * @param latitude latitude in decimal degrees
+ * @param longitude longitude in decimal degrees
+ * @return a directions endpoint
+ */
public static DirectionsEndpoint fromLatitudeLongitude(double latitude, double longitude) {
return new DirectionsEndpoint(formatCoordinatePair(latitude, longitude));
}
+ /**
+ * Converts this endpoint into the format used by query parameters.
+ *
+ * @return the query string value
+ */
public String toQueryString() {
return formattedLocation;
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsResponse.java b/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsResponse.java
index 7cb7485..936dcf4 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsResponse.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsResponse.java
@@ -14,6 +14,15 @@ public record DirectionsResponse(
List steps,
List> stepPaths
) {
+ /**
+ * Canonical constructor that normalizes potentially-null optionals and lists.
+ *
+ * @param origin origin place, if available
+ * @param destination destination place, if available
+ * @param routes routes returned by the API
+ * @param steps steps returned by the API
+ * @param stepPaths step path coordinate arrays returned by the API
+ */
public DirectionsResponse {
origin = normalizeOptional(origin);
destination = normalizeOptional(destination);
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsRoute.java b/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsRoute.java
index 2eee6e2..a47ae5b 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsRoute.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsRoute.java
@@ -15,6 +15,16 @@ public record DirectionsRoute(
List stepIndexes,
Optional transportType
) {
+ /**
+ * Canonical constructor that normalizes potentially-null optionals and lists.
+ *
+ * @param name route name, if available
+ * @param distanceMeters route distance in meters, if available
+ * @param durationSeconds route duration in seconds, if available
+ * @param hasTolls whether the route has tolls, if available
+ * @param stepIndexes indexes into the directions steps array
+ * @param transportType transport type, if available
+ */
public DirectionsRoute {
name = normalizeOptional(name);
distanceMeters = normalizeOptional(distanceMeters);
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsStep.java b/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsStep.java
index 038da06..3f0525c 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsStep.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/DirectionsStep.java
@@ -13,6 +13,15 @@ public record DirectionsStep(
Optional instructions,
Optional transportType
) {
+ /**
+ * Canonical constructor that normalizes potentially-null optionals.
+ *
+ * @param stepPathIndex index into the step paths array, if available
+ * @param distanceMeters step distance in meters, if available
+ * @param durationSeconds step duration in seconds, if available
+ * @param instructions instructions text, if available
+ * @param transportType transport type, if available
+ */
public DirectionsStep {
stepPathIndex = normalizeOptional(stepPathIndex);
distanceMeters = normalizeOptional(distanceMeters);
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/ErrorResponse.java b/src/main/java/com/williamcallahan/applemaps/domain/model/ErrorResponse.java
index 9edd90f..88a2cd9 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/ErrorResponse.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/ErrorResponse.java
@@ -7,6 +7,12 @@
* Information about an error that occurs while processing a request.
*/
public record ErrorResponse(String message, List details) {
+ /**
+ * Canonical constructor that normalizes potentially-null lists to immutable lists.
+ *
+ * @param message error message
+ * @param details error details
+ */
public ErrorResponse {
message = Objects.requireNonNull(message, "message");
details = List.copyOf(Objects.requireNonNullElse(details, List.of()));
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/EtaEstimate.java b/src/main/java/com/williamcallahan/applemaps/domain/model/EtaEstimate.java
index cf20993..b88f78c 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/EtaEstimate.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/EtaEstimate.java
@@ -13,6 +13,15 @@ public record EtaEstimate(
Optional staticTravelTimeSeconds,
Optional transportType
) {
+ /**
+ * Canonical constructor that normalizes potentially-null optionals.
+ *
+ * @param destination destination location, if available
+ * @param distanceMeters travel distance in meters, if available
+ * @param expectedTravelTimeSeconds expected travel time in seconds, if available
+ * @param staticTravelTimeSeconds static travel time in seconds, if available
+ * @param transportType transport type, if available
+ */
public EtaEstimate {
destination = normalizeOptional(destination);
distanceMeters = normalizeOptional(distanceMeters);
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/EtaResponse.java b/src/main/java/com/williamcallahan/applemaps/domain/model/EtaResponse.java
index 9357ad5..7312fd8 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/EtaResponse.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/EtaResponse.java
@@ -7,6 +7,11 @@
* Estimated time of arrival results for destinations.
*/
public record EtaResponse(List etas) {
+ /**
+ * Canonical constructor that normalizes potentially-null lists to immutable lists.
+ *
+ * @param etas ETA estimates returned by the API
+ */
public EtaResponse {
etas = normalizeList(etas);
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/PaginationInfo.java b/src/main/java/com/williamcallahan/applemaps/domain/model/PaginationInfo.java
index 907ea87..7f9fb3c 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/PaginationInfo.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/PaginationInfo.java
@@ -12,6 +12,14 @@ public record PaginationInfo(
long totalPageCount,
long totalResults
) {
+ /**
+ * Canonical constructor that normalizes potentially-null page tokens.
+ *
+ * @param nextPageToken next page token, if available
+ * @param prevPageToken previous page token, if available
+ * @param totalPageCount total number of pages
+ * @param totalResults total number of results
+ */
public PaginationInfo {
nextPageToken = normalizeOptional(nextPageToken);
prevPageToken = normalizeOptional(prevPageToken);
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/Place.java b/src/main/java/com/williamcallahan/applemaps/domain/model/Place.java
index 839a333..7402ef6 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/Place.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/Place.java
@@ -18,6 +18,19 @@ public record Place(
String country,
String countryCode
) {
+ /**
+ * Canonical constructor that normalizes potentially-null optionals and lists.
+ *
+ * @param id place identifier, if available
+ * @param alternateIds alternate place identifiers
+ * @param name place name
+ * @param coordinate place coordinate
+ * @param displayMapRegion map region to display, if available
+ * @param formattedAddressLines formatted address lines
+ * @param structuredAddress structured address, if available
+ * @param country country name
+ * @param countryCode country code
+ */
public Place {
id = normalizeOptional(id);
alternateIds = normalizeList(alternateIds);
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/PlaceLookupError.java b/src/main/java/com/williamcallahan/applemaps/domain/model/PlaceLookupError.java
index 8c2f5b4..ad3ae28 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/PlaceLookupError.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/PlaceLookupError.java
@@ -6,6 +6,12 @@
* An error associated with a place lookup request.
*/
public record PlaceLookupError(PlaceLookupErrorCode errorCode, String id) {
+ /**
+ * Canonical constructor that validates required fields.
+ *
+ * @param errorCode error code returned by the API
+ * @param id place identifier associated with the error
+ */
public PlaceLookupError {
Objects.requireNonNull(errorCode, "errorCode");
Objects.requireNonNull(id, "id");
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/PlaceLookupErrorCode.java b/src/main/java/com/williamcallahan/applemaps/domain/model/PlaceLookupErrorCode.java
index 6a66e1a..b33425d 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/PlaceLookupErrorCode.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/PlaceLookupErrorCode.java
@@ -4,7 +4,16 @@
* Error codes returned when looking up places by identifier.
*/
public enum PlaceLookupErrorCode {
+ /**
+ * The request provided an invalid place identifier.
+ */
FAILED_INVALID_ID,
+ /**
+ * The requested place could not be found.
+ */
FAILED_NOT_FOUND,
+ /**
+ * The lookup failed due to an internal error.
+ */
FAILED_INTERNAL_ERROR
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/PlaceResults.java b/src/main/java/com/williamcallahan/applemaps/domain/model/PlaceResults.java
index dac6a60..8a53dc6 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/PlaceResults.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/PlaceResults.java
@@ -7,6 +7,11 @@
* An object that contains an array of places.
*/
public record PlaceResults(List results) {
+ /**
+ * Canonical constructor that normalizes potentially-null lists to immutable lists.
+ *
+ * @param results places returned by the API
+ */
public PlaceResults {
results = normalizeList(results);
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/PlacesResponse.java b/src/main/java/com/williamcallahan/applemaps/domain/model/PlacesResponse.java
index 9ce1d2a..d8ed36d 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/PlacesResponse.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/PlacesResponse.java
@@ -7,6 +7,12 @@
* A list of place results and lookup errors.
*/
public record PlacesResponse(List results, List errors) {
+ /**
+ * Canonical constructor that normalizes potentially-null lists to immutable lists.
+ *
+ * @param results places returned by the API
+ * @param errors lookup errors
+ */
public PlacesResponse {
results = normalizeList(results);
errors = normalizeList(errors);
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/PoiCategory.java b/src/main/java/com/williamcallahan/applemaps/domain/model/PoiCategory.java
index 69102ca..59949fd 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/PoiCategory.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/PoiCategory.java
@@ -4,51 +4,97 @@
* Defines Apple Maps point-of-interest categories.
*/
public enum PoiCategory {
+ /** Airport. */
AIRPORT("Airport"),
+ /** Airport gate. */
AIRPORT_GATE("AirportGate"),
+ /** Airport terminal. */
AIRPORT_TERMINAL("AirportTerminal"),
+ /** Amusement park. */
AMUSEMENT_PARK("AmusementPark"),
+ /** ATM. */
ATM("ATM"),
+ /** Aquarium. */
AQUARIUM("Aquarium"),
+ /** Bakery. */
BAKERY("Bakery"),
+ /** Bank. */
BANK("Bank"),
+ /** Beach. */
BEACH("Beach"),
+ /** Brewery. */
BREWERY("Brewery"),
+ /** Bowling alley. */
BOWLING("Bowling"),
+ /** Cafe. */
CAFE("Cafe"),
+ /** Campground. */
CAMPGROUND("Campground"),
+ /** Car rental. */
CAR_RENTAL("CarRental"),
+ /** EV charger. */
EV_CHARGER("EVCharger"),
+ /** Fire station. */
FIRE_STATION("FireStation"),
+ /** Fitness center. */
FITNESS_CENTER("FitnessCenter"),
+ /** Food market. */
FOOD_MARKET("FoodMarket"),
+ /** Gas station. */
GAS_STATION("GasStation"),
+ /** Hospital. */
HOSPITAL("Hospital"),
+ /** Hotel. */
HOTEL("Hotel"),
+ /** Laundry. */
LAUNDRY("Laundry"),
+ /** Library. */
LIBRARY("Library"),
+ /** Marina. */
MARINA("Marina"),
+ /** Movie theater. */
MOVIE_THEATER("MovieTheater"),
+ /** Museum. */
MUSEUM("Museum"),
+ /** National park. */
NATIONAL_PARK("NationalPark"),
+ /** Nightlife. */
NIGHTLIFE("Nightlife"),
+ /** Park. */
PARK("Park"),
+ /** Parking. */
PARKING("Parking"),
+ /** Pharmacy. */
PHARMACY("Pharmacy"),
+ /** Playground. */
PLAYGROUND("Playground"),
+ /** Police. */
POLICE("Police"),
+ /** Post office. */
POST_OFFICE("PostOffice"),
+ /** Public transport. */
PUBLIC_TRANSPORT("PublicTransport"),
+ /** Religious site. */
RELIGIOUS_SITE("ReligiousSite"),
+ /** Restaurant. */
RESTAURANT("Restaurant"),
+ /** Restroom. */
RESTROOM("Restroom"),
+ /** School. */
SCHOOL("School"),
+ /** Stadium. */
STADIUM("Stadium"),
+ /** Store. */
STORE("Store"),
+ /** Theater. */
THEATER("Theater"),
+ /** University. */
UNIVERSITY("University"),
+ /** Winery. */
WINERY("Winery"),
+ /** Zoo. */
ZOO("Zoo"),
+ /** Landmark. */
LANDMARK("Landmark");
private final String apiValue;
@@ -57,6 +103,11 @@ public enum PoiCategory {
this.apiValue = apiValue;
}
+ /**
+ * Returns the Apple Maps Server API value for this category.
+ *
+ * @return the value used by the API
+ */
public String apiValue() {
return apiValue;
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/RouteLocation.java b/src/main/java/com/williamcallahan/applemaps/domain/model/RouteLocation.java
index 872c02a..04e6a80 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/RouteLocation.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/RouteLocation.java
@@ -8,14 +8,31 @@
public record RouteLocation(String coordinatePair) {
private static final String COORDINATE_SEPARATOR = ",";
+ /**
+ * Canonical constructor that validates the coordinate pair is non-null.
+ *
+ * @param coordinatePair formatted coordinate pair
+ */
public RouteLocation {
coordinatePair = Objects.requireNonNull(coordinatePair, "coordinatePair");
}
+ /**
+ * Creates a route location from latitude/longitude coordinates.
+ *
+ * @param latitude latitude in decimal degrees
+ * @param longitude longitude in decimal degrees
+ * @return a route location
+ */
public static RouteLocation fromLatitudeLongitude(double latitude, double longitude) {
return new RouteLocation(formatCoordinatePair(latitude, longitude));
}
+ /**
+ * Converts this location into the format used by query parameters.
+ *
+ * @return the query string value
+ */
public String toQueryString() {
return coordinatePair;
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/SearchACResultType.java b/src/main/java/com/williamcallahan/applemaps/domain/model/SearchACResultType.java
index 302939d..6f6f474 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/SearchACResultType.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/SearchACResultType.java
@@ -4,10 +4,15 @@
* Enumerated values that indicate the result type for autocomplete requests.
*/
public enum SearchACResultType {
+ /** Point of interest result. */
POI("poi"),
+ /** Address result. */
ADDRESS("address"),
+ /** Physical feature result. */
PHYSICAL_FEATURE("physicalFeature"),
+ /** Point of interest result. */
POINT_OF_INTEREST("pointOfInterest"),
+ /** Query result. */
QUERY("query");
private final String apiValue;
@@ -16,6 +21,11 @@ public enum SearchACResultType {
this.apiValue = apiValue;
}
+ /**
+ * Returns the Apple Maps Server API value for this result type.
+ *
+ * @return the value used by the API
+ */
public String apiValue() {
return apiValue;
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/SearchAutocompleteResponse.java b/src/main/java/com/williamcallahan/applemaps/domain/model/SearchAutocompleteResponse.java
index 61bb72d..43ec162 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/SearchAutocompleteResponse.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/SearchAutocompleteResponse.java
@@ -7,6 +7,11 @@
* An object that contains autocomplete results.
*/
public record SearchAutocompleteResponse(List results) {
+ /**
+ * Canonical constructor that normalizes potentially-null lists to immutable lists.
+ *
+ * @param results autocomplete results returned by the API
+ */
public SearchAutocompleteResponse {
results = normalizeList(results);
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/SearchLocation.java b/src/main/java/com/williamcallahan/applemaps/domain/model/SearchLocation.java
index 600d2a5..8950caa 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/SearchLocation.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/SearchLocation.java
@@ -8,10 +8,22 @@
public record SearchLocation(String coordinatePair) {
private static final String COORDINATE_SEPARATOR = ",";
+ /**
+ * Canonical constructor that validates the coordinate pair is non-null.
+ *
+ * @param coordinatePair formatted coordinate pair
+ */
public SearchLocation {
Objects.requireNonNull(coordinatePair, "coordinatePair");
}
+ /**
+ * Creates a search location from latitude/longitude coordinates.
+ *
+ * @param latitude latitude in decimal degrees
+ * @param longitude longitude in decimal degrees
+ * @return a search location
+ */
public static SearchLocation fromLatitudeLongitude(
double latitude,
double longitude
@@ -19,6 +31,11 @@ public static SearchLocation fromLatitudeLongitude(
return new SearchLocation(formatCoordinatePair(latitude, longitude));
}
+ /**
+ * Converts this location into the format used by query parameters.
+ *
+ * @return the query string value
+ */
public String toQueryString() {
return coordinatePair;
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/SearchRegion.java b/src/main/java/com/williamcallahan/applemaps/domain/model/SearchRegion.java
index d0cfb97..37ab954 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/SearchRegion.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/SearchRegion.java
@@ -9,10 +9,24 @@
public record SearchRegion(String coordinateBounds) {
private static final String COORDINATE_SEPARATOR = ",";
+ /**
+ * Canonical constructor that validates the coordinate bounds are non-null.
+ *
+ * @param coordinateBounds formatted coordinate bounds string
+ */
public SearchRegion {
Objects.requireNonNull(coordinateBounds, "coordinateBounds");
}
+ /**
+ * Creates a search region from a bounding box.
+ *
+ * @param northLatitude northern latitude in decimal degrees
+ * @param eastLongitude eastern longitude in decimal degrees
+ * @param southLatitude southern latitude in decimal degrees
+ * @param westLongitude western longitude in decimal degrees
+ * @return a search region
+ */
public static SearchRegion fromBounds(
double northLatitude,
double eastLongitude,
@@ -22,6 +36,12 @@ public static SearchRegion fromBounds(
return new SearchRegion(formatBounds(northLatitude, eastLongitude, southLatitude, westLongitude));
}
+ /**
+ * Creates a search region from a {@link SearchMapRegion}.
+ *
+ * @param searchMapRegion the map region to convert
+ * @return a search region
+ */
public static SearchRegion fromSearchMapRegion(SearchMapRegion searchMapRegion) {
Objects.requireNonNull(searchMapRegion, "searchMapRegion");
return fromBounds(
@@ -32,6 +52,11 @@ public static SearchRegion fromSearchMapRegion(SearchMapRegion searchMapRegion)
);
}
+ /**
+ * Converts this region into the format used by query parameters.
+ *
+ * @return the query string value
+ */
public String toQueryString() {
return coordinateBounds;
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/SearchRegionPriority.java b/src/main/java/com/williamcallahan/applemaps/domain/model/SearchRegionPriority.java
index 76462f7..6b05cd9 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/SearchRegionPriority.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/SearchRegionPriority.java
@@ -4,7 +4,9 @@
* Indicates the importance of the configured search region.
*/
public enum SearchRegionPriority {
+ /** Default priority. */
DEFAULT("default"),
+ /** Required priority. */
REQUIRED("required");
private final String apiValue;
@@ -13,6 +15,11 @@ public enum SearchRegionPriority {
this.apiValue = apiValue;
}
+ /**
+ * Returns the Apple Maps Server API value for this priority.
+ *
+ * @return the value used by the API
+ */
public String apiValue() {
return apiValue;
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/SearchResponse.java b/src/main/java/com/williamcallahan/applemaps/domain/model/SearchResponse.java
index 2a7b26d..0643ab8 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/SearchResponse.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/SearchResponse.java
@@ -12,6 +12,13 @@ public record SearchResponse(
Optional paginationInfo,
List results
) {
+ /**
+ * Canonical constructor that normalizes potentially-null optionals and lists.
+ *
+ * @param displayMapRegion display map region returned by the API
+ * @param paginationInfo pagination information, if available
+ * @param results search results returned by the API
+ */
public SearchResponse {
displayMapRegion = Objects.requireNonNull(displayMapRegion, "displayMapRegion");
paginationInfo = normalizeOptional(paginationInfo);
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/SearchResponsePlace.java b/src/main/java/com/williamcallahan/applemaps/domain/model/SearchResponsePlace.java
index f569da3..a8faed0 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/SearchResponsePlace.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/SearchResponsePlace.java
@@ -19,6 +19,20 @@ public record SearchResponsePlace(
String countryCode,
Optional poiCategory
) {
+ /**
+ * Canonical constructor that normalizes potentially-null optionals and lists.
+ *
+ * @param id place identifier, if available
+ * @param alternateIds alternate place identifiers
+ * @param name place name
+ * @param coordinate place coordinate
+ * @param displayMapRegion map region to display, if available
+ * @param formattedAddressLines formatted address lines
+ * @param structuredAddress structured address, if available
+ * @param country country name
+ * @param countryCode country code
+ * @param poiCategory point-of-interest category, if available
+ */
public SearchResponsePlace {
id = normalizeOptional(id);
alternateIds = normalizeList(alternateIds);
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/SearchResultType.java b/src/main/java/com/williamcallahan/applemaps/domain/model/SearchResultType.java
index 4cf4b34..9ec370e 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/SearchResultType.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/SearchResultType.java
@@ -4,9 +4,13 @@
* Enumerated values that indicate the result type for search requests.
*/
public enum SearchResultType {
+ /** Point of interest result. */
POI("poi"),
+ /** Address result. */
ADDRESS("address"),
+ /** Physical feature result. */
PHYSICAL_FEATURE("physicalFeature"),
+ /** Point of interest result. */
POINT_OF_INTEREST("pointOfInterest");
private final String apiValue;
@@ -15,6 +19,11 @@ public enum SearchResultType {
this.apiValue = apiValue;
}
+ /**
+ * Returns the Apple Maps Server API value for this result type.
+ *
+ * @return the value used by the API
+ */
public String apiValue() {
return apiValue;
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/StructuredAddress.java b/src/main/java/com/williamcallahan/applemaps/domain/model/StructuredAddress.java
index c7e6450..3cda3c6 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/StructuredAddress.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/StructuredAddress.java
@@ -20,6 +20,21 @@ public record StructuredAddress(
Optional subThoroughfare,
Optional thoroughfare
) {
+ /**
+ * Canonical constructor that normalizes potentially-null optionals and lists.
+ *
+ * @param administrativeArea administrative area, if available
+ * @param administrativeAreaCode administrative area code, if available
+ * @param subAdministrativeArea sub-administrative area, if available
+ * @param areasOfInterest areas of interest
+ * @param dependentLocalities dependent localities
+ * @param fullThoroughfare full thoroughfare, if available
+ * @param locality locality, if available
+ * @param postCode postal code, if available
+ * @param subLocality sub-locality, if available
+ * @param subThoroughfare sub-thoroughfare, if available
+ * @param thoroughfare thoroughfare, if available
+ */
public StructuredAddress {
administrativeArea = normalizeOptional(administrativeArea);
administrativeAreaCode = normalizeOptional(administrativeAreaCode);
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/TokenResponse.java b/src/main/java/com/williamcallahan/applemaps/domain/model/TokenResponse.java
index 44b9fee..8f1b7d1 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/TokenResponse.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/TokenResponse.java
@@ -6,6 +6,12 @@
* An object that contains an access token and its expiration time in seconds.
*/
public record TokenResponse(String accessToken, long expiresInSeconds) {
+ /**
+ * Canonical constructor that validates required fields.
+ *
+ * @param accessToken access token string
+ * @param expiresInSeconds expiration time in seconds
+ */
public TokenResponse {
Objects.requireNonNull(accessToken, "accessToken");
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/TransportType.java b/src/main/java/com/williamcallahan/applemaps/domain/model/TransportType.java
index e856617..8ea2e40 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/TransportType.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/TransportType.java
@@ -4,9 +4,13 @@
* Supported transportation modes for routing and ETA requests.
*/
public enum TransportType {
+ /** Automobile transport mode. */
AUTOMOBILE("Automobile"),
+ /** Transit transport mode. */
TRANSIT("Transit"),
+ /** Walking transport mode. */
WALKING("Walking"),
+ /** Cycling transport mode. */
CYCLING("Cycling");
private final String apiValue;
@@ -15,6 +19,11 @@ public enum TransportType {
this.apiValue = apiValue;
}
+ /**
+ * Returns the Apple Maps Server API value for this transport type.
+ *
+ * @return the value used by the API
+ */
public String apiValue() {
return apiValue;
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/model/UserLocation.java b/src/main/java/com/williamcallahan/applemaps/domain/model/UserLocation.java
index 01d5fce..fa5acd3 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/model/UserLocation.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/model/UserLocation.java
@@ -8,10 +8,22 @@
public record UserLocation(String coordinatePair) {
private static final String COORDINATE_SEPARATOR = ",";
+ /**
+ * Canonical constructor that validates the coordinate pair is non-null.
+ *
+ * @param coordinatePair formatted coordinate pair
+ */
public UserLocation {
Objects.requireNonNull(coordinatePair, "coordinatePair");
}
+ /**
+ * Creates a user location from latitude/longitude coordinates.
+ *
+ * @param latitude latitude in decimal degrees
+ * @param longitude longitude in decimal degrees
+ * @return a user location
+ */
public static UserLocation fromLatitudeLongitude(
double latitude,
double longitude
@@ -19,6 +31,11 @@ public static UserLocation fromLatitudeLongitude(
return new UserLocation(formatCoordinatePair(latitude, longitude));
}
+ /**
+ * Converts this location into the format used by query parameters.
+ *
+ * @return the query string value
+ */
public String toQueryString() {
return coordinatePair;
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/port/AppleMapsGateway.java b/src/main/java/com/williamcallahan/applemaps/domain/port/AppleMapsGateway.java
index 8a4be74..17985c1 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/port/AppleMapsGateway.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/port/AppleMapsGateway.java
@@ -20,26 +20,92 @@
* Port for invoking Apple Maps Server API operations.
*/
public interface AppleMapsGateway {
+ /**
+ * Performs a geocode request.
+ *
+ * @param input request parameters
+ * @return geocode results
+ */
PlaceResults geocode(GeocodeInput input);
+ /**
+ * Performs a text search request.
+ *
+ * @param input request parameters
+ * @return search results
+ */
SearchResponse search(SearchInput input);
+ /**
+ * Performs an autocomplete request.
+ *
+ * @param input request parameters
+ * @return autocomplete results
+ */
SearchAutocompleteResponse autocomplete(SearchAutocompleteInput input);
+ /**
+ * Resolves a completion URL returned from an autocomplete response.
+ *
+ * @param completionUrl the URL to resolve
+ * @return search results for the completion URL
+ */
SearchResponse resolveCompletionUrl(String completionUrl);
+ /**
+ * Performs a reverse geocode request.
+ *
+ * @param latitude latitude in decimal degrees
+ * @param longitude longitude in decimal degrees
+ * @param language response language (BCP 47)
+ * @return reverse geocode results
+ */
PlaceResults reverseGeocode(double latitude, double longitude, String language);
+ /**
+ * Performs a directions request.
+ *
+ * @param input request parameters
+ * @return directions results
+ */
DirectionsResponse directions(DirectionsInput input);
+ /**
+ * Performs an ETA request.
+ *
+ * @param input request parameters
+ * @return ETA results
+ */
EtaResponse etas(EtaInput input);
+ /**
+ * Looks up a place by ID.
+ *
+ * @param placeId place identifier
+ * @param language response language (BCP 47)
+ * @return a place
+ */
Place lookupPlace(String placeId, String language);
+ /**
+ * Performs a place lookup request.
+ *
+ * @param input request parameters
+ * @return places results
+ */
PlacesResponse lookupPlaces(PlaceLookupInput input);
+ /**
+ * Performs an alternate IDs lookup request.
+ *
+ * @param input request parameters
+ * @return alternate IDs results
+ */
AlternateIdsResponse lookupAlternateIds(AlternateIdsInput input);
+ /**
+ * Releases any resources owned by the gateway.
+ */
default void close() {
}
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/request/AlternateIdsInput.java b/src/main/java/com/williamcallahan/applemaps/domain/request/AlternateIdsInput.java
index 6b812d2..4de1ce9 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/request/AlternateIdsInput.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/request/AlternateIdsInput.java
@@ -15,17 +15,33 @@ public record AlternateIdsInput(List ids) {
private static final String PARAMETER_IDS = "ids";
private static final String LIST_SEPARATOR = ",";
+ /**
+ * Canonical constructor that validates required fields and normalizes collections.
+ *
+ * @param ids place identifiers to resolve alternate IDs for
+ */
public AlternateIdsInput {
ids = normalizeList(ids);
validateIds(ids);
}
+ /**
+ * Converts this input to a query string suitable for the Apple Maps Server API.
+ *
+ * @return a query string beginning with {@code ?}
+ */
public String toQueryString() {
List parameters = new ArrayList<>();
parameters.add(formatParameter(PARAMETER_IDS, joinEncoded(ids)));
return QUERY_PREFIX + String.join(PARAMETER_SEPARATOR, parameters);
}
+ /**
+ * Creates a builder initialized with the required place IDs.
+ *
+ * @param ids the place IDs to resolve alternate IDs for
+ * @return a builder
+ */
public static Builder builder(List ids) {
return new Builder(ids);
}
@@ -55,6 +71,9 @@ private static String encode(String rawText) {
return URLEncoder.encode(rawText, StandardCharsets.UTF_8);
}
+ /**
+ * Builder for {@link AlternateIdsInput}.
+ */
public static final class Builder {
private final List ids;
@@ -62,6 +81,11 @@ private Builder(List ids) {
this.ids = normalizeList(ids);
}
+ /**
+ * Builds a validated {@link AlternateIdsInput}.
+ *
+ * @return an input instance
+ */
public AlternateIdsInput build() {
return new AlternateIdsInput(ids);
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/request/DirectionsInput.java b/src/main/java/com/williamcallahan/applemaps/domain/request/DirectionsInput.java
index a442177..0e0ef0e 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/request/DirectionsInput.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/request/DirectionsInput.java
@@ -44,6 +44,21 @@ public record DirectionsInput(
private static final String PARAMETER_TRANSPORT_TYPE = "transportType";
private static final String PARAMETER_USER_LOCATION = "userLocation";
+ /**
+ * Canonical constructor that validates required fields and normalizes optional values.
+ *
+ * @param origin the origin endpoint
+ * @param destination the destination endpoint
+ * @param arrivalDate optional arrival date/time (format as expected by the API)
+ * @param avoid route features to avoid
+ * @param departureDate optional departure date/time (format as expected by the API)
+ * @param language optional response language (BCP 47)
+ * @param requestsAlternateRoutes optional flag to request alternate routes
+ * @param searchLocation optional search location hint
+ * @param searchRegion optional search region hint
+ * @param transportType optional transport type
+ * @param userLocation optional user location hint
+ */
public DirectionsInput {
origin = Objects.requireNonNull(origin, "origin");
destination = Objects.requireNonNull(destination, "destination");
@@ -59,6 +74,11 @@ public record DirectionsInput(
validateDates(arrivalDate, departureDate);
}
+ /**
+ * Converts this input to a query string suitable for the Apple Maps Server API.
+ *
+ * @return a query string beginning with {@code ?}
+ */
public String toQueryString() {
List parameters = new ArrayList<>();
parameters.add(formatParameter(PARAMETER_ORIGIN, encode(origin.toQueryString())));
@@ -82,6 +102,13 @@ public String toQueryString() {
return QUERY_PREFIX + String.join(PARAMETER_SEPARATOR, parameters);
}
+ /**
+ * Creates a builder initialized with the required origin and destination.
+ *
+ * @param origin the origin endpoint
+ * @param destination the destination endpoint
+ * @return a builder
+ */
public static Builder builder(DirectionsEndpoint origin, DirectionsEndpoint destination) {
return new Builder(origin, destination);
}
@@ -115,6 +142,9 @@ private static String encode(String rawText) {
return URLEncoder.encode(rawText, StandardCharsets.UTF_8);
}
+ /**
+ * Builder for {@link DirectionsInput}.
+ */
public static final class Builder {
private final DirectionsEndpoint origin;
private final DirectionsEndpoint destination;
@@ -133,51 +163,110 @@ private Builder(DirectionsEndpoint origin, DirectionsEndpoint destination) {
this.destination = Objects.requireNonNull(destination, "destination");
}
+ /**
+ * Sets the arrival date/time parameter (format as expected by the API).
+ *
+ * @param arrivalDate the arrival date/time, or {@code null} to clear
+ * @return this builder
+ */
public Builder arrivalDate(String arrivalDate) {
this.arrivalDate = Optional.ofNullable(arrivalDate);
return this;
}
+ /**
+ * Sets route features to avoid.
+ *
+ * @param avoid route features to avoid (empty means no avoid filters)
+ * @return this builder
+ */
public Builder avoid(List avoid) {
this.avoid = normalizeList(avoid);
return this;
}
+ /**
+ * Sets the departure date/time parameter (format as expected by the API).
+ *
+ * @param departureDate the departure date/time, or {@code null} to clear
+ * @return this builder
+ */
public Builder departureDate(String departureDate) {
this.departureDate = Optional.ofNullable(departureDate);
return this;
}
+ /**
+ * Sets the response language (BCP 47).
+ *
+ * @param language the language tag, or {@code null} to clear
+ * @return this builder
+ */
public Builder language(String language) {
this.language = Optional.ofNullable(language);
return this;
}
+ /**
+ * Sets whether alternate routes should be requested.
+ *
+ * @param requestsAlternateRoutes {@code true} to request alternate routes, or {@code null} to clear
+ * @return this builder
+ */
public Builder requestsAlternateRoutes(Boolean requestsAlternateRoutes) {
this.requestsAlternateRoutes = Optional.ofNullable(requestsAlternateRoutes);
return this;
}
+ /**
+ * Sets the search location hint used by the API.
+ *
+ * @param searchLocation the search location, or {@code null} to clear
+ * @return this builder
+ */
public Builder searchLocation(RouteLocation searchLocation) {
this.searchLocation = Optional.ofNullable(searchLocation);
return this;
}
+ /**
+ * Sets the search region hint used by the API.
+ *
+ * @param searchRegion the search region, or {@code null} to clear
+ * @return this builder
+ */
public Builder searchRegion(SearchRegion searchRegion) {
this.searchRegion = Optional.ofNullable(searchRegion);
return this;
}
+ /**
+ * Sets the requested transport type.
+ *
+ * @param transportType the transport type, or {@code null} to clear
+ * @return this builder
+ */
public Builder transportType(TransportType transportType) {
this.transportType = Optional.ofNullable(transportType);
return this;
}
+ /**
+ * Sets the user location hint used by the API.
+ *
+ * @param userLocation the user location, or {@code null} to clear
+ * @return this builder
+ */
public Builder userLocation(RouteLocation userLocation) {
this.userLocation = Optional.ofNullable(userLocation);
return this;
}
+ /**
+ * Builds a validated {@link DirectionsInput}.
+ *
+ * @return an input instance
+ */
public DirectionsInput build() {
return new DirectionsInput(
origin,
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/request/EtaInput.java b/src/main/java/com/williamcallahan/applemaps/domain/request/EtaInput.java
index 4738dd8..f941235 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/request/EtaInput.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/request/EtaInput.java
@@ -29,6 +29,15 @@ public record EtaInput(
private static final String PARAMETER_DEPARTURE_DATE = "departureDate";
private static final String PARAMETER_ARRIVAL_DATE = "arrivalDate";
+ /**
+ * Canonical constructor that validates required fields and normalizes optional values.
+ *
+ * @param origin origin location
+ * @param destinations destination locations (maximum 10)
+ * @param transportType optional transport type
+ * @param departureDate optional departure date/time (format as expected by the API)
+ * @param arrivalDate optional arrival date/time (format as expected by the API)
+ */
public EtaInput {
origin = Objects.requireNonNull(origin, "origin");
destinations = normalizeList(destinations);
@@ -38,6 +47,11 @@ public record EtaInput(
validateDestinations(destinations);
}
+ /**
+ * Converts this input to a query string suitable for the Apple Maps Server API.
+ *
+ * @return a query string beginning with {@code ?}
+ */
public String toQueryString() {
List parameters = new ArrayList<>();
parameters.add(formatParameter(PARAMETER_ORIGIN, origin.toQueryString()));
@@ -48,6 +62,13 @@ public String toQueryString() {
return QUERY_PREFIX + String.join(PARAMETER_SEPARATOR, parameters);
}
+ /**
+ * Creates a builder initialized with the required origin and destinations.
+ *
+ * @param origin the route origin
+ * @param destinations the destinations (maximum 10)
+ * @return a builder
+ */
public static Builder builder(RouteLocation origin, List destinations) {
return new Builder(origin, destinations);
}
@@ -84,6 +105,9 @@ private static String encode(String rawText) {
return URLEncoder.encode(rawText, StandardCharsets.UTF_8);
}
+ /**
+ * Builder for {@link EtaInput}.
+ */
public static final class Builder {
private final RouteLocation origin;
private final List destinations;
@@ -96,21 +120,44 @@ private Builder(RouteLocation origin, List destinations) {
this.destinations = normalizeList(destinations);
}
+ /**
+ * Sets the requested transport type.
+ *
+ * @param transportType the transport type, or {@code null} to clear
+ * @return this builder
+ */
public Builder transportType(TransportType transportType) {
this.transportType = Optional.ofNullable(transportType);
return this;
}
+ /**
+ * Sets the departure date/time parameter (format as expected by the API).
+ *
+ * @param departureDate the departure date/time, or {@code null} to clear
+ * @return this builder
+ */
public Builder departureDate(String departureDate) {
this.departureDate = Optional.ofNullable(departureDate);
return this;
}
+ /**
+ * Sets the arrival date/time parameter (format as expected by the API).
+ *
+ * @param arrivalDate the arrival date/time, or {@code null} to clear
+ * @return this builder
+ */
public Builder arrivalDate(String arrivalDate) {
this.arrivalDate = Optional.ofNullable(arrivalDate);
return this;
}
+ /**
+ * Builds a validated {@link EtaInput}.
+ *
+ * @return an input instance
+ */
public EtaInput build() {
return new EtaInput(origin, destinations, transportType, departureDate, arrivalDate);
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/request/GeocodeInput.java b/src/main/java/com/williamcallahan/applemaps/domain/request/GeocodeInput.java
index 9b92bed..d6da610 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/request/GeocodeInput.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/request/GeocodeInput.java
@@ -31,6 +31,16 @@ public record GeocodeInput(
private static final String PARAMETER_SEARCH_REGION = "searchRegion";
private static final String PARAMETER_USER_LOCATION = "userLocation";
+ /**
+ * Canonical constructor that validates required fields and normalizes optional values.
+ *
+ * @param address address text to geocode
+ * @param limitToCountries country codes to restrict results to
+ * @param language optional response language (BCP 47)
+ * @param searchLocation optional search location hint
+ * @param searchRegion optional search region hint
+ * @param userLocation optional user location hint
+ */
public GeocodeInput {
address = Objects.requireNonNull(address, "address");
limitToCountries = normalizeList(limitToCountries);
@@ -40,6 +50,11 @@ public record GeocodeInput(
userLocation = normalizeOptional(userLocation);
}
+ /**
+ * Converts this input to a query string suitable for the Apple Maps Server API.
+ *
+ * @return a query string beginning with {@code ?}
+ */
public String toQueryString() {
List parameters = new ArrayList<>();
parameters.add(formatParameter(PARAMETER_QUERY, encode(address)));
@@ -53,6 +68,12 @@ public String toQueryString() {
return QUERY_PREFIX + String.join(PARAMETER_SEPARATOR, parameters);
}
+ /**
+ * Creates a builder initialized with the required address string.
+ *
+ * @param address the address text to geocode
+ * @return a builder
+ */
public static Builder builder(String address) {
return new Builder(address);
}
@@ -80,6 +101,9 @@ private static String encode(String value) {
return URLEncoder.encode(value, StandardCharsets.UTF_8);
}
+ /**
+ * Builder for {@link GeocodeInput}.
+ */
public static final class Builder {
private final String address;
private List limitToCountries = List.of();
@@ -92,31 +116,66 @@ private Builder(String address) {
this.address = Objects.requireNonNull(address, "address");
}
+ /**
+ * Limits results to specific country codes.
+ *
+ * @param countries country codes (for example, ISO 3166-1 alpha-2)
+ * @return this builder
+ */
public Builder limitToCountries(List countries) {
this.limitToCountries = normalizeList(countries);
return this;
}
+ /**
+ * Sets the response language (BCP 47).
+ *
+ * @param language the language tag, or {@code null} to clear
+ * @return this builder
+ */
public Builder language(String language) {
this.language = Optional.ofNullable(language);
return this;
}
+ /**
+ * Sets the search location hint used by the API.
+ *
+ * @param searchLocation the search location, or {@code null} to clear
+ * @return this builder
+ */
public Builder searchLocation(SearchLocation searchLocation) {
this.searchLocation = Optional.ofNullable(searchLocation);
return this;
}
+ /**
+ * Sets the search region hint used by the API.
+ *
+ * @param searchRegion the search region, or {@code null} to clear
+ * @return this builder
+ */
public Builder searchRegion(SearchRegion searchRegion) {
this.searchRegion = Optional.ofNullable(searchRegion);
return this;
}
+ /**
+ * Sets the user location hint used by the API.
+ *
+ * @param userLocation the user location, or {@code null} to clear
+ * @return this builder
+ */
public Builder userLocation(UserLocation userLocation) {
this.userLocation = Optional.ofNullable(userLocation);
return this;
}
+ /**
+ * Builds a {@link GeocodeInput}.
+ *
+ * @return an input instance
+ */
public GeocodeInput build() {
return new GeocodeInput(address, limitToCountries, language, searchLocation, searchRegion, userLocation);
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/request/PlaceLookupInput.java b/src/main/java/com/williamcallahan/applemaps/domain/request/PlaceLookupInput.java
index d7b4e3d..0aa9fa7 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/request/PlaceLookupInput.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/request/PlaceLookupInput.java
@@ -17,12 +17,23 @@ public record PlaceLookupInput(List ids, Optional language) {
private static final String PARAMETER_IDS = "ids";
private static final String PARAMETER_LANGUAGE = "lang";
+ /**
+ * Canonical constructor that validates required fields and normalizes optional values.
+ *
+ * @param ids place identifiers to look up
+ * @param language optional response language (BCP 47)
+ */
public PlaceLookupInput {
ids = normalizeList(ids);
language = normalizeOptional(language);
validateIds(ids);
}
+ /**
+ * Converts this input to a query string suitable for the Apple Maps Server API.
+ *
+ * @return a query string beginning with {@code ?}
+ */
public String toQueryString() {
List parameters = new ArrayList<>();
parameters.add(formatParameter(PARAMETER_IDS, joinEncoded(ids)));
@@ -30,6 +41,12 @@ public String toQueryString() {
return QUERY_PREFIX + String.join(PARAMETER_SEPARATOR, parameters);
}
+ /**
+ * Creates a builder initialized with the required place IDs.
+ *
+ * @param ids place identifiers to look up
+ * @return a builder
+ */
public static Builder builder(List ids) {
return new Builder(ids);
}
@@ -63,6 +80,9 @@ private static String encode(String rawText) {
return URLEncoder.encode(rawText, StandardCharsets.UTF_8);
}
+ /**
+ * Builder for {@link PlaceLookupInput}.
+ */
public static final class Builder {
private final List ids;
private Optional language = Optional.empty();
@@ -71,11 +91,22 @@ private Builder(List ids) {
this.ids = normalizeList(ids);
}
+ /**
+ * Sets the response language (BCP 47).
+ *
+ * @param language the language tag, or {@code null} to clear
+ * @return this builder
+ */
public Builder language(String language) {
this.language = Optional.ofNullable(language);
return this;
}
+ /**
+ * Builds a validated {@link PlaceLookupInput}.
+ *
+ * @return an input instance
+ */
public PlaceLookupInput build() {
return new PlaceLookupInput(ids, language);
}
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/request/SearchAutocompleteInput.java b/src/main/java/com/williamcallahan/applemaps/domain/request/SearchAutocompleteInput.java
index ccc7dad..24a91ac 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/request/SearchAutocompleteInput.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/request/SearchAutocompleteInput.java
@@ -48,6 +48,22 @@ public record SearchAutocompleteInput(
private static final String PARAMETER_INCLUDE_ADDRESS_CATEGORIES = "includeAddressCategories";
private static final String PARAMETER_EXCLUDE_ADDRESS_CATEGORIES = "excludeAddressCategories";
+ /**
+ * Canonical constructor that validates required fields and normalizes optional values.
+ *
+ * @param q query string
+ * @param excludePoiCategories POI categories to exclude
+ * @param includePoiCategories POI categories to include
+ * @param limitToCountries country codes to restrict results to
+ * @param resultTypeFilter autocomplete result type filter
+ * @param includeAddressCategories address categories to include
+ * @param excludeAddressCategories address categories to exclude
+ * @param language optional response language (BCP 47)
+ * @param searchLocation optional search location hint
+ * @param searchRegion optional search region hint
+ * @param userLocation optional user location hint
+ * @param searchRegionPriority optional search region priority
+ */
public SearchAutocompleteInput {
q = Objects.requireNonNull(q, "q");
excludePoiCategories = normalizeList(excludePoiCategories);
@@ -63,6 +79,11 @@ public record SearchAutocompleteInput(
searchRegionPriority = normalizeOptional(searchRegionPriority);
}
+ /**
+ * Converts this input to a query string suitable for the Apple Maps Server API.
+ *
+ * @return a query string beginning with {@code ?}
+ */
public String toQueryString() {
List parameters = new ArrayList<>();
parameters.add(formatParameter(PARAMETER_QUERY, encode(q)));
@@ -92,6 +113,12 @@ public String toQueryString() {
return QUERY_PREFIX + String.join(PARAMETER_SEPARATOR, parameters);
}
+ /**
+ * Creates a builder initialized with the required query string.
+ *
+ * @param q query string
+ * @return a builder
+ */
public static Builder builder(String q) {
return new Builder(q);
}
@@ -126,6 +153,9 @@ private static String encode(String value) {
return URLEncoder.encode(value, StandardCharsets.UTF_8);
}
+ /**
+ * Builder for {@link SearchAutocompleteInput}.
+ */
public static final class Builder {
private final String q;
private List excludePoiCategories = List.of();
@@ -144,61 +174,132 @@ private Builder(String q) {
this.q = Objects.requireNonNull(q, "q");
}
+ /**
+ * Sets POI categories to exclude.
+ *
+ * @param categories categories to exclude
+ * @return this builder
+ */
public Builder excludePoiCategories(List categories) {
this.excludePoiCategories = normalizeList(categories);
return this;
}
+ /**
+ * Sets POI categories to include.
+ *
+ * @param categories categories to include
+ * @return this builder
+ */
public Builder includePoiCategories(List categories) {
this.includePoiCategories = normalizeList(categories);
return this;
}
+ /**
+ * Limits results to specific country codes.
+ *
+ * @param countries country codes (for example, ISO 3166-1 alpha-2)
+ * @return this builder
+ */
public Builder limitToCountries(List countries) {
this.limitToCountries = normalizeList(countries);
return this;
}
+ /**
+ * Sets the autocomplete result type filter.
+ *
+ * @param resultTypes result types to include
+ * @return this builder
+ */
public Builder resultTypeFilter(List resultTypes) {
this.resultTypeFilter = normalizeList(resultTypes);
return this;
}
+ /**
+ * Sets address categories to include.
+ *
+ * @param categories address categories to include
+ * @return this builder
+ */
public Builder includeAddressCategories(List categories) {
this.includeAddressCategories = normalizeList(categories);
return this;
}
+ /**
+ * Sets address categories to exclude.
+ *
+ * @param categories address categories to exclude
+ * @return this builder
+ */
public Builder excludeAddressCategories(List categories) {
this.excludeAddressCategories = normalizeList(categories);
return this;
}
+ /**
+ * Sets the response language (BCP 47).
+ *
+ * @param language the language tag, or {@code null} to clear
+ * @return this builder
+ */
public Builder language(String language) {
this.language = Optional.ofNullable(language);
return this;
}
+ /**
+ * Sets the search location hint used by the API.
+ *
+ * @param searchLocation the search location, or {@code null} to clear
+ * @return this builder
+ */
public Builder searchLocation(SearchLocation searchLocation) {
this.searchLocation = Optional.ofNullable(searchLocation);
return this;
}
+ /**
+ * Sets the search region hint used by the API.
+ *
+ * @param searchRegion the search region, or {@code null} to clear
+ * @return this builder
+ */
public Builder searchRegion(SearchRegion searchRegion) {
this.searchRegion = Optional.ofNullable(searchRegion);
return this;
}
+ /**
+ * Sets the user location hint used by the API.
+ *
+ * @param userLocation the user location, or {@code null} to clear
+ * @return this builder
+ */
public Builder userLocation(UserLocation userLocation) {
this.userLocation = Optional.ofNullable(userLocation);
return this;
}
+ /**
+ * Sets the search region priority.
+ *
+ * @param searchRegionPriority the search region priority, or {@code null} to clear
+ * @return this builder
+ */
public Builder searchRegionPriority(SearchRegionPriority searchRegionPriority) {
this.searchRegionPriority = Optional.ofNullable(searchRegionPriority);
return this;
}
+ /**
+ * Builds a {@link SearchAutocompleteInput}.
+ *
+ * @return an input instance
+ */
public SearchAutocompleteInput build() {
return new SearchAutocompleteInput(
q,
diff --git a/src/main/java/com/williamcallahan/applemaps/domain/request/SearchInput.java b/src/main/java/com/williamcallahan/applemaps/domain/request/SearchInput.java
index a166b08..f2bea05 100644
--- a/src/main/java/com/williamcallahan/applemaps/domain/request/SearchInput.java
+++ b/src/main/java/com/williamcallahan/applemaps/domain/request/SearchInput.java
@@ -52,6 +52,24 @@ public record SearchInput(
private static final String PARAMETER_INCLUDE_ADDRESS_CATEGORIES = "includeAddressCategories";
private static final String PARAMETER_EXCLUDE_ADDRESS_CATEGORIES = "excludeAddressCategories";
+ /**
+ * Canonical constructor that validates required fields and normalizes optional values.
+ *
+ * @param q query string
+ * @param excludePoiCategories POI categories to exclude
+ * @param includePoiCategories POI categories to include
+ * @param limitToCountries country codes to restrict results to
+ * @param resultTypeFilter search result type filter
+ * @param includeAddressCategories address categories to include
+ * @param excludeAddressCategories address categories to exclude
+ * @param language optional response language (BCP 47)
+ * @param searchLocation optional search location hint
+ * @param searchRegion optional search region hint
+ * @param userLocation optional user location hint
+ * @param searchRegionPriority optional search region priority
+ * @param enablePagination optional flag to enable pagination
+ * @param pageToken optional page token for pagination
+ */
public SearchInput {
q = Objects.requireNonNull(q, "q");
excludePoiCategories = normalizeList(excludePoiCategories);
@@ -69,6 +87,11 @@ public record SearchInput(
pageToken = normalizeOptional(pageToken);
}
+ /**
+ * Converts this input to a query string suitable for the Apple Maps Server API.
+ *
+ * @return a query string beginning with {@code ?}
+ */
public String toQueryString() {
List parameters = new ArrayList<>();
parameters.add(formatParameter(PARAMETER_QUERY, encode(q)));
@@ -100,6 +123,12 @@ public String toQueryString() {
return QUERY_PREFIX + String.join(PARAMETER_SEPARATOR, parameters);
}
+ /**
+ * Creates a builder initialized with the required query string.
+ *
+ * @param q query string
+ * @return a builder
+ */
public static Builder builder(String q) {
return new Builder(q);
}
@@ -134,6 +163,9 @@ private static String encode(String value) {
return URLEncoder.encode(value, StandardCharsets.UTF_8);
}
+ /**
+ * Builder for {@link SearchInput}.
+ */
public static final class Builder {
private final String q;
private List excludePoiCategories = List.of();
@@ -154,71 +186,154 @@ private Builder(String q) {
this.q = Objects.requireNonNull(q, "q");
}
+ /**
+ * Sets POI categories to exclude.
+ *
+ * @param categories categories to exclude
+ * @return this builder
+ */
public Builder excludePoiCategories(List categories) {
this.excludePoiCategories = normalizeList(categories);
return this;
}
+ /**
+ * Sets POI categories to include.
+ *
+ * @param categories categories to include
+ * @return this builder
+ */
public Builder includePoiCategories(List categories) {
this.includePoiCategories = normalizeList(categories);
return this;
}
+ /**
+ * Limits results to specific country codes.
+ *
+ * @param countries country codes (for example, ISO 3166-1 alpha-2)
+ * @return this builder
+ */
public Builder limitToCountries(List countries) {
this.limitToCountries = normalizeList(countries);
return this;
}
+ /**
+ * Sets the search result type filter.
+ *
+ * @param resultTypes result types to include
+ * @return this builder
+ */
public Builder resultTypeFilter(List resultTypes) {
this.resultTypeFilter = normalizeList(resultTypes);
return this;
}
+ /**
+ * Sets address categories to include.
+ *
+ * @param categories address categories to include
+ * @return this builder
+ */
public Builder includeAddressCategories(List categories) {
this.includeAddressCategories = normalizeList(categories);
return this;
}
+ /**
+ * Sets address categories to exclude.
+ *
+ * @param categories address categories to exclude
+ * @return this builder
+ */
public Builder excludeAddressCategories(List categories) {
this.excludeAddressCategories = normalizeList(categories);
return this;
}
+ /**
+ * Sets the response language (BCP 47).
+ *
+ * @param language the language tag, or {@code null} to clear
+ * @return this builder
+ */
public Builder language(String language) {
this.language = Optional.ofNullable(language);
return this;
}
+ /**
+ * Sets the search location hint used by the API.
+ *
+ * @param searchLocation the search location, or {@code null} to clear
+ * @return this builder
+ */
public Builder searchLocation(SearchLocation searchLocation) {
this.searchLocation = Optional.ofNullable(searchLocation);
return this;
}
+ /**
+ * Sets the search region hint used by the API.
+ *
+ * @param searchRegion the search region, or {@code null} to clear
+ * @return this builder
+ */
public Builder searchRegion(SearchRegion searchRegion) {
this.searchRegion = Optional.ofNullable(searchRegion);
return this;
}
+ /**
+ * Sets the user location hint used by the API.
+ *
+ * @param userLocation the user location, or {@code null} to clear
+ * @return this builder
+ */
public Builder userLocation(UserLocation userLocation) {
this.userLocation = Optional.ofNullable(userLocation);
return this;
}
+ /**
+ * Sets the search region priority.
+ *
+ * @param searchRegionPriority the search region priority, or {@code null} to clear
+ * @return this builder
+ */
public Builder searchRegionPriority(SearchRegionPriority searchRegionPriority) {
this.searchRegionPriority = Optional.ofNullable(searchRegionPriority);
return this;
}
+ /**
+ * Sets whether pagination should be enabled.
+ *
+ * @param enablePagination {@code true} to enable pagination, or {@code null} to clear
+ * @return this builder
+ */
public Builder enablePagination(Boolean enablePagination) {
this.enablePagination = Optional.ofNullable(enablePagination);
return this;
}
+ /**
+ * Sets the page token used for pagination.
+ *
+ * @param pageToken the page token, or {@code null} to clear
+ * @return this builder
+ */
public Builder pageToken(String pageToken) {
this.pageToken = Optional.ofNullable(pageToken);
return this;
}
+ /**
+ * Builds a {@link SearchInput}.
+ *
+ * @return an input instance
+ */
public SearchInput build() {
return new SearchInput(
q,
diff --git a/src/test/java/com/williamcallahan/applemaps/adapters/mapsserver/AppleMapsAuthorizationServiceTest.java b/src/test/java/com/williamcallahan/applemaps/adapters/mapsserver/AppleMapsAuthorizationServiceTest.java
index 198f8ea..40c00d8 100644
--- a/src/test/java/com/williamcallahan/applemaps/adapters/mapsserver/AppleMapsAuthorizationServiceTest.java
+++ b/src/test/java/com/williamcallahan/applemaps/adapters/mapsserver/AppleMapsAuthorizationServiceTest.java
@@ -1,6 +1,7 @@
package com.williamcallahan.applemaps.adapters.mapsserver;
-import com.williamcallahan.applemaps.adapters.jackson.AppleMapsObjectMapperFactory;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
import java.net.Authenticator;
import java.net.CookieHandler;
import java.net.ProxySelector;
@@ -29,12 +30,14 @@
import java.util.concurrent.Executor;
import java.util.concurrent.Flow;
import java.util.concurrent.atomic.AtomicInteger;
+
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSession;
+
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import com.williamcallahan.applemaps.adapters.jackson.AppleMapsObjectMapperFactory;
class AppleMapsAuthorizationServiceTest {
private static final URI TOKEN_URI = URI.create("https://maps-api.apple.com/v1/token");
@@ -61,6 +64,7 @@ void getAccessTokenCachesUntilExpiring() {
TOKEN_URI,
REQUEST_TIMEOUT,
AUTH_TOKEN,
+ "origin",
tokenClock
)
)
@@ -87,6 +91,7 @@ void getAccessTokenRefreshesAfterExpiry() {
TOKEN_URI,
REQUEST_TIMEOUT,
AUTH_TOKEN,
+ "origin",
tokenClock
)
)