Skip to content

Commit

Permalink
Fixes #21383: Add logic to handle campaign within Rudder
Browse files Browse the repository at this point in the history
  • Loading branch information
VinceMacBuche committed Jul 11, 2022
1 parent 53f0b88 commit d855f09
Show file tree
Hide file tree
Showing 28 changed files with 1,282 additions and 160 deletions.
Expand Up @@ -395,3 +395,12 @@ ALTER TABLE statusupdate set (autovacuum_vacuum_threshold = 0);
* end
*************************************************************************************
*/


CREATE TABLE CampaignEvent (
campaignId text
, eventid text PRIMARY KEY
, state text
, startDate timestamp with time zone NOT NULL
, endDate timestamp with time zone NOT NULL
);
Expand Up @@ -418,26 +418,19 @@ trait RudderJsonDecoders {

}

/*
* This last class provides utility methods to get JsonQuery objects from the request.
* We want to get ride of RestExtractorService but for now, we keep it for the parameter parts.
*/
class ZioJsonExtractor(queryParser: CmdbQueryParser with JsonQueryLexer) {
import JsonResponseObjects._
import JsonQueryObjects._
import implicits._
object ZioJsonExtractor {

/**
* Parse request body as JSON, and decode it as type `A`.
* This is the root method to transform a JSON query into a Rest object.
*/
def parseJson[A](req: Req)(implicit decoder: JsonDecoder[A]): PureResult[A] = {
if(req.json_?) {
// copied from `Req.forcedBodyAsJson`
def r = """; *charset=(.*)""".r
def r2 = """[^=]*$""".r
// copied from `Req.forcedBodyAsJson`
def r = """; *charset=(.*)""".r
def r2 = """[^=]*$""".r
def charset: String = req.contentType.flatMap(ct => r.findFirstIn(ct).flatMap(r2.findFirstIn)).getOrElse("UTF-8")
// end copy
// end copy

req.body match {
case eb: EmptyBox => Left(Unexpected((eb ?~! "error when accessing request body").messageChain))
Expand All @@ -447,6 +440,17 @@ class ZioJsonExtractor(queryParser: CmdbQueryParser with JsonQueryLexer) {
Left(Unexpected("Cannot parse non-JSON request as JSON; please check content-type."))
}
}
}

/*
* This last class provides utility methods to get JsonQuery objects from the request.
* We want to get ride of RestExtractorService but for now, we keep it for the parameter parts.
*/
class ZioJsonExtractor(queryParser: CmdbQueryParser with JsonQueryLexer) {
import JsonResponseObjects._
import JsonQueryObjects._
import implicits._
import ZioJsonExtractor.parseJson

/**
* Utilities to extract values from params Map
Expand Down
Expand Up @@ -141,8 +141,8 @@ class AutomaticReportLogger(
val highest = reportsRepository.getHighestId()
logger.trace(s"***** highest report id = ${highest} and last processed id = ${lastId}")
highest match {
case Full(currentId) if (currentId > lastId) =>
logReportsBetween(lastId, currentId)
case Full(currentId) if (currentId > lastId.getOrElse(0L)) =>
logReportsBetween(lastId.getOrElse(0L), currentId)

case _ =>
logger.trace("***** no reports to log")
Expand Down Expand Up @@ -182,7 +182,7 @@ class AutomaticReportLogger(
, allNodes: Map[NodeId, NodeInfo], rules: Map[RuleId, Rule], directives: FullActiveTechniqueCategory
): Box[Long] = {
for {
reports <- reportsRepository.getReportsByKindBeetween(fromId, maxId, batchSize, reportsKind)
reports <- reportsRepository.getReportsByKindBetween(fromId, Some(maxId), batchSize, reportsKind)
} yield {
//when we get an empty here, it means that we don't have more non-compliant report
//in the interval, just return the max id
Expand Down
@@ -0,0 +1,114 @@
/*
*************************************************************************************
* Copyright 2022 Normation SAS
*************************************************************************************
*
* This file is part of Rudder.
*
* Rudder is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In accordance with the terms of section 7 (7. Additional Terms.) of
* the GNU General Public License version 3, the copyright holders add
* the following Additional permissions:
* Notwithstanding to the terms of section 5 (5. Conveying Modified Source
* Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
* Public License version 3, when you create a Related Module, this
* Related Module is not considered as a part of the work and may be
* distributed under the license agreement of your choice.
* A "Related Module" means a set of sources files including their
* documentation that, without modification of the Source Code, enables
* supplementary functions or services in addition to those offered by
* the Software.
*
* Rudder is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Rudder. If not, see <http://www.gnu.org/licenses/>.
*
*************************************************************************************
*/

package com.normation.rudder.campaigns

import com.normation.errors.IOResult
import com.normation.rudder.campaigns.CampaignEventState.Scheduled
import com.normation.rudder.db.Doobie
import doobie.Read
import doobie.Write
import doobie.implicits._
import doobie.implicits.javasql._
import doobie.implicits.toSqlInterpolator
import org.joda.time.DateTime
import zio.interop.catz._

import java.sql.Timestamp


trait CampaignEventRepository {
def getAllActiveCampaignEvents() : IOResult[List[CampaignEvent]]
def get(campaignEventId: CampaignEventId) : IOResult[CampaignEvent]
def getEventsForCampaign(campaignId: CampaignId, state: Option[CampaignEventState]) : IOResult[List[CampaignEvent]]
def saveCampaignEvent(c : CampaignEvent) : IOResult[CampaignEvent]
}

class CampaignEventRepositoryImpl(doobie: Doobie) extends CampaignEventRepository {

import doobie._


implicit val stateWrite : Write[CampaignEventState] = Write[String].contramap(_.value)

implicit val eventWrite: Write[CampaignEvent] =
Write[(String,String,CampaignEventState,Timestamp,Timestamp)].contramap{
case event => (event.id.value,event.campaignId.value, event.state , new java.sql.Timestamp(event.start.getMillis), new java.sql.Timestamp(event.end.getMillis))
}

implicit val eventRead : Read[CampaignEvent] =
Read[(String,String,String,Timestamp,Timestamp)].map {
d : (String,String,String,Timestamp,Timestamp) =>
CampaignEvent(
CampaignEventId(d._1)
, CampaignId(d._2)
, CampaignEventState.parse(d._3).getOrElse(Scheduled)
, new DateTime(d._4.getTime())
, new DateTime(d._5.getTime())
)
}

def getAllActiveCampaignEvents(): IOResult[List[CampaignEvent]] = {
val q = sql"select eventId, campaignId, state, startDate, endDate from CampaignEvent where state != 'finished' and state != 'skipped'"
transactIOResult(s"error when getting active campaign events")(xa => q.query[CampaignEvent].to[List].transact(xa))
}


def get(id : CampaignEventId): IOResult[CampaignEvent] = {
val q = sql"select eventId, campaignId, state, startDate, endDate from CampaignEvent where eventId = '${id.value}'"
transactIOResult(s"error when getting campaign event with id ${id.value}")(xa => q.query[CampaignEvent].unique.transact(xa))
}



def saveCampaignEvent(c: CampaignEvent): IOResult[CampaignEvent] = {
import doobie._
val query =
sql"""insert into campaignEvent (eventId, campaignId, state, startDate, endDate) values (${c})
| ON CONFLICT (eventId) DO UPDATE
| SET state = ${c.state}; """.stripMargin

transactIOResult(s"error when inserting event with id ${c.campaignId.value}")(xa => query.update.run.transact(xa)).map(_ => c)
}

def getEventsForCampaign(campaignId: CampaignId, state: Option[CampaignEventState]): IOResult[List[CampaignEvent]] = {
val q = sql"select eventId, campaignId, state, startDate, endDate from CampaignEvent where campaignId = ${campaignId.value}"
transactIOResult(s"error when getting campaign events for campaign ${campaignId.value}")(xa => q.query.to[List].transact(xa))
}

}

@@ -0,0 +1,93 @@
/*
*************************************************************************************
* Copyright 2022 Normation SAS
*************************************************************************************
*
* This file is part of Rudder.
*
* Rudder is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In accordance with the terms of section 7 (7. Additional Terms.) of
* the GNU General Public License version 3, the copyright holders add
* the following Additional permissions:
* Notwithstanding to the terms of section 5 (5. Conveying Modified Source
* Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
* Public License version 3, when you create a Related Module, this
* Related Module is not considered as a part of the work and may be
* distributed under the license agreement of your choice.
* A "Related Module" means a set of sources files including their
* documentation that, without modification of the Source Code, enables
* supplementary functions or services in addition to those offered by
* the Software.
*
* Rudder is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Rudder. If not, see <http://www.gnu.org/licenses/>.
*
*************************************************************************************
*/

package com.normation.rudder.campaigns

import better.files.File
import com.normation.errors.IOResult
import zio.ZIO


trait CampaignRepository {
def getAll(): IOResult[List[Campaign]]
def get(id : CampaignId) : IOResult[Campaign]
def save(c : Campaign): IOResult[Campaign]
}


class CampaignRepositoryImpl(campaignSerializer: CampaignSerializer, path : File) extends CampaignRepository {

def getAll(): IOResult[List[Campaign]] = {
for {
jsonFiles <- IOResult.effect{path.collectChildren(_.extension.exists(_ =="json"))}
campaigns <- ZIO.foreach(jsonFiles.toList) {
json => campaignSerializer.parse(json.contentAsString)
}
} yield {
campaigns
}
}
def get(id : CampaignId) : IOResult[Campaign] = {
for {
content <- IOResult.effect (s"error when getting campaign file for campain with id '${id.value}'"){
val file = path / (s"${id.value}.json")
file.createFileIfNotExists(createParents = true)
file
}
campaign <- campaignSerializer.parse(content.contentAsString)
} yield {
campaign
}
}
def save(c : Campaign): IOResult[Campaign] = {
for {
file <- IOResult.effect (s"error when creating campaign file for campain with id '${c.info.id.value}'"){
val file = path / (s"${c.info.id.value}.json")

file.createFileIfNotExists(true)
file
}
_ <- CampaignLogger.info(file.pathAsString)
content <- campaignSerializer.serialize(c)
_ <- IOResult.effect{
file.write(content)
}
} yield {
c
}
}
}

0 comments on commit d855f09

Please sign in to comment.