diff --git a/modules/loaders-common/src/main/scala/com/snowplowanalytics/snowplow/loaders/transform/TypedTabledEntity.scala b/modules/loaders-common/src/main/scala/com/snowplowanalytics/snowplow/loaders/transform/TypedTabledEntity.scala index 4f2c043..f4b773a 100644 --- a/modules/loaders-common/src/main/scala/com/snowplowanalytics/snowplow/loaders/transform/TypedTabledEntity.scala +++ b/modules/loaders-common/src/main/scala/com/snowplowanalytics/snowplow/loaders/transform/TypedTabledEntity.scala @@ -58,8 +58,11 @@ object TypedTabledEntity { subVersions: Set[SchemaSubVersion], schemas: NonEmptyList[SelfDescribingSchema[Schema]] ): Option[TypedTabledEntity] = - schemas - .traverse(sds => fieldFromSchema(tabledEntity, sds.schema).map((_, sds))) + schemas.toList + .flatMap { sds => + fieldFromSchema(tabledEntity, sds.schema).map((_, sds)) + } + .toNel .map { nel => val (rootField, rootSchema) = nel.head val tte = TypedTabledEntity(tabledEntity, rootField, Set(keyToSubVersion(rootSchema.self.schemaKey)), Nil) diff --git a/modules/loaders-common/src/test/resources/iglu-client-embedded/schemas/myvendor/test_empty_prop/jsonschema/1-0-0 b/modules/loaders-common/src/test/resources/iglu-client-embedded/schemas/myvendor/test_empty_prop/jsonschema/1-0-0 new file mode 100644 index 0000000..c4b25db --- /dev/null +++ b/modules/loaders-common/src/test/resources/iglu-client-embedded/schemas/myvendor/test_empty_prop/jsonschema/1-0-0 @@ -0,0 +1,12 @@ +{ + "$schema": "http://iglucentral.com/schemas/com.snowplowanalytics.self-desc/schema/jsonschema/1-0-0#", + "self": { + "vendor": "myvendor", + "name": "test_empty_prop", + "format": "jsonschema", + "version": "1-0-0" + }, + "properties": {}, + "additionalProperties": false, + "type": "object" +} diff --git a/modules/loaders-common/src/test/resources/iglu-client-embedded/schemas/myvendor/test_empty_prop/jsonschema/1-0-1 b/modules/loaders-common/src/test/resources/iglu-client-embedded/schemas/myvendor/test_empty_prop/jsonschema/1-0-1 new file mode 100644 index 0000000..0359cdc --- /dev/null +++ b/modules/loaders-common/src/test/resources/iglu-client-embedded/schemas/myvendor/test_empty_prop/jsonschema/1-0-1 @@ -0,0 +1,14 @@ +{ + "$schema": "http://iglucentral.com/schemas/com.snowplowanalytics.self-desc/schema/jsonschema/1-0-0#", + "self": { + "vendor": "myvendor", + "name": "test_empty_prop", + "format": "jsonschema", + "version": "1-0-1" + }, + "properties": { + "myString": {"type": "string"} + }, + "additionalProperties": false, + "type": "object" +} diff --git a/modules/loaders-common/src/test/scala/com.snowplowanalytics.snowplow.loaders/transform/NonAtomicFieldsSpec.scala b/modules/loaders-common/src/test/scala/com.snowplowanalytics.snowplow.loaders/transform/NonAtomicFieldsSpec.scala index 062fd4d..1092231 100644 --- a/modules/loaders-common/src/test/scala/com.snowplowanalytics.snowplow.loaders/transform/NonAtomicFieldsSpec.scala +++ b/modules/loaders-common/src/test/scala/com.snowplowanalytics.snowplow.loaders/transform/NonAtomicFieldsSpec.scala @@ -29,6 +29,7 @@ class NonAtomicFieldsSpec extends Specification with CatsEffect { return nothing for the Iglu Central ad_break_end_event schema $ue4 return a JSON field for the Iglu Central anything-a schema $ue5 return a field prefixed with underscore if field starts with a digit $ueDigit + return a merged schema if the batch has a schema with empty properties and additionalProperties=false $emptyProp when resolving for known schemas in contexts should return an un-merged schema if the batch uses the first schema in a series $c1 @@ -229,6 +230,37 @@ class NonAtomicFieldsSpec extends Specification with CatsEffect { } } + def emptyProp = { + val tabledEntity = TabledEntity(TabledEntity.UnstructEvent, "myvendor", "test_empty_prop", 1) + + val input = Map( + tabledEntity -> Set((0, 1), (0, 0)) + ) + + val expected = { + val expectedStruct = Type.Struct( + NonEmptyVector.of( + Field("my_string", Type.String, Nullable).copy(accessors = Set("myString")) + ) + ) + + val expectedField = Field("unstruct_event_myvendor_test_empty_prop_1", expectedStruct, Nullable, Set.empty) + + TypedTabledEntity( + tabledEntity, + expectedField, + Set((0, 1)), + Nil + ) + } + + NonAtomicFields.resolveTypes(embeddedResolver, input, List.empty).map { case NonAtomicFields.Result(fields, failures) => + (failures must beEmpty) and + (fields must haveSize(1)) and + (fields.head must beEqualTo(expected)) + } + } + def c1 = { val tabledEntity = TabledEntity(TabledEntity.Context, "myvendor", "myschema", 7)