diff --git a/CHANGELOG b/CHANGELOG index 8555ede..906f788 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +2023-09-13 Release 5.5.0 + + Added support for predicate filters + 2022-04-01 Release 5.4.0 Rebranding (Urban Airship to Airship and Connect to Real-Time Data Streaming) diff --git a/src/main/java/com/urbanairship/connect/client/model/GsonUtil.java b/src/main/java/com/urbanairship/connect/client/model/GsonUtil.java index 52742db..55b4ccc 100644 --- a/src/main/java/com/urbanairship/connect/client/model/GsonUtil.java +++ b/src/main/java/com/urbanairship/connect/client/model/GsonUtil.java @@ -12,6 +12,7 @@ import com.urbanairship.connect.client.model.request.filters.DeviceType; import com.urbanairship.connect.client.model.request.filters.Filter; import com.urbanairship.connect.client.model.request.filters.NotificationFilter; +import com.urbanairship.connect.client.model.request.filters.PredicateFilter; public final class GsonUtil { @@ -22,6 +23,7 @@ public final class GsonUtil { .registerTypeAdapter(DeviceFilter.class, DeviceFilter.SERIALIZER) .registerTypeAdapter(Filter.class, Filter.SERIALIZER) .registerTypeAdapter(NotificationFilter.class, NotificationFilter.SERIALIZER) + .registerTypeAdapter(PredicateFilter.class, PredicateFilter.SERIALIZER) .registerTypeAdapter(Subset.SampleSubset.class, Subset.SAMPLE_SUBSET_SERIALIZER) .registerTypeAdapter(Subset.PartitionSubset.class, Subset.PARTITION_SUBSET_SERIALIZER) .registerTypeAdapter(StreamRequestPayload.class, StreamRequestPayload.SERIALIZER) diff --git a/src/main/java/com/urbanairship/connect/client/model/request/filters/Filter.java b/src/main/java/com/urbanairship/connect/client/model/request/filters/Filter.java index 69b0ea3..f401054 100644 --- a/src/main/java/com/urbanairship/connect/client/model/request/filters/Filter.java +++ b/src/main/java/com/urbanairship/connect/client/model/request/filters/Filter.java @@ -22,6 +22,7 @@ public final class Filter { private final Set deviceTypes; private final Set notifications; private final Set devices; + private final Set predicates; private final Set types; private final Optional latency; @@ -32,11 +33,13 @@ public static Builder newBuilder() { private Filter(Set deviceTypes, Set notifications, Set devices, + Set predicates, Set types, Optional latency) { this.deviceTypes = deviceTypes; this.notifications = notifications; this.devices = devices; + this.predicates = predicates; this.types = types; this.latency = latency; } @@ -53,6 +56,10 @@ public Set getDevices() { return devices; } + public Set getPredicates() { + return predicates; + } + public Set getTypes() { return types; } @@ -70,6 +77,7 @@ public boolean equals(Object o) { if (!deviceTypes.equals(filter.deviceTypes)) return false; if (!devices.equals(filter.devices)) return false; + if (!predicates.equals(filter.predicates)) return false; if (!latency.equals(filter.latency)) return false; if (!notifications.equals(filter.notifications)) return false; if (!types.equals(filter.types)) return false; @@ -82,6 +90,7 @@ public int hashCode() { int result = deviceTypes.hashCode(); result = 31 * result + notifications.hashCode(); result = 31 * result + devices.hashCode(); + result = 31 * result + predicates.hashCode(); result = 31 * result + types.hashCode(); result = 31 * result + latency.hashCode(); return result; @@ -93,6 +102,7 @@ public String toString() { "deviceTypes=" + deviceTypes + ", notifications=" + notifications + ", devices=" + devices + + ", predicates=" + predicates + ", types=" + types + ", latency=" + latency + '}'; @@ -103,6 +113,7 @@ public static final class Builder { private final Set deviceTypes = new HashSet<>(); private final Set notifications = new HashSet<>(); private final Set devices = new HashSet<>(); + private final Set predicates = new HashSet<>(); private final Set types = new HashSet<>(); private Long latency = null; @@ -124,6 +135,11 @@ public Builder addDevices(DeviceFilter... devices) { return this; } + public Builder addPredicates(PredicateFilter... predicates) { + Collections.addAll(this.predicates, predicates); + return this; + } + public Builder addEventTypes(String... types) { for (String type : types) { this.types.add(type.toUpperCase()); @@ -141,7 +157,8 @@ public Filter build() { Preconditions.checkArgument(( !deviceTypes.isEmpty() || !notifications.isEmpty() || - !deviceTypes.isEmpty() || + !devices.isEmpty() || + !predicates.isEmpty() || !types.isEmpty() || latency != null), "Cannot create an empty filter payload"); @@ -151,6 +168,7 @@ public Filter build() { ImmutableSet.copyOf(deviceTypes), ImmutableSet.copyOf(notifications), ImmutableSet.copyOf(devices), + ImmutableSet.copyOf(predicates), ImmutableSet.copyOf(types), Optional.fromNullable(latency) ); @@ -159,6 +177,7 @@ public Filter build() { public static final String DEVICE_TYPES_KEY = "device_types"; public static final String DEVICES_KEY = "devices"; + public static final String PREDICATES_KEY = "predicates"; public static final String NOTIFICATIONS_KEY = "notifications"; public static final String EVENT_TYPES_KEY = "types"; public static final String LATENCY_KEY = "latency"; @@ -173,6 +192,9 @@ public JsonElement serialize(Filter filter, Type type, JsonSerializationContext if (!filter.getDevices().isEmpty()) { obj.add(DEVICES_KEY, context.serialize(filter.getDevices())); } + if (!filter.getPredicates().isEmpty()) { + obj.add(PREDICATES_KEY, context.serialize(filter.getPredicates())); + } if (!filter.getNotifications().isEmpty()) { obj.add(NOTIFICATIONS_KEY, context.serialize(filter.getNotifications())); } diff --git a/src/main/java/com/urbanairship/connect/client/model/request/filters/PredicateFilter.java b/src/main/java/com/urbanairship/connect/client/model/request/filters/PredicateFilter.java new file mode 100644 index 0000000..03f3abe --- /dev/null +++ b/src/main/java/com/urbanairship/connect/client/model/request/filters/PredicateFilter.java @@ -0,0 +1,60 @@ +/* +Copyright 2015-2022 Airship and Contributors +*/ + +package com.urbanairship.connect.client.model.request.filters; + +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import java.lang.reflect.Type; +import java.util.Objects; + +public final class PredicateFilter { + + private final JsonObject value; + + public PredicateFilter(JsonObject value) { + this.value = Preconditions.checkNotNull(value, "Predicate filter values must be provided"); + } + + public JsonObject getValue() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PredicateFilter that = (PredicateFilter) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("value", value) + .toString(); + } + + public static final JsonSerializer SERIALIZER = new JsonSerializer() { + + @Override + public JsonElement serialize(PredicateFilter predicateFilter, Type type, JsonSerializationContext jsonSerializationContext) { + return predicateFilter.getValue(); + } + }; +} diff --git a/src/test/java/com/urbanairship/connect/client/model/request/filters/FilterSerializerTest.java b/src/test/java/com/urbanairship/connect/client/model/request/filters/FilterSerializerTest.java index 68a7bdd..fc320c2 100644 --- a/src/test/java/com/urbanairship/connect/client/model/request/filters/FilterSerializerTest.java +++ b/src/test/java/com/urbanairship/connect/client/model/request/filters/FilterSerializerTest.java @@ -26,10 +26,14 @@ public void testSerializationAllOptionsPresent() throws Exception { String eventType = "OPEN"; long latency = RandomUtils.nextLong(1L, 100000L); String pushId = UUID.randomUUID().toString(); + String predicateJson = "{\"and\":[{\"key\":\"type\",\"value\":{\"equals\":\"CUSTOM\"}},{\"scope\":\"body\",\"key\":\"name\",\"value\":{\"equals\":\"initial_open\"}}]}"; + JsonElement predicateObject = parser.parse(predicateJson); + PredicateFilter predicate = new PredicateFilter(predicateObject.getAsJsonObject()); Filter filter = Filter.newBuilder() .addDevices(new DeviceFilter(DeviceFilterType.ANDROID_CHANNEL, deviceId)) .addDeviceTypes(DeviceType.ANDROID) + .addPredicates(predicate) .addEventTypes(eventType) .addNotifications(new NotificationFilter(NotificationFilter.Type.PUSH_ID, pushId)) .setLatency(latency) @@ -43,11 +47,14 @@ public void testSerializationAllOptionsPresent() throws Exception { "\"notifications\":[{" + "\"push_id\":\"%s\"" + "}]," + + "\"predicates\":[" + + "%s" + + "]," + "\"types\":[\"%s\"]," + "\"devices\":[{" + "\"android_channel\":\"%s\"" + "}]" + - "}", latency, pushId, eventType, deviceId); + "}", latency, pushId, predicateJson, eventType, deviceId); JsonElement expected = parser.parse(json);