io.circe.generic.JsonCodec Scala Examples

The following examples show how to use io.circe.generic.JsonCodec. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example.
Example 1
Source File: entities.scala    From sonar-scala   with GNU Lesser General Public License v3.0 5 votes vote down vote up
package com.mwz.sonar.scala
package pr
package github

import com.mwz.sonar.scala.pr.github.Codec._
import io.circe.generic.JsonCodec
import io.circe.generic.extras.ConfiguredJsonCodec

@JsonCodec
final case class PullRequest(number: Int, head: Head)

@JsonCodec
final case class Head(sha: String)

@JsonCodec
final case class Comment(
  id: Int,
  path: String,
  position: Option[Int], // Line in the diff.
  user: User,
  body: String
)

@ConfiguredJsonCodec
final case class NewComment(
  body: String,
  commitId: String,
  path: String,
  position: Int
)

@JsonCodec
final case class User(login: String)

@JsonCodec
final case class File(
  filename: String,
  status: String,
  patch: String
)

@ConfiguredJsonCodec
final case class Status(
  state: String,
  targetUrl: String,
  description: String,
  context: String
)

@ConfiguredJsonCodec
final case class NewStatus(
  state: String,
  targetUrl: String,
  description: String,
  context: String
) 
Example 2
Source File: Metadata.scala    From sonar-scala   with GNU Lesser General Public License v3.0 5 votes vote down vote up
package com.mwz.sonar.scala

import java.nio.file.Paths
import java.nio.file.StandardOpenOption

import cats.data.NonEmptyChain
import cats.effect.Blocker
import cats.effect.ExitCode
import cats.effect.IO
import cats.effect.IOApp
import cats.instances.string._
import com.mwz.sonar.scala.metadata._
import com.mwz.sonar.scala.metadata.scalastyle.ScalastyleRules
import com.mwz.sonar.scala.metadata.scalastyle.ScalastyleRulesRepository
import com.mwz.sonar.scala.metadata.scapegoat.ScapegoatRules
import com.mwz.sonar.scala.metadata.scapegoat.ScapegoatRulesRepository
import fs2.Stream
import fs2.io.file._
import fs2.text
import io.circe.Printer
import io.circe.generic.JsonCodec
import io.circe.syntax._

@JsonCodec
final case class SonarScalaMetadata(
  rules: Rules,
  repositories: Map[String, RulesRepository]
)

@JsonCodec
final case class Rules(
  scalastyle: NonEmptyChain[Rule],
  scapegoat: NonEmptyChain[Rule]
)

object Metadata extends IOApp {
  private val metadata: SonarScalaMetadata =
    SonarScalaMetadata(
      rules = Rules(sort(ScalastyleRules.rules), sort(ScapegoatRules.rules)),
      repositories = Map(
        ScalastyleRulesRepository.RepositoryKey ->
        ScalastyleRulesRepository.rulesRepository
          .copy(rules = sort(ScalastyleRulesRepository.rulesRepository.rules)),
        ScapegoatRulesRepository.RepositoryKey ->
        ScapegoatRulesRepository.rulesRepository
          .copy(rules = sort(ScapegoatRulesRepository.rulesRepository.rules))
      )
    )
  private val printer: Printer =
    Printer.spaces2SortKeys.copy(
      colonLeft = "",
      lbraceLeft = "",
      rbraceRight = "",
      lbracketLeft = "",
      lrbracketsEmpty = "",
      rbracketRight = "",
      arrayCommaLeft = "",
      objectCommaLeft = ""
    )

  // Chain is missing sortBy, which should be added in 2.2.0.
  private def sort(rules: NonEmptyChain[Rule]): NonEmptyChain[Rule] =
    NonEmptyChain.fromNonEmptyList(rules.toNonEmptyList.sortBy(_.name))

  def run(args: List[String]): IO[ExitCode] = {
    val write: Stream[IO, Unit] = Stream.resource(Blocker[IO]).flatMap { blocker =>
      Stream[IO, String](metadata.asJson.printWith(printer))
        .through(text.utf8Encode)
        .through(
          writeAll(
            Paths.get("sonar-scala-metadata.json"),
            blocker,
            List(StandardOpenOption.TRUNCATE_EXISTING)
          )
        )
    }
    write.compile.drain.as(ExitCode.Success)
  }
} 
Example 3
Source File: model.scala    From sonar-scala   with GNU Lesser General Public License v3.0 5 votes vote down vote up
package com.mwz.sonar.scala.metadata

import cats.data.Chain
import cats.data.NonEmptyChain
import enumeratum._
import io.circe._
import io.circe.generic.JsonCodec
import io.circe.generic.encoding._
import org.sonar.api.server.rule.RuleParamType
import shapeless._
import shapeless.record._

@JsonCodec
final case class RulesRepository(
  key: String,
  name: String,
  rules: NonEmptyChain[Rule]
)

object Rule {
  implicit val ruleEncoder: Encoder[Rule] =
    ReprObjectEncoder.deriveReprAsObjectEncoder.contramap { rule =>
      LabelledGeneric[Rule]
        .to(rule)
        .-(Symbol("sonarMdDescription"))
        .renameField(Symbol("mdDescription"), Symbol("description"))
    }
}

@JsonCodec(decodeOnly = true)
final case class Rule(
  key: String,
  name: String,
  mdDescription: String,
  sonarMdDescription: String,
  severity: Severity,
  template: Boolean,
  params: Chain[Param]
)

@JsonCodec
final case class Param(
  name: String,
  typ: ParamType,
  description: String,
  default: String
)

sealed trait ParamType extends EnumEntry
object ParamType extends Enum[ParamType] with CirceEnum[ParamType] with CatsEnum[ParamType] {
  final case object String extends ParamType
  final case object Text extends ParamType
  final case object Boolean extends ParamType
  final case object Integer extends ParamType
  final case object Float extends ParamType
  val values = findValues

  implicit class ParamTypeSyntax(private val paramType: ParamType) extends AnyVal {
    def asSonarRuleParamType: RuleParamType =
      paramType match {
        case String  => RuleParamType.STRING
        case Text    => RuleParamType.TEXT
        case Boolean => RuleParamType.BOOLEAN
        case Integer => RuleParamType.INTEGER
        case Float   => RuleParamType.FLOAT
      }
  }
}

sealed trait Severity extends EnumEntry
object Severity extends Enum[Severity] with CirceEnum[Severity] with CatsEnum[Severity] {
  final case object Info extends Severity
  final case object Minor extends Severity
  final case object Major extends Severity
  final case object Critical extends Severity
  final case object Blocker extends Severity
  val values = findValues
} 
Example 4
Source File: Raw.scala    From geotrellis-pointcloud   with Apache License 2.0 5 votes vote down vote up
package geotrellis.pointcloud.raster.ept

import geotrellis.util.RangeReader
import geotrellis.vector.Extent

import cats.syntax.either._
import io.circe.generic.JsonCodec
import io.circe.parser.decode

import java.net.URI

@JsonCodec
case class Raw(
  bounds: Seq[Double],
  boundsConforming: Seq[Double],
  dataType: String,
  hierarchyType: String,
  points: Long,
  schema: Seq[Field],
  span: Int,
  srs: SRS,
  version: String
) {
  def extent: Extent = {
    val Seq(xmin, ymin, _, xmax, ymax, _) = bounds
    Extent(xmin, ymin, xmax, ymax)
  }
}

object Raw {
  def apply(eptSource: String): Raw = {
    val rr = RangeReader(new URI(eptSource).resolve("ept.json").toString)
    val jsonString = new String(rr.readAll)
    decode[Raw](jsonString).valueOr(throw _)
  }
} 
Example 5
Source File: SRS.scala    From geotrellis-pointcloud   with Apache License 2.0 5 votes vote down vote up
package geotrellis.pointcloud.raster.ept

import geotrellis.proj4.{CRS, LatLng}
import io.circe.generic.JsonCodec

import scala.util.Try

@JsonCodec
case class SRS(authority: Option[String], horizontal: Option[String], vertical: Option[String], wkt: Option[String]) {
  def toCRS(defaultCRS: CRS = LatLng): CRS = {
    val parsed: Option[CRS] = for { txt <- wkt; crs <- Try { CRS.fromWKT(txt) }.toOption.flatten } yield crs
    val fromCode = authority.filter(_.toLowerCase == "epsg").fold(defaultCRS) { _ =>
      horizontal.map(epsg => CRS.fromEpsgCode(epsg.toInt)).getOrElse(defaultCRS)
    }
    parsed.getOrElse(fromCode)
  }
} 
Example 6
Source File: VoxelKey.scala    From geotrellis-pointcloud   with Apache License 2.0 5 votes vote down vote up
package geotrellis.pointcloud.layer

import geotrellis.layer._
import geotrellis.util._

import io.circe.generic.JsonCodec

// --- //


      (k, sk) => VoxelKey(sk.col, sk.row, k.layer)
    )
  }

  implicit val depthComponent: Component[VoxelKey, DepthKey] = {
    Component[VoxelKey, DepthKey](
      k => DepthKey(k.layer),
      (k, dk) => VoxelKey(k.col, k.row, dk.depth)
    )
  }
} 
Example 7
Source File: package.scala    From scalanda-v20   with MIT License 5 votes vote down vote up
package com.msilb.scalandav20.model

import java.time.Instant

import com.msilb.scalandav20.model.primitives.{DecimalNumber, InstrumentName}
import io.circe.generic.JsonCodec
import io.circe.generic.extras.semiauto.{deriveEnumerationDecoder, deriveEnumerationEncoder}
import io.circe.syntax._
import io.circe.{Decoder, Encoder}
import io.circe.java8.time._

package object pricing {

  type PriceValue = String

  sealed trait PricingStreamItem

  @JsonCodec
  case class PricingHeartbeat(`type`: String, time: Instant) extends PricingStreamItem

  sealed trait PriceStatus

  object PriceStatus {

    case object tradeable extends PriceStatus

    case object `non-tradeable` extends PriceStatus

    case object invalid extends PriceStatus

    implicit val decodePriceStatus: Decoder[PriceStatus] = deriveEnumerationDecoder
    implicit val encodePriceStatus: Encoder[PriceStatus] = deriveEnumerationEncoder
  }

  @JsonCodec
  case class PriceBucket(price: PriceValue, liquidity: Int)

  @JsonCodec
  case class QuoteHomeConversionFactors(positiveUnits: DecimalNumber, negativeUnits: DecimalNumber)

  @JsonCodec
  case class UnitsAvailableDetails(long: DecimalNumber, short: DecimalNumber)

  @JsonCodec
  case class UnitsAvailable(default: UnitsAvailableDetails,
                            reduceFirst: UnitsAvailableDetails,
                            reduceOnly: UnitsAvailableDetails,
                            openOnly: UnitsAvailableDetails)

  @JsonCodec
  case class Price(`type`: String,
                   instrument: InstrumentName,
                   time: Instant,
                   status: PriceStatus,
                   bids: Seq[PriceBucket],
                   asks: Seq[PriceBucket],
                   closeoutBid: PriceValue,
                   closeoutAsk: PriceValue,
                   quoteHomeConversionFactors: Option[QuoteHomeConversionFactors],
                   unitsAvailable: Option[UnitsAvailable]) extends PricingStreamItem

  object PricingStreamItem {
    implicit val decodePricingStreamItem: Decoder[PricingStreamItem] = Decoder.instance { c =>
      c.downField("type").as[String].flatMap {
        case "HEARTBEAT" => c.as[PricingHeartbeat]
        case _ => c.as[Price]
      }
    }
    implicit val encodePricingStreamItem: Encoder[PricingStreamItem] = Encoder.instance {
      case t: PricingHeartbeat => t.asJson
      case t: Price => t.asJson
    }
  }

} 
Example 8
Source File: package.scala    From scalanda-v20   with MIT License 5 votes vote down vote up
package com.msilb.scalandav20.model

import com.msilb.scalandav20.model.pricing.PriceValue
import com.msilb.scalandav20.model.primitives.{AccountUnits, DecimalNumber, InstrumentName}
import com.msilb.scalandav20.model.trades.TradeID
import io.circe.generic.JsonCodec

package object positions {

  @JsonCodec
  case class PositionSide(units: DecimalNumber,
                          averagePrice: Option[PriceValue],
                          tradeIDs: Option[Seq[TradeID]],
                          pl: AccountUnits,
                          unrealizedPL: Option[AccountUnits],
                          resettablePL: AccountUnits)

  @JsonCodec
  case class Position(instrument: InstrumentName,
                      pl: AccountUnits,
                      unrealizedPL: Option[AccountUnits],
                      resettablePL: AccountUnits,
                      long: PositionSide,
                      short: PositionSide)

  @JsonCodec
  case class CalculatedPositionState(instrument: InstrumentName,
                                     netUnrealizedPL: AccountUnits,
                                     longUnrealizedPL: AccountUnits,
                                     shortUnrealizedPL: AccountUnits)

} 
Example 9
Source File: Request.scala    From scalanda-v20   with MIT License 5 votes vote down vote up
package com.msilb.scalandav20.client

