簡體   English   中英

[Scala] [播放]如何使用三種可能的配置驗證事件?

[英][Scala][Play] How can I validate an event with three possible configurations?

我正在將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,Disney和Paramount)之一。 我可以通過代碼交換我驗證過的那個,但這不是重點。 但是,我想驗證這三個中的任何一個而不必增加其他消費者。 我嘗試了一些不同的想法,但是沒有一個可以編譯。 我對Play和Kafka還是陌生的,想知道是否有個好方法。

提前致謝!

我將假設可能的配置數量是有限的,並且在編譯時都是已知的(在您的示例中為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。 但是,由於您具有密封的層次結構,因此可以逐個嘗試每個選項。 (而且,您幾乎必須這樣做,因為我們不能靜態地保證DreamWorksEvent不具有與DisneyEvent相同的Json結構-您需要確定首先嘗試哪種方法,並在第一次解析失敗時回DisneyEvent將JSON解析為另一種類型) 。

現在,您的其他代碼非常通用。 要添加新的事件類型,您只需向MovieEvent添加另一個子類,並確保您的解析邏輯可以處理該新情況。 這里的魔力在於,在引用MovieEvent時不必指定T ,因為您知道自己擁有密封的MovieEvent ,因此可以通過模式匹配來恢復T

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM