Skip to content

Commit a15e481

Browse files
authored
Ignore previously serialized fields for compatibility with old JSONs (#1447)
1 parent cb85935 commit a15e481

7 files changed

Lines changed: 2451 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
Mapbox welcomes participation and contributions from everyone.
44

55
### main
6+
- Fixed `RouteOptions#toUrl` for a case when `RouteOptions` was deserialized from a json generated by an old version of mapbox-java. [#1447](https://github.com/mapbox/mapbox-java/pull/1447)
67

78
### v6.5.0 - May 25, 2022
89
- No additional changes

services-directions-models/src/main/java/com/mapbox/api/directions/v5/models/RouteOptions.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,18 @@
1717
import com.mapbox.api.directions.v5.utils.ParseUtils;
1818
import com.mapbox.auto.value.gson.SerializableJsonElement;
1919
import com.mapbox.geojson.Point;
20+
2021
import java.io.UnsupportedEncodingException;
2122
import java.net.MalformedURLException;
2223
import java.net.URL;
2324
import java.net.URLDecoder;
2425
import java.net.URLEncoder;
26+
import java.util.Collections;
27+
import java.util.HashSet;
2528
import java.util.LinkedHashMap;
2629
import java.util.List;
2730
import java.util.Map;
31+
import java.util.Set;
2832

2933
/**
3034
* Defines route request parameters.
@@ -39,6 +43,20 @@ public abstract class RouteOptions extends DirectionsJsonObject {
3943
private static final String UTF_8 = "UTF-8";
4044
private static final String ACCESS_TOKEN_URL_PARAM_NAME = "access_token";
4145

46+
/**
47+
* Fields that aren't supposed to be serialized.
48+
* - <b>access_token</b> - token must not be a part of RouteOptions,
49+
* it is provided via {@link RouteOptions#toUrl}
50+
* - <b>uuid</b> - legacy field, skipped for backward compatibility
51+
*/
52+
private static final Set<String> DEPRECATED_SERIALIZED_FIELDS = Collections.unmodifiableSet(
53+
new HashSet<String>() {
54+
{
55+
add(ACCESS_TOKEN_URL_PARAM_NAME);
56+
add("uuid");
57+
}
58+
});
59+
4260
/**
4361
* Build a new instance of {@link RouteOptions} and sets default values for:
4462
* <ul>
@@ -968,6 +986,9 @@ public URL toUrl(@NonNull String accessToken) {
968986
if (unrecognized != null) {
969987
for (Map.Entry<String, SerializableJsonElement> entry : unrecognized.entrySet()) {
970988
JsonElement element = entry.getValue().getElement();
989+
if (DEPRECATED_SERIALIZED_FIELDS.contains(entry.getKey())) {
990+
continue;
991+
}
971992
if (element.isJsonPrimitive()) {
972993
appendQueryParameter(sb, entry.getKey(), element.getAsString());
973994
} else {

services-directions-models/src/test/java/com/mapbox/api/directions/v5/models/RouteOptionsTest.java

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
package com.mapbox.api.directions.v5.models;
22

3-
import static com.google.gson.JsonParser.parseString;
4-
import static com.mapbox.api.directions.v5.utils.Asserts.assertContains;
5-
import static org.junit.Assert.assertEquals;
6-
import static org.junit.Assert.assertNotNull;
7-
import static org.junit.Assert.assertNull;
8-
import static org.junit.Assert.assertTrue;
93
import com.google.gson.Gson;
104
import com.google.gson.GsonBuilder;
115
import com.google.gson.JsonObject;
@@ -14,6 +8,8 @@
148
import com.mapbox.api.directions.v5.utils.MutateJsonUtil;
159
import com.mapbox.core.TestUtils;
1610
import com.mapbox.geojson.Point;
11+
import org.junit.Test;
12+
1713
import java.io.IOException;
1814
import java.net.MalformedURLException;
1915
import java.net.URL;
@@ -24,7 +20,16 @@
2420
import java.util.LinkedHashMap;
2521
import java.util.List;
2622
import java.util.Set;
27-
import org.junit.Test;
23+
24+
import static com.google.gson.JsonParser.parseString;
25+
import static com.mapbox.api.directions.v5.utils.Asserts.assertContains;
26+
import static com.mapbox.api.directions.v5.utils.Asserts.assertContainsExactCount;
27+
import static com.mapbox.api.directions.v5.utils.Asserts.assertNoDuplicatedParameters;
28+
import static org.junit.Assert.assertEquals;
29+
import static org.junit.Assert.assertFalse;
30+
import static org.junit.Assert.assertNotNull;
31+
import static org.junit.Assert.assertNull;
32+
import static org.junit.Assert.assertTrue;
2833

2934
public class RouteOptionsTest extends TestUtils {
3035
/**
@@ -713,6 +718,45 @@ public void routeOptionsWithUnrecognizedObjectsAndArrays() throws IOException {
713718
RouteOptions.fromJson(mutatedRouteOptionsJson.toString()).toUrl("testToken");
714719
}
715720

721+
@Test
722+
public void routeOptionsDoNotAddAccessTokenFromJsonToUrl() {
723+
Gson gson = new GsonBuilder().create();
724+
JsonObject routeOptionsJson = gson.fromJson(optionsJson, JsonObject.class);
725+
routeOptionsJson.addProperty("access_token", "wrong");
726+
String routeOptionsJsonString = routeOptionsJson.toString();
727+
728+
URL url = RouteOptions.fromJson(routeOptionsJsonString).toUrl("right");
729+
730+
String query = url.getQuery();
731+
assertContainsExactCount(query, "access_token", 1);
732+
assertContains(query, "access_token=right");
733+
}
734+
735+
@Test
736+
public void routeOptionsDoNotAddUUIDFromJsonToUrl() {
737+
Gson gson = new GsonBuilder().create();
738+
JsonObject routeOptionsJson = gson.fromJson(optionsJson, JsonObject.class);
739+
routeOptionsJson.addProperty("uuid", "test");
740+
String routeOptionsJsonString = routeOptionsJson.toString();
741+
742+
URL url = RouteOptions.fromJson(routeOptionsJsonString).toUrl("test");
743+
744+
assertFalse(
745+
"url shouldn't contain uuid: " + url,
746+
url.getQuery().contains("uuid")
747+
);
748+
}
749+
750+
@Test
751+
public void sanityCheckForPreviouslySerializedRouteResponse() throws IOException {
752+
String serializedDirectionResponse = loadJsonFixture("directions_v5_serialized_with_legacy_fields.json");
753+
DirectionsResponse response = DirectionsResponse.fromJson(serializedDirectionResponse);
754+
755+
URL url = response.routes().get(0).routeOptions().toUrl("token");
756+
757+
assertNoDuplicatedParameters(url);
758+
}
759+
716760
@Test
717761
public void emptyExcludeObjectsCleansUpExcludes() {
718762
RouteOptions routeOptions = routeOptions().toBuilder()

services-directions-models/src/test/java/com/mapbox/api/directions/v5/utils/Asserts.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package com.mapbox.api.directions.v5.utils;
22

3+
import java.net.URL;
4+
import java.util.HashSet;
35
import java.util.Set;
46

7+
import static com.mapbox.api.directions.v5.utils.StringUtil.countSubstrings;
8+
import static org.junit.Assert.assertEquals;
59
import static org.junit.Assert.assertFalse;
610
import static org.junit.Assert.assertTrue;
711

@@ -26,4 +30,34 @@ public static <T> void assertContains(Set<T> set, T object) {
2630
set.contains(object)
2731
);
2832
}
33+
34+
public static void assertContainsExactCount(String string, String substring, int count) {
35+
int substringsFound = countSubstrings(string, substring);
36+
assertEquals(
37+
"String \"" + string + "\" contains " +
38+
substringsFound + " \"" + substring + "\"",
39+
substringsFound,
40+
count
41+
);
42+
}
43+
44+
/***
45+
* Verifies that URL doesn't contain duplicated query parameters.
46+
* Examples:
47+
* https://api.mapbox.com/test?a=1&a=2 - invalid
48+
* https://api.mapbox.com/test?a=1&b=2 - valid
49+
* @param url
50+
*/
51+
public static void assertNoDuplicatedParameters(URL url) {
52+
String query = url.getQuery();
53+
Set<String> queryParameters = new HashSet<>();
54+
for (String param : query.split("&")) {
55+
String parameterName = param.split("=")[0];
56+
assertFalse(
57+
"parameter \"" + parameterName + "\" is duplicated in query response",
58+
queryParameters.contains(parameterName)
59+
);
60+
queryParameters.add(parameterName);
61+
}
62+
}
2963
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.mapbox.api.directions.v5.utils;
2+
3+
import org.junit.Test;
4+
5+
import static com.mapbox.api.directions.v5.utils.StringUtil.countSubstrings;
6+
import static org.junit.Assert.assertEquals;
7+
8+
public class CountSubstringsTest {
9+
10+
@Test
11+
public void zeroSubstrings() {
12+
int result = countSubstrings("test", "zero");
13+
assertEquals(0, result);
14+
}
15+
16+
@Test
17+
public void oneSubstring() {
18+
int result = countSubstrings("test-one-substring", "one");
19+
assertEquals(1, result);
20+
}
21+
22+
@Test
23+
public void twoSubstring() {
24+
int result = countSubstrings("twotest-two-substring", "two");
25+
assertEquals(2, result);
26+
}
27+
28+
@Test
29+
public void substringsAtTheEnd() {
30+
int result = countSubstrings("testheyheyhey", "hey");
31+
assertEquals(3, result);
32+
}
33+
34+
@Test(expected = IllegalArgumentException.class)
35+
public void emptySubstring() {
36+
countSubstrings("test", "");
37+
}
38+
39+
40+
@Test(expected = IllegalArgumentException.class)
41+
public void emptyString() {
42+
countSubstrings("", "test");
43+
}
44+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.mapbox.api.directions.v5.utils;
2+
3+
public final class StringUtil {
4+
/***
5+
* Counts how many times the substring is present in the string.
6+
* See examples in {@link CountSubstringsTest}
7+
* @param string
8+
* @param substring
9+
* @return
10+
*/
11+
public static int countSubstrings(String string, String substring) {
12+
if (substring == null || substring.isEmpty()) {
13+
throw new IllegalArgumentException("substring can't be null or empty");
14+
}
15+
if (string == null || string.isEmpty()) {
16+
throw new IllegalArgumentException("string can't be null or empty");
17+
}
18+
int nextIndex = -1;
19+
int substringsFound = -1;
20+
do {
21+
substringsFound++;
22+
nextIndex = string.indexOf(substring, nextIndex + 1);
23+
} while (nextIndex != -1);
24+
return substringsFound;
25+
}
26+
}

0 commit comments

Comments
 (0)