Skip to content

Commit

Permalink
BigQuery final type suggestion should always allow null (close #153)
Browse files Browse the repository at this point in the history
  • Loading branch information
istreeter authored and voropaevp committed Apr 27, 2023
1 parent fd96623 commit 9c5b9f1
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,27 +42,27 @@ object Field {
case Some(types) if types.possiblyWithNull(CommonProperties.Type.Object) =>
val subfields = topSchema.properties.map(_.value).getOrElse(Map.empty)
if (subfields.isEmpty) {
Suggestion.finalSuggestion(topSchema, required)(name)
Suggestion.finalSuggestion(topSchema, required && !types.nullable)(name)
} else {
val requiredKeys = topSchema.required.toList.flatMap(_.value)
val fields = subfields.map { case (key, schema) =>
build(key, schema, requiredKeys.contains(key))
}
val subFields = fields.toList.sortBy(field => (Mode.sort(field.mode), field.name))
Field(name, Type.Record(subFields), Mode.required(required))
Field(name, Type.Record(subFields), Mode.required(required && !types.nullable))
}
case Some(types) if types.possiblyWithNull(CommonProperties.Type.Array) =>
topSchema.items match {
case Some(ArrayProperty.Items.ListItems(schema)) =>
build(name, schema, false).copy(mode = Mode.Repeated)
case _ =>
Suggestion.finalSuggestion(topSchema, required)(name)
Suggestion.finalSuggestion(topSchema, required && !types.nullable)(name)
}
case _ =>
Suggestion.suggestions
.find(suggestion => suggestion(topSchema, required).isDefined)
.flatMap(_.apply(topSchema, required))
.getOrElse(Suggestion.finalSuggestion(topSchema, required))
.getOrElse(Suggestion.finalSuggestion(topSchema, required = false))
.apply(name)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ class FieldSpec extends org.specs2.Specification { def is = s2"""
build generates repeated string for empty schema in items $e7
build generates repeated record for nullable array $e8
normalName handles camel case and disallowed characters $e9
build generates nullable field for oneOf types $e10
build generates nullable field for nullable object without nested keys $e11
build generates nullable field for nullable array without items $e12
"""

def e1 = {
Expand Down Expand Up @@ -231,5 +234,84 @@ class FieldSpec extends org.specs2.Specification { def is = s2"""
(fieldNormalName("1test1,Test2Test3Test4.test5;test6") must beEqualTo("_1test1_test2_test3_test4_test5_test6"))
}

def e10 = {
val input = SpecHelpers.parseSchema(
"""
| {
| "type": "object",
| "required": ["xyz"],
| "properties": {
| "xyz": {
| "oneOf": [
| {"type": "string"},
| {"type": "number"},
| {"type": "null"}
| ]
| }
| }
| }
""".stripMargin)

val expected = Field(
"foo",
Type.Record(List(
Field("xyz", Type.String, Mode.Nullable)
)),
Mode.Nullable
)

Field.build("foo", input, false) must beEqualTo(expected)
}

def e11 = {
val input = SpecHelpers.parseSchema(
"""
| {
| "type": "object",
| "required": ["xyz"],
| "properties": {
| "xyz": {
| "type": ["object", "null"]
| }
| }
| }
""".stripMargin)

val expected = Field(
"foo",
Type.Record(List(
Field("xyz", Type.String, Mode.Nullable)
)),
Mode.Nullable
)

Field.build("foo", input, false) must beEqualTo(expected)
}

def e12 = {
val input = SpecHelpers.parseSchema(
"""
| {
| "type": "object",
| "required": ["xyz"],
| "properties": {
| "xyz": {
| "type": ["array", "null"]
| }
| }
| }
""".stripMargin)

val expected = Field(
"foo",
Type.Record(List(
Field("xyz", Type.String, Mode.Nullable)
)),
Mode.Nullable
)

Field.build("foo", input, false) must beEqualTo(expected)
}

private def fieldNormalName(name: String) = Field(name, Type.String, Mode.Nullable).normalName
}

0 comments on commit 9c5b9f1

Please sign in to comment.