Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #21821: Refactor without trait #4505

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ trait CampaignEventRepository {
def deleteEvent(
id: Option[CampaignEventId] = None,
states: List[String] = Nil,
campaignType: Option[CampaignType] = None,
campaignType: Option[String] = None,
campaignId: Option[CampaignId] = None,
afterDate: Option[DateTime] = None,
beforeDate: Option[DateTime] = None
Expand All @@ -69,7 +69,7 @@ trait CampaignEventRepository {
*/
def getWithCriteria(
states: List[String] = Nil,
campaignType: Option[CampaignType] = None,
campaignType: Option[String] = None,
campaignId: Option[CampaignId] = None,
limit: Option[Int] = None,
offset: Option[Int] = None,
Expand All @@ -91,8 +91,7 @@ class CampaignEventRepositoryImpl(doobie: Doobie, campaignSerializer: CampaignSe

implicit val eventWrite: Write[CampaignEvent] = {
Write[(String, String, String, CampaignEventState, DateTime, DateTime, String)].contramap {
case event =>
(event.id.value, event.campaignId.value, event.name, event.state, event.start, event.end, event.campaignType.value)
case event => (event.id.value, event.campaignId.value, event.name, event.state, event.start, event.end, event.campaignType)
}
}

Expand All @@ -106,7 +105,7 @@ class CampaignEventRepositoryImpl(doobie: Doobie, campaignSerializer: CampaignSe
d._4,
d._5,
d._6,
campaignSerializer.campaignType(d._7)
d._7
)
}
}
Expand All @@ -119,7 +118,7 @@ class CampaignEventRepositoryImpl(doobie: Doobie, campaignSerializer: CampaignSe

def getWithCriteria(
states: List[String] = Nil,
campaignType: Option[CampaignType] = None,
campaignType: Option[String] = None,
campaignId: Option[CampaignId] = None,
limit: Option[Int] = None,
offset: Option[Int] = None,
Expand All @@ -131,7 +130,7 @@ class CampaignEventRepositoryImpl(doobie: Doobie, campaignSerializer: CampaignSe

import cats.syntax.list._
val campaignIdQuery = campaignId.map(c => fr"campaignId = ${c.value}")
val campaignTypeQuery = campaignType.map(c => fr"campaignType = ${c.value}")
val campaignTypeQuery = campaignType.map(c => fr"campaignType = ${c}")
val stateQuery = states.toNel.map(s => Fragments.in(fr"state->>'value'", s))
val afterQuery = afterDate.map(d => fr"endDate >= ${new java.sql.Timestamp(d.getMillis)}")
val beforeQuery = beforeDate.map(d => fr"startDate <= ${new java.sql.Timestamp(d.getMillis)}")
Expand Down Expand Up @@ -174,7 +173,7 @@ class CampaignEventRepositoryImpl(doobie: Doobie, campaignSerializer: CampaignSe
def deleteEvent(
id: Option[CampaignEventId] = None,
states: List[String] = Nil,
campaignType: Option[CampaignType] = None,
campaignType: Option[String] = None,
campaignId: Option[CampaignId] = None,
afterDate: Option[DateTime] = None,
beforeDate: Option[DateTime] = None
Expand All @@ -183,7 +182,7 @@ class CampaignEventRepositoryImpl(doobie: Doobie, campaignSerializer: CampaignSe
import cats.syntax.list._
val eventIdQuery = id.map(c => fr"eventId = ${c.value}")
val campaignIdQuery = campaignId.map(c => fr"campaignId = ${c.value}")
val campaignTypeQuery = campaignType.map(c => fr"campaignType = ${c.value}")
val campaignTypeQuery = campaignType.map(c => fr"campaignType = ${c}")
val stateQuery = states.toNel.map(s => Fragments.in(fr"state->>'value'", s))
val afterQuery = afterDate.map(d => fr"endDate >= ${new java.sql.Timestamp(d.getMillis)}")
val beforeQuery = beforeDate.map(d => fr"startDate <= ${new java.sql.Timestamp(d.getMillis)}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,16 @@ import zio.json.jsonField
import zio.json.jsonHint

case class CampaignParsingInfo(
campaignType: CampaignType,
campaignType: String,
version: Int
)

trait Campaign {
def info: CampaignInfo
def details: CampaignDetails
def campaignType: CampaignType
def copyWithId(newId: CampaignId): Campaign
def version: Int
def campaignType: String
def version: Int
def copyWithId(campaignId: CampaignId): Campaign
}

case class CampaignInfo(
Expand Down Expand Up @@ -156,16 +156,14 @@ case class OneShot(start: DateTime, end: DateTime) extends CampaignSchedule

trait CampaignDetails

case class CampaignType(value: String)

case class CampaignEvent(
id: CampaignEventId,
campaignId: CampaignId,
name: String,
state: CampaignEventState,
start: DateTime,
end: DateTime,
campaignType: CampaignType
campaignType: String
)
case class CampaignEventId(value: String)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
package com.normation.rudder.campaigns

import com.normation.errors._
import com.normation.errors.Inconsistency
import com.normation.errors.IOResult
import com.normation.utils.DateFormaterService
import org.joda.time.DateTime
Expand All @@ -49,59 +48,43 @@ import zio.json.EncoderOps
import zio.json.JsonDecoder
import zio.json.JsonEncoder
import zio.json.ast.Json
import zio.syntax._

trait JSONTranslateCampaign {

def getRawJson(): PartialFunction[Campaign, IOResult[Json]]
def getRawJson(campaign: Campaign): IOResult[Json]

// serialize the campaign based on its campaignType
def handle(pretty: Boolean) = getRawJson().andThen(r => r.map(json => if (pretty) json.toJsonPretty else json.toJson))
def handle(pretty: Boolean, campaign: Campaign) =
getRawJson(campaign).map(json => if (pretty) json.toJsonPretty else json.toJson)

def read(): PartialFunction[(String, CampaignParsingInfo), IOResult[Campaign]]
def read(json: String, parsingInfo: CampaignParsingInfo): IOResult[Campaign]

def campaignType(): PartialFunction[String, CampaignType]
}

class CampaignSerializer {

private[this] var tranlaters: List[JSONTranslateCampaign] = Nil
private[this] var tranlaters: Map[String, JSONTranslateCampaign] = Map.empty
import CampaignSerializer._

def getJson(campaign: Campaign) = {
tranlaters.map(_.getRawJson()).fold(Jsonbase) { case (a, b) => b orElse a }(campaign).flatMap { json =>
CampaignParsingInfo(campaign.campaignType, campaign.version).toJsonAST.toIO.map(json.merge)

}
}

val Jsonbase: PartialFunction[Campaign, IOResult[Json]] = {
case c: Campaign => Inconsistency(s"No translater for campaign ${c.info.id.value}").fail
getTranslater(campaign.campaignType)
.flatMap(_.getRawJson(campaign))
.flatMap(json => CampaignParsingInfo(campaign.campaignType, campaign.version).toJsonAST.toIO.map(json.merge))
}

val base: PartialFunction[Campaign, IOResult[String]] = {
case c: Campaign => Inconsistency(s"No translater for campaign ${c.info.id.value}").fail
}

val readBase: PartialFunction[(String, CampaignParsingInfo), IOResult[Campaign]] = {
case (_, v) => Inconsistency(s"could not translate into campaign of type ${v.campaignType.value} version ${v.version}").fail
}

val campaignTypeBase: PartialFunction[String, CampaignType] = { case c: String => CampaignType(c) }

def addJsonTranslater(c: JSONTranslateCampaign) = tranlaters = c :: tranlaters
def addJsonTranslater(campaignType: String, c: JSONTranslateCampaign) = tranlaters = tranlaters + ((campaignType, c))

def serialize(campaign: Campaign): IOResult[String] = getJson(campaign).map(_.toJsonPretty)
def parse(string: String): IOResult[Campaign] = {
def getTranslater(campaignType: String): IOResult[JSONTranslateCampaign] =
tranlaters.get(campaignType).notOptional(s"No Json translator found for campaign type ${campaignType}")
def serialize(campaign: Campaign): IOResult[String] = getJson(campaign).map(_.toJsonPretty)
def parse(string: String): IOResult[Campaign] = {
for {
baseInfo <- string.fromJson[CampaignParsingInfo].toIO
res <- tranlaters.map(_.read()).fold(readBase) { case (a, b) => b orElse a }((string, baseInfo))
baseInfo <- string.fromJson[CampaignParsingInfo].toIO
translater <- getTranslater(baseInfo.campaignType)
res <- translater.read(string, baseInfo)
} yield {
res
}
}
def campaignType(string: String): CampaignType =
tranlaters.map(_.campaignType()).fold(campaignTypeBase) { case (a, b) => b orElse a }(string)

}

Expand Down Expand Up @@ -159,14 +142,12 @@ object CampaignSerializer {
implicit val statusInfoDecoder: JsonDecoder[CampaignStatus] = DeriveJsonDecoder.gen
implicit val dayTimeDecoder: JsonDecoder[DayTime] = DeriveJsonDecoder.gen
implicit val scheduleDecoder: JsonDecoder[CampaignSchedule] = DeriveJsonDecoder.gen
implicit val campaignTypeDecoder: JsonDecoder[CampaignType] = JsonDecoder[String].map(CampaignType)
implicit val campaignInfoDecoder: JsonDecoder[CampaignInfo] = DeriveJsonDecoder.gen

implicit val campaignEventIdDecoder: JsonDecoder[CampaignEventId] = JsonDecoder[String].map(CampaignEventId)
implicit val campaignEventStateDecoder: JsonDecoder[CampaignEventState] = DeriveJsonDecoder.gen
implicit val campaignEventDecoder: JsonDecoder[CampaignEvent] = DeriveJsonDecoder.gen

implicit val campaignTypeEncoder: JsonEncoder[CampaignType] = JsonEncoder[String].contramap(_.value)
implicit val campaignEventIdEncoder: JsonEncoder[CampaignEventId] = JsonEncoder[String].contramap(_.value)
implicit val campaignEventStateEncoder: JsonEncoder[CampaignEventState] = DeriveJsonEncoder.gen
implicit val campaignEventEncoder: JsonEncoder[CampaignEvent] = DeriveJsonEncoder.gen
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@
package com.normation.rudder.campaigns

import cats.implicits._
import com.normation.errors.Inconsistency
import com.normation.errors.IOResult
import com.normation.errors.RudderError
import com.normation.utils.DateFormaterService
import com.normation.utils.StringUuidGenerator
import com.normation.zio.ZioRuntime
Expand All @@ -53,7 +51,7 @@ import zio.duration._
import zio.syntax._

trait CampaignHandler {
def handle(mainCampaignService: MainCampaignService, event: CampaignEvent): PartialFunction[Campaign, IOResult[CampaignEvent]]
def handle(campaign: Campaign, mainCampaignService: MainCampaignService, event: CampaignEvent): IOResult[CampaignEvent]
}

object MainCampaignService {
Expand All @@ -67,12 +65,16 @@ object MainCampaignService {

class MainCampaignService(repo: CampaignEventRepository, campaignRepo: CampaignRepository, uuidGen: StringUuidGenerator) {

private[this] var services: List[CampaignHandler] = Nil
def registerService(s: CampaignHandler) = {
services = s :: services
private[this] var services: Map[String, CampaignHandler] = Map()
def registerService(campaignType: String, s: CampaignHandler) = {
services = services + ((campaignType, s))
init()
}

import com.normation.errors._
def getService(campaignType: String): IOResult[CampaignHandler] =
services.get(campaignType).notOptional(s"No campaign service found for campaign type ${campaignType}")

private[this] var inner: Option[CampaignScheduler] = None

def saveCampaign(c: Campaign) = {
Expand Down Expand Up @@ -100,34 +102,33 @@ class MainCampaignService(repo: CampaignEventRepository, campaignRepo: CampaignR
}
}

def handle(eventId: CampaignEventId) = {
@nowarn
def failingLog(eventId: CampaignEventId)(err: RudderError) = {
for {
_ <- CampaignLogger.error(
s"An error occurred while treating campaign event ${eventId.value}, error details : ${err.fullMsg} "
)
_ <- err.fail
} yield {
()
}
}

def base(event: CampaignEvent): PartialFunction[Campaign, IOResult[CampaignEvent]] = { case _ => event.succeed }
def wait(eventId: CampaignEventId) = {

val now = DateTime.now()

@nowarn
def failingLog(err: RudderError) = {
for {
_ <- CampaignLogger.error(
s"An error occurred while treating campaign event ${eventId.value}, error details : ${err.fullMsg} "
)
_ <- err.fail
} yield {
()
}
}
(for {

event <- repo.get(eventId)
campaign <- campaignRepo.get(event.campaignId)
_ <- CampaignLogger.debug(s"Got Campaign ${event.campaignId.value} for event ${event.id.value}")

_ <- CampaignLogger.debug(s"Start handling campaign event '${event.id.value}' state is ${event.state.value}")
wait <-
_ <- CampaignLogger.debug(s"Start handling campaign event '${event.id.value}' state is ${event.state.value}")
wait <-
event.state match {
case Finished | Skipped(_) =>
().succeed
handle(event)
case Scheduled =>
campaign.info.status match {
case Disabled(reason) =>
Expand All @@ -149,12 +150,13 @@ class MainCampaignService(repo: CampaignEventRepository, campaignRepo: CampaignR
.serialize(event.start)}"
)
_ <- ZIO.sleep(Duration.fromMillis(event.start.getMillis - now.getMillis))
_ <- handle(event)
} yield {
().succeed
}
} else {
CampaignLogger.debug(
s"${event.id.value} should be treated by ${event.campaignType.value} handler for Scheduled state"
s"${event.id.value} should be treated by ${event.campaignType} handler for Scheduled state"
)
}
}
Expand Down Expand Up @@ -183,38 +185,42 @@ class MainCampaignService(repo: CampaignEventRepository, campaignRepo: CampaignR
.serialize(event.end)}"
)
_ <- ZIO.sleep(Duration.fromMillis(event.end.getMillis - now.getMillis))
_ <- handle(event)
} yield {
()
}
} else {
CampaignLogger.debug(
s"${event.id.value} should be treated by ${event.campaignType.value} handler for Running state"
)
CampaignLogger.debug(s"${event.id.value} should be treated by ${event.campaignType} handler for Running state")
}
}
} yield {
event
}).provide(zclock).catchAll(failingLog(eventId))
}

// Get updated event and campaign, state of the event could have changed, campaign parameter also
def handle(event: CampaignEvent) = {
// Get updated event and campaign, state of the event could have changed, campaign parameter also
(for {
updatedEvent <- repo.get(event.id)
updatedCampaign <- campaignRepo.get(event.campaignId)
_ <- CampaignLogger.debug(s"Got Updated campaign and event for event ${event.id.value}")

handle =
services.map(_.handle(main, updatedEvent)).foldLeft(base(updatedEvent)) { case (base, handler) => handler orElse base }
newCampaign <- handle
.apply(updatedCampaign)
.catchAll(err => {
for {
_ <- CampaignLogger.error(err.fullMsg)
} yield {
event
newCampaign <- getService(updatedCampaign.campaignType).flatMap { handler =>
handler
.handle(updatedCampaign, main, updatedEvent)
.catchAll(err => {
for {
_ <- CampaignLogger.error(err.fullMsg)
} yield {
event
}
})
}
})
_ <-
_ <-
CampaignLogger.debug(
s"Campaign event ${newCampaign.id.value} update, previous state was ${event.state.value} new state${newCampaign.state.value}"
)
save <- repo.saveCampaignEvent(newCampaign)
post <-
save <- repo.saveCampaignEvent(newCampaign)
post <-
newCampaign.state match {
case Finished | Skipped(_) =>
for {
Expand All @@ -232,14 +238,13 @@ class MainCampaignService(repo: CampaignEventRepository, campaignRepo: CampaignR
}
} yield {
()
}).provide(zclock).catchAll(failingLog)

}).catchAll(failingLog(event.id))
}

def loop() = {
for {
c <- queue.take
_ <- handle(c).forkDaemon
_ <- wait(c).forkDaemon
} yield {
()
}
Expand Down
Loading