Skip to content

Add Remote Config conditions to template #489

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 27, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
144 changes: 144 additions & 0 deletions src/main/java/com/google/firebase/remoteconfig/Condition.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* 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;

/**
* 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need a constructor that accepts the color as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a constructor with color. Should we amend the API proposal as well?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to include a note. Looks like an oversight if it's not already there.

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.UNSPECIFIED;
}

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 {@link String} name of the condition.
*/
@NonNull
public String getName() {
return name;
}

/**
* Gets the expression of the condition.
*
* @return The {@link String} expression of the condition.
*/
@NonNull
public String getExpression() {
return expression;
}

/**
* Gets the tag color of the condition.
*
* @return The {@link String} 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.
*
* <p>See <a href="https://firebase.google.com/docs/remote-config/condition-reference">
* condition expressions</a> 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.
*
* <p>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.
* Passing null sets to {@code TagColor.UNSPECIFIED}
* @return This {@link Condition}.
*/
public Condition setTagColor(@Nullable TagColor tagColor) {
this.tagColor = tagColor == null ? TagColor.UNSPECIFIED : tagColor;
return this;
}

ConditionResponse toConditionResponse() {
return new ConditionResponse()
.setName(this.name)
.setExpression(this.expression)
.setTagColor(this.tagColor.getColor());
}
}
26 changes: 26 additions & 0 deletions src/main/java/com/google/firebase/remoteconfig/TagColor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.google.firebase.remoteconfig;

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;
}
}
44 changes: 42 additions & 2 deletions src/main/java/com/google/firebase/remoteconfig/Template.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -31,23 +33,32 @@ public final class Template {

private String etag;
private Map<String, Parameter> parameters;
private List<Condition> 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<String, TemplateResponse.ParameterResponse> 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));
}
}
}

/**
Expand All @@ -70,6 +81,16 @@ public Map<String, Parameter> getParameters() {
return this.parameters;
}

/**
* Gets the list of conditions of the template.
*
* @return A non-null list of conditions
*/
@NonNull
public List<Condition> getConditions() {
return conditions;
}

/**
* Sets the map of parameters of the template.
*
Expand All @@ -84,16 +105,35 @@ 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<Condition> conditions) {
checkNotNull(conditions, "conditions must not be null.");
this.conditions = conditions;
return this;
}

Template setETag(String etag) {
this.etag = etag;
return this;
}

TemplateResponse toTemplateResponse() {
Map<String, TemplateResponse.ParameterResponse> parameterResponses = new HashMap<>();
for (Map.Entry<String, Parameter> entry : parameters.entrySet()) {
for (Map.Entry<String, Parameter> entry : this.parameters.entrySet()) {
parameterResponses.put(entry.getKey(), entry.getValue().toParameterResponse());
}
return new TemplateResponse().setParameters(parameterResponses);
List<TemplateResponse.ConditionResponse> conditionResponses = new ArrayList<>();
for (Condition condition : this.conditions) {
conditionResponses.add(condition.toConditionResponse());
}
return new TemplateResponse()
.setParameters(parameterResponses)
.setConditions(conditionResponses);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.google.api.client.util.Key;

import java.util.List;
import java.util.Map;

/**
Expand All @@ -29,16 +30,29 @@ public final class TemplateResponse {
@Key("parameters")
private Map<String, ParameterResponse> parameters;

@Key("conditions")
private List<ConditionResponse> conditions;

public Map<String, ParameterResponse> getParameters() {
return parameters;
}

public List<ConditionResponse> getConditions() {
return conditions;
}

public TemplateResponse setParameters(
Map<String, ParameterResponse> parameters) {
this.parameters = parameters;
return this;
}

public TemplateResponse setConditions(
List<ConditionResponse> conditions) {
this.conditions = conditions;
return this;
}

/**
* The Data Transfer Object for parsing Remote Config parameter responses from the
* Remote Config service.
Expand Down Expand Up @@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import com.google.firebase.testing.TestUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -86,6 +87,7 @@ public void testGetTemplate() throws Exception {

Template template = client.getTemplate();

// Check Parameters
assertEquals(TEST_ETAG, template.getETag());
Map<String, Parameter> parameters = template.getParameters();
assertEquals(2, parameters.size());
Expand All @@ -107,6 +109,24 @@ public void testGetTemplate() throws Exception {
assertTrue(
headerParameter.getDefaultValue() instanceof ParameterValue.InAppDefault);
checkGetRequestHeader(interceptor.getLastRequest());

// Check Conditions
List<Condition> actualConditions = template.getConditions();
List<Condition> expectedConditions = new ArrayList<>();
expectedConditions
.add(new Condition("ios_en", "device.os == 'ios' && device.country in ['us', 'uk']")
.setTagColor(TagColor.INDIGO));
expectedConditions
.add(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).getName(), actualConditions.get(i).getName());
assertEquals(expectedConditions.get(i).getExpression(),
actualConditions.get(i).getExpression());
assertEquals(expectedConditions.get(i).getTagColor(), actualConditions.get(i).getTagColor());
}
}

@Test
Expand Down
Loading