【问题标题】:[Scala][Play] How can I validate an event with three possible configurations?[Scala][Play] 如何验证具有三种可能配置的事件?
【发布时间】:2016-10-13 18:13:21
【问题描述】:

我将 Play Framework (Scala) 用于微服务,并使用 Kafka 作为事件总线。我有一个事件消费者,它映射到一个事件类,如下所示:

case class MovieEvent[T] (
                          mediaId: String,
                          config: T
                        )

object MovieEvent {
  implicit def movieEventFormat[T: Format]: Format[MovieEvent[T]] =
    ((__ \ "mediaId").format[String] ~
      (__ \ "config").format[T]
      )(MovieEvent.apply _, unlift(MovieEvent.unapply))
}

object MovieProvider extends SerializableEnumeration {
  implicit val providerReads: Reads[MovieProvider.Value] = SerializableEnumeration.jsonReader(MovieProvider)
  implicit val providerWrites: Writes[MovieProvider.Value] = SerializableEnumeration.jsonWrites
  val  Dreamworks, Disney, Paramount = Value
}

消费者看起来像:

class MovieEventConsumer @Inject()(movieService: MovieService
                                    ) extends ConsumerRecordProcessor with LazyLogging {
  override def process(record: IncomingRecord): Unit = {

    val movieEventJson = Json.parse(record.valueString).validate[MovieEvent[DreamworksConfiguration]]
    movieEventJson match {
      case event: JsSuccess[MovieEvent[DreamworksJobOptions]] => processMovieEvent(event.get)
      case er: JsError =>
        logger.error("Unrecognized MovieEvent, attempting to parse as MovieUploadEvent: " + JsError.toJson(er).toString())
        try {
          val data = (Json.parse(record.valueString) \ "upload").as[MovieUploadEvent]
          processUploadEvent(data)
        } catch {
          case er: Exception => logger.error("Unrecognized kafka event", er)
        }
    }
  }

  def processMovieEvent[T](event: MovieEvent[T]): Unit = {
    logger.debug(s"Received movie event: ${event}")
    movieService.createMovieJob(event)
  }

  def processUploadEvent(event: MovieUploadEvent): Unit = {
    logger.debug(s"Received upload event: ${event}")
    movieService.addToCollection(event)
  }

}

目前,我只能验证三种不同的 MovieEvent 配置(Dreamwork、迪士尼和派拉蒙)中的一种。我可以换掉我通过代码验证的那个,但这不是重点。但是,我想验证这三个中的任何一个,而不必增加额外的消费者。我尝试过一些不同的想法,但没有一个可以编译。我对 Play 和 Kafka 还很陌生,想知道是否有一种好方法可以做到这一点。

提前致谢!

【问题讨论】:

    标签: json scala playframework playframework-2.0 apache-kafka


    【解决方案1】:

    我将假设可能的配置数量是有限的,并且在编译时都是已知的(在您的示例中为 3)。

    一种可能性是使MovieEvent 成为具有通用类型T 的密封特征。这是一个最小的例子:

    case class DreamWorksJobOptions(anOption: String, anotherOption: String)
    case class DisneyJobOptions(anOption: String)
    
    sealed trait MovieEvent[T] {
      def mediaId: String
      def config: T
    }
    case class DreamWorksEvent(mediaId: String, config: DreamWorksJobOptions) extends MovieEvent[DreamWorksJobOptions]
    case class DisneyEvent(mediaId: String, config: DisneyJobOptions) extends MovieEvent[DisneyJobOptions]
    
    def tryParse(jsonString: String): MovieEvent[_] = {
      // ... parsing logic goes here
      DreamWorksEvent("dw", DreamWorksJobOptions("some option", "another option"))
    }
    
    val parseResult = tryParse("asdfasdf")
    
    parseResult match {
      case DreamWorksEvent(mediaId, config) => println(mediaId + " : " + config.anOption + " : " + config.anotherOption)
      case DisneyEvent(mediaId, config) => println(mediaId + config)
    }
    

    打印出来

    dw : some option : another option
    

    我省略了解析部分,因为我无权访问 Play Json atm。但是由于您有一个密封的层次结构,您可以一个一个地尝试每个选项。 (而且您几乎必须这样做,因为我们不能静态保证 DreamWorksEventDisneyEvent 不具有相同的 Json 结构 - 您需要决定先尝试哪种类型,然后在第一次尝试时将 JSON 解析为另一种类型无法解析)。

    现在您的其他代码非常通用。要添加新的事件类型,您只需向MovieEvent 添加另一个子类,并确保您的解析逻辑处理该新案例。这里的神奇之处在于,您不必在引用 MovieEvent 时指定您的 T,因为您知道您有一个密封的层次结构,因此可以通过模式匹配恢复 T

    【讨论】:

      猜你喜欢
      • 2017-03-27
      • 1970-01-01
      • 2013-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-17
      • 1970-01-01
      • 2016-10-01
      相关资源
      最近更新 更多