Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package io.github.pgmarc.space;

import io.github.pgmarc.space.deserializers.ErrorDeserializer;
import io.github.pgmarc.space.deserializers.FeatureEvaluationDeserializer;
import io.github.pgmarc.space.exceptions.SpaceApiException;
import io.github.pgmarc.space.features.Consumption;
import io.github.pgmarc.space.features.FeatureEvaluationResult;
import io.github.pgmarc.space.serializers.ConsumptionSerializer;
import okhttp3.*;
import org.json.JSONObject;

import java.io.IOException;

public final class FeaturesEndpoint {

private static final String ENDPOINT = "features";
private static final MediaType JSON = MediaType.get("application/json");
private static final String STATUS_CODE = "statusCode";


private final OkHttpClient client;
private final HttpUrl baseUrl;
private final Headers requiredHeaders;
private final ConsumptionSerializer consumptionSerializer;
private final ErrorDeserializer errorDeserializer;

FeaturesEndpoint(OkHttpClient client, HttpUrl baseUrl, String apiKey) {
this.client = client;
this.baseUrl = baseUrl.newBuilder().addPathSegment(ENDPOINT).build();
this.requiredHeaders = new Headers.Builder().add("Accept", JSON.toString())
.add("x-api-key", apiKey).build();
this.consumptionSerializer = new ConsumptionSerializer();
this.errorDeserializer = new ErrorDeserializer();
}

private static String formatFeatureId(String service, String feature) {
return service.toLowerCase() + "-" + feature;
}

public FeatureEvaluationResult evaluate(String userId, String service, String feature) throws IOException {
HttpUrl url = this.baseUrl.newBuilder().addEncodedPathSegment(userId)
.addEncodedPathSegment(formatFeatureId(service, feature)).build();
Request request = new Request(url, requiredHeaders ,"POST" , RequestBody.EMPTY);

FeatureEvaluationResult res = null;
try (Response response = client.newCall(request).execute()) {
JSONObject jsonResponse = new JSONObject(response.body().string());
if (!response.isSuccessful()) {
jsonResponse.put(STATUS_CODE, response.code());
throw new SpaceApiException(errorDeserializer.fromJson(jsonResponse));
}
FeatureEvaluationDeserializer deserializer = new FeatureEvaluationDeserializer(service.length());
res = deserializer.fromJson(jsonResponse);
}

return res;
}

public FeatureEvaluationResult evaluateOptimistically(String userId, String service, String featureId, Consumption consumption)
throws IOException {
HttpUrl url = this.baseUrl.newBuilder().addEncodedPathSegment(userId)
.addEncodedPathSegment(formatFeatureId(service, featureId)).build();
RequestBody body = RequestBody.create(consumptionSerializer.toJson(consumption).toString(), JSON);
Request request = new Request(url, requiredHeaders ,"POST" , body);

FeatureEvaluationResult res = null;
try (Response response = client.newCall(request).execute()) {
JSONObject jsonResponse = new JSONObject(response.body().string());
if (!response.isSuccessful()) {
jsonResponse.put(STATUS_CODE, response.code());
throw new SpaceApiException(errorDeserializer.fromJson(jsonResponse));
}
FeatureEvaluationDeserializer deserializer = new FeatureEvaluationDeserializer(service.length());
res = deserializer.fromJson(jsonResponse);
}

return res;
}

public enum Revert {
OLDEST_VALUE(false),
NEWEST_VALUE(true);

private final boolean latest;

Revert(boolean latest) {
this.latest = latest;
}

public boolean isLatest() {
return latest;
}
}

public boolean revert(String userId, String service, String feature, Revert revert) throws IOException {
HttpUrl url = this.baseUrl.newBuilder().addEncodedPathSegment(userId)
.addEncodedPathSegment(formatFeatureId(service, feature))
.addQueryParameter("revert", String.valueOf(true))
.addQueryParameter("latest", String.valueOf(revert.isLatest())).build();

Request request = new Request(url, requiredHeaders ,"POST" , RequestBody.EMPTY);

try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
JSONObject jsonResponse = new JSONObject(response.body().string());
jsonResponse.put(STATUS_CODE, response.code());
throw new SpaceApiException(errorDeserializer.fromJson(jsonResponse));
}

return response.code() == 204;
}

}

