Skip to content

Commit 313ae57

Browse files
committed
Fix record-level Avro doc
1 parent fb98d33 commit 313ae57

File tree

2 files changed

+54
-7
lines changed

2 files changed

+54
-7
lines changed

scio-avro/src/main/scala/com/spotify/scio/avro/types/SchemaProvider.scala

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,14 @@ private[types] object SchemaProvider {
9898
private def getFields(t: Type): Iterable[(Symbol, Option[String])] =
9999
t.decls.filter(isField) zip fieldDoc(t)
100100

101+
// Scala 2.13.17+ resolves annotation type using `com.spotify.scio.avro.doc` alias; previous Scala
102+
// versions (and all 2.12 versions) resolve to `com.spotify.scio.avro.types.doc`
103+
// See: https://github.com/spotify/scio/issues/5874
104+
private val DocTypes = Set("com.spotify.scio.avro.types.doc", "com.spotify.scio.avro.doc")
105+
101106
private def recordDoc(t: Type): Option[String] = {
102-
val tpe = "com.spotify.scio.avro.types.doc"
103107
t.typeSymbol.annotations
104-
.find(_.tree.tpe.toString == tpe)
108+
.find(a => DocTypes.contains(a.tree.tpe.toString))
105109
.map { a =>
106110
val q"new $_($v)" = a.tree: @nowarn
107111
val Literal(Constant(s)) = v: @nowarn
@@ -110,13 +114,9 @@ private[types] object SchemaProvider {
110114
}
111115

112116
private def fieldDoc(t: Type): Iterable[Option[String]] = {
113-
// Scala 2.13.17+ resolves annotation type using `com.spotify.scio.avro.doc` alias; previous Scala
114-
// versions (and all 2.12 versions) resolve to `com.spotify.scio.avro.types.doc`
115-
// See: https://github.com/spotify/scio/issues/5874
116-
val tpes = Set("com.spotify.scio.avro.types.doc", "com.spotify.scio.avro.doc")
117117
t.typeSymbol.asClass.primaryConstructor.typeSignature.paramLists.head.map {
118118
_.annotations
119-
.find(a => tpes.contains(a.tree.tpe.toString))
119+
.find(a => DocTypes.contains(a.tree.tpe.toString))
120120
.map { a =>
121121
val q"new $_($v)" = a.tree: @nowarn
122122
val Literal(Constant(s)) = v: @nowarn
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2026 Spotify AB
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.spotify.scio.avro
18+
19+
import com.spotify.scio.avro.types.Schemas.parseSchema
20+
import org.scalatest.flatspec.AnyFlatSpec
21+
import org.scalatest.matchers.should.Matchers
22+
23+
@AvroType.toSchema
24+
@doc("User record schema")
25+
case class UserType(@doc("user name") name: String, @doc("user age") age: Int)
26+
27+
// We have to keep this test out of the `com.spotify.scio.avro.types` package to replicate the Scala
28+
// issue outlined in https://github.com/spotify/scio/issues/5874
29+
class AvroTypeTest extends AnyFlatSpec with Matchers {
30+
"AvroType" should "preserve doc and field-level annotations" in {
31+
val userSchema: String =
32+
s"""
33+
|{
34+
| "type": "record",
35+
| "namespace": "com.spotify.scio.avro",
36+
| "doc": "User record schema",
37+
| "name": "UserType",
38+
| "fields": [
39+
| { "name": "name", "type": "string", "doc": "user name"},
40+
| { "name": "age", "type": "int", "doc": "user age"}
41+
| ]
42+
|}
43+
|""".stripMargin
44+
45+
AvroType[UserType].schema.toString shouldBe parseSchema(userSchema).toString
46+
}
47+
}

0 commit comments

Comments
 (0)