1 /*
2  *  Make.org Core API
3  *  Copyright (C) 2018 Make.org
4  *
5  * This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU Affero General Public License as
7  *  published by the Free Software Foundation, either version 3 of the
8  *  License, or (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU Affero General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Affero General Public License
16  *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  *
18  */
19 
20 package org.make.core
21 
22 import java.time.{LocalDate, ZonedDateTime}
23 import com.sksamuel.avro4s._
24 import enumeratum.values.{StringEnum, StringEnumEntry}
25 import org.apache.avro.{Schema, SchemaBuilder}
26 import org.make.api.technical.crm.SendMessages
27 import org.make.core.history.HistoryActions.VoteTrust
28 import org.make.core.history.HistoryActions.VoteTrust.Trusted
29 import org.make.core.proposal.{ProposalStatus, ProposalType}
30 import org.make.core.reference.{Country, Language}
31 import org.make.core.technical.Multilingual
32 
33 trait AvroSerializers {
34 
35   implicit object LocalDateSchemaFor extends SchemaFor[LocalDate] {
36     override def schema(fieldMapper: FieldMapper): Schema = Schema.create(Schema.Type.STRING)
37   }
38 
39   implicit object DateTimeEncoder extends Encoder[LocalDate] {
40     override def encode(value: LocalDate, schema: Schema, fieldMapper: FieldMapper): String = value.toString
41   }
42 
43   implicit object DateTimeDecoder extends Decoder[LocalDate] {
44     override def decode(value: Any, schema: Schema, fieldMapper: FieldMapper): LocalDate =
45       LocalDate.parse(value.toString)
46   }
47 
48   implicit object ZonedDateTimeSchemaFor extends SchemaFor[ZonedDateTime] {
49     override def schema(fieldMapper: FieldMapper): Schema = Schema.create(Schema.Type.STRING)
50   }
51 
52   implicit object ZonedDateTimeEncoder extends Encoder[ZonedDateTime] {
53     override def encode(value: ZonedDateTime, schema: Schema, fieldMapper: FieldMapper): String =
54       DateHelper.format(value)
55   }
56 
57   implicit object ZonedDateTimeDecoder extends Decoder[ZonedDateTime] {
58     override def decode(value: Any, schema: Schema, fieldMapper: FieldMapper): ZonedDateTime =
59       ZonedDateTime.parse(value.toString)
60   }
61 
62   implicit object CountrySchemaFor extends SchemaFor[Country] {
63     override def schema(fieldMapper: FieldMapper): Schema = Schema.create(Schema.Type.STRING)
64   }
65 
66   implicit object CountryEncoder extends Encoder[Country] {
67     override def encode(value: Country, schema: Schema, fieldMapper: FieldMapper): String = value.value
68   }
69 
70   implicit object CountryDecoder extends Decoder[Country] {
71     override def decode(value: Any, schema: Schema, fieldMapper: FieldMapper): Country = Country(value.toString)
72   }
73 
74   implicit object LanguageSchemaFor extends SchemaFor[Language] {
75     override def schema(fieldMapper: FieldMapper): Schema = Schema.create(Schema.Type.STRING)
76   }
77 
78   implicit object LanguageEncoder extends Encoder[Language] {
79     override def encode(value: Language, schema: Schema, fieldMapper: FieldMapper): String = value.value
80   }
81 
82   implicit object LanguageDecoder extends Decoder[Language] {
83     override def decode(value: Any, schema: Schema, fieldMapper: FieldMapper): Language = Language(value.toString)
84   }
85 
86   implicit def multilingualSchema[T <: String](implicit schemaFor: SchemaFor[T]): SchemaFor[Multilingual[T]] =
87     fieldMapper => SchemaBuilder.map().values(schemaFor.schema(fieldMapper))
88 
89   implicit def multilingualEncoder[T <: String](implicit encoder: Encoder[Map[String, T]]): Encoder[Multilingual[T]] =
90     (value, schema, fieldMapper) => {
91       encoder.encode(value.toMap.map {
92         case (k, v) => (k.value, v)
93       }, schema, fieldMapper)
94     }
95 
96   @SuppressWarnings(Array("org.wartremover.warts.AsInstanceOf"))
97   implicit def multilingualDecoder[T <: String](
98     implicit valueDecoder: Decoder[Map[String, T]]
99   ): Decoder[Multilingual[T]] =
100     (value, schema, fieldMapper) => {
101       val map = valueDecoder.decode(value, schema, fieldMapper)
102       Multilingual.fromMap(map)
103     }
104 
105   // do not use automatic StringEnum typeclasses, to keep legacy complex schema
106   implicit val proposalStatusSchemaFor: SchemaFor[ProposalStatus] = SchemaFor.gen[ProposalStatus]
107   implicit val proposalStatusDecoder: Decoder[ProposalStatus] = Decoder.gen
108   implicit val proposalStatusEncoder: Encoder[ProposalStatus] = Encoder.gen
109 
110   // do not use automatic StringEnum typeclasses, to keep legacy complex schema
111   implicit val proposalTypeSchemaFor: SchemaFor[ProposalType] = SchemaFor.gen[ProposalType]
112   implicit val proposalTypeDecoder: Decoder[ProposalType] = Decoder.gen
113   implicit val proposalTypeEncoder: Encoder[ProposalType] = Encoder.gen
114 
115   implicit val voteTrustDecoder: Decoder[VoteTrust] = (value: Any, schema: Schema, fieldMapper: FieldMapper) => {
116     // Add a fallback to trusted since some events in kafka are corrupted
117     Option(value).map(stringEnumDecoder[VoteTrust].decode(_, schema, fieldMapper)).getOrElse(Trusted)
118   }
119 
120   implicit def stringEnumSchemaFor[A <: StringEnumEntry]: SchemaFor[A] = _ => Schema.create(Schema.Type.STRING)
121 
122   implicit def stringEnumEncoder[A <: StringEnumEntry]: Encoder[A] = (a, _, _) => a.value
123 
124   @SuppressWarnings(Array("org.wartremover.warts.Throw"))
125   implicit def stringEnumDecoder[A <: StringEnumEntry](implicit stringEnum: StringEnum[A]): Decoder[A] =
126     (value, _, _) =>
127       stringEnum
128         .withValueOpt(value.toString)
129         .getOrElse(throw new IllegalArgumentException(s"$value is not a ${stringEnum.toString}"))
130 
131   implicit val requestContextSchemaFor: SchemaFor[RequestContext] = SchemaFor.gen[RequestContext]
132   implicit val requestContextAvroDecoder: Decoder[RequestContext] = Decoder.gen[RequestContext]
133   implicit val requestContextAvroEncoder: Encoder[RequestContext] = Encoder.gen[RequestContext]
134 
135   implicit val sendMessagesSchemaFor: SchemaFor[SendMessages] = SchemaFor.gen[SendMessages]
136   implicit val sendMessagesAvroDecoder: Decoder[SendMessages] = Decoder.gen[SendMessages]
137   implicit val sendMessagesAvroEncoder: Encoder[SendMessages] = Encoder.gen[SendMessages]
138 
139 }
140 
141 object AvroSerializers extends AvroSerializers
Line Stmt Id Pos Tree Symbol Tests Code
36 6906 1416 - 1449 Apply org.apache.avro.Schema.create org.apache.avro.Schema.create(STRING)
40 6177 1612 - 1626 Apply java.time.LocalDate.toString value.toString()
45 7577 1808 - 1822 Apply scala.Any.toString value.toString()
45 7124 1792 - 1823 Apply java.time.LocalDate.parse java.time.LocalDate.parse(value.toString())
49 6314 1965 - 1998 Apply org.apache.avro.Schema.create org.make.api.avro.avrocompatibilitytest,org.make.api.makekafkatest org.apache.avro.Schema.create(STRING)
54 7657 2180 - 2204 Apply org.make.core.DefaultDateHelper.format org.make.api.kafkaconsumertest DateHelper.format(value)
59 6884 2403 - 2417 Apply scala.Any.toString value.toString()
59 6042 2383 - 2418 Apply java.time.ZonedDateTime.parse java.time.ZonedDateTime.parse(value.toString())
63 7811 2548 - 2581 Apply org.apache.avro.Schema.create org.make.api.avro.avrocompatibilitytest,org.make.api.makekafkatest org.apache.avro.Schema.create(STRING)
67 6912 2739 - 2750 Select org.make.core.reference.Country.value org.make.api.kafkaconsumertest value.value
71 7579 2905 - 2928 Apply org.make.core.reference.Country.apply org.make.core.reference.Country.apply(value.toString())
71 6137 2913 - 2927 Apply scala.Any.toString value.toString()
75 7077 3060 - 3093 Apply org.apache.avro.Schema.create org.make.api.avro.avrocompatibilitytest,org.make.api.makekafkatest org.apache.avro.Schema.create(STRING)
79 6276 3254 - 3265 Select org.make.core.reference.Language.value value.value
83 7660 3432 - 3446 Apply scala.Any.toString value.toString()
83 6852 3423 - 3447 Apply org.make.core.reference.Language.apply org.make.core.reference.Language.apply(value.toString())
87 7816 3583 - 3640 Apply org.apache.avro.SchemaBuilder.MapBuilder.values org.make.api.avro.avrocompatibilitytest,org.make.api.makekafkatest org.apache.avro.SchemaBuilder.map().values(schemaFor.schema(fieldMapper))
87 6000 3610 - 3639 Apply com.sksamuel.avro4s.SchemaFor.schema org.make.api.avro.avrocompatibilitytest,org.make.api.makekafkatest schemaFor.schema(fieldMapper)
91 7559 3820 - 3881 Apply scala.collection.MapOps.map value.toMap.map[String, T](((x0$1: (org.make.core.reference.Language, T)) => x0$1 match { case (_1: org.make.core.reference.Language, _2: T): (org.make.core.reference.Language, T)((k @ _), (v @ _)) => scala.Tuple2.apply[String, T](k.value, v) }))
91 6693 3805 - 3903 Apply com.sksamuel.avro4s.Encoder.encode encoder.encode(value.toMap.map[String, T](((x0$1: (org.make.core.reference.Language, T)) => x0$1 match { case (_1: org.make.core.reference.Language, _2: T): (org.make.core.reference.Language, T)((k @ _), (v @ _)) => scala.Tuple2.apply[String, T](k.value, v) })), schema, fieldMapper)
92 7038 3862 - 3869 Select org.make.core.reference.Language.value k.value
92 6141 3861 - 3873 Apply scala.Tuple2.apply scala.Tuple2.apply[String, T](k.value, v)
101 6282 4162 - 4209 Apply com.sksamuel.avro4s.Decoder.decode valueDecoder.decode(value, schema, fieldMapper)
102 7728 4216 - 4241 Apply org.make.core.technical.Multilingual.fromMap org.make.core.technical.Multilingual.fromMap[T](map)
117 7779 5089 - 5186 Apply scala.Option.getOrElse scala.Option.apply[Any](value).map[org.make.core.history.HistoryActions.VoteTrust](((x$1: Any) => AvroSerializers.this.stringEnumDecoder[org.make.core.history.HistoryActions.VoteTrust]((VoteTrust: enumeratum.values.StringEnum[org.make.core.history.HistoryActions.VoteTrust])).decode(x$1, schema, fieldMapper))).getOrElse[org.make.core.history.HistoryActions.VoteTrust](org.make.core.history.HistoryActions.VoteTrust.Trusted)
117 6855 5107 - 5166 Apply com.sksamuel.avro4s.Decoder.decode AvroSerializers.this.stringEnumDecoder[org.make.core.history.HistoryActions.VoteTrust]((VoteTrust: enumeratum.values.StringEnum[org.make.core.history.HistoryActions.VoteTrust])).decode(x$1, schema, fieldMapper)
117 5963 5178 - 5185 Select org.make.core.history.HistoryActions.VoteTrust.Trusted org.make.core.history.HistoryActions.VoteTrust.Trusted
120 6905 5270 - 5303 Apply org.apache.avro.Schema.create org.make.api.avro.avrocompatibilitytest,org.make.api.makekafkatest org.apache.avro.Schema.create(STRING)
122 6203 5387 - 5394 Select enumeratum.values.ValueEnumEntry.value a.value
128 7489 5619 - 5633 Apply scala.Any.toString value.toString()
129 6311 5586 - 5732 Apply scala.Option.getOrElse stringEnum.withValueOpt(value.toString()).getOrElse[A](throw new scala.`package`.IllegalArgumentException(("".+(value).+(" is not a ").+(stringEnum.toString()): String)))
129 6696 5654 - 5731 Throw <nosymbol> throw new scala.`package`.IllegalArgumentException(("".+(value).+(" is not a ").+(stringEnum.toString()): String))