public String generatePricingTokenForUser(String userId) throws IOException {

HttpUrl url = this.baseUrl.newBuilder().addEncodedPathSegment(userId)
.addPathSegment("pricing-token").build();
Request request = new Request(url, requiredHeaders ,"POST" , RequestBody.EMPTY);

FeatureEvaluationResult res = null;
try (Response response = client.newCall(request).execute()) {
JSONObject jsonResponse = new JSONObject(response.body().string());
if (!response.isSuccessful()) {
jsonResponse.put(STATUS_CODE, response.code());
throw new SpaceApiException(errorDeserializer.fromJson(jsonResponse));
}
return jsonResponse.getString("pricingToken");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public final class SpaceClient {
private final String apiKey;

private ContractsEndpoint contracts;
private FeaturesEndpoint features;

private SpaceClient(OkHttpClient httpClient, HttpUrl baseUrl, String apiKey) {
this.httpClient = httpClient;
Expand All @@ -22,11 +23,18 @@ private SpaceClient(OkHttpClient httpClient, HttpUrl baseUrl, String apiKey) {

public ContractsEndpoint contracts() {
if (contracts == null) {
this.contracts = new ContractsEndpoint(httpClient, baseUrl, apiKey);
contracts = new ContractsEndpoint(httpClient, baseUrl, apiKey);
}
return contracts;
}

public FeaturesEndpoint features() {
if (features == null) {
features = new FeaturesEndpoint(httpClient, baseUrl, apiKey);
}
return features;
}

public static Builder builder(String host, String apiKey) {
return new Builder(host, apiKey);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.github.pgmarc.space.contracts;

import java.util.Objects;

public final class AddOn {

private final String name;
Expand All @@ -19,28 +21,14 @@ public long getQuantity() {
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
AddOn addOn = (AddOn) o;
return quantity == addOn.quantity && Objects.equals(name, addOn.name);
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AddOn other = (AddOn) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
public int hashCode() {
return Objects.hash(name, quantity);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,13 @@ Duration getDuration() {
return renewalDays;
}

boolean isExpired(LocalDateTime dateTime) {
return endDate.isAfter(ZonedDateTime.of(dateTime, ZoneId.of("UTC")));
boolean isActive(ZonedDateTime date) {
return (startDate.isEqual(date) || startDate.isBefore(date)) &&
(endDate.isAfter(date) || endDate.isEqual(date));
}

boolean isExpired(ZonedDateTime date) {
return startDate.isBefore(date) && endDate.isBefore(date);
}

boolean isAutoRenewable() {
Expand Down Expand Up @@ -77,7 +82,7 @@ public enum Keys {

private final String name;

private Keys(String name) {
Keys(String name) {
this.name = name;
}

Expand All @@ -88,40 +93,15 @@ public String toString() {
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((startDate == null) ? 0 : startDate.hashCode());
result = prime * result + ((endDate == null) ? 0 : endDate.hashCode());
result = prime * result + ((renewalDays == null) ? 0 : renewalDays.hashCode());
return result;
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
BillingPeriod that = (BillingPeriod) o;
return Objects.equals(startDate, that.startDate) && Objects.equals(endDate, that.endDate) && Objects.equals(renewalDays, that.renewalDays);
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BillingPeriod other = (BillingPeriod) obj;
if (startDate == null) {
if (other.startDate != null)
return false;
} else if (!startDate.equals(other.startDate))
return false;
if (endDate == null) {
if (other.endDate != null)
return false;
} else if (!endDate.equals(other.endDate))
return false;
if (renewalDays == null) {
if (other.renewalDays != null)
return false;
} else if (!renewalDays.equals(other.renewalDays))
return false;
return true;
public int hashCode() {
return Objects.hash(startDate, endDate, renewalDays);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public final class Service {
private final String name;
private final String version;
private final Map<String, AddOn> addOns;
private String plan;
private final String plan;

private Service(Builder builder) {
this.name = builder.name;
Expand Down Expand Up @@ -52,46 +52,15 @@ public String toString() {
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((version == null) ? 0 : version.hashCode());
result = prime * result + ((addOns == null) ? 0 : addOns.hashCode());
result = prime * result + ((plan == null) ? 0 : plan.hashCode());
return result;
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
Service service = (Service) o;
return Objects.equals(name, service.name) && Objects.equals(version, service.version) && Objects.equals(addOns, service.addOns) && Objects.equals(plan, service.plan);
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Service other = (Service) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (version == null) {
if (other.version != null)
return false;
} else if (!version.equals(other.version))
return false;
if (addOns == null) {
if (other.addOns != null)
return false;
} else if (!addOns.equals(other.addOns))
return false;
if (plan == null) {
if (other.plan != null)
return false;
} else if (!plan.equals(other.plan))
return false;
return true;
public int hashCode() {
return Objects.hash(name, version, addOns, plan);
}

public static final class Builder {
Expand Down
Loading