Skip to content

Commit a282afc

Browse files
Merge pull request #64 from softlayer/allow_api_typeclass_coercion
Allow type coercion when determining API types
2 parents e0d978c + dd5d023 commit a282afc

File tree

4 files changed

+87
-25
lines changed

4 files changed

+87
-25
lines changed

src/main/java/com/softlayer/api/json/GsonJsonMarshallerFactory.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,8 @@
1414
import java.text.DateFormat;
1515
import java.text.ParseException;
1616
import java.text.SimpleDateFormat;
17-
import java.util.ArrayList;
18-
import java.util.Base64;
19-
import java.util.GregorianCalendar;
20-
import java.util.HashMap;
21-
import java.util.List;
22-
import java.util.Map;
17+
import java.util.*;
18+
import java.util.function.Supplier;
2319

2420
import com.google.gson.Gson;
2521
import com.google.gson.GsonBuilder;
@@ -191,12 +187,12 @@ public Entity read(JsonReader in) throws IOException {
191187
// we're an adapter for. So if we have SoftLayer_Something and a newer release of the
192188
// API has a type extending it but we don't have a generated class for it, it will get
193189
// properly serialized to a SoftLayer_Something.
190+
// If the API returns a type that isn't the same or a subtype of the type class,
191+
// try as best we can to fit the data within the type class.
194192
Class<? extends Entity> clazz = typeClasses.get(apiTypeName);
195193
Entity result;
196-
if (clazz == null) {
194+
if (clazz == null || !typeClass.isAssignableFrom(clazz)) {
197195
result = readForThisType(in);
198-
} else if (!typeClass.isAssignableFrom(clazz)) {
199-
throw new RuntimeException("Expecting " + typeClass + " to be super type of " + clazz);
200196
} else {
201197
result = ((EntityTypeAdapter) gson.getAdapter(clazz)).readForThisType(in);
202198
}

src/test/java/com/softlayer/api/json/GsonJsonMarshallerFactoryTest.java

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.google.gson.reflect.TypeToken;
2222
import com.softlayer.api.service.Entity;
2323
import com.softlayer.api.service.TestEntity;
24+
import com.softlayer.api.service.TestThing;
2425

2526
public class GsonJsonMarshallerFactoryTest {
2627

@@ -51,21 +52,22 @@ private String toJson(Object obj) throws Exception {
5152
public void testRead() throws Exception {
5253
Entity entity = fromJson(Entity.class,
5354
"{"
54-
+ "\"complexType\": \"SoftLayer_TestEntity\","
55-
+ "\"bar\": \"some string\","
56-
+ "\"foo\": \"another string\","
57-
+ "\"baz\": null,"
58-
+ "\"date\": \"1984-02-25T20:15:25-06:00\","
59-
+ "\"notApiProperty\": \"bad value\","
60-
+ "\"child\": {"
61-
+ " \"complexType\": \"SoftLayer_TestEntity\","
62-
+ " \"bar\": \"child string\""
63-
+ "},"
64-
+ "\"moreChildren\": ["
65-
+ " { \"complexType\": \"SoftLayer_TestEntity\", \"bar\": \"child 1\" },"
66-
+ " { \"complexType\": \"SoftLayer_TestEntity\", \"bar\": \"child 2\" }"
67-
+ "]"
68-
+ "}");
55+
+ "\"complexType\": \"SoftLayer_TestEntity\","
56+
+ "\"bar\": \"some string\","
57+
+ "\"foo\": \"another string\","
58+
+ "\"baz\": null,"
59+
+ "\"date\": \"1984-02-25T20:15:25-06:00\","
60+
+ "\"notApiProperty\": \"bad value\","
61+
+ "\"child\": {"
62+
+ " \"complexType\": \"SoftLayer_TestEntity\","
63+
+ " \"bar\": \"child string\""
64+
+ "},"
65+
+ "\"moreChildren\": ["
66+
+ " { \"complexType\": \"SoftLayer_TestEntity\", \"bar\": \"child 1\" },"
67+
+ " { \"complexType\": \"SoftLayer_TestEntity\", \"bar\": \"child 2\" }"
68+
+ "],"
69+
+ "\"testThing\": {\"complexType\": \"SoftLayer_TestThing\", \"id\": 123}"
70+
+ "}");
6971
assertEquals(TestEntity.class, entity.getClass());
7072
TestEntity obj = (TestEntity) entity;
7173
assertEquals("some string", obj.getFoo());
@@ -85,6 +87,69 @@ public void testRead() throws Exception {
8587
assertEquals(2, obj.getMoreChildren().size());
8688
assertEquals("child 1", obj.getMoreChildren().get(0).getFoo());
8789
assertEquals("child 2", obj.getMoreChildren().get(1).getFoo());
90+
assertEquals(TestThing.class, obj.getTestThing().getClass());
91+
assertEquals(123, obj.getTestThing().getId().intValue());
92+
}
93+
94+
@Test
95+
public void testReadPropertyWithIncorrectComplexTypeCoercesTheType() throws Exception {
96+
Entity entity = fromJson(Entity.class,
97+
"{"
98+
+ "\"complexType\": \"SoftLayer_TestEntity\","
99+
+ "\"testThing\": {\"complexType\": \"SoftLayer_TestEntity\", \"id\": 123, \"foo\": \"unknown!\"}"
100+
+ "}");
101+
assertEquals(TestEntity.class, entity.getClass());
102+
TestEntity obj = (TestEntity) entity;
103+
assertEquals(0, obj.getUnknownProperties().size());
104+
assertEquals(TestThing.class, obj.getTestThing().getClass());
105+
assertEquals(123, obj.getTestThing().getId().intValue());
106+
assertEquals(1, obj.getTestThing().getUnknownProperties().size());
107+
assertEquals("unknown!", obj.getTestThing().getUnknownProperties().get("foo"));
108+
}
109+
110+
111+
@Test
112+
public void testReadPropertyWithUnknownComplexTypeCoercesTheType() throws Exception {
113+
Entity entity = fromJson(Entity.class,
114+
"{"
115+
+ "\"complexType\": \"SoftLayer_TestEntity\","
116+
+ "\"testThing\": {\"complexType\": \"WhoKnows\", \"id\": 123, \"foo\": \"unknown!\"}"
117+
+ "}");
118+
assertEquals(TestEntity.class, entity.getClass());
119+
TestEntity obj = (TestEntity) entity;
120+
assertEquals(0, obj.getUnknownProperties().size());
121+
assertEquals(TestThing.class, obj.getTestThing().getClass());
122+
assertEquals(123, obj.getTestThing().getId().intValue());
123+
assertEquals(1, obj.getTestThing().getUnknownProperties().size());
124+
assertEquals("unknown!", obj.getTestThing().getUnknownProperties().get("foo"));
125+
}
126+
127+
@Test
128+
public void testReadPropertyThrowsExceptionWithoutComplexType() {
129+
130+
Exception e = assertThrows(RuntimeException.class, () -> {
131+
Entity entity = fromJson(Entity.class,
132+
"{"
133+
+ "\"complexType\": \"SoftLayer_TestEntity\","
134+
+ "\"testThing\": {\"id\": 123}"
135+
+ "}");
136+
});
137+
138+
assertEquals("Expected 'complexType' as first property", e.getMessage());
139+
}
140+
141+
@Test
142+
public void testReadPropertyThrowsExceptioWithComplexTypeNotFirst() {
143+
144+
Exception e = assertThrows(RuntimeException.class, () -> {
145+
Entity entity = fromJson(Entity.class,
146+
"{"
147+
+ "\"testThing\": {\"id\": 123},"
148+
+ "\"complexType\": \"SoftLayer_TestEntity\""
149+
+ "}");
150+
});
151+
152+
assertEquals("Expected 'complexType' as first property", e.getMessage());
88153
}
89154

90155
@Test

src/test/java/com/softlayer/api/service/TestEntity.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import com.softlayer.api.annotation.ApiType;
1212
import com.softlayer.api.ApiClient;
1313
import com.softlayer.api.ResponseHandler;
14-
import com.softlayer.api.ResultLimit;
1514

1615
@ApiType("SoftLayer_TestEntity")
1716
public class TestEntity extends Entity {

src/test/java/com/softlayer/api/service/TestThing.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
import com.softlayer.api.annotation.ApiMethod;
1111
import com.softlayer.api.annotation.ApiProperty;
1212
import com.softlayer.api.annotation.ApiService;
13+
import com.softlayer.api.annotation.ApiType;
1314

15+
@ApiType("SoftLayer_TestThing")
1416
public class TestThing extends Entity {
1517

1618
@ApiProperty(canBeNullOrNotSet = true)

0 commit comments

Comments
 (0)