diff --git a/src/main/java/com/google/firebase/remoteconfig/Condition.java b/src/main/java/com/google/firebase/remoteconfig/Condition.java new file mode 100644 index 000000000..c10498c36 --- /dev/null +++ b/src/main/java/com/google/firebase/remoteconfig/Condition.java @@ -0,0 +1,171 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.firebase.remoteconfig; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Strings; +import com.google.firebase.internal.NonNull; +import com.google.firebase.internal.Nullable; +import com.google.firebase.remoteconfig.internal.TemplateResponse.ConditionResponse; + +import java.util.Objects; + +/** + * Represents a Remote Config condition that can be included in a {@link Template}. + * A condition targets a specific group of users. A list of these conditions make up + * part of a Remote Config template. + */ +public final class Condition { + + private String name; + private String expression; + private TagColor tagColor; + + /** + * Creates a new {@link Condition}. + * + * @param name A non-null, non-empty, and unique name of this condition. + * @param expression A non-null and non-empty expression of this condition. + */ + public Condition(@NonNull String name, @NonNull String expression) { + this(name, expression, null); + } + + /** + * Creates a new {@link Condition}. + * + * @param name A non-null, non-empty, and unique name of this condition. + * @param expression A non-null and non-empty expression of this condition. + * @param tagColor A color associated with this condition for display purposes in the + * Firebase Console. Not specifying this value results in the console picking an + * arbitrary color to associate with the condition. + */ + public Condition(@NonNull String name, @NonNull String expression, @Nullable TagColor tagColor) { + checkArgument(!Strings.isNullOrEmpty(name), "condition name must not be null or empty"); + checkArgument(!Strings.isNullOrEmpty(expression), + "condition expression must not be null or empty"); + this.name = name; + this.expression = expression; + this.tagColor = tagColor; + } + + Condition(@NonNull ConditionResponse conditionResponse) { + checkNotNull(conditionResponse); + this.name = conditionResponse.getName(); + this.expression = conditionResponse.getExpression(); + if (conditionResponse.getTagColor() == null) { + this.tagColor = TagColor.UNSPECIFIED; + } else { + this.tagColor = TagColor.valueOf(conditionResponse.getTagColor()); + } + } + + /** + * Gets the name of the condition. + * + * @return The name of the condition. + */ + @NonNull + public String getName() { + return name; + } + + /** + * Gets the expression of the condition. + * + * @return The expression of the condition. + */ + @NonNull + public String getExpression() { + return expression; + } + + /** + * Gets the tag color of the condition used for display purposes in the Firebase Console. + * + * @return The tag color of the condition. + */ + @NonNull + public TagColor getTagColor() { + return tagColor; + } + + /** + * Sets the name of the condition. + * + * @param name A non-empty and unique name of this condition. + * @return This {@link Condition}. + */ + public Condition setName(@NonNull String name) { + checkArgument(!Strings.isNullOrEmpty(name), "condition name must not be null or empty"); + this.name = name; + return this; + } + + /** + * Sets the expression of the condition. + * + *

See + * condition expressions for the expected syntax of this field. + * + * @param expression The logic of this condition. + * @return This {@link Condition}. + */ + public Condition setExpression(@NonNull String expression) { + checkArgument(!Strings.isNullOrEmpty(expression), + "condition expression must not be null or empty"); + this.expression = expression; + return this; + } + + /** + * Sets the tag color of the condition. + * + *

The color associated with this condition for display purposes in the Firebase Console. + * Not specifying this value results in the console picking an arbitrary color to associate + * with the condition. + * + * @param tagColor The tag color of this condition. + * @return This {@link Condition}. + */ + public Condition setTagColor(@Nullable TagColor tagColor) { + this.tagColor = tagColor; + return this; + } + + ConditionResponse toConditionResponse() { + return new ConditionResponse() + .setName(this.name) + .setExpression(this.expression) + .setTagColor(this.tagColor == null ? null : this.tagColor.getColor()); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Condition condition = (Condition) o; + return Objects.equals(name, condition.name) + && Objects.equals(expression, condition.expression) && tagColor == condition.tagColor; + } +} diff --git a/src/main/java/com/google/firebase/remoteconfig/TagColor.java b/src/main/java/com/google/firebase/remoteconfig/TagColor.java new file mode 100644 index 000000000..ed7b8cdf4 --- /dev/null +++ b/src/main/java/com/google/firebase/remoteconfig/TagColor.java @@ -0,0 +1,45 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.firebase.remoteconfig; + +/** + * Colors that are associated with conditions for display purposes in the Firebase Console. + */ +public enum TagColor { + BLUE("BLUE"), + BROWN("BROWN"), + CYAN("CYAN"), + DEEP_ORANGE("DEEP_ORANGE"), + GREEN("GREEN"), + INDIGO("INDIGO"), + LIME("LIME"), + ORANGE("ORANGE"), + PINK("PINK"), + PURPLE("PURPLE"), + TEAL("TEAL"), + UNSPECIFIED("CONDITION_DISPLAY_COLOR_UNSPECIFIED"); + + private final String color; + + TagColor(String color) { + this.color = color; + } + + public String getColor() { + return color; + } +} diff --git a/src/main/java/com/google/firebase/remoteconfig/Template.java b/src/main/java/com/google/firebase/remoteconfig/Template.java index 53e5b6085..902273aa0 100644 --- a/src/main/java/com/google/firebase/remoteconfig/Template.java +++ b/src/main/java/com/google/firebase/remoteconfig/Template.java @@ -21,7 +21,9 @@ import com.google.firebase.internal.NonNull; import com.google.firebase.remoteconfig.internal.TemplateResponse; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -31,23 +33,32 @@ public final class Template { private String etag; private Map parameters; + private List conditions; /** * Creates a new {@link Template}. */ public Template() { parameters = new HashMap<>(); + conditions = new ArrayList<>(); } Template(@NonNull TemplateResponse templateResponse) { checkNotNull(templateResponse); this.parameters = new HashMap<>(); + this.conditions = new ArrayList<>(); if (templateResponse.getParameters() != null) { for (Map.Entry entry : templateResponse.getParameters().entrySet()) { this.parameters.put(entry.getKey(), new Parameter(entry.getValue())); } } + if (templateResponse.getConditions() != null) { + for (TemplateResponse.ConditionResponse conditionResponse + : templateResponse.getConditions()) { + this.conditions.add(new Condition(conditionResponse)); + } + } } /** @@ -70,6 +81,16 @@ public Map getParameters() { return this.parameters; } + /** + * Gets the list of conditions of the template. + * + * @return A non-null list of conditions + */ + @NonNull + public List getConditions() { + return conditions; + } + /** * Sets the map of parameters of the template. * @@ -84,6 +105,19 @@ public Template setParameters( return this; } + /** + * Sets the list of conditions of the template. + * + * @param conditions A non-null list of conditions in descending order by priority. + * @return This {@link Template} instance. + */ + public Template setConditions( + @NonNull List conditions) { + checkNotNull(conditions, "conditions must not be null."); + this.conditions = conditions; + return this; + } + Template setETag(String etag) { this.etag = etag; return this; @@ -91,9 +125,15 @@ Template setETag(String etag) { TemplateResponse toTemplateResponse() { Map parameterResponses = new HashMap<>(); - for (Map.Entry entry : parameters.entrySet()) { + for (Map.Entry entry : this.parameters.entrySet()) { parameterResponses.put(entry.getKey(), entry.getValue().toParameterResponse()); } - return new TemplateResponse().setParameters(parameterResponses); + List conditionResponses = new ArrayList<>(); + for (Condition condition : this.conditions) { + conditionResponses.add(condition.toConditionResponse()); + } + return new TemplateResponse() + .setParameters(parameterResponses) + .setConditions(conditionResponses); } } diff --git a/src/main/java/com/google/firebase/remoteconfig/internal/TemplateResponse.java b/src/main/java/com/google/firebase/remoteconfig/internal/TemplateResponse.java index e36b4583e..cd1463e15 100644 --- a/src/main/java/com/google/firebase/remoteconfig/internal/TemplateResponse.java +++ b/src/main/java/com/google/firebase/remoteconfig/internal/TemplateResponse.java @@ -18,6 +18,7 @@ import com.google.api.client.util.Key; +import java.util.List; import java.util.Map; /** @@ -29,16 +30,29 @@ public final class TemplateResponse { @Key("parameters") private Map parameters; + @Key("conditions") + private List conditions; + public Map getParameters() { return parameters; } + public List getConditions() { + return conditions; + } + public TemplateResponse setParameters( Map parameters) { this.parameters = parameters; return this; } + public TemplateResponse setConditions( + List conditions) { + this.conditions = conditions; + return this; + } + /** * The Data Transfer Object for parsing Remote Config parameter responses from the * Remote Config service. @@ -114,4 +128,47 @@ public ParameterValueResponse setUseInAppDefault(boolean useInAppDefault) { return this; } } + + /** + * The Data Transfer Object for parsing Remote Config condition responses from the + * Remote Config service. + **/ + public static final class ConditionResponse { + + @Key("name") + private String name; + + @Key("expression") + private String expression; + + @Key("tagColor") + private String tagColor; + + public String getName() { + return name; + } + + public String getExpression() { + return expression; + } + + public String getTagColor() { + return tagColor; + } + + public ConditionResponse setName(String name) { + this.name = name; + return this; + } + + public ConditionResponse setExpression(String expression) { + this.expression = expression; + return this; + } + + public ConditionResponse setTagColor(String tagColor) { + this.tagColor = tagColor; + return this; + } + } } diff --git a/src/test/java/com/google/firebase/remoteconfig/ConditionTest.java b/src/test/java/com/google/firebase/remoteconfig/ConditionTest.java new file mode 100644 index 000000000..1c8b8cd57 --- /dev/null +++ b/src/test/java/com/google/firebase/remoteconfig/ConditionTest.java @@ -0,0 +1,40 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.firebase.remoteconfig; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import org.junit.Test; + +public class ConditionTest { + + @Test + public void testEquality() { + final Condition conditionOne = new Condition("ios", "device.os == 'ios'", TagColor.GREEN); + final Condition conditionTwo = new Condition("ios", "device.os == 'ios'", TagColor.GREEN); + final Condition conditionThree = new Condition("android", "device.os == 'android'"); + final Condition conditionFour = new Condition("android", "device.os == 'android'"); + final Condition conditionFive = new Condition("ios", "device.os == 'ios'", TagColor.BLUE); + + assertEquals(conditionOne, conditionTwo); + assertEquals(conditionThree, conditionFour); + assertNotEquals(conditionOne, conditionThree); + assertNotEquals(conditionTwo, conditionFour); + assertNotEquals(conditionOne, conditionFive); + } +} diff --git a/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigClientImplTest.java b/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigClientImplTest.java index d733ed71a..aabc98a26 100644 --- a/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigClientImplTest.java +++ b/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigClientImplTest.java @@ -86,6 +86,7 @@ public void testGetTemplate() throws Exception { Template template = client.getTemplate(); + // Check Parameters assertEquals(TEST_ETAG, template.getETag()); Map parameters = template.getParameters(); assertEquals(2, parameters.size()); @@ -107,6 +108,20 @@ public void testGetTemplate() throws Exception { assertTrue( headerParameter.getDefaultValue() instanceof ParameterValue.InAppDefault); checkGetRequestHeader(interceptor.getLastRequest()); + + // Check Conditions + List actualConditions = template.getConditions(); + List expectedConditions = ImmutableList.of( + new Condition("ios_en", "device.os == 'ios' && device.country in ['us', 'uk']") + .setTagColor(TagColor.INDIGO), + new Condition("android_en", + "device.os == 'android' && device.country in ['us', 'uk']") + .setTagColor(TagColor.UNSPECIFIED) + ); + assertEquals(expectedConditions.size(), actualConditions.size()); + for (int i = 0; i < expectedConditions.size(); i++) { + assertEquals(expectedConditions.get(i), actualConditions.get(i)); + } } @Test diff --git a/src/test/resources/getRemoteConfig.json b/src/test/resources/getRemoteConfig.json index 86322f267..df54117bb 100644 --- a/src/test/resources/getRemoteConfig.json +++ b/src/test/resources/getRemoteConfig.json @@ -7,8 +7,7 @@ }, { "name": "android_en", - "expression": "device.os == 'android' && device.country in ['us', 'uk']", - "tagColor": "GREEN" + "expression": "device.os == 'android' && device.country in ['us', 'uk']" } ], "parameters": {