import com.msilb.scalandav20.model.orders.OrderRequest
import com.msilb.scalandav20.model.trades.ClientExtensions
import com.msilb.scalandav20.model.transactions.{StopLossDetails, TakeProfitDetails, TrailingStopLossDetails}
import io.circe.generic.JsonCodec

sealed trait Request

object Request {

  @JsonCodec
  case class AccountConfigChangeRequest(alias: Option[String] = None,
                                        marginRate: Option[String] = None) extends Request

  @JsonCodec
  case class CreateOrderRequest(order: OrderRequest) extends Request

  @JsonCodec
  case class ReplaceOrderRequest(order: OrderRequest) extends Request

  @JsonCodec
  case class OrderClientExtensionsModifyRequest(clientExtensions: Option[ClientExtensions] = None,
                                                tradeClientExtensions: Option[ClientExtensions] = None) extends Request

  @JsonCodec
  case class CloseTradeRequest(units: String = "ALL") extends Request

  @JsonCodec
  case class TradeClientExtensionsModifyRequest(clientExtensions: ClientExtensions) extends Request

  @JsonCodec
  case class TradesDependentOrdersModifyRequest(takeProfit: Option[TakeProfitDetails] = None,
                                                stopLoss: Option[StopLossDetails] = None,
                                                trailingStopLoss: Option[TrailingStopLossDetails] = None) extends Request

  @JsonCodec
  case class ClosePositionRequest(longUnits: Option[String] = None,
                                  longClientExtensions: Option[ClientExtensions] = None,
                                  shortUnits: Option[String] = None,
                                  shortClientExtensions: Option[ClientExtensions] = None) extends Request

} 
Example 10
Source File: DocumentedApi.scala    From endpoints4s   with MIT License 5 votes vote down vote up
package sample.algebra

import endpoints4s.algebra.{BasicAuthentication, circe, Endpoints}
import io.circe.generic.JsonCodec

trait DocumentedApi extends Endpoints with BasicAuthentication with circe.JsonEntitiesFromCodecs {

  val items =
    endpoint(
      get(
        path / "items" / segment[String]("category") /? qs[Option[Int]]("page")
      ),
      ok(
        jsonResponse[List[Item]],
        Some("List all the items of the given category")
      )
    )

  val itemId = segment[String]("id")

  val item =
    endpoint(
      get(path / "item" / itemId),
      wheneverFound(
        ok(jsonResponse[Item], Some("The item identified by 'id'")),
        Some("Item not found")
      )
    )

  val admin =
    authenticatedEndpoint(
      Get,
      path / "admin",
      requestEntity = emptyRequest,
      response = ok(emptyResponse, Some("Administration page")),
      endpointDocs = EndpointDocs().withSummary(Some("Authentication endpoint"))
    )

}

@JsonCodec
case class Item(name: String) 
Example 11
Source File: ApiAlg.scala    From endpoints4s   with MIT License 5 votes vote down vote up
package sample

import endpoints4s.algebra.BasicAuthentication.Credentials
import endpoints4s.algebra._
import io.circe.generic.JsonCodec

trait ApiAlg extends Endpoints with circe.JsonEntitiesFromCodecs with BasicAuthentication {

  val index: Endpoint[(String, Int, String), User] =
    endpoint(
      get(
        path / "user" / segment[String]() /? (qs[Int]("age") & qs[String](
          "toto"
        ))
      ),
      ok(jsonResponse[User])
    )

  val action =
    endpoint(
      post(path / "action", jsonRequest[ActionParameter]),
      ok(jsonResponse[ActionResult])
    )

  val actionFut: Endpoint[ActionParameter, ActionResult] =
    endpoint(
      post(path / "actionFut", jsonRequest[ActionParameter]),
      ok(jsonResponse[ActionResult])
    )

  val maybe =
    endpoint(get(path / "option"), wheneverFound(ok(emptyResponse)))

  val auth: Endpoint[Credentials, Option[Unit]] =
    authenticatedEndpoint(Get, path / "auth", response = ok(emptyResponse))

}

@JsonCodec
case class User(name: String, age: Int)

@JsonCodec
case class ActionParameter()

@JsonCodec
case class ActionResult(s: String) 
Example 12
Source File: HealthCheckRoutes.scala    From healthchecks   with MIT License 5 votes vote down vote up

package com.github.everpeace.healthchecks.route

import akka.http.scaladsl.model.StatusCodes._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.directives.PathDirectives
import akka.http.scaladsl.server.{PathMatchers, Route}
import cats.data.Validated.{Invalid, Valid}
import com.github.everpeace.healthchecks.{HealthCheck, HealthCheckResult}
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport._
import io.circe.JsonObject
import io.circe.generic.JsonCodec
import io.circe.generic.auto._

import scala.collection.convert.DecorateAsScala
import scala.concurrent.{ExecutionContext, Future}

object HealthCheckRoutes extends DecorateAsScala {

  @JsonCodec case class HealthCheckResultJson(
      name: String,
      severity: String,
      status: String,
      messages: List[String])

  @JsonCodec case class ResponseJson(status: String, check_results: List[HealthCheckResultJson])

  private def status(s: Boolean) = if (s) "healthy" else "unhealthy"

  private def statusCode(s: Boolean) = if (s) OK else ServiceUnavailable

  private def toResultJson(check: HealthCheck, result: HealthCheckResult) =
    HealthCheckResultJson(
      check.name,
      check.severity.toString,
      status(result.isValid),
      result match {
        case Valid(_)        => List()
        case Invalid(errors) => errors.toList
      }
    )

  def health(
      checks: HealthCheck*
    )(implicit
      ec: ExecutionContext
    ): Route = health("health", checks.toList)

  def health(
      path: String,
      checks: List[HealthCheck]
    )(implicit
      ec: ExecutionContext
    ): Route = {
    require(checks.nonEmpty, "checks must not empty.")
    require(
      checks.map(_.name).toSet.size == checks.length,
      s"HealthCheck name should be unique (given HealthCheck names = [${checks.map(_.name).mkString(",")}])."
    )
    val rootSlashRemoved =
      if (path.startsWith("/")) path.substring(1) else path
    PathDirectives.path(PathMatchers.separateOnSlashes(rootSlashRemoved)) {
      parameter("full" ? false) { full =>
        get {
          def isHealthy(checkAndResults: List[(HealthCheck, HealthCheckResult)]) =
            checkAndResults.forall(cr => cr._2.isValid || (!cr._1.severity.isFatal))
          val checkAndResultsFuture = Future.traverse(checks) { c =>
            c.run().map(c -> _)
          }
          if (full) {
            complete {
              checkAndResultsFuture.map { checkAndResults =>
                val healthy = isHealthy(checkAndResults)
                statusCode(healthy) -> ResponseJson(
                  status(healthy),
                  checkAndResults.map {
                    case (check, result) => toResultJson(check, result)
                  }
                )
              }
            }
          } else {
            complete {
              checkAndResultsFuture.map { checkAndResults =>
                statusCode(isHealthy(checkAndResults)) -> JsonObject.empty
              }
            }
          }
        }
      }
    }
  }
} 
Example 13
Source File: TileInfo.scala    From franklin   with Apache License 2.0 5 votes vote down vote up
package com.azavea.franklin.datamodel

import cats.implicits._
import com.azavea.stac4s._
import io.circe.generic.JsonCodec

import java.net.URLEncoder
import java.nio.charset.StandardCharsets

@JsonCodec
case class TileInfo(
    extent: StacExtent,
    title: Option[String],
    description: Option[String],
    tileMatrixSetLinks: List[TileMatrixSetLink],
    links: List[TileSetLink]
)

object TileInfo {

  val webMercatorQuadLink = TileMatrixSetLink(
    "WebMercatorQuad",
    "http://schemas.opengis.net/tms/1.0/json/examples/WebMercatorQuad.json"
  )

  def fromStacItem(host: String, collectionId: String, item: StacItem): Option[TileInfo] = {
    val spatialExtent = SpatialExtent(List(item.bbox))
    val stacExtent    = StacExtent(spatialExtent, Interval(List.empty))

    val cogTileLinks = item.assets.collect {
      case (key, asset) if asset._type === Some(`image/cog`) =>
        val encodedItemId = URLEncoder.encode(item.id, StandardCharsets.UTF_8.toString)
        val encodedKey    = URLEncoder.encode(key, StandardCharsets.UTF_8.toString)
        val href =
          s"$host/tiles/collections/$collectionId/items/$encodedItemId/{tileMatrixSetId}/{tileMatrix}/{tileCol}/{tileRow}/?asset=$encodedKey"
        val mediaType = Some(`image/png`)
        TileSetLink(href, StacLinkType.Item, mediaType, None, Some(true))
    }

    cogTileLinks.isEmpty match {
      case false =>
        Some(TileInfo(stacExtent, None, None, List(webMercatorQuadLink), cogTileLinks.toList))
      case _ => None
    }
  }

  def fromStacCollection(host: String, collection: StacCollection): TileInfo = {
    val mvtHref =
      s"$host/tiles/collections/${collection.id}/footprint/{tileMatrixSetId}/{tileMatrix}/{tileCol}/{tileRow}"
    val tileEndpointLink = TileSetLink(
      mvtHref,
      StacLinkType.VendorLinkType("tiles"),
      Some(VendorMediaType("application/vnd.mapbox-vector-tile")),
      Some(s"${collection.id} -- Footprints"),
      Some(true)
    )

    val tileJsonHref =
      s"$host/tiles/collections/${collection.id}/footprint/tile-json"

    val tileJsonLink = TileSetLink(
      tileJsonHref,
      StacLinkType.VendorLinkType("tile-json"),
      Some(`application/json`),
      Some(s"${collection.id} -- Footprints TileJSON"),
      Some(false)
    )

    TileInfo(
      collection.extent,
      collection.title map { title => s"$title - MVT" },
      Some("Mapbox Vector Tile representation of item footprints for this collection"),
      List(webMercatorQuadLink),
      List(
        tileEndpointLink,
        tileJsonLink
      )
    )
  }
} 
Example 14
Source File: RestResource.scala    From pizza-auth-3   with MIT License 5 votes vote down vote up
package moe.pizza.auth.webapp.rest

import moe.pizza.auth.config.ConfigFile.ConfigFile
import moe.pizza.auth.graphdb.EveMapDb
import moe.pizza.auth.interfaces.{PilotGrader, UserDatabase, BroadcastService}
import BroadcastService._
import moe.pizza.auth.tasks.Update
import moe.pizza.crestapi.CrestApi
import org.http4s.{HttpService, _}
import org.http4s.dsl.{Root, _}
import org.http4s.server._
import org.http4s.server.staticcontent.ResourceService
import org.http4s.server.syntax.ServiceOps
import org.joda.time.DateTime
import play.twirl.api.Html
import moe.pizza.eveapi._
import scala.concurrent.ExecutionContext.Implicits.global
import org.http4s.twirl._
import scala.concurrent.Future
import scala.util.Try
import scalaz._
import Scalaz._
import scala.util.{Success => TSuccess}
import scala.util.{Failure => TFailure}
import scala.concurrent.duration._
import scala.concurrent.Await
import moe.pizza.crestapi.character.location.Types.Location
import org.slf4j.LoggerFactory
import io.circe.generic.auto._
import io.circe.syntax._
import org.http4s.circe._
import io.circe.Json
import io.circe.generic.JsonCodec

class RestResource(fullconfig: ConfigFile,
                   graders: PilotGrader,
                   portnumber: Int = 9021,
                   ud: UserDatabase,
                   crestapi: Option[CrestApi] = None,
                   eve: Option[EVEAPI] = None,
                   mapper: Option[EveMapDb] = None,
                   updater: Option[Update] = None,
                   broadcasters: List[BroadcastService] =
                     List.empty[BroadcastService]) {

  case class ApiError(`type`: String, message: String)

  case class PingRequest(message: String, from: String, to: String)
  case class PingResponse(total: Int)

  def resource = HttpService {

    case req @ GET -> Root / "api" / "v1" / "ping" / "group" / group => {
      req.decode[Json] { p =>
        p.as[PingRequest] match {
          case Left(failure) =>
            BadRequest(ApiError(
              "bad_post_body",
              "Unable to process your post body, please format it correctly").asJson)
          case Right(pingreq) =>
            val users = ud.getUsers(s"authgroup=${group}")
            val templatedMessage = templates.txt.broadcast(pingreq.message,
                                                           pingreq.to,
                                                           pingreq.from,
                                                           DateTime.now())
            val sendreqs =
              ud.sendGroupAnnouncement(broadcasters,
                                       templatedMessage.toString(),
                                       pingreq.from,
                                       users)
            val r = Await.result(Future.sequence(sendreqs), 2 seconds).sum
            Ok(PingResponse(r).asJson)
          }
      }
    }

  }

}