Skip to content

Commit 6ff4df7

Browse files
author
Varun Rathore
committed
Implementation for Fetching and Caching Server Side Remote Config
1 parent d6d9b86 commit 6ff4df7

22 files changed

+2269
-97
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
2+
/*
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package com.google.firebase.remoteconfig;
19+
20+
import static com.google.common.base.Preconditions.checkArgument;
21+
import static com.google.common.base.Preconditions.checkNotNull;
22+
23+
import com.google.common.collect.ImmutableList;
24+
import com.google.firebase.internal.NonNull;
25+
import com.google.firebase.remoteconfig.internal.ServerTemplateResponse.AndConditionResponse;
26+
import com.google.firebase.remoteconfig.internal.ServerTemplateResponse.OneOfConditionResponse;
27+
28+
import java.util.ArrayList;
29+
import java.util.List;
30+
import java.util.stream.Collectors;
31+
32+
final class AndCondition {
33+
private final ImmutableList<OneOfCondition> conditions;
34+
35+
AndCondition(@NonNull List<OneOfCondition> conditions) {
36+
checkNotNull(conditions, "List of conditions for AND operation must not be null.");
37+
checkArgument(!conditions.isEmpty(),
38+
"List of conditions for AND operation must not be empty.");
39+
this.conditions = ImmutableList.copyOf(conditions);
40+
}
41+
42+
AndCondition(AndConditionResponse andConditionResponse) {
43+
List<OneOfConditionResponse> conditionList = andConditionResponse.getConditions();
44+
checkNotNull(conditionList, "List of conditions for AND operation must not be null.");
45+
checkArgument(!conditionList.isEmpty(),
46+
"List of conditions for AND operation must not be empty");
47+
this.conditions = conditionList.stream()
48+
.map(OneOfCondition::new)
49+
.collect(ImmutableList.toImmutableList());
50+
}
51+
52+
@NonNull
53+
List<OneOfCondition> getConditions() {
54+
return new ArrayList<>(conditions);
55+
}
56+
57+
AndConditionResponse toAndConditionResponse() {
58+
return new AndConditionResponse()
59+
.setConditions(this.conditions.stream()
60+
.map(OneOfCondition::toOneOfConditionResponse)
61+
.collect(Collectors.toList()));
62+
}
63+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
2+
/*
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package com.google.firebase.remoteconfig;
19+
20+
import static com.google.common.base.Preconditions.checkArgument;
21+
import static com.google.common.base.Preconditions.checkNotNull;
22+
23+
import com.google.common.base.Strings;
24+
import com.google.common.collect.ImmutableList;
25+
import com.google.firebase.internal.NonNull;
26+
import com.google.firebase.remoteconfig.internal.ServerTemplateResponse.CustomSignalConditionResponse;
27+
28+
import java.util.ArrayList;
29+
import java.util.List;
30+
31+
final class CustomSignalCondition {
32+
private final String customSignalKey;
33+
private final CustomSignalOperator customSignalOperator;
34+
private final ImmutableList<String> targetCustomSignalValues;
35+
36+
public CustomSignalCondition(
37+
@NonNull String customSignalKey,
38+
@NonNull CustomSignalOperator customSignalOperator,
39+
@NonNull List<String> targetCustomSignalValues) {
40+
checkArgument(
41+
!Strings.isNullOrEmpty(customSignalKey), "Custom signal key must not be null or empty.");
42+
checkNotNull(customSignalOperator);
43+
checkNotNull(targetCustomSignalValues);
44+
checkArgument(
45+
!targetCustomSignalValues.isEmpty(), "Target custom signal values must not be empty.");
46+
this.customSignalKey = customSignalKey.trim();
47+
this.customSignalOperator = customSignalOperator;
48+
this.targetCustomSignalValues = ImmutableList.copyOf(targetCustomSignalValues);
49+
}
50+
51+
CustomSignalCondition(CustomSignalConditionResponse customSignalCondition) {
52+
checkArgument(
53+
!Strings.isNullOrEmpty(customSignalCondition.getKey()),
54+
"Custom signal key must not be null or empty.");
55+
checkArgument(
56+
!customSignalCondition.getTargetValues().isEmpty(),
57+
"Target custom signal values must not be empty.");
58+
this.customSignalKey = customSignalCondition.getKey().trim();
59+
List<String> targetCustomSignalValuesList = customSignalCondition.getTargetValues();
60+
this.targetCustomSignalValues = ImmutableList.copyOf(targetCustomSignalValuesList);
61+
switch (customSignalCondition.getOperator()) {
62+
case "NUMERIC_EQUAL":
63+
this.customSignalOperator = CustomSignalOperator.NUMERIC_EQUAL;
64+
break;
65+
case "NUMERIC_GREATER_EQUAL":
66+
this.customSignalOperator = CustomSignalOperator.NUMERIC_GREATER_EQUAL;
67+
break;
68+
case "NUMERIC_GREATER_THAN":
69+
this.customSignalOperator = CustomSignalOperator.NUMERIC_GREATER_THAN;
70+
break;
71+
case "NUMERIC_LESS_EQUAL":
72+
this.customSignalOperator = CustomSignalOperator.NUMERIC_LESS_EQUAL;
73+
break;
74+
case "NUMERIC_LESS_THAN":
75+
this.customSignalOperator = CustomSignalOperator.NUMERIC_LESS_THAN;
76+
break;
77+
case "NUMERIC_NOT_EQUAL":
78+
this.customSignalOperator = CustomSignalOperator.NUMERIC_NOT_EQUAL;
79+
break;
80+
case "SEMANTIC_VERSION_EQUAL":
81+
this.customSignalOperator = CustomSignalOperator.SEMANTIC_VERSION_EQUAL;
82+
break;
83+
case "SEMANTIC_VERSION_GREATER_EQUAL":
84+
this.customSignalOperator = CustomSignalOperator.SEMANTIC_VERSION_GREATER_EQUAL;
85+
break;
86+
case "SEMANTIC_VERSION_GREATER_THAN":
87+
this.customSignalOperator = CustomSignalOperator.SEMANTIC_VERSION_GREATER_THAN;
88+
break;
89+
case "SEMANTIC_VERSION_LESS_EQUAL":
90+
this.customSignalOperator = CustomSignalOperator.SEMANTIC_VERSION_LESS_EQUAL;
91+
break;
92+
case "SEMANTIC_VERSION_LESS_THAN":
93+
this.customSignalOperator = CustomSignalOperator.SEMANTIC_VERSION_LESS_THAN;
94+
break;
95+
case "SEMANTIC_VERSION_NOT_EQUAL":
96+
this.customSignalOperator = CustomSignalOperator.SEMANTIC_VERSION_NOT_EQUAL;
97+
break;
98+
case "STRING_CONTAINS":
99+
this.customSignalOperator = CustomSignalOperator.STRING_CONTAINS;
100+
break;
101+
case "STRING_CONTAINS_REGEX":
102+
this.customSignalOperator = CustomSignalOperator.STRING_CONTAINS_REGEX;
103+
break;
104+
case "STRING_DOES_NOT_CONTAIN":
105+
this.customSignalOperator = CustomSignalOperator.STRING_DOES_NOT_CONTAIN;
106+
break;
107+
case "STRING_EXACTLY_MATCHES":
108+
this.customSignalOperator = CustomSignalOperator.STRING_EXACTLY_MATCHES;
109+
break;
110+
default:
111+
this.customSignalOperator = CustomSignalOperator.UNSPECIFIED;
112+
}
113+
checkArgument(
114+
this.customSignalOperator != CustomSignalOperator.UNSPECIFIED,
115+
"Custom signal operator passed is invalid");
116+
}
117+
118+
@NonNull
119+
String getCustomSignalKey() {
120+
return customSignalKey;
121+
}
122+
123+
@NonNull
124+
CustomSignalOperator getCustomSignalOperator() {
125+
return customSignalOperator;
126+
}
127+
128+
@NonNull
129+
List<String> getTargetCustomSignalValues() {
130+
return new ArrayList<>(targetCustomSignalValues);
131+
}
132+
133+
CustomSignalConditionResponse toCustomConditonResponse() {
134+
CustomSignalConditionResponse customSignalConditionResponse =
135+
new CustomSignalConditionResponse();
136+
customSignalConditionResponse.setKey(this.customSignalKey);
137+
customSignalConditionResponse.setOperator(this.customSignalOperator.getOperator());
138+
customSignalConditionResponse.setTargetValues(this.targetCustomSignalValues);
139+
return customSignalConditionResponse;
140+
}
141+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2020 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.remoteconfig;
18+
19+
import static com.google.common.base.Preconditions.checkArgument;
20+
21+
import com.google.common.base.Strings;
22+
import com.google.firebase.internal.NonNull;
23+
24+
enum CustomSignalOperator {
25+
NUMERIC_EQUAL("NUMERIC_EQUAL"),
26+
NUMERIC_GREATER_EQUAL("NUMERIC_GREATER_EQUAL"),
27+
NUMERIC_GREATER_THAN("NUMERIC_GREATER_THAN"),
28+
NUMERIC_LESS_EQUAL("NUMERIC_LESS_EQUAL"),
29+
NUMERIC_LESS_THAN("NUMERIC_LESS_THAN"),
30+
NUMERIC_NOT_EQUAL("NUMERIC_NOT_EQUAL"),
31+
SEMANTIC_VERSION_EQUAL("SEMANTIC_VERSION_EQUAL"),
32+
SEMANTIC_VERSION_GREATER_EQUAL("SEMANTIC_VERSION_GREATER_EQUAL"),
33+
SEMANTIC_VERSION_GREATER_THAN("SEMANTIC_VERSION_GREATER_THAN"),
34+
SEMANTIC_VERSION_LESS_EQUAL("SEMANTIC_VERSION_LESS_EQUAL"),
35+
SEMANTIC_VERSION_LESS_THAN("SEMANTIC_VERSION_LESS_THAN"),
36+
SEMANTIC_VERSION_NOT_EQUAL("SEMANTIC_VERSION_NOT_EQUAL"),
37+
STRING_CONTAINS("STRING_CONTAINS"),
38+
STRING_CONTAINS_REGEX("STRING_CONTAINS_REGEX"),
39+
STRING_DOES_NOT_CONTAIN("STRING_DOES_NOT_CONTAIN"),
40+
STRING_EXACTLY_MATCHES("STRING_EXACTLY_MATCHES"),
41+
UNSPECIFIED("CUSTOM_SIGNAL_OPERATOR_UNSPECIFIED");
42+
43+
private final String operator;
44+
45+
CustomSignalOperator(@NonNull String operator) {
46+
checkArgument(!Strings.isNullOrEmpty(operator), "Operator must not be null or empty.");
47+
this.operator = operator;
48+
}
49+
50+
@NonNull
51+
String getOperator() {
52+
return operator;
53+
}
54+
}

src/main/java/com/google/firebase/remoteconfig/FirebaseRemoteConfig.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,73 @@ protected Template execute() throws FirebaseRemoteConfigException {
101101
};
102102
}
103103

104+
/**
105+
* Alternative to {@link #getServerTemplate} where developers can initialize with a pre-cached
106+
* template or config.
107+
*/
108+
public ServerTemplateImpl.Builder serverTemplateBuilder() {
109+
return new ServerTemplateImpl.Builder(this.remoteConfigClient);
110+
}
111+
112+
/**
113+
* Initializes a template instance and loads the latest template data.
114+
*
115+
* @param defaultConfig Default parameter values to use if a getter references a parameter not
116+
* found in the template.
117+
* @return A {@link Template} instance with the latest template data.
118+
*/
119+
public ServerTemplate getServerTemplate(KeysAndValues defaultConfig)
120+
throws FirebaseRemoteConfigException {
121+
return getServerTemplateOp(defaultConfig).call();
122+
}
123+
124+
/**
125+
* Initializes a template instance without any defaults and loads the latest template data.
126+
*
127+
* @return A {@link Template} instance with the latest template data.
128+
*/
129+
public ServerTemplate getServerTemplate() throws FirebaseRemoteConfigException {
130+
return getServerTemplate(null);
131+
}
132+
133+
/**
134+
* Initializes a template instance and asynchronously loads the latest template data.
135+
*
136+
* @param defaultConfig Default parameter values to use if a getter references a parameter not
137+
* found in the template.
138+
* @return A {@link Template} instance with the latest template data.
139+
*/
140+
public ApiFuture<ServerTemplate> getServerTemplateAsync(KeysAndValues defaultConfig) {
141+
return getServerTemplateOp(defaultConfig).callAsync(app);
142+
}
143+
144+
/**
145+
* Initializes a template instance without any defaults and asynchronously loads the latest
146+
* template data.
147+
*
148+
* @return A {@link Template} instance with the latest template data.
149+
*/
150+
public ApiFuture<ServerTemplate> getServerTemplateAsync() {
151+
return getServerTemplateAsync(null);
152+
}
153+
154+
private CallableOperation<ServerTemplate, FirebaseRemoteConfigException> getServerTemplateOp(
155+
KeysAndValues defaultConfig) {
156+
return new CallableOperation<ServerTemplate, FirebaseRemoteConfigException>() {
157+
@Override
158+
protected ServerTemplate execute() throws FirebaseRemoteConfigException {
159+
String serverTemplateData = remoteConfigClient.getServerTemplate();
160+
ServerTemplate template =
161+
serverTemplateBuilder()
162+
.defaultConfig(defaultConfig)
163+
.cachedTemplate(serverTemplateData)
164+
.build();
165+
166+
return template;
167+
}
168+
};
169+
}
170+
104171
/**
105172
* Gets the requested version of the of the Remote Config template.
106173
*

src/main/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigClient.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,7 @@ Template publishTemplate(Template template, boolean validateOnly,
4040

4141
ListVersionsResponse listVersions(
4242
ListVersionsOptions options) throws FirebaseRemoteConfigException;
43-
}
43+
44+
String getServerTemplate() throws FirebaseRemoteConfigException;
45+
46+
}

0 commit comments

Comments
 (0)