Skip to content

Commit 2a9a4b7

Browse files
authored
Merge pull request #7712 from DataDog/eric.firth/add-avro-schema-for-objects
[DSM] Add avro schema object extraction
2 parents 17dca46 + d786629 commit 2a9a4b7

File tree

2 files changed

+56
-7
lines changed

2 files changed

+56
-7
lines changed

dd-java-agent/instrumentation/avro/src/main/java/datadog/trace/instrumentation/avro/SchemaExtractor.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ public static boolean extractProperty(
3232

3333
switch (field.schema().getType()) {
3434
case RECORD:
35-
type = "object";
35+
type = "#/components/schemas/" + field.schema().getFullName();
36+
if (!extractSchema(field.schema(), builder, depth)) {
37+
return false;
38+
}
3639
break;
3740
case ENUM:
3841
type = "string";
@@ -41,10 +44,26 @@ public static boolean extractProperty(
4144
case ARRAY:
4245
array = true;
4346
type = getType(field.schema().getElementType().getType().getName());
47+
if (type == "record") {
48+
type = "#/components/schemas/" + field.schema().getElementType().getFullName();
49+
if (!extractSchema(field.schema().getElementType(), builder, depth)) {
50+
return false;
51+
}
52+
;
53+
}
4454
break;
4555
case MAP:
4656
type = "object";
47-
description = "Map type";
57+
String keys = "string";
58+
String values = getType(field.schema().getValueType().getType().getName());
59+
if (values == "record") {
60+
values = "#/components/schemas/" + field.schema().getValueType().getFullName();
61+
if (!extractSchema(field.schema().getValueType(), builder, depth)) {
62+
return false;
63+
}
64+
;
65+
}
66+
description = "Map type with " + keys + " keys and " + values + " values";
4867
break;
4968
case STRING:
5069
type = "string";
@@ -164,6 +183,8 @@ private static String getType(String type) {
164183
return "boolean";
165184
case "null":
166185
return "null";
186+
case "record":
187+
return "record";
167188
default:
168189
return "string";
169190
}

dd-java-agent/instrumentation/avro/src/test/groovy/AvroDatumReaderTest.groovy

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ class AvroDatumReaderTest extends AgentTestRunner {
2323

2424
return true
2525
}
26-
String schemaID = "5493435211744749109"
27-
String openApiSchemaDef = "{\"components\":{\"schemas\":{\"TestRecord\":{\"properties\":{\"stringField\":{\"type\":\"string\"},\"intField\":{\"format\":\"int32\",\"type\":\"integer\"},\"longField\":{\"format\":\"int64\",\"type\":\"integer\"},\"floatField\":{\"format\":\"float\",\"type\":\"number\"},\"doubleField\":{\"format\":\"double\",\"type\":\"number\"},\"booleanField\":{\"type\":\"boolean\"},\"bytesField\":{\"format\":\"byte\",\"type\":\"string\"},\"nullField\":{\"type\":\"null\"},\"enumField\":{\"enum\":[\"A\",\"B\",\"C\"],\"type\":\"string\"},\"fixedField\":{\"type\":\"string\"},\"recordField\":{\"type\":\"object\"},\"arrayField\":{\"items\":{\"type\":\"integer\"},\"type\":\"array\"},\"mapField\":{\"description\":\"Map type\",\"type\":\"object\"}},\"type\":\"object\"}}},\"openapi\":\"3.0.0\"}"
26+
String schemaID = "8924443781494069161"
27+
String openApiSchemaDef = "{\"components\":{\"schemas\":{\"TestRecord\":{\"properties\":{\"stringField\":{\"type\":\"string\"},\"intField\":{\"format\":\"int32\",\"type\":\"integer\"},\"longField\":{\"format\":\"int64\",\"type\":\"integer\"},\"floatField\":{\"format\":\"float\",\"type\":\"number\"},\"doubleField\":{\"format\":\"double\",\"type\":\"number\"},\"booleanField\":{\"type\":\"boolean\"},\"bytesField\":{\"format\":\"byte\",\"type\":\"string\"},\"nullField\":{\"type\":\"null\"},\"enumField\":{\"enum\":[\"A\",\"B\",\"C\"],\"type\":\"string\"},\"fixedField\":{\"type\":\"string\"},\"recordField\":{\"type\":\"#/components/schemas/NestedRecord\"},\"arrayField\":{\"items\":{\"type\":\"integer\"},\"type\":\"array\"},\"mapField\":{\"description\":\"Map type with string keys and string values\",\"type\":\"object\"},\"arrayNestedField\":{\"items\":{\"type\":\"#/components/schemas/OtherNestedRecord\"},\"type\":\"array\"},\"mapNestedField\":{\"description\":\"Map type with string keys and #/components/schemas/ThirdTypeOfNestedRecord values\",\"type\":\"object\"}},\"type\":\"object\"},\"NestedRecord\":{\"properties\":{\"nestedString\":{\"type\":\"string\"}},\"type\":\"object\"},\"OtherNestedRecord\":{\"properties\":{\"nestedString\":{\"type\":\"string\"}},\"type\":\"object\"},\"ThirdTypeOfNestedRecord\":{\"properties\":{\"nestedString\":{\"type\":\"string\"}},\"type\":\"object\"}}},\"openapi\":\"3.0.0\"}"
2828
String schemaStr = '''
2929
{
3030
"type": "record",
@@ -42,14 +42,14 @@ class AvroDatumReaderTest extends AgentTestRunner {
4242
{"name": "fixedField", "type": {"type": "fixed", "name": "TestFixed", "size": 16}},
4343
{"name": "recordField", "type": {"type": "record", "name": "NestedRecord", "fields": [{"name": "nestedString", "type": "string"}]}},
4444
{"name": "arrayField", "type": {"type": "array", "items": "int"}},
45-
{"name": "mapField", "type": {"type": "map", "values": "string"}}
45+
{"name": "mapField", "type": {"type": "map", "values": "string"}},
46+
{"name": "arrayNestedField", "type": { "type": "array", "items": {"type": "record", "name": "OtherNestedRecord", "fields": [{"name": "nestedString", "type": "string"}]}}},
47+
{"name": "mapNestedField", "type": {"type": "map", "values": {"type": "record", "name": "ThirdTypeOfNestedRecord", "fields": [{"name": "nestedString", "type": "string"}]}}}
4648
]
4749
}
4850
'''
4951
Schema schemaDef = new Schema.Parser().parse(schemaStr)
5052

51-
52-
5353
void 'test extract avro schema on deserialize'() {
5454

5555
setup:
@@ -80,6 +80,20 @@ class AvroDatumReaderTest extends AgentTestRunner {
8080
map.put("key2", "value2")
8181
datum.put("mapField", map)
8282

83+
// array of nested fields
84+
GenericRecord nestedRecordA = new GenericData.Record(schemaDef.getField("arrayNestedField").schema().getElementType())
85+
nestedRecordA.put("nestedString", "a")
86+
GenericRecord nestedRecordB = new GenericData.Record(schemaDef.getField("arrayNestedField").schema().getElementType())
87+
nestedRecordB.put("nestedString", "b")
88+
datum.put("arrayNestedField", Arrays.asList(nestedRecordA, nestedRecordB))
89+
90+
// map of nested fields
91+
Map<String, GenericRecord> nestedMap = new HashMap<>()
92+
GenericRecord nestedRecordC = new GenericData.Record(schemaDef.getField("mapNestedField").schema().getValueType())
93+
nestedRecordC.put("nestedString", "a")
94+
nestedMap.put("key1", nestedRecordC)
95+
datum.put("mapNestedField", nestedMap)
96+
8397
when:
8498
def bytes
8599
ByteArrayOutputStream out = new ByteArrayOutputStream()
@@ -153,6 +167,20 @@ class AvroDatumReaderTest extends AgentTestRunner {
153167
map.put("key2", "value2")
154168
datum.put("mapField", map)
155169

170+
// array of nested fields
171+
GenericRecord nestedRecordA = new GenericData.Record(schemaDef.getField("arrayNestedField").schema().getElementType())
172+
nestedRecordA.put("nestedString", "a")
173+
GenericRecord nestedRecordB = new GenericData.Record(schemaDef.getField("arrayNestedField").schema().getElementType())
174+
nestedRecordB.put("nestedString", "b")
175+
datum.put("arrayNestedField", Arrays.asList(nestedRecordA, nestedRecordB))
176+
177+
// map of nested fields
178+
Map<String, GenericRecord> nestedMap = new HashMap<>()
179+
GenericRecord nestedRecordC = new GenericData.Record(schemaDef.getField("mapNestedField").schema().getValueType())
180+
nestedRecordC.put("nestedString", "a")
181+
nestedMap.put("key1", nestedRecordC)
182+
datum.put("mapNestedField", nestedMap)
183+
156184
when:
157185
def bytes
158186
ByteArrayOutputStream out = new ByteArrayOutputStream()

0 commit comments

Comments
 (0)