cats.data.OptionT Scala Examples
The following examples show how to use cats.data.OptionT.
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: NatchezHttp4sModule.scala From skunk with MIT License | 8 votes |
// Copyright (c) 2018-2020 by Rob Norris // This software is licensed under the MIT License (MIT). // For more information see LICENSE or https://opensource.org/licenses/MIT package natchez.http4s import cats.~> import cats.data.{ Kleisli, OptionT } import cats.effect.Bracket import cats.implicits._ import natchez.{ EntryPoint, Kernel, Span } import org.http4s.HttpRoutes import natchez.Trace import natchez.Tags import scala.util.control.NonFatal import org.http4s.Response import cats.effect.Resource import cats.Defer import natchez.TraceValue import cats.Monad object implicits { // Given an entry point and HTTP Routes in Kleisli[F, Span[F], ?] return routes in F. A new span // is created with the URI path as the name, either as a continuation of the incoming trace, if // any, or as a new root. This can likely be simplified, I just did what the types were saying // and it works so :shrug: private def liftT[F[_]: Bracket[?[_], Throwable]]( entryPoint: EntryPoint[F])( routes: HttpRoutes[Kleisli[F, Span[F], ?]] ): HttpRoutes[F] = Kleisli { req => type G[A] = Kleisli[F, Span[F], A] val lift = λ[F ~> G](fa => Kleisli(_ => fa)) val kernel = Kernel(req.headers.toList.map(h => (h.name.value -> h.value)).toMap) val spanR = entryPoint.continueOrElseRoot(req.uri.path, kernel) OptionT { spanR.use { span => val lower = λ[G ~> F](_(span)) routes.run(req.mapK(lift)).mapK(lower).map(_.mapK(lower)).value } } } implicit class EntryPointOps[F[_]](self: EntryPoint[F]) { private def dummySpan( implicit ev: Monad[F] ): Span[F] = new Span[F] { val kernel: F[Kernel] = Kernel(Map.empty).pure[F] def put(fields: (String, TraceValue)*): F[Unit] = Monad[F].unit def span(name: String): Resource[F, Span[F]] = Monad[Resource[F, ?]].pure(this) } def liftT(routes: HttpRoutes[Kleisli[F, Span[F], ?]])( implicit ev: Bracket[F, Throwable] ): HttpRoutes[F] = implicits.liftT(self)(routes) def natchezMiddleware[F[_]: Bracket[?[_], Throwable]: Trace](routes: HttpRoutes[F]): HttpRoutes[F] = Kleisli { req => val addRequestFields: F[Unit] = Trace[F].put( Tags.http.method(req.method.name), Tags.http.url(req.uri.renderString) ) def addResponseFields(res: Response[F]): F[Unit] = Trace[F].put( Tags.http.status_code(res.status.code.toString) ) def addErrorFields(e: Throwable): F[Unit] = Trace[F].put( Tags.error(true), "error.message" -> e.getMessage, "error.stacktrace" -> e.getStackTrace.mkString("\n"), ) OptionT { routes(req).onError { case NonFatal(e) => OptionT.liftF(addRequestFields *> addErrorFields(e)) } .value.flatMap { case Some(handler) => addRequestFields *> addResponseFields(handler).as(handler.some) case None => Option.empty[Response[F]].pure[F] } } } }
Example 2
package tofu.concurrent.syntax import cats.data.OptionT import cats.effect.Resource import cats.effect.concurrent.Ref import cats.{Functor, Monad} import tofu.BracketThrow import tofu.concurrent.impl.FocusedRef import tofu.optics.{Contains, PProperty} import tofu.syntax.monadic._ object ref { implicit final class TofuRefOps[F[_], A](private val self: Ref[F, A]) extends AnyVal { def focused[B](focus: A Contains B)(implicit F: Functor[F]): Ref[F, B] = FocusedRef(self, focus) def optimisticModifyRes[B, X, R](prop: PProperty[A, A, R, X])(init: => Resource[F, X])(f: X => R)(implicit F: BracketThrow[F] ): F[R] = OptionT(self.get.map(prop.downcast)).getOrElseF( init.use(x => self.modify(a => prop.downcast(a).fold((prop.set(a, x), f(x)))((a, _)))) ) } }
Example 3
Source File: CfpCtrl.scala From gospeak with Apache License 2.0 | 5 votes |
package gospeak.web.api.published import cats.data.OptionT import com.mohiva.play.silhouette.api.Silhouette import gospeak.core.domain.{Cfp, CommonCfp, ExternalCfp} import gospeak.core.services.storage.{PublicExternalCfpRepo, PublicGroupRepo} import gospeak.web.AppConf import gospeak.web.api.domain.ApiCfp import gospeak.web.api.domain.utils.ApiResult import gospeak.web.auth.domain.CookieEnv import gospeak.web.utils.ApiCtrl import gospeak.libs.scala.domain.Page import play.api.mvc.{Action, AnyContent, ControllerComponents} class CfpCtrl(cc: ControllerComponents, silhouette: Silhouette[CookieEnv], conf: AppConf, groupRepo: PublicGroupRepo, externalCfpRepo: PublicExternalCfpRepo) extends ApiCtrl(cc, silhouette, conf) { def list(params: Page.Params): Action[AnyContent] = UserAwareAction[Seq[ApiCfp.Published]] { implicit req => for { cfps <- externalCfpRepo.listIncoming(params) groups <- groupRepo.list(cfps.items.flatMap(_.internal.map(_.group.id))) } yield ApiResult.of(cfps, (cfp: CommonCfp) => ApiCfp.published(cfp, groups)) } def detail(cfp: Cfp.Slug): Action[AnyContent] = UserAwareAction[ApiCfp.Published] { implicit req => (for { cfpElt <- OptionT(externalCfpRepo.findCommon(cfp)) groups <- OptionT.liftF(groupRepo.list(cfpElt.internal.map(_.group.id).toList)) res = ApiResult.of(ApiCfp.published(cfpElt, groups)) } yield res).value.map(_.getOrElse(cfpNotFound(cfp))) } def detailExt(cfp: ExternalCfp.Id): Action[AnyContent] = UserAwareAction[ApiCfp.Published] { implicit req => (for { cfpElt <- OptionT(externalCfpRepo.findCommon(cfp)) groups <- OptionT.liftF(groupRepo.list(cfpElt.internal.map(_.group.id).toList)) res = ApiResult.of(ApiCfp.published(cfpElt, groups)) } yield res).value.map(_.getOrElse(cfpNotFound(cfp))) } }
Example 4
Source File: SpeakerCtrl.scala From gospeak with Apache License 2.0 | 5 votes |
package gospeak.web.pages.orga.speakers import cats.data.OptionT import com.mohiva.play.silhouette.api.Silhouette import gospeak.core.domain.{Group, User} import gospeak.core.services.storage.{OrgaEventRepo, OrgaGroupRepo, OrgaProposalRepo, OrgaUserRepo} import gospeak.web.AppConf import gospeak.web.auth.domain.CookieEnv import gospeak.web.domain.Breadcrumb import gospeak.web.pages.orga.GroupCtrl import gospeak.web.pages.orga.speakers.SpeakerCtrl._ import gospeak.web.utils.{OrgaReq, UICtrl} import gospeak.libs.scala.domain.Page import play.api.mvc.{Action, AnyContent, ControllerComponents} class SpeakerCtrl(cc: ControllerComponents, silhouette: Silhouette[CookieEnv], conf: AppConf, userRepo: OrgaUserRepo, val groupRepo: OrgaGroupRepo, eventRepo: OrgaEventRepo, proposalRepo: OrgaProposalRepo) extends UICtrl(cc, silhouette, conf) with UICtrl.OrgaAction { def list(group: Group.Slug, params: Page.Params): Action[AnyContent] = OrgaAction(group) { implicit req => userRepo.speakers(params).map(speakers => Ok(html.list(speakers)(listBreadcrumb))) } def detail(group: Group.Slug, speaker: User.Slug, params: Page.Params): Action[AnyContent] = OrgaAction(group) { implicit req => (for { speakerElt <- OptionT(userRepo.find(speaker)) proposals <- OptionT.liftF(proposalRepo.listFull(speakerElt.id, params)) speakers <- OptionT.liftF(userRepo.list(proposals.items.flatMap(_.users))) userRatings <- OptionT.liftF(proposalRepo.listRatings(proposals.items.map(_.id))) res = Ok(html.detail(speakerElt, proposals, speakers, userRatings)(breadcrumb(speakerElt))) } yield res).value.map(_.getOrElse(speakerNotFound(group, speaker))) } } object SpeakerCtrl { def listBreadcrumb(implicit req: OrgaReq[AnyContent]): Breadcrumb = GroupCtrl.breadcrumb.add("Speakers" -> routes.SpeakerCtrl.list(req.group.slug)) def breadcrumb(speaker: User)(implicit req: OrgaReq[AnyContent]): Breadcrumb = listBreadcrumb.add(speaker.name.value -> routes.SpeakerCtrl.detail(req.group.slug, speaker.slug)) }
Example 5
Source File: CfpCtrl.scala From gospeak with Apache License 2.0 | 5 votes |
package gospeak.web.pages.orga.cfps import cats.data.OptionT import cats.effect.IO import com.mohiva.play.silhouette.api.Silhouette import gospeak.core.domain.utils.OrgaCtx import gospeak.core.domain.{Cfp, Event, Group} import gospeak.core.services.storage._ import gospeak.web.AppConf import gospeak.web.auth.domain.CookieEnv import gospeak.web.domain.Breadcrumb import gospeak.web.pages.orga.GroupCtrl import gospeak.web.pages.orga.cfps.CfpCtrl._ import gospeak.web.pages.orga.events.routes.{EventCtrl => EventRoutes} import gospeak.web.utils.{GsForms, OrgaReq, UICtrl} import gospeak.libs.scala.domain.Page import play.api.data.Form import play.api.mvc.{Action, AnyContent, ControllerComponents, Result} class CfpCtrl(cc: ControllerComponents, silhouette: Silhouette[CookieEnv], conf: AppConf, userRepo: OrgaUserRepo, val groupRepo: OrgaGroupRepo, cfpRepo: OrgaCfpRepo, eventRepo: OrgaEventRepo, proposalRepo: OrgaProposalRepo) extends UICtrl(cc, silhouette, conf) with UICtrl.OrgaAction { def list(group: Group.Slug, params: Page.Params): Action[AnyContent] = OrgaAction(group) { implicit req => val customParams = params.withNullsFirst cfpRepo.list(customParams).map(cfps => Ok(html.list(cfps)(listBreadcrumb))) // TODO listWithProposalCount } def create(group: Group.Slug, event: Option[Event.Slug]): Action[AnyContent] = OrgaAction(group) { implicit req => createView(group, GsForms.cfp, event) } def doCreate(group: Group.Slug, event: Option[Event.Slug]): Action[AnyContent] = OrgaAction(group) { implicit req => GsForms.cfp.bindFromRequest.fold( formWithErrors => createView(group, formWithErrors, event), data => (for { // TODO check if slug not already exist cfpElt <- OptionT.liftF(cfpRepo.create(data)) redirect <- OptionT.liftF(event.map { e => eventRepo.attachCfp(e, cfpElt.id) .map(_ => Redirect(EventRoutes.detail(group, e))) // TODO recover and redirect to cfp detail }.getOrElse { IO.pure(Redirect(routes.CfpCtrl.detail(group, data.slug))) }) } yield redirect).value.map(_.getOrElse(groupNotFound(group))) ) } private def createView(group: Group.Slug, form: Form[Cfp.Data], event: Option[Event.Slug])(implicit req: OrgaReq[AnyContent]): IO[Result] = { val b = listBreadcrumb.add("New" -> routes.CfpCtrl.create(group)) IO.pure(Ok(html.create(form, event)(b))) } def detail(group: Group.Slug, cfp: Cfp.Slug, params: Page.Params): Action[AnyContent] = OrgaAction(group) { implicit req => (for { cfpElt <- OptionT(cfpRepo.find(cfp)) proposals <- OptionT.liftF(proposalRepo.listFull(cfp, params)) speakers <- OptionT.liftF(userRepo.list(proposals.items.flatMap(_.users).distinct)) userRatings <- OptionT.liftF(proposalRepo.listRatings(cfp)) b = breadcrumb(cfpElt) } yield Ok(html.detail(cfpElt, proposals, speakers, userRatings)(b))).value.map(_.getOrElse(cfpNotFound(group, cfp))) } def edit(group: Group.Slug, cfp: Cfp.Slug, redirect: Option[String]): Action[AnyContent] = OrgaAction(group) { implicit req => editView(group, cfp, GsForms.cfp, redirect) } def doEdit(group: Group.Slug, cfp: Cfp.Slug, redirect: Option[String]): Action[AnyContent] = OrgaAction(group) { implicit req => GsForms.cfp.bindFromRequest.fold( formWithErrors => editView(group, cfp, formWithErrors, redirect), data => (for { cfpOpt <- OptionT.liftF(cfpRepo.find(data.slug)) res <- OptionT.liftF(cfpOpt match { case Some(duplicate) if data.slug != cfp => editView(group, cfp, GsForms.cfp.fillAndValidate(data).withError("slug", s"Slug already taken by cfp: ${duplicate.name.value}"), redirect) case _ => cfpRepo.edit(cfp, data).map { _ => redirectOr(redirect, routes.CfpCtrl.detail(group, data.slug)) } }) } yield res).value.map(_.getOrElse(groupNotFound(group))) ) } private def editView(group: Group.Slug, cfp: Cfp.Slug, form: Form[Cfp.Data], redirect: Option[String])(implicit req: OrgaReq[AnyContent], ctx: OrgaCtx): IO[Result] = { (for { cfpElt <- OptionT(cfpRepo.find(cfp)) filledForm = if (form.hasErrors) form else form.fill(cfpElt.data) b = breadcrumb(cfpElt).add("Edit" -> routes.CfpCtrl.edit(group, cfp)) } yield Ok(html.edit(cfpElt, filledForm, redirect)(b))).value.map(_.getOrElse(cfpNotFound(group, cfp))) } } object CfpCtrl { def listBreadcrumb(implicit req: OrgaReq[AnyContent]): Breadcrumb = GroupCtrl.breadcrumb.add("CFPs" -> routes.CfpCtrl.list(req.group.slug)) def breadcrumb(cfp: Cfp)(implicit req: OrgaReq[AnyContent]): Breadcrumb = listBreadcrumb.add(cfp.name.value -> routes.CfpCtrl.detail(req.group.slug, cfp.slug)) }
Example 6
Source File: VideoCtrl.scala From gospeak with Apache License 2.0 | 5 votes |
package gospeak.web.pages.published.videos import cats.data.OptionT import com.mohiva.play.silhouette.api.Silhouette import gospeak.core.domain.Video import gospeak.core.services.storage.PublicVideoRepo import gospeak.infra.services.EmbedSrv import gospeak.libs.scala.domain.{Html, Page, Url} import gospeak.web.AppConf import gospeak.web.auth.domain.CookieEnv import gospeak.web.domain.Breadcrumb import gospeak.web.pages.published.HomeCtrl import gospeak.web.pages.published.videos.VideoCtrl._ import gospeak.web.utils.UICtrl import play.api.mvc.{Action, AnyContent, ControllerComponents} class VideoCtrl(cc: ControllerComponents, silhouette: Silhouette[CookieEnv], conf: AppConf, videoRepo: PublicVideoRepo) extends UICtrl(cc, silhouette, conf) { def list(params: Page.Params): Action[AnyContent] = UserAwareAction { implicit req => videoRepo.list(params).map(videos => Ok(html.list(videos)(listBreadcrumb()))) } def detail(video: Url.Video.Id): Action[AnyContent] = UserAwareAction { implicit req => (for { videoElt <- OptionT(videoRepo.find(video)) embed: Html <- OptionT.liftF(EmbedSrv.embedCode(videoElt.url)) b = breadcrumb(videoElt) } yield Ok(html.detail(videoElt, embed)(b))).value.map(_.getOrElse(publicVideoNotFound(video))) } } object VideoCtrl { def listBreadcrumb(): Breadcrumb = HomeCtrl.breadcrumb().add("Videos" -> routes.VideoCtrl.list()) def breadcrumb(video: Video): Breadcrumb = listBreadcrumb().add(video.title -> routes.VideoCtrl.detail(video.id)) }
Example 7
Source File: SpeakerCtrl.scala From gospeak with Apache License 2.0 | 5 votes |
package gospeak.web.pages.published.speakers import cats.data.OptionT import cats.effect.IO import com.mohiva.play.silhouette.api.Silhouette import gospeak.core.domain.{Proposal, Talk, User} import gospeak.core.services.email.EmailSrv import gospeak.core.services.storage._ import gospeak.libs.scala.domain.Page import gospeak.web.AppConf import gospeak.web.auth.domain.CookieEnv import gospeak.web.domain.Breadcrumb import gospeak.web.emails.Emails import gospeak.web.pages.published.HomeCtrl import gospeak.web.pages.published.speakers.SpeakerCtrl._ import gospeak.web.utils._ import play.api.mvc._ class SpeakerCtrl(cc: ControllerComponents, silhouette: Silhouette[CookieEnv], conf: AppConf, userRepo: PublicUserRepo, talkRepo: PublicTalkRepo, externalProposalRepo: PublicExternalProposalRepo, groupRepo: PublicGroupRepo, emailSrv: EmailSrv) extends UICtrl(cc, silhouette, conf) { def list(params: Page.Params): Action[AnyContent] = UserAwareAction { implicit req => userRepo.listPublic(params).map(speakers => Ok(html.list(speakers)(listBreadcrumb()))) } def detail(user: User.Slug): Action[AnyContent] = UserAwareAction { implicit req => (for { speakerElt <- OptionT(userRepo.findPublic(user)) groups <- OptionT.liftF(groupRepo.listFull(speakerElt.id)) talks <- OptionT.liftF(talkRepo.listAll(speakerElt.id, Talk.Status.Public)) proposals <- OptionT.liftF(externalProposalRepo.listAllCommon(speakerElt.id, Proposal.Status.Accepted)) users <- OptionT.liftF(userRepo.list((groups.flatMap(_.owners.toList) ++ talks.flatMap(_.users)).distinct)) res = Ok(html.detail(speakerElt, groups, talks, proposals.groupBy(_.talk.id), users)(breadcrumb(speakerElt))) } yield res).value.map(_.getOrElse(publicUserNotFound(user))) } def talk(user: User.Slug, talk: Talk.Slug): Action[AnyContent] = UserAwareAction { implicit req => (for { userElt <- OptionT(userRepo.findPublic(user)) talkElt <- OptionT(talkRepo.findPublic(talk, userElt.id)) proposals <- OptionT.liftF(externalProposalRepo.listAllCommon(talkElt.id, Proposal.Status.Accepted)) users <- OptionT.liftF(userRepo.list((talkElt.users ++ proposals.flatMap(_.users)).distinct)) res = Ok(html.talk(userElt, talkElt, proposals, users)(breadcrumb(userElt, talkElt))) } yield res).value.map(_.getOrElse(publicTalkNotFound(user, talk))) } def contactSpeaker(user: User.Slug): Action[AnyContent] = UserAction { implicit req => val next = Redirect(routes.SpeakerCtrl.detail(user)) GsForms.speakerContact.bindFromRequest.fold( formWithErrors => IO.pure(next.flashing(formWithErrors.flash)), data => (for { speakerElt <- OptionT(userRepo.findPublic(user)(req.userAware)) _ <- OptionT.liftF(emailSrv.send(Emails.contactSpeaker(data.subject, data.content, speakerElt.user))) res = next.flashing("success" -> "The message has been sent!") } yield res).value.map(_.getOrElse(publicUserNotFound(user)))) } } object SpeakerCtrl { def listBreadcrumb(): Breadcrumb = HomeCtrl.breadcrumb().add("Speakers" -> routes.SpeakerCtrl.list()) def breadcrumb(speaker: User.Full): Breadcrumb = listBreadcrumb().add(speaker.name.value -> routes.SpeakerCtrl.detail(speaker.slug)) def breadcrumb(speaker: User.Full, talk: Talk): Breadcrumb = breadcrumb(speaker).add("Talks" -> routes.SpeakerCtrl.detail(speaker.slug)).add(talk.title.value -> routes.SpeakerCtrl.talk(speaker.slug, talk.slug)) }
Example 8
Source File: CfpCtrl.scala From gospeak with Apache License 2.0 | 5 votes |
package gospeak.web.pages.user.talks.cfps import cats.data.OptionT import com.mohiva.play.silhouette.api.Silhouette import gospeak.core.domain.Talk import gospeak.core.services.storage.{SpeakerCfpRepo, SpeakerProposalRepo, SpeakerTalkRepo} import gospeak.web.AppConf import gospeak.web.auth.domain.CookieEnv import gospeak.web.domain.Breadcrumb import gospeak.web.pages.user.talks.TalkCtrl import gospeak.web.pages.user.talks.cfps.CfpCtrl._ import gospeak.web.utils.{UICtrl, UserReq} import gospeak.libs.scala.domain.Page import play.api.mvc._ class CfpCtrl(cc: ControllerComponents, silhouette: Silhouette[CookieEnv], conf: AppConf, cfpRepo: SpeakerCfpRepo, talkRepo: SpeakerTalkRepo, proposalRepo: SpeakerProposalRepo) extends UICtrl(cc, silhouette, conf) { def list(talk: Talk.Slug, params: Page.Params): Action[AnyContent] = UserAction { implicit req => (for { talkElt <- OptionT(talkRepo.find(talk)) cfps <- OptionT.liftF(cfpRepo.availableFor(talkElt.id, params)) b = listBreadcrumb(talkElt) } yield Ok(html.list(talkElt, cfps)(b))).value.map(_.getOrElse(talkNotFound(talk))) } } object CfpCtrl { def listBreadcrumb(talk: Talk)(implicit req: UserReq[AnyContent]): Breadcrumb = TalkCtrl.breadcrumb(talk).add("Proposing" -> routes.CfpCtrl.list(talk.slug)) }
Example 9
Source File: Selective.scala From tofu with Apache License 2.0 | 5 votes |
package tofu.control import cats.data.{EitherT, OptionT, ReaderT, WriterT} import cats.instances.option._ import cats.syntax.coflatMap._ import cats.syntax.option._ import cats.{Applicative, Monad, Monoid} import simulacrum._ import tofu.control.impl._ @typeclass trait Selective[F[_]] extends Applicative[F] { @noop def selectAp[A, B](fe: F[Either[A, B]])(ff: => F[A => B]): F[B] @noop def select[A](fo: F[Option[A]])(fa: => F[A]): F[A] = selectAp[Unit, A](map(fo)(_.toRight(())))(map(fa)(a => (_: Unit) => a)) def selectRight[A](fb: F[A], fo: F[Option[A]]): F[A] = select(fo)(fb) @noop def orElses[A](fx: F[Option[A]])(fy: => F[Option[A]]): F[Option[A]] = select(map(fx)(_.coflatten))(fy) def whens[A](fb: F[Boolean])(fa: => F[A]): F[Option[A]] = select(map(fb)(x => if (x) None else Some(none[A])))(map(fa)(_.some)) def unlesss[A](fb: F[Boolean])(fa: => F[A]): F[Option[A]] = select(map(fb)(x => if (x) Some(none[A]) else None))(map(fa)(_.some)) def whens_[A](fb: F[Boolean])(fa: => F[A]): F[Unit] = select(map(fb)(x => if (x) None else Some(())))(void(fa)) def unlesss_[A](fb: F[Boolean])(fa: => F[A]): F[Unit] = select(map(fb)(x => if (x) Some(()) else None))(void(fa)) @noop def optionMonoid[A]: Monoid[F[Option[A]]] = new Monoid[F[Option[A]]] { def empty: F[Option[A]] = pure(None) def combine(x: F[Option[A]], y: F[Option[A]]): F[Option[A]] = orElses(x)(y) } } object Selective extends SelectiveInstances trait SelectiveInstances extends SelectiveInstances2 { final implicit def selectiveOverMonad[F[_]: Monad]: SelectiveOverMonad[F] = new SelectiveOverMonad[F] } trait SelectiveInstances2 { final implicit def selectiveOptionT[F[_]: Selective]: Selective[OptionT[F, *]] = new SelectiveOptionT[F] final implicit def selectiveEitherT[F[_]: Selective, E]: Selective[EitherT[F, E, *]] = new SelectiveEitherT[F, E] final implicit def selectiveReaderT[F[_]: Selective, R]: Selective[ReaderT[F, R, *]] = new SelectiveReaderT[F, R] final implicit def selectiveWriterT[F[_]: Selective, W: Monoid]: Selective[WriterT[F, W, *]] = new SelectiveWriterT[F, W] }
Example 10
Source File: SelectiveInstances.scala From tofu with Apache License 2.0 | 5 votes |
package tofu.control.impl import cats.data.{EitherT, OptionT, ReaderT, WriterT} import cats.instances.either._ import cats.instances.option._ import cats.instances.tuple._ import cats.{Applicative, Monad, Monoid} import tofu.control.Selective import tofu.syntax.either._ import tofu.syntax.monadic._ class SelectiveOverMonad[F[_]](implicit val F: Monad[F]) extends Selective[F] with ApplicativeDelegate[F] { def selectAp[A, B](fe: F[Either[A, B]])(ff: => F[A => B]): F[B] = F.flatMap(fe) { case Left(a) => F.map(ff)(_(a)) case Right(b) => F.pure(b) } override def select[A](fo: F[Option[A]])(fa: => F[A]): F[A] = F.flatMap(fo) { case None => fa case Some(a) => F.pure(a) } override def whens[A](fb: F[Boolean])(fa: => F[A]): F[Option[A]] = F.flatMap(fb) { if (_) F.map(fa)(Some(_)) else F.pure(None) } override def unlesss[A](fb: F[Boolean])(fa: => F[A]): F[Option[A]] = F.flatMap(fb) { if (_) F.pure(None) else F.map(fa)(Some(_)) } override def whens_[A](fb: F[Boolean])(fa: => F[A]): F[Unit] = F.flatMap(fb) { if (_) F.unit else F.void(fa) } override def unlesss_[A](fb: F[Boolean])(fa: => F[A]): F[Unit] = F.flatMap(fb) { if (_) F.void(fa) else F.unit } } trait SelectiveWithMap[F[_]] extends Selective[F] { def smap[A, B](fa: F[A])(f: A => B): F[B] def ap[A, B](ff: F[A => B])(fa: F[A]): F[B] = selectAp[A, B](smap(fa)(Left(_)))(ff) override def map[A, B](fa: F[A])(f: A => B): F[B] = smap(fa)(f) } class SelectiveOptionT[F[_]](implicit F: Selective[F]) extends Selective[OptionT[F, *]] { def selectAp[A, B](fe: OptionT[F, Either[A, B]])(ff: => OptionT[F, A => B]): OptionT[F, B] = OptionT(F.selectAp(fe.value.map { case Some(Left(a)) => Left(a) case Some(Right(b)) => Right(Some(b)) case None => Right(None) })(ff.value.map { case Some(f) => b => Some(f(b)) case None => _ => None })) def ap[A, B](ff: OptionT[F, A => B])(fa: OptionT[F, A]): OptionT[F, B] = OptionT(F.ap[Option[A], Option[B]](ff.value.map(fo => (fo ap _)))(fa.value)) def pure[A](x: A): OptionT[F, A] = OptionT.pure(x) } class SelectiveEitherT[F[_], L](implicit F: Selective[F]) extends Selective[EitherT[F, L, *]] { def selectAp[A, B](fe: EitherT[F, L, Either[A, B]])(ff: => EitherT[F, L, A => B]): EitherT[F, L, B] = EitherT(F.selectAp[A, Either[L, B]](fe.value.map { case Right(Left(a)) => Left(a) case rr @ Right(Right(_)) => rr.asInstanceOf[Either[A, Either[L, B]]] case l @ Left(_) => Right(l.coerceR[B]) })(ff.value.map { case Right(f) => b => Right(f(b)) case l @ Left(_) => _ => l.coerceR[B] })) def ap[A, B](ff: EitherT[F, L, A => B])(fa: EitherT[F, L, A]): EitherT[F, L, B] = EitherT(F.ap[Either[L, A], Either[L, B]](ff.value.map(fo => (fo ap _)))(fa.value)) def pure[A](x: A): EitherT[F, L, A] = EitherT.pure(x) } class SelectiveReaderT[F[_], R](implicit FS: Selective[F]) extends Selective[ReaderT[F, R, *]] with ApplicativeDelegate[ReaderT[F, R, *]] { val F: Applicative[ReaderT[F, R, *]] = implicitly def selectAp[A, B](fe: ReaderT[F, R, Either[A, B]])(ff: => ReaderT[F, R, A => B]): ReaderT[F, R, B] = ReaderT(r => FS.selectAp(fe.run(r))(ff.run(r))) } class SelectiveWriterT[F[_], W: Monoid](implicit FS: Selective[F]) extends Selective[WriterT[F, W, *]] with ApplicativeDelegate[WriterT[F, W, *]] { val F: Applicative[WriterT[F, W, *]] = implicitly def selectAp[A, B](fe: WriterT[F, W, Either[A, B]])(ff: => WriterT[F, W, A => B]): WriterT[F, W, B] = WriterT(FS.selectAp[(W, A), (W, B)](FS.map(fe.run) { case (w, Left(a)) => Left((w, a)) case (w, Right(b)) => Right((w, b)) })(FS.map(ff.run)(wf => (wf ap _)))) }
Example 11
Source File: GroupCtrl.scala From gospeak with Apache License 2.0 | 5 votes |
package gospeak.web.api.published import cats.data.OptionT import com.mohiva.play.silhouette.api.Silhouette import gospeak.core.domain.{Event, Group, Proposal} import gospeak.core.services.meetup.MeetupSrv import gospeak.core.services.storage._ import gospeak.web.AppConf import gospeak.web.api.domain._ import gospeak.web.api.domain.utils.ApiResult import gospeak.web.auth.domain.CookieEnv import gospeak.web.utils.ApiCtrl import gospeak.libs.scala.Extensions._ import gospeak.libs.scala.domain.Page import play.api.mvc.{Action, AnyContent, ControllerComponents} class GroupCtrl(cc: ControllerComponents, silhouette: Silhouette[CookieEnv], conf: AppConf, groupRepo: PublicGroupRepo, eventRepo: PublicEventRepo, proposalRepo: PublicProposalRepo, venueRepo: PublicVenueRepo, userRepo: PublicUserRepo, groupSettingsRepo: PublicGroupSettingsRepo, meetupSrv: MeetupSrv) extends ApiCtrl(cc, silhouette, conf) { def list(params: Page.Params): Action[AnyContent] = UserAwareAction[Seq[ApiGroup.Published]] { implicit req => groupRepo.listFull(params).map(ApiResult.of(_, ApiGroup.published)) } def detail(group: Group.Slug): Action[AnyContent] = UserAwareAction[ApiGroup.Published] { implicit req => groupRepo.findFull(group).map(_.map(g => ApiResult.of(ApiGroup.published(g))).getOrElse(groupNotFound(group))) } def events(group: Group.Slug, params: Page.Params): Action[AnyContent] = UserAwareAction[Seq[ApiEvent.Published]] { implicit req => (for { groupElt <- OptionT(groupRepo.find(group)) events <- OptionT.liftF(eventRepo.listPublished(groupElt.id, params)) talks <- OptionT.liftF(proposalRepo.listPublic(events.items.flatMap(_.talks).distinct)) speakers <- OptionT.liftF(userRepo.list(talks.flatMap(_.speakers.toList).distinct)) res = ApiResult.of(events, ApiEvent.published(_, talks, speakers)) } yield res).value.map(_.getOrElse(groupNotFound(group))) } def event(group: Group.Slug, event: Event.Slug): Action[AnyContent] = UserAwareAction[ApiEvent.Published] { implicit req => (for { groupElt <- OptionT(groupRepo.find(group)) eventElt <- OptionT(eventRepo.findPublished(groupElt.id, event)) talks <- OptionT.liftF(proposalRepo.listPublic(eventElt.talks.distinct)) speakers <- OptionT.liftF(userRepo.list(talks.flatMap(_.speakers.toList).distinct)) res = ApiResult.of(ApiEvent.published(eventElt, talks, speakers)) } yield res).value.map(_.getOrElse(eventNotFound(group, event))) } def eventDrawMeetupAttendee(group: Group.Slug, event: Event.Slug): Action[AnyContent] = UserAwareAction[Seq[ApiAttendee.Published]] { implicit req => (for { groupElt <- OptionT(groupRepo.find(group)) eventElt <- OptionT(eventRepo.findPublished(groupElt.id, event)) creds <- OptionT(groupSettingsRepo.findMeetup(groupElt.id)) attendees <- OptionT(eventElt.event.refs.meetup.map(r => meetupSrv.getAttendees(r.group, r.event, conf.app.aesKey, creds)).sequence) host = req.getQueryString("host") response = req.getQueryString("response") cleanAttendees = attendees.filter(a => a.id.value != 0L && response.forall(_ == a.response) && host.forall(_ == a.host.toString)) res = ApiResult.of(cleanAttendees.map(ApiAttendee.published(_, creds.group))) } yield res).value.map(_.getOrElse(eventNotFound(group, event))) } def talks(group: Group.Slug, params: Page.Params): Action[AnyContent] = UserAwareAction[Seq[ApiProposal.Published]] { implicit req => (for { groupElt <- OptionT(groupRepo.find(group)) talks <- OptionT.liftF(proposalRepo.listPublicFull(groupElt.id, params)) speakers <- OptionT.liftF(userRepo.list(talks.items.flatMap(_.speakers.toList).distinct)) res = ApiResult.of(talks, ApiProposal.published(_, speakers)) } yield res).value.map(_.getOrElse(groupNotFound(group))) } def talk(group: Group.Slug, talk: Proposal.Id): Action[AnyContent] = UserAwareAction[ApiProposal.Published] { implicit req => (for { groupElt <- OptionT(groupRepo.find(group)) talkElt <- OptionT(proposalRepo.findPublicFull(groupElt.id, talk)) speakers <- OptionT.liftF(userRepo.list(talkElt.speakers.toList.distinct)) res = ApiResult.of(ApiProposal.published(talkElt, speakers)) } yield res).value.map(_.getOrElse(talkNotFound(group, talk))) } def speakers(group: Group.Slug, params: Page.Params): Action[AnyContent] = UserAwareAction[Seq[ApiUser.Published]] { implicit req => (for { groupElt <- OptionT(groupRepo.find(group)) speakers <- OptionT.liftF(userRepo.speakersPublic(groupElt.id, params)) // TODO add proposals, talks, groups member and owners for each speaker res = ApiResult.of(speakers, ApiUser.published) } yield res).value.map(_.getOrElse(groupNotFound(group))) } }
Example 12
Source File: EmbedSuite.scala From tofu with Apache License 2.0 | 5 votes |
package tofu.higherKind.derived import cats.data.{IorT, NonEmptyChain} import cats.free.Free import derevo.derive import tofu.higherKind.Embed import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers import cats.data.OptionT import EmbedSuite.Foo import tofu.syntax.monadic._ import tofu.syntax.embed._ import cats.instances.either._ import cats.instances.option._ import cats.syntax.traverse._ import cats.syntax.either._ import scala.util.Try class EmbedSuite extends AnyFlatSpec with Matchers { val checkingFoo1: Foo[Either[String, *]] = new Foo[Either[String, *]] { override def foo(x: Int, s: String): Either[String, Double] = Try(s.toDouble).toEither.left.map(_ => s"could not parse $s as double").map(_ * x) override def bar(a: List[Int]): Either[String, Unit] = a.headOption.toRight("must contain at least one element").void def baz(a: List[String]): OptionT[Either[String, *], Unit] = OptionT(a.headOption.traverse(_.asLeft[Unit])) } "embed" should "generate nice embed" in { val rightFoo = checkingFoo1.asRight[String].embed val leftFoo = "failed".asLeft[Foo[Either[String, *]]].embed rightFoo.foo(2, "2.3") should ===(Right(4.6)) rightFoo.foo(2, "fail") should ===(Left("could not parse fail as double")) rightFoo.bar(List(4, 5, 6)) should ===(Right(())) rightFoo.bar(List()) should ===(Left("must contain at least one element")) leftFoo.foo(2, "2.3") should ===(Left("failed")) leftFoo.foo(2, "fail") should ===(Left("failed")) leftFoo.bar(List(4, 5, 6)) should ===(Left("failed")) leftFoo.bar(List()) should ===(Left("failed")) } } object EmbedSuite { @derive(embed) trait Foo[F[_]] { def foo(x: Int, s: String): F[Double] def bar(a: List[Int]): F[Unit] def baz(a: List[String]): OptionT[F, Unit] } Embed[Foo] }
Example 13
Source File: ArchiveCache.scala From nexus with Apache License 2.0 | 5 votes |
package ch.epfl.bluebrain.nexus.kg.archives import akka.actor.{ActorSystem, NotInfluenceReceiveTimeout} import cats.Monad import cats.data.OptionT import cats.effect.{Effect, Timer} import cats.implicits._ import ch.epfl.bluebrain.nexus.kg.archives.ArchiveCache._ import ch.epfl.bluebrain.nexus.kg.config.KgConfig.ArchivesConfig import ch.epfl.bluebrain.nexus.kg.resources.ResId import ch.epfl.bluebrain.nexus.sourcing.StateMachine import ch.epfl.bluebrain.nexus.sourcing.akka.StopStrategy import ch.epfl.bluebrain.nexus.sourcing.akka.statemachine.AkkaStateMachine import retry.RetryPolicy class ArchiveCache[F[_]: Monad](ref: StateMachine[F, String, State, Command, Unit]) { def put(value: Archive): OptionT[F, Archive] = OptionT(ref.evaluate(value.resId.show, Write(value)).map(_.toOption.flatten)) } object ArchiveCache { private[archives] type State = Option[Archive] private[archives] type Command = Write final private[archives] case class Write(bundle: Archive) extends NotInfluenceReceiveTimeout final def apply[F[_]: Timer](implicit as: ActorSystem, cfg: ArchivesConfig, F: Effect[F]): F[ArchiveCache[F]] = { implicit val retryPolicy: RetryPolicy[F] = cfg.cache.retry.retryPolicy[F] val invalidationStrategy = StopStrategy.lapsedSinceLastInteraction[State, Command](cfg.cacheInvalidateAfter) val evaluate: (State, Command) => F[Either[Unit, State]] = { case (None, Write(bundle)) => F.pure(Right(Some(bundle))) case (Some(_), _) => F.pure(Left(())) // It already exists, so we don't want to replace it } AkkaStateMachine .sharded[F]("archives", None, evaluate, invalidationStrategy, cfg.cache.akkaStateMachineConfig, cfg.cache.shards) .map(new ArchiveCache[F](_)) } }
Example 14
Source File: package.scala From nexus with Apache License 2.0 | 5 votes |
package ch.epfl.bluebrain.nexus.kg import akka.http.scaladsl.model.StatusCode import akka.http.scaladsl.server.Directives.complete import akka.http.scaladsl.server.{MalformedQueryParamRejection, Route} import cats.Functor import cats.data.{EitherT, OptionT} import cats.instances.future._ import ch.epfl.bluebrain.nexus.iam.types.Permission import ch.epfl.bluebrain.nexus.kg.marshallers.instances._ import ch.epfl.bluebrain.nexus.kg.resources.Rejection.NotFound.notFound import ch.epfl.bluebrain.nexus.kg.resources.{Ref, Rejection, ResourceV} import ch.epfl.bluebrain.nexus.kg.routes.OutputFormat.{DOT, Triples} import ch.epfl.bluebrain.nexus.rdf.Iri.AbsoluteIri import monix.execution.Scheduler.Implicits.global import scala.concurrent.Future package object routes { private[routes] def completeWithFormat( fetched: Future[Either[Rejection, (StatusCode, ResourceV)]] )(implicit format: NonBinaryOutputFormat): Route = completeWithFormat(EitherT(fetched)) private def completeWithFormat( fetched: EitherT[Future, Rejection, (StatusCode, ResourceV)] )(implicit format: NonBinaryOutputFormat): Route = format match { case f: JsonLDOutputFormat => implicit val format = f complete(fetched.value) case Triples => implicit val format = Triples complete(fetched.map { case (status, resource) => status -> resource.value.graph.ntriples }.value) case DOT => implicit val format = DOT complete(fetched.map { case (status, resource) => status -> resource.value.graph.dot() }.value) } private[routes] val read: Permission = Permission.unsafe("resources/read") private[routes] val schemaError = MalformedQueryParamRejection("schema", "The provided schema does not match the schema on the Uri") implicit private[routes] class FOptionSyntax[F[_], A](private val fOpt: F[Option[A]]) extends AnyVal { def toNotFound(id: AbsoluteIri)(implicit F: Functor[F]): EitherT[F, Rejection, A] = OptionT(fOpt).toRight(notFound(Ref(id))) } }
Example 15
Source File: TracedHttpRoute.scala From http4s-tracer with Apache License 2.0 | 5 votes |
package dev.profunktor.tracer import cats.Monad import cats.data.{Kleisli, OptionT} import cats.syntax.flatMap._ import cats.syntax.functor._ import dev.profunktor.tracer.Tracer.TraceId import org.http4s.{HttpRoutes, Request, Response} object TracedHttpRoute { case class TracedRequest[F[_]](traceId: TraceId, request: Request[F]) def apply[F[_]: Monad: Tracer]( pf: PartialFunction[TracedRequest[F], F[Response[F]]] ): HttpRoutes[F] = Kleisli[OptionT[F, ?], Request[F], Response[F]] { req => OptionT { Tracer[F] .getTraceId(req) .map(x => TracedRequest[F](x.getOrElse(TraceId("-")), req)) .flatMap { tr => val rs: OptionT[F, Response[F]] = pf.andThen(OptionT.liftF(_)).applyOrElse(tr, Function.const(OptionT.none)) rs.value } } } }
Example 16
Source File: AuthTracedHttpRoute.scala From http4s-tracer with Apache License 2.0 | 5 votes |
package dev.profunktor.tracer.auth import cats.Monad import cats.data.{Kleisli, OptionT} import cats.syntax.flatMap._ import cats.syntax.functor._ import dev.profunktor.tracer.Tracer import dev.profunktor.tracer.Tracer.TraceId import org.http4s.{AuthedRequest, AuthedRoutes, Response} object AuthTracedHttpRoute { case class AuthTracedRequest[F[_], T](traceId: TraceId, request: AuthedRequest[F, T]) def apply[T, F[_]: Monad: Tracer]( pf: PartialFunction[AuthTracedRequest[F, T], F[Response[F]]] ): AuthedRoutes[T, F] = Kleisli[OptionT[F, ?], AuthedRequest[F, T], Response[F]] { req => OptionT { Tracer[F] .getTraceId(req.req) .map(x => AuthTracedRequest[F, T](x.getOrElse(TraceId("-")), req)) .flatMap { tr => val rs: OptionT[F, Response[F]] = pf.andThen(OptionT.liftF(_)).applyOrElse(tr, Function.const(OptionT.none)) rs.value } } } }
Example 17
Source File: SecurityServiceSpec.scala From cluster-broccoli with Apache License 2.0 | 5 votes |
package de.frosner.broccoli.services import cats.data.OptionT import com.mohiva.play.silhouette.api.LoginInfo import com.mohiva.play.silhouette.api.services.IdentityService import com.mohiva.play.silhouette.api.util.Credentials import com.mohiva.play.silhouette.impl.exceptions.InvalidPasswordException import com.mohiva.play.silhouette.impl.providers.CredentialsProvider import de.frosner.broccoli.auth.{Account, AuthConfiguration, AuthMode, Role} import org.mockito.Mock import org.specs2.concurrent.ExecutionEnv import org.specs2.mock.Mockito import org.specs2.mutable.Specification import org.specs2.specification.mutable.ExecutionEnvironment import scala.concurrent.Future import scala.concurrent.duration.Duration class SecurityServiceSpec extends Specification with Mockito with ExecutionEnvironment { def configWithAccounts(accounts: Seq[Account]): AuthConfiguration = AuthConfiguration( mode = AuthMode.Conf, session = AuthConfiguration.Session(timeout = Duration(1, "hour"), allowMultiLogin = true), cookie = AuthConfiguration.Cookie(secure = true), conf = AuthConfiguration.Conf( accounts = accounts .map( a => AuthConfiguration.ConfAccount( a.name, "", a.instanceRegex, a.role )) .toList), allowedFailedLogins = 3 ) val identityService = mock[IdentityService[Account]] val account = Account("frank", "^test.*", Role.Administrator) override def is(implicit executionEnv: ExecutionEnv): Any = "An authentication check" should { "succeed if the credentials provider authenticates" in { val login = LoginInfo(CredentialsProvider.ID, account.name) val credentials = Credentials(account.name, "pass") val credentialsProvider = mock[CredentialsProvider] credentialsProvider.authenticate(credentials) returns Future.successful(login) SecurityService(configWithAccounts(List(account)), credentialsProvider, identityService) .authenticate(credentials) must beSome(login).await } "fail if the credentials provider fails to authenticate" in { val credentials = Credentials(account.name, "pass") val credentialsProvider = mock[CredentialsProvider] credentialsProvider.authenticate(credentials) returns Future.failed(new InvalidPasswordException("foo")) SecurityService(configWithAccounts(List(account)), credentialsProvider, identityService) .authenticate(credentials) must beNone.await } "succeed if the number of failed logins is equal to the allowed ones" in { val credentials = Credentials(account.name, "pass") val failedCredentials = credentials.copy(password = "foo") val login = LoginInfo(CredentialsProvider.ID, credentials.identifier) val credentialsProvider = mock[CredentialsProvider] credentialsProvider.authenticate(failedCredentials) returns Future.failed(new InvalidPasswordException("foo")) credentialsProvider.authenticate(credentials) returns Future.successful(login) val service = SecurityService(configWithAccounts(List(account)), credentialsProvider, identityService) val failedAttempts = for (attemptNo <- 1 to service.allowedFailedLogins) { service.authenticate(failedCredentials) must beNone.await } service.authenticate(credentials) must beSome(login).await } "fail if the number of failed logins is greater than the allowed number" in { val credentials = Credentials(account.name, "password") val failedCredentials = credentials.copy(password = "foo") val login = LoginInfo(CredentialsProvider.ID, credentials.identifier) val credentialsProvider = mock[CredentialsProvider] credentialsProvider.authenticate(failedCredentials) returns Future.failed(new InvalidPasswordException("foo")) credentialsProvider.authenticate(credentials) returns Future.successful(login) val service = SecurityService(configWithAccounts(List(account)), credentialsProvider, identityService) val failedAttempts = for (attemptNo <- 0 to service.allowedFailedLogins) { service.authenticate(failedCredentials) must beNone.await } service.authenticate(credentials) must beNone.await } } }
Example 18
Source File: SecurityController.scala From cluster-broccoli with Apache License 2.0 | 5 votes |
package de.frosner.broccoli.controllers import javax.inject.Inject import cats.data.{EitherT, OptionT} import cats.instances.future._ import cats.syntax.either._ import com.mohiva.play.silhouette.api.util.Credentials import com.mohiva.play.silhouette.impl.providers.CredentialsProvider import de.frosner.broccoli.services.{SecurityService, WebSocketService} import jp.t2v.lab.play2.auth.{BroccoliSimpleAuthorization, LoginLogout} import play.api.{Environment, Logger} import play.api.cache.CacheApi import play.api.data.Forms._ import play.api.data._ import play.api.libs.json.Json import play.api.mvc.{Action, AnyContent, Controller, Results} import scala.concurrent.Future case class SecurityController @Inject()( override val securityService: SecurityService, override val cacheApi: CacheApi, override val playEnv: Environment, webSocketService: WebSocketService ) extends Controller with LoginLogout with BroccoliSimpleAuthorization { private val log = Logger(getClass) import scala.concurrent.ExecutionContext.Implicits.global // https://www.playframework.com/documentation/2.5.x/ScalaForms val loginForm = Form { mapping( SecurityController.UsernameFormKey -> text, SecurityController.PasswordFormKey -> text )(Credentials.apply)(Credentials.unapply) } def login: Action[AnyContent] = Action.async { implicit request => getSessionId(request).map(id => (id, webSocketService.closeConnections(id))) match { case Some((id, true)) => log.info(s"Removing websocket connection of $id due to another login") case _ => } (for { credentials <- EitherT.fromEither[Future]( loginForm.bindFromRequest().fold(Function.const(Results.BadRequest.asLeft), _.asRight)) login <- OptionT(securityService.authenticate(credentials)).toRight(Results.Unauthorized) result <- EitherT.right(gotoLoginSucceeded(login.providerKey)) user <- OptionT(resolveUser(login.providerKey)).toRight(Results.Unauthorized) } yield { val userResult = Results.Ok(Json.toJson(user)) result.copy( header = result.header.copy( headers = userResult.header.headers .get("Content-Type") .map { contentType => result.header.headers.updated("Content-Type", contentType) } .getOrElse(result.header.headers) ), body = userResult.body ) }).merge } def logout = Action.async(parse.empty) { implicit request => gotoLogoutSucceeded.andThen { case tryResult => getSessionId(request).map(id => (id, webSocketService.closeConnections(id))) match { case Some((id, true)) => log.info(s"Removing websocket connection of $id due to logout") case Some((id, false)) => log.info(s"There was no websocket connection for session $id") case None => log.info(s"No session available to logout from") } } } def verify = StackAction(parse.empty) { implicit request => Ok(loggedIn.name) } } object SecurityController { val UsernameFormKey = "username" val PasswordFormKey = "password" }
Example 19
Source File: SecurityService.scala From cluster-broccoli with Apache License 2.0 | 5 votes |
package de.frosner.broccoli.services import javax.inject.{Inject, Singleton} import cats.data.{EitherT, OptionT} import cats.instances.future._ import cats.syntax.either._ import com.mohiva.play.silhouette.api.LoginInfo import com.mohiva.play.silhouette.api.services.IdentityService import com.mohiva.play.silhouette.api.util.Credentials import com.mohiva.play.silhouette.impl.exceptions.{IdentityNotFoundException, InvalidPasswordException} import com.mohiva.play.silhouette.impl.providers.CredentialsProvider import de.frosner.broccoli.auth.{Account, AuthConfiguration, AuthMode} import scala.concurrent.{ExecutionContext, Future} sealed trait LoginError object LoginError { final case object InvalidPassword extends LoginError final case object UnknownUser extends LoginError final case object Locked extends LoginError } @Singleton() case class SecurityService @Inject()( configuration: AuthConfiguration, credentialsProvider: CredentialsProvider, identityService: IdentityService[Account] )(implicit ec: ExecutionContext) { private val log = play.api.Logger(getClass) val sessionTimeoutInSeconds: Int = configuration.session.timeout.toSeconds.toInt val allowedFailedLogins: Int = configuration.allowedFailedLogins val authMode: AuthMode = configuration.mode val cookieSecure: Boolean = configuration.cookie.secure val allowMultiLogin: Boolean = configuration.session.allowMultiLogin @volatile private var failedLoginAttempts: Map[String, Int] = Map.empty def authenticate(credentials: Credentials): Future[Option[LoginInfo]] = EitherT .rightT(credentials) .ensure(LoginError.Locked)(c => failedLoginAttempts.getOrElse(c.identifier, 0) <= allowedFailedLogins) .flatMapF(credentialsProvider.authenticate(_).map(_.asRight).recover { case _: InvalidPasswordException => LoginError.InvalidPassword.asLeft case _: IdentityNotFoundException => LoginError.UnknownUser.asLeft }) .leftMap { error => // Login if an account was locked val attempts = failedLoginAttempts.getOrElse(credentials.identifier, 0) if (error == LoginError.Locked) { log.warn( s"Credentials for '${credentials.identifier}' exceeded the allowed number of failed logins: " + s"$allowedFailedLogins (has $attempts)") } // Track the failed attempt failedLoginAttempts = failedLoginAttempts.updated(credentials.identifier, attempts + 1) error } .toOption .value }
Example 20
Source File: UserRepositoryInMemoryInterpreter.scala From scala-pet-store with Apache License 2.0 | 5 votes |
package io.github.pauljamescleary.petstore package infrastructure.repository.inmemory import java.util.Random import cats.implicits._ import cats.Applicative import cats.data.OptionT import domain.users.{User, UserRepositoryAlgebra} import tsec.authentication.IdentityStore import scala.collection.concurrent.TrieMap class UserRepositoryInMemoryInterpreter[F[_]: Applicative] extends UserRepositoryAlgebra[F] with IdentityStore[F, Long, User] { private val cache = new TrieMap[Long, User] private val random = new Random def create(user: User): F[User] = { val id = random.nextLong val toSave = user.copy(id = id.some) cache += (id -> toSave) toSave.pure[F] } def update(user: User): OptionT[F, User] = OptionT { user.id.traverse { id => cache.update(id, user) user.pure[F] } } def get(id: Long): OptionT[F, User] = OptionT.fromOption(cache.get(id)) def delete(id: Long): OptionT[F, User] = OptionT.fromOption(cache.remove(id)) def findByUserName(userName: String): OptionT[F, User] = OptionT.fromOption(cache.values.find(u => u.userName == userName)) def list(pageSize: Int, offset: Int): F[List[User]] = cache.values.toList.sortBy(_.lastName).slice(offset, offset + pageSize).pure[F] def deleteByUserName(userName: String): OptionT[F, User] = OptionT.fromOption( for { user <- cache.values.find(u => u.userName == userName) removed <- cache.remove(user.id.get) } yield removed, ) } object UserRepositoryInMemoryInterpreter { def apply[F[_]: Applicative]() = new UserRepositoryInMemoryInterpreter[F] }
Example 21
Source File: CorrelationIdMiddleware.scala From scala-server-toolkit with MIT License | 5 votes |
package com.avast.sst.http4s.server.middleware import java.util.UUID import cats.data.{Kleisli, OptionT} import cats.effect.Sync import cats.syntax.functor._ import com.avast.sst.http4s.server.middleware.CorrelationIdMiddleware.CorrelationId import io.chrisdavenport.vault.Key import org.http4s.util.CaseInsensitiveString import org.http4s.{Header, HttpRoutes, Request, Response} import org.slf4j.LoggerFactory class CorrelationIdMiddleware[F[_]: Sync]( correlationIdHeaderName: CaseInsensitiveString, attributeKey: Key[CorrelationId], generator: () => String ) { private val logger = LoggerFactory.getLogger(this.getClass) private val F = Sync[F] def wrap(routes: HttpRoutes[F]): HttpRoutes[F] = Kleisli[OptionT[F, *], Request[F], Response[F]] { request => request.headers.get(correlationIdHeaderName) match { case Some(header) => val requestWithAttribute = request.withAttribute(attributeKey, CorrelationId(header.value)) routes(requestWithAttribute).map(r => r.withHeaders(r.headers.put(header))) case None => for { newCorrelationId <- OptionT.liftF(F.delay(generator())) _ <- log(newCorrelationId) requestWithAttribute = request.withAttribute(attributeKey, CorrelationId(newCorrelationId)) response <- routes(requestWithAttribute) } yield response.withHeaders(response.headers.put(Header(correlationIdHeaderName.value, newCorrelationId))) } } def retrieveCorrelationId(request: Request[F]): Option[CorrelationId] = request.attributes.lookup(attributeKey) private def log(newCorrelationId: String) = { OptionT.liftF { F.delay { if (logger.isDebugEnabled()) { logger.debug(s"Generated new correlation ID: $newCorrelationId") } } } } } object CorrelationIdMiddleware { final case class CorrelationId(value: String) extends AnyVal @SuppressWarnings(Array("scalafix:Disable.toString")) def default[F[_]: Sync]: F[CorrelationIdMiddleware[F]] = { Key.newKey[F, CorrelationId].map { attributeKey => new CorrelationIdMiddleware(CaseInsensitiveString("Correlation-ID"), attributeKey, () => UUID.randomUUID().toString) } } }
Example 22
Source File: Streamable.scala From AckCord with MIT License | 5 votes |
package ackcord.util import scala.concurrent.Future import akka.NotUsed import akka.stream.scaladsl.Source import cats.{Foldable, Id} import cats.data.OptionT trait Streamable[F[_]] { def toSource[A](fa: F[A]): Source[A, NotUsed] def optionToSource[A](opt: OptionT[F, A]): Source[A, NotUsed] = toSource(opt.value).mapConcat(_.toList) } object Streamable { def apply[F[_]](implicit F: Streamable[F]): Streamable[F] = F type OptionTRequest[A] = OptionT[Future, A] implicit val idStreamable: Streamable[Id] = new Streamable[Id] { override def toSource[A](fa: Id[A]): Source[A, NotUsed] = Source.single(fa) override def optionToSource[A](opt: OptionT[Id, A]): Source[A, NotUsed] = Source(opt.value.toList) } implicit val futureStreamable: Streamable[Future] = new Streamable[Future] { override def toSource[A](fa: Future[A]): Source[A, NotUsed] = Source.future(fa) } implicit def futureFoldableStreamable[F[_]: Foldable]: Streamable[λ[A => Future[F[A]]]] = new Streamable[λ[A => Future[F[A]]]] { override def toSource[A](fa: Future[F[A]]): Source[A, NotUsed] = { import cats.syntax.all._ Source.future(fa).mapConcat(_.toList) } } implicit val futureOptionTStreamable: Streamable[OptionT[Future, *]] = new Streamable[OptionT[Future, *]] { override def toSource[A](fa: OptionT[Future, A]): Source[A, NotUsed] = Source.future(fa.value).mapConcat(_.toList) } implicit val sourceStreamable: Streamable[Source[*, NotUsed]] = new Streamable[Source[?, NotUsed]] { override def toSource[A](fa: Source[A, NotUsed]): Source[A, NotUsed] = fa } }
Example 23
Source File: MetaMapper.scala From Waves with MIT License | 5 votes |
package com.wavesplatform.lang.contract.meta import cats.implicits._ import cats.data.OptionT import com.wavesplatform.lang.contract.DApp import com.wavesplatform.lang.v1.compiler.Types.FINAL import com.wavesplatform.protobuf.dapp.DAppMeta object MetaMapper { def toProto[V <: MetaVersion](version: V)(data: List[List[FINAL]]): Either[String, DAppMeta] = version.strategy.toProto(data) def dicFromProto(dApp: DApp): Either[String, ParsedMeta] = extractMeta(dApp).value.map(opt => ParsedMeta(dApp.meta.version, opt)) private def extractMeta(dApp: DApp) = for { version <- OptionT(resolveVersion(dApp.meta.version)) data <- OptionT.liftF(version.strategy.fromProto(dApp.meta)) } yield data private def resolveVersion(version: Int): Either[String, Option[MetaVersion]] = version match { case 0 => Right(None) case 1 => Right(Some(V1)) case 2 => Right(Some(V2)) case n if n > 0 => Left(s"Unsupported meta version $n") case n => Left(s"Illegal meta version $n, expected positive value") } }
Example 24
Source File: ArchiveCache.scala From nexus-kg with Apache License 2.0 | 5 votes |
package ch.epfl.bluebrain.nexus.kg.archives import akka.actor.{ActorSystem, NotInfluenceReceiveTimeout} import cats.Monad import cats.data.OptionT import cats.effect.{Effect, Timer} import cats.implicits._ import ch.epfl.bluebrain.nexus.kg.archives.ArchiveCache._ import ch.epfl.bluebrain.nexus.kg.config.AppConfig._ import ch.epfl.bluebrain.nexus.kg.resources.ResId import ch.epfl.bluebrain.nexus.sourcing.StateMachine import ch.epfl.bluebrain.nexus.sourcing.akka.StopStrategy import ch.epfl.bluebrain.nexus.sourcing.akka.statemachine.AkkaStateMachine import retry.RetryPolicy class ArchiveCache[F[_]: Monad](ref: StateMachine[F, String, State, Command, Unit]) { def put(value: Archive): OptionT[F, Archive] = OptionT(ref.evaluate(value.resId.show, Write(value)).map(_.toOption.flatten)) } object ArchiveCache { private[archives] type State = Option[Archive] private[archives] type Command = Write private[archives] final case class Write(bundle: Archive) extends NotInfluenceReceiveTimeout final def apply[F[_]: Timer](implicit as: ActorSystem, cfg: ArchivesConfig, F: Effect[F]): F[ArchiveCache[F]] = { implicit val retryPolicy: RetryPolicy[F] = cfg.cache.retry.retryPolicy[F] val invalidationStrategy = StopStrategy.lapsedSinceLastInteraction[State, Command](cfg.cacheInvalidateAfter) val evaluate: (State, Command) => F[Either[Unit, State]] = { case (None, Write(bundle)) => F.pure(Right(Some(bundle))) case (Some(_), _) => F.pure(Left(())) // It already exists, so we don't want to replace it } AkkaStateMachine .sharded[F]("archives", None, evaluate, invalidationStrategy, cfg.cache.akkaStateMachineConfig, cfg.cache.shards) .map(new ArchiveCache[F](_)) } }
Example 25
Source File: package.scala From nexus-kg with Apache License 2.0 | 5 votes |
package ch.epfl.bluebrain.nexus.kg import akka.http.scaladsl.model.StatusCode import akka.http.scaladsl.server.Directives.complete import akka.http.scaladsl.server.{MalformedQueryParamRejection, Route} import cats.Functor import cats.data.{EitherT, OptionT} import cats.instances.future._ import ch.epfl.bluebrain.nexus.iam.client.types._ import ch.epfl.bluebrain.nexus.kg.marshallers.instances._ import ch.epfl.bluebrain.nexus.kg.resources.Rejection.NotFound.notFound import ch.epfl.bluebrain.nexus.kg.resources.{Ref, Rejection, ResourceV} import ch.epfl.bluebrain.nexus.kg.routes.OutputFormat.{DOT, Triples} import ch.epfl.bluebrain.nexus.rdf.Iri.AbsoluteIri import monix.execution.Scheduler.Implicits.global import scala.concurrent.Future package object routes { private[routes] def completeWithFormat( fetched: Future[Either[Rejection, (StatusCode, ResourceV)]] )(implicit format: NonBinaryOutputFormat): Route = completeWithFormat(EitherT(fetched)) private def completeWithFormat( fetched: EitherT[Future, Rejection, (StatusCode, ResourceV)] )(implicit format: NonBinaryOutputFormat): Route = format match { case f: JsonLDOutputFormat => implicit val format = f complete(fetched.value) case Triples => implicit val format = Triples complete(fetched.map { case (status, resource) => status -> resource.value.graph.ntriples }.value) case DOT => implicit val format = DOT complete(fetched.map { case (status, resource) => status -> resource.value.graph.dot() }.value) } private[routes] val read: Permission = Permission.unsafe("resources/read") private[routes] val schemaError = MalformedQueryParamRejection("schema", "The provided schema does not match the schema on the Uri") private[routes] implicit class FOptionSyntax[F[_], A](private val fOpt: F[Option[A]]) extends AnyVal { def toNotFound(id: AbsoluteIri)(implicit F: Functor[F]): EitherT[F, Rejection, A] = OptionT(fOpt).toRight(notFound(Ref(id))) } }
Example 26
Source File: Http4sUtils.scala From core with Apache License 2.0 | 5 votes |
package com.smartbackpackerapp.http import cats.{Applicative, Monad} import cats.data.{Kleisli, OptionT} import cats.effect.IO import monix.eval.Task import monix.execution.Scheduler.Implicits.global import org.http4s.server.AuthMiddleware import org.http4s.{EntityBody, Request} import scala.concurrent.Await import scala.concurrent.duration.Duration object Http4sUtils { private def authUser[F[_]](implicit F: Applicative[F]): Kleisli[OptionT[F, ?], Request[F], String] = Kleisli(_ => OptionT.liftF(F.pure("access_token"))) def middleware[F[_]: Monad]: AuthMiddleware[F, String] = AuthMiddleware.apply[F, String](authUser) val taskMiddleware: AuthMiddleware[Task, String] = middleware[Task] val ioMiddleware: AuthMiddleware[IO, String] = middleware[IO] implicit class ByteVector2String(body: EntityBody[IO]) { def asString: String = { val array = body.compile.toVector.unsafeRunSync().toArray new String(array.map(_.toChar)) } } implicit class ByteVector2StringTask(body: EntityBody[Task]) { def asString: String = { val array = Await.result(body.compile.toVector.runAsync, Duration.Inf).toArray new String(array.map(_.toChar)) } } }
Example 27
Source File: JwtTokenAuthMiddleware.scala From core with Apache License 2.0 | 5 votes |
package com.smartbackpackerapp.http.auth import cats.data.{EitherT, Kleisli, OptionT} import cats.effect.Sync import cats.syntax.applicativeError._ import cats.syntax.functor._ import com.smartbackpackerapp.http.auth.JwtTokenAuthMiddleware.AuthConfig import org.http4s.Credentials.Token import org.http4s.dsl.Http4sDsl import org.http4s.{AuthScheme, AuthedService, Request} import org.http4s.headers.Authorization import org.http4s.server.AuthMiddleware import tsec.jws.mac.JWTMac import tsec.mac.imports._ object JwtTokenAuthMiddleware { def apply[F[_] : Sync](apiToken: Option[String]): F[AuthMiddleware[F, String]] = new Middleware[F](apiToken).middleware case class AuthConfig(jwtKey: MacSigningKey[HMACSHA256]) } class Middleware[F[_]](apiToken: Option[String])(implicit F: Sync[F]) { private val ifEmpty = F.raiseError[AuthMiddleware[F, String]](new Exception("Api Token not found")) private def generateJwtKey(token: String): F[MacSigningKey[HMACSHA256]] = { F.catchNonFatal(HMACSHA256.buildKeyUnsafe(token.getBytes)) } val middleware: F[AuthMiddleware[F, String]] = apiToken.fold(ifEmpty) { token => generateJwtKey(token).map { jwtKey => val config = AuthConfig(jwtKey) new JwtTokenAuthMiddleware[F](config).middleware } } } class JwtTokenAuthMiddleware[F[_] : Sync](config: AuthConfig) extends Http4sDsl[F] { private val onFailure: AuthedService[String, F] = Kleisli(req => OptionT.liftF(Forbidden(req.authInfo))) private def bearerTokenFromRequest(request: Request[F]): OptionT[F, String] = OptionT.fromOption[F] { request.headers.get(Authorization).collect { case Authorization(Token(AuthScheme.Bearer, token)) => token } } private def verifyToken(request: Request[F], jwtKey: MacSigningKey[HMACSHA256]): OptionT[F, String] = for { token <- bearerTokenFromRequest(request) verified <- OptionT.liftF(JWTMac.verifyAndParse[F, HMACSHA256](token, jwtKey)) accessToken <- OptionT.fromOption[F](verified.body.subject) } yield accessToken private def authUser(jwtKey: MacSigningKey[HMACSHA256]): Kleisli[F, Request[F], Either[String, String]] = Kleisli { request => verifyToken(request, jwtKey).value.map { option => Either.cond[String, String](option.isDefined, option.get, "Unable to authorize token") }.recoverWith { case MacVerificationError(msg) => EitherT.leftT(msg).value } } def middleware: AuthMiddleware[F, String] = AuthMiddleware(authUser(config.jwtKey), onFailure) }
Example 28
Source File: HttpMetricsMiddleware.scala From core with Apache License 2.0 | 5 votes |
package com.smartbackpackerapp.http.metrics import cats.data.{Kleisli, OptionT} import cats.effect.Sync import cats.syntax.flatMap._ import cats.syntax.functor._ import com.codahale.metrics._ import com.smartbackpackerapp.common.Log import com.smartbackpackerapp.http.ApiVersion import org.http4s.AuthedService import org.http4s.Uri.Path object HttpMetricsMiddleware { def apply[F[_]](registry: MetricRegistry, service: AuthedService[String, F]) (implicit F: Sync[F], L: Log[F]): AuthedService[String, F] = { Kleisli { req => OptionT.liftF(F.delay(System.nanoTime())).flatMap { start => service(req).semiflatMap { response => HttpMetrics.parse(req.req.uri.path).fold(F.delay(response)) { path => for { _ <- F.delay(registry.meter(s"requests-$path").mark()) _ <- if (response.status.isSuccess) F.delay(registry.meter(s"success-$path").mark()) else F.delay(registry.meter(s"failure-${response.status.code}-$path").mark()) time <- F.delay((System.nanoTime() - start) / 1000000) _ <- F.delay(registry.histogram(s"response-time-$path").update(time)) _ <- L.info(s"HTTP Response Time: $time ms") } yield response } } } } } } object HttpMetrics { def parse(path: Path): Option[String] = { if (path.contains("/traveling")) Some(s"$ApiVersion-traveling") else if (path.contains("/airlines")) Some(s"$ApiVersion-airlines") else if (path.contains("/ranking")) Some(s"$ApiVersion-ranking") else if (path.contains("/health")) Some(s"$ApiVersion-health") else if (path.contains("/countries")) Some(s"$ApiVersion-countries") else None } }
Example 29
Source File: CollectionsServiceSpec.scala From franklin with Apache License 2.0 | 5 votes |
package com.azavea.franklin.api.services import cats.data.OptionT import cats.effect.IO import cats.implicits._ import com.azavea.franklin.Generators import com.azavea.franklin.api.{TestClient, TestServices} import com.azavea.franklin.database.TestDatabaseSpec import com.azavea.franklin.datamodel.CollectionsResponse import com.azavea.stac4s.StacCollection import com.azavea.stac4s.testing._ import org.http4s.circe.CirceEntityDecoder._ import org.http4s.{Method, Request, Uri} import org.specs2.{ScalaCheck, Specification} import java.net.URLEncoder import java.nio.charset.StandardCharsets class CollectionsServiceSpec extends Specification with ScalaCheck with TestDatabaseSpec with Generators { def is = s2""" This specification verifies that the collections service can run without crashing The collections service should: - create and delete collections $createDeleteCollectionExpectation - list collections $listCollectionsExpectation - get collections by id $getCollectionsExpectation """ val testServices: TestServices[IO] = new TestServices[IO](transactor) val testClient: TestClient[IO] = new TestClient[IO](testServices.collectionsService, testServices.collectionItemsService) def listCollectionsExpectation = prop { (stacCollectionA: StacCollection, stacCollectionB: StacCollection) => { val listIO = ( testClient.getCollectionResource(stacCollectionA), testClient.getCollectionResource(stacCollectionB) ).tupled use { _ => val request = Request[IO](method = Method.GET, Uri.unsafeFromString(s"/collections")) (for { resp <- testServices.collectionsService.routes.run(request) decoded <- OptionT.liftF { resp.as[CollectionsResponse] } } yield decoded).value } val result = listIO.unsafeRunSync.get.collections map { _.id } (result must contain(stacCollectionA.id)) and (result must contain(stacCollectionB.id)) } } def getCollectionsExpectation = prop { (stacCollection: StacCollection) => val fetchIO = testClient.getCollectionResource(stacCollection) use { collection => val encodedId = URLEncoder.encode(collection.id, StandardCharsets.UTF_8.toString) val request = Request[IO](method = Method.GET, Uri.unsafeFromString(s"/collections/$encodedId")) (for { resp <- testServices.collectionsService.routes.run(request) decoded <- OptionT.liftF { resp.as[StacCollection] } } yield (decoded, collection)).value } val (fetched, inserted) = fetchIO.unsafeRunSync.get fetched must beTypedEqualTo(inserted) } // since creation / deletion is a part of the collection resource, and accurate creation is checked // in getCollectionsExpectation, this test just makes sure that if other tests are failing, it's // not because create/delete are broken def createDeleteCollectionExpectation = prop { (stacCollection: StacCollection) => (testClient .getCollectionResource(stacCollection) use { _ => IO.unit }).unsafeRunSync must beTypedEqualTo( () ) } }
Example 30
Source File: DoobieUserRepositoryInterpreter.scala From scala-pet-store with Apache License 2.0 | 5 votes |
package io.github.pauljamescleary.petstore package infrastructure.repository.doobie import cats.data.OptionT import cats.effect.Bracket import cats.implicits._ import doobie._ import doobie.implicits._ import io.circe.parser.decode import io.circe.syntax._ import domain.users.{Role, User, UserRepositoryAlgebra} import io.github.pauljamescleary.petstore.infrastructure.repository.doobie.SQLPagination._ import tsec.authentication.IdentityStore private object UserSQL { // H2 does not support JSON data type. implicit val roleMeta: Meta[Role] = Meta[String].imap(decode[Role](_).leftMap(throw _).merge)(_.asJson.toString) def insert(user: User): Update0 = sql""" INSERT INTO USERS (USER_NAME, FIRST_NAME, LAST_NAME, EMAIL, HASH, PHONE, ROLE) VALUES (${user.userName}, ${user.firstName}, ${user.lastName}, ${user.email}, ${user.hash}, ${user.phone}, ${user.role}) """.update def update(user: User, id: Long): Update0 = sql""" UPDATE USERS SET FIRST_NAME = ${user.firstName}, LAST_NAME = ${user.lastName}, EMAIL = ${user.email}, HASH = ${user.hash}, PHONE = ${user.phone}, ROLE = ${user.role} WHERE ID = $id """.update def select(userId: Long): Query0[User] = sql""" SELECT USER_NAME, FIRST_NAME, LAST_NAME, EMAIL, HASH, PHONE, ID, ROLE FROM USERS WHERE ID = $userId """.query def byUserName(userName: String): Query0[User] = sql""" SELECT USER_NAME, FIRST_NAME, LAST_NAME, EMAIL, HASH, PHONE, ID, ROLE FROM USERS WHERE USER_NAME = $userName """.query[User] def delete(userId: Long): Update0 = sql""" DELETE FROM USERS WHERE ID = $userId """.update val selectAll: Query0[User] = sql""" SELECT USER_NAME, FIRST_NAME, LAST_NAME, EMAIL, HASH, PHONE, ID, ROLE FROM USERS """.query } class DoobieUserRepositoryInterpreter[F[_]: Bracket[?[_], Throwable]](val xa: Transactor[F]) extends UserRepositoryAlgebra[F] with IdentityStore[F, Long, User] { self => import UserSQL._ def create(user: User): F[User] = insert(user).withUniqueGeneratedKeys[Long]("ID").map(id => user.copy(id = id.some)).transact(xa) def update(user: User): OptionT[F, User] = OptionT.fromOption[F](user.id).semiflatMap { id => UserSQL.update(user, id).run.transact(xa).as(user) } def get(userId: Long): OptionT[F, User] = OptionT(select(userId).option.transact(xa)) def findByUserName(userName: String): OptionT[F, User] = OptionT(byUserName(userName).option.transact(xa)) def delete(userId: Long): OptionT[F, User] = get(userId).semiflatMap(user => UserSQL.delete(userId).run.transact(xa).as(user)) def deleteByUserName(userName: String): OptionT[F, User] = findByUserName(userName).mapFilter(_.id).flatMap(delete) def list(pageSize: Int, offset: Int): F[List[User]] = paginate(pageSize, offset)(selectAll).to[List].transact(xa) } object DoobieUserRepositoryInterpreter { def apply[F[_]: Bracket[?[_], Throwable]](xa: Transactor[F]): DoobieUserRepositoryInterpreter[F] = new DoobieUserRepositoryInterpreter(xa) }
Example 31
Source File: DoobieOrderRepositoryInterpreter.scala From scala-pet-store with Apache License 2.0 | 5 votes |
package io.github.pauljamescleary.petstore package infrastructure.repository.doobie import cats.data.OptionT import cats.effect.Bracket import cats.implicits._ import doobie._ import doobie.implicits._ import doobie.implicits.legacy.instant._ import domain.orders.{Order, OrderRepositoryAlgebra, OrderStatus} private object OrderSQL { implicit val StatusMeta: Meta[OrderStatus] = Meta[String].imap(OrderStatus.withName)(_.entryName) def select(orderId: Long): Query0[Order] = sql""" SELECT PET_ID, SHIP_DATE, STATUS, COMPLETE, ID, USER_ID FROM ORDERS WHERE ID = $orderId """.query[Order] def insert(order: Order): Update0 = sql""" INSERT INTO ORDERS (PET_ID, SHIP_DATE, STATUS, COMPLETE, USER_ID) VALUES (${order.petId}, ${order.shipDate}, ${order.status}, ${order.complete}, ${order.userId.get}) """.update def delete(orderId: Long): Update0 = sql""" DELETE FROM ORDERS WHERE ID = $orderId """.update } class DoobieOrderRepositoryInterpreter[F[_]: Bracket[?[_], Throwable]](val xa: Transactor[F]) extends OrderRepositoryAlgebra[F] { import OrderSQL._ def create(order: Order): F[Order] = insert(order) .withUniqueGeneratedKeys[Long]("ID") .map(id => order.copy(id = id.some)) .transact(xa) def get(orderId: Long): F[Option[Order]] = OrderSQL.select(orderId).option.transact(xa) def delete(orderId: Long): F[Option[Order]] = OptionT(get(orderId)) .semiflatMap(order => OrderSQL.delete(orderId).run.transact(xa).as(order)) .value } object DoobieOrderRepositoryInterpreter { def apply[F[_]: Bracket[?[_], Throwable]]( xa: Transactor[F], ): DoobieOrderRepositoryInterpreter[F] = new DoobieOrderRepositoryInterpreter(xa) }
Example 32
Source File: MessageHandler.scala From gospeak with Apache License 2.0 | 5 votes |
package gospeak.web.services import java.time.LocalDateTime import cats.data.OptionT import cats.effect.IO import gospeak.core.ApplicationConf import gospeak.core.domain.Group import gospeak.core.domain.Group.Settings.Action import gospeak.core.domain.Group.Settings.Action.Trigger import gospeak.core.domain.messages.Message import gospeak.core.domain.utils.Constants import gospeak.core.services.email.EmailSrv import gospeak.core.services.slack.SlackSrv import gospeak.core.services.storage.{OrgaGroupRepo, OrgaGroupSettingsRepo} import gospeak.core.services.twitter.{Tweets, TwitterSrv} import gospeak.libs.scala.Extensions._ import gospeak.libs.scala.domain.{CustomException, EmailAddress} import gospeak.web.services.MessageSrv._ import io.circe.Json import org.slf4j.LoggerFactory import scala.util.control.NonFatal class MessageHandler(appConf: ApplicationConf, groupRepo: OrgaGroupRepo, groupSettingsRepo: OrgaGroupSettingsRepo, emailSrv: EmailSrv, slackSrv: SlackSrv, twitterSrv: TwitterSrv) { private val logger = LoggerFactory.getLogger(this.getClass) def groupActionHandler(msg: Message): IO[Unit] = (msg match { case m: Message.GroupMessage => handleGroupAction(m.group.slug, m, eMessage(m)) case _ => IO.pure(0) }).map(_ => ()).recover { case NonFatal(_) => () } def gospeakHandler(msg: Message): IO[Unit] = (msg match { case m: Message.ExternalCfpCreated => gospeakTwitt(m) case _ => IO.pure(0) }).map(_ => ()).recover { case NonFatal(_) => () } def logHandler(msg: Message): IO[Unit] = IO.pure(logger.info(s"Message sent: $msg")) private def handleGroupAction(group: Group.Slug, msg: Message.GroupMessage, data: Json): IO[Int] = (for { groupElt <- OptionT(groupRepo.find(group)) actions <- OptionT.liftF(groupSettingsRepo.findActions(groupElt.id)) accounts <- OptionT.liftF(groupSettingsRepo.findAccounts(groupElt.id)) actionsToExec = Trigger.all.filter(_.message == msg.ref).flatMap(actions.getOrElse(_, Seq())) results <- OptionT.liftF(actionsToExec.map(execGroupAction(accounts, _, data)).sequence) } yield results.length).value.map(_.getOrElse(0)) private def execGroupAction(accounts: Group.Settings.Accounts, action: Action, data: Json): IO[Unit] = action match { case email: Action.Email => (for { to <- email.to.render(data).left.map(e => CustomException(e.message)).flatMap(EmailAddress.from).map(EmailAddress.Contact(_)) subject <- email.subject.render(data).left.map(e => CustomException(e.message)) content <- email.content.render(data).map(_.toHtml).leftMap(e => CustomException(e.message)) } yield emailSrv.send(EmailSrv.Email( from = Constants.Gospeak.noreplyEmail, to = Seq(to), subject = subject, content = EmailSrv.HtmlContent(content.value) ))).toIO.flatMap(identity).map(_ => ()) case Action.Slack(slack) => accounts.slack.map(slackSrv.exec(slack, data, _, appConf.aesKey)).getOrElse(IO.raiseError(CustomException("No credentials for Slack"))) } private def gospeakTwitt(msg: Message.ExternalCfpCreated): IO[Int] = { if (msg.cfp.isActive(LocalDateTime.now())) { twitterSrv.tweet(Tweets.externalCfpCreated(msg)).map(_ => 1) } else { IO.pure(0) } } }
Example 33
Source File: AuthRepo.scala From gospeak with Apache License 2.0 | 5 votes |
package gospeak.web.auth.services import cats.data.OptionT import com.mohiva.play.silhouette.api.LoginInfo import com.mohiva.play.silhouette.api.services.IdentityService import com.mohiva.play.silhouette.api.util.PasswordInfo import com.mohiva.play.silhouette.persistence.daos.DelegableAuthInfoDAO import gospeak.core.domain.User import gospeak.core.domain.User._ import gospeak.core.services.storage.{AuthGroupRepo, AuthUserRepo} import gospeak.web.auth.domain.AuthUser import gospeak.libs.scala.domain.Done import scala.concurrent.Future import scala.reflect.ClassTag // TODO merge it with AuthSrv class AuthRepo(userRepo: AuthUserRepo, groupRepo: AuthGroupRepo) extends DelegableAuthInfoDAO[PasswordInfo] with IdentityService[AuthUser] { override val classTag: ClassTag[PasswordInfo] = scala.reflect.classTag[PasswordInfo] override def retrieve(loginInfo: LoginInfo): Future[Option[AuthUser]] = (for { user <- OptionT(userRepo.find(toDomain(loginInfo))) groups <- OptionT.liftF(groupRepo.list(user.id)) } yield AuthUser(loginInfo, user, groups)).value.unsafeToFuture() override def find(loginInfo: LoginInfo): Future[Option[PasswordInfo]] = userRepo.findCredentials(toDomain(loginInfo)).map(_.map(toSilhouette)).unsafeToFuture() override def add(loginInfo: LoginInfo, authInfo: PasswordInfo): Future[PasswordInfo] = userRepo.createCredentials(toDomain(loginInfo, authInfo)).map(toSilhouette).unsafeToFuture() override def update(loginInfo: LoginInfo, authInfo: PasswordInfo): Future[PasswordInfo] = userRepo.editCredentials(toDomain(loginInfo))(toDomain(authInfo)).map(_ => authInfo).unsafeToFuture() // add or update override def save(loginInfo: LoginInfo, authInfo: PasswordInfo): Future[PasswordInfo] = userRepo.findCredentials(toDomain(loginInfo)).flatMap { opt => opt.map(_ => userRepo.editCredentials(toDomain(loginInfo))(toDomain(authInfo))) .getOrElse(userRepo.createCredentials(toDomain(loginInfo, authInfo)).map(_ => Done)) }.map(_ => authInfo).unsafeToFuture() override def remove(loginInfo: LoginInfo): Future[Unit] = userRepo.removeCredentials(toDomain(loginInfo)).map(_ => ()).unsafeToFuture() private def toDomain(loginInfo: LoginInfo): User.Login = User.Login(ProviderId(loginInfo.providerID), ProviderKey(loginInfo.providerKey)) private def toDomain(authInfo: PasswordInfo): User.Password = User.Password(Hasher(authInfo.hasher), PasswordValue(authInfo.password), authInfo.salt.map(Salt)) private def toDomain(loginInfo: LoginInfo, authInfo: PasswordInfo): User.Credentials = User.Credentials(toDomain(loginInfo), toDomain(authInfo)) private def toSilhouette(p: User.Password): PasswordInfo = PasswordInfo(p.hasher.value, p.password.value, p.salt.map(_.value)) private def toSilhouette(c: User.Credentials): PasswordInfo = toSilhouette(c.pass) }
Example 34
Source File: ApiCfpCtrl.scala From gospeak with Apache License 2.0 | 5 votes |
package gospeak.web.api.orga import cats.data.OptionT import com.mohiva.play.silhouette.api.Silhouette import gospeak.core.domain.{Cfp, Group} import gospeak.core.services.storage.{OrgaCfpRepo, OrgaGroupRepo, OrgaUserRepo} import gospeak.web.AppConf import gospeak.web.api.domain.ApiCfp import gospeak.web.api.domain.utils.ApiResult import gospeak.web.auth.domain.CookieEnv import gospeak.web.utils.ApiCtrl import gospeak.libs.scala.domain.Page import play.api.mvc.{Action, AnyContent, ControllerComponents} class ApiCfpCtrl(cc: ControllerComponents, silhouette: Silhouette[CookieEnv], conf: AppConf, userRepo: OrgaUserRepo, val groupRepo: OrgaGroupRepo, cfpRepo: OrgaCfpRepo) extends ApiCtrl(cc, silhouette, conf) with ApiCtrl.OrgaAction { def list(group: Group.Slug, params: Page.Params): Action[AnyContent] = OrgaAction[Seq[ApiCfp.Orga]](group) { implicit req => for { cfps <- cfpRepo.list(params) users <- userRepo.list(cfps.items.flatMap(_.users)) } yield ApiResult.of(cfps, (c: Cfp) => ApiCfp.orga(c, users)) } def detail(group: Group.Slug, cfp: Cfp.Slug): Action[AnyContent] = OrgaAction[ApiCfp.Orga](group) { implicit req => (for { cfpElt <- OptionT(cfpRepo.find(cfp)) users <- OptionT.liftF(userRepo.list(cfpElt.users)) res = ApiResult.of(ApiCfp.orga(cfpElt, users)) } yield res).value.map(_.getOrElse(cfpNotFound(cfp))) } // TODO def userRatings(group: Group.Slug, cfp: Cfp.Slug) }
Example 35
Source File: ApiEventCtrl.scala From gospeak with Apache License 2.0 | 5 votes |
package gospeak.web.api.orga import cats.data.OptionT import com.mohiva.play.silhouette.api.Silhouette import gospeak.core.domain.{Event, Group} import gospeak.core.services.storage.{OrgaEventRepo, OrgaGroupRepo, OrgaProposalRepo, OrgaUserRepo} import gospeak.web.AppConf import gospeak.web.api.domain.ApiEvent import gospeak.web.api.domain.utils.ApiResult import gospeak.web.auth.domain.CookieEnv import gospeak.web.utils.ApiCtrl import gospeak.libs.scala.domain.Page import play.api.mvc.{Action, AnyContent, ControllerComponents} class ApiEventCtrl(cc: ControllerComponents, silhouette: Silhouette[CookieEnv], conf: AppConf, userRepo: OrgaUserRepo, val groupRepo: OrgaGroupRepo, eventRepo: OrgaEventRepo, proposalRepo: OrgaProposalRepo) extends ApiCtrl(cc, silhouette, conf) with ApiCtrl.OrgaAction { def list(group: Group.Slug, params: Page.Params): Action[AnyContent] = OrgaAction[Seq[ApiEvent.Orga]](group) { implicit req => for { events <- eventRepo.listFull(params) proposals <- proposalRepo.list(events.items.flatMap(_.talks)) users <- userRepo.list(events.items.flatMap(_.users) ++ proposals.flatMap(_.users)) } yield ApiResult.of(events, (e: Event.Full) => ApiEvent.orga(e, proposals, users)) } def detail(group: Group.Slug, event: Event.Slug): Action[AnyContent] = OrgaAction[ApiEvent.Orga](group) { implicit req => (for { eventElt <- OptionT(eventRepo.findFull(event)) proposals <- OptionT.liftF(proposalRepo.list(eventElt.talks)) users <- OptionT.liftF(userRepo.list(eventElt.users ++ proposals.flatMap(_.users))) res = ApiResult.of(ApiEvent.orga(eventElt, proposals, users)) } yield res).value.map(_.getOrElse(eventNotFound(group, event))) } }
Example 36
Source File: ApiProposalCtrl.scala From gospeak with Apache License 2.0 | 5 votes |
package gospeak.web.api.orga import cats.data.OptionT import com.mohiva.play.silhouette.api.Silhouette import gospeak.core.domain.{Cfp, Group, Proposal} import gospeak.core.services.storage._ import gospeak.web.AppConf import gospeak.web.api.domain.utils.ApiResult import gospeak.web.api.domain.{ApiComment, ApiProposal} import gospeak.web.auth.domain.CookieEnv import gospeak.web.utils.ApiCtrl import gospeak.libs.scala.domain.Page import play.api.mvc.{Action, AnyContent, ControllerComponents} class ApiProposalCtrl(cc: ControllerComponents, silhouette: Silhouette[CookieEnv], conf: AppConf, userRepo: OrgaUserRepo, val groupRepo: OrgaGroupRepo, cfpRepo: OrgaCfpRepo, proposalRepo: OrgaProposalRepo, commentRepo: OrgaCommentRepo, userRequestRepo: OrgaUserRequestRepo) extends ApiCtrl(cc, silhouette, conf) with ApiCtrl.OrgaAction { def list(group: Group.Slug, cfp: Cfp.Slug, params: Page.Params): Action[AnyContent] = OrgaAction[Seq[ApiProposal.Orga]](group) { implicit req => for { proposals <- proposalRepo.listFull(cfp, params) users <- userRepo.list(proposals.items.flatMap(_.users)) } yield ApiResult.of(proposals, (p: Proposal.Full) => ApiProposal.orga(p, users)) } def listAll(group: Group.Slug, params: Page.Params): Action[AnyContent] = OrgaAction[Seq[ApiProposal.Orga]](group) { implicit req => for { proposals <- proposalRepo.listFull(params) users <- userRepo.list(proposals.items.flatMap(_.users)) } yield ApiResult.of(proposals, (p: Proposal.Full) => ApiProposal.orga(p, users)) } def detail(group: Group.Slug, cfp: Cfp.Slug, proposal: Proposal.Id): Action[AnyContent] = OrgaAction[ApiProposal.Orga](group) { implicit req => (for { proposalElt <- OptionT(proposalRepo.findFull(cfp, proposal)) users <- OptionT.liftF(userRepo.list(proposalElt.users)) res = ApiResult.of(ApiProposal.orga(proposalElt, users)) } yield res).value.map(_.getOrElse(proposalNotFound(cfp, proposal))) } def ratings(group: Group.Slug, cfp: Cfp.Slug, proposal: Proposal.Id): Action[AnyContent] = OrgaAction[Seq[ApiProposal.Orga.Rating]](group) { implicit req => for { ratings <- proposalRepo.listRatings(proposal) users <- userRepo.list(ratings.flatMap(_.users)) } yield ApiResult.of(ratings.map(ApiProposal.Orga.Rating.from(_, users))) } def speakerComments(group: Group.Slug, cfp: Cfp.Slug, proposal: Proposal.Id): Action[AnyContent] = OrgaAction[Seq[ApiComment]](group) { implicit req => commentRepo.getComments(proposal).map(comments => ApiResult.of(comments.map(ApiComment.from))) } def orgaComments(group: Group.Slug, cfp: Cfp.Slug, proposal: Proposal.Id): Action[AnyContent] = OrgaAction[Seq[ApiComment]](group) { implicit req => commentRepo.getOrgaComments(proposal).map(comments => ApiResult.of(comments.map(ApiComment.from))) } def invites(group: Group.Slug, cfp: Cfp.Slug, proposal: Proposal.Id): Action[AnyContent] = OrgaAction[Int](group) { implicit req => userRequestRepo.listPendingInvites(proposal) ??? // TODO } }
Example 37
Source File: CredentialStore.scala From tsec with MIT License | 5 votes |
package tsec.authentication.credentials import cats.data.OptionT import cats.effect.Sync import cats.syntax.all._ import tsec.passwordhashers._ import tsec.passwordhashers.jca._ def updateCredentials(credentials: C, update: P => F[Unit]): F[Unit] = putCredentials(credentials, update) def isAuthenticated(credentials: C): F[Boolean] } abstract class PasswordStore[F[_], Id, P](implicit P: PasswordHasher[F, P], F: Sync[F]) extends CredentialStore[F, RawCredentials[Id], PasswordHash[P]] { def retrievePass(id: Id): OptionT[F, PasswordHash[P]] def putCredentials(credentials: RawCredentials[Id], put: PasswordHash[P] => F[Unit]): F[Unit] = for { hash <- P.hashpw(credentials.rawPassword) _ <- put(hash) } yield () def putCredentials(raw: Array[Byte], put: PasswordHash[P] => F[Unit]): F[Unit] = for { hash <- P.hashpw(raw) _ <- put(hash) } yield () def putCredentials(raw: Array[Char], put: PasswordHash[P] => F[Unit]): F[Unit] = for { hash <- P.hashpw(raw) _ <- put(hash) } yield () def isAuthenticated(credentials: RawCredentials[Id]): F[Boolean] = for { pass <- retrievePass(credentials.identity) .getOrElseF(F.raiseError(CredentialsError("No such user"))) check <- P.checkpwBool(credentials.rawPassword, pass) } yield check def isAuthenticated(id: Id, raw: Array[Byte]): F[Boolean] = for { pass <- retrievePass(id) .getOrElseF(F.raiseError(CredentialsError("No such user"))) check <- P.checkpwBool(raw, pass) } yield check def isAuthenticated(id: Id, raw: Array[Char]): F[Boolean] = for { pass <- retrievePass(id) .getOrElseF(F.raiseError(CredentialsError("No such user"))) check <- P.checkpwBool(raw, pass) } yield check } trait SCryptPasswordStore[F[_], Id] extends PasswordStore[F, Id, SCrypt] trait BCryptPasswordStore[F[_], Id] extends PasswordStore[F, Id, BCrypt]
Example 38
Source File: KamonSupport.scala From kamon-http4s with Apache License 2.0 | 5 votes |
package kamon.http4s package middleware.server import cats.data.{Kleisli, OptionT} import cats.effect.{Resource, Sync} import cats.implicits._ import kamon.Kamon import kamon.context.Storage import kamon.instrumentation.http.HttpServerInstrumentation.RequestHandler import kamon.instrumentation.http.HttpServerInstrumentation import org.http4s.{HttpRoutes, Request, Response} object KamonSupport { def apply[F[_]: Sync](service: HttpRoutes[F], interface: String, port: Int): HttpRoutes[F] = { val httpServerConfig = Kamon.config().getConfig("kamon.instrumentation.http4s.server") val instrumentation = HttpServerInstrumentation.from(httpServerConfig, "http4s.server", interface, port) Kleisli(kamonService[F](service, instrumentation)(_)) } private def kamonService[F[_]](service: HttpRoutes[F], instrumentation: HttpServerInstrumentation) (request: Request[F]) (implicit F: Sync[F]): OptionT[F, Response[F]] = OptionT { getHandler(instrumentation)(request).use { handler => for { resOrUnhandled <- service(request).value.attempt respWithContext <- kamonServiceHandler(handler, resOrUnhandled, instrumentation.settings) } yield respWithContext } } private def processRequest[F[_]](requestHandler: RequestHandler)(implicit F: Sync[F]): Resource[F, RequestHandler] = Resource.make(F.delay(requestHandler.requestReceived()))(h => F.delay(h.responseSent())) private def withContext[F[_]](requestHandler: RequestHandler)(implicit F: Sync[F]): Resource[F, Storage.Scope] = Resource.make(F.delay(Kamon.storeContext(requestHandler.context)))( scope => F.delay(scope.close())) private def getHandler[F[_]](instrumentation: HttpServerInstrumentation)(request: Request[F])(implicit F: Sync[F]): Resource[F, RequestHandler] = for { handler <- Resource.liftF(F.delay(instrumentation.createHandler(buildRequestMessage(request)))) _ <- processRequest(handler) _ <- withContext(handler) } yield handler private def kamonServiceHandler[F[_]](requestHandler: RequestHandler, e: Either[Throwable, Option[Response[F]]], settings: HttpServerInstrumentation.Settings) (implicit F: Sync[F]): F[Option[Response[F]]] = e match { case Left(e) => F.delay { requestHandler.span.fail(e.getMessage) Some(requestHandler.buildResponse(errorResponseBuilder, requestHandler.context)) } *> F.raiseError(e) case Right(None) => F.delay { requestHandler.span.name(settings.unhandledOperationName) val response: Response[F] = requestHandler.buildResponse[Response[F]]( notFoundResponseBuilder, requestHandler.context ) Some(response) } case Right(Some(response)) => F.delay { val a = requestHandler.buildResponse(getResponseBuilder(response), requestHandler.context) Some(a) } } }
Example 39
Source File: package.scala From tsec with MIT License | 5 votes |
package tsec import cats.data.OptionT import org.bouncycastle.util.encoders.Hex import org.http4s.server.Middleware import org.http4s.{Request, Response} import tsec.common.ManagedRandom package object csrf { type CSRFToken = CSRFToken.Token object CSRFToken extends ManagedRandom { type Token <: String def apply(s: String): CSRFToken = s.asInstanceOf[CSRFToken] def subst[F[_]](value: F[String]): F[CSRFToken] = value.asInstanceOf[F[CSRFToken]] def generateHexBase(tokenLength: Int = 32): String = { val tokenBytes = new Array[Byte](tokenLength) nextBytes(tokenBytes) Hex.toHexString(tokenBytes) } } type CSRFMiddleware[F[_]] = Middleware[OptionT[F, ?], Request[F], Response[F], Request[F], Response[F]] }
Example 40
Source File: BLPAuthorization.scala From tsec with MIT License | 5 votes |
package tsec.authorization import cats.MonadError import cats.data.OptionT import cats.syntax.functor._ import tsec.authentication sealed abstract case class BLPWriteAction[F[_], Role, A, Auth](authLevel: Role)( implicit authInfo: AuthorizationInfo[F, Role, A], enum: SimpleAuthEnum[Role, Int], F: MonadError[F, Throwable] ) extends BLPAuthorization[F, A, Auth] { def isAuthorized( toAuth: authentication.SecuredRequest[F, A, Auth] ): OptionT[F, authentication.SecuredRequest[F, A, Auth]] = { val out = authInfo.fetchInfo(toAuth.identity).map { info => val userAuthLevel = enum.getRepr(info) if (enum.contains(info) && userAuthLevel == enum.getRepr(authLevel)) Some(toAuth) else None } OptionT(out) } } object BLPWriteAction { def apply[F[_], Role, A, Auth](authLevel: Role)( implicit authInfo: AuthorizationInfo[F, Role, A], enum: SimpleAuthEnum[Role, Int], F: MonadError[F, Throwable] ): F[BLPWriteAction[F, Role, A, Auth]] = if (enum.getRepr(authLevel) < 0) F.raiseError(InvalidAuthLevelError) else F.pure(new BLPWriteAction[F, Role, A, Auth](authLevel) {}) }
Example 41
Source File: BasicDAC.scala From tsec with MIT License | 5 votes |
package tsec.authorization import cats.{Eq, MonadError} import cats.data.OptionT import cats.syntax.all._ import tsec.authentication.SecuredRequest abstract class BasicDAC[F[_], G, U, Auth](implicit eq: Eq[G], F: MonadError[F, Throwable]) extends Authorization[F, U, Auth] { def fetchGroup: F[AuthGroup[G]] def fetchOwner: F[G] def fetchAccess(u: SecuredRequest[F, U, Auth]): F[G] def isAuthorized(toAuth: SecuredRequest[F, U, Auth]): OptionT[F, SecuredRequest[F, U, Auth]] = { val out = for { owner <- fetchOwner group <- fetchGroup access <- fetchAccess(toAuth) } yield { if (eq.eqv(access, owner) || group.contains(access)) Some(toAuth) else None } OptionT(out) } }
Example 42
Source File: DynamicRBAC.scala From tsec with MIT License | 5 votes |
package tsec.authorization import cats.MonadError import cats.data.OptionT import cats.syntax.all._ import tsec.authentication case class DynamicRBAC[F[_], Role, U, Auth](dynamic: DynamicAuthGroup[F, Role])( implicit authInfo: AuthorizationInfo[F, Role, U], enum: SimpleAuthEnum[Role, String], F: MonadError[F, Throwable] ) extends Authorization[F, U, Auth] { def isAuthorized( toAuth: authentication.SecuredRequest[F, U, Auth] ): OptionT[F, authentication.SecuredRequest[F, U, Auth]] = OptionT(for { info <- authInfo.fetchInfo(toAuth.identity) group <- dynamic.fetchGroupInfo } yield { if (enum.contains(info) && group.contains(info)) Some(toAuth) else None }) }
Example 43
Source File: Authorization.scala From tsec with MIT License | 5 votes |
package tsec.authorization import cats.Monad import cats.data.OptionT import cats.kernel.Monoid import tsec.authentication.SecuredRequest trait Authorization[F[_], Identity, Auth] { def isAuthorized(toAuth: SecuredRequest[F, Identity, Auth]): OptionT[F, SecuredRequest[F, Identity, Auth]] } object Authorization { implicit def authorizationMonoid[F[_]: Monad, I, Auth]: Monoid[Authorization[F, I, Auth]] = new Monoid[Authorization[F, I, Auth]] { def empty: Authorization[F, I, Auth] = new Authorization[F, I, Auth] { def isAuthorized(toAuth: SecuredRequest[F, I, Auth]): OptionT[F, SecuredRequest[F, I, Auth]] = OptionT.pure(toAuth) } def combine(x: Authorization[F, I, Auth], y: Authorization[F, I, Auth]): Authorization[F, I, Auth] = new Authorization[F, I, Auth] { def isAuthorized(toAuth: SecuredRequest[F, I, Auth]): OptionT[F, SecuredRequest[F, I, Auth]] = x.isAuthorized(toAuth).flatMap(y.isAuthorized) } } }
Example 44
Source File: BasicRBAC.scala From tsec with MIT License | 5 votes |
package tsec.authorization import cats.MonadError import cats.data.OptionT import cats.syntax.functor._ import tsec.authentication import scala.reflect.ClassTag sealed abstract case class BasicRBAC[F[_], R, U, Auth](authorized: AuthGroup[R])( implicit role: AuthorizationInfo[F, R, U], enum: SimpleAuthEnum[R, String], F: MonadError[F, Throwable] ) extends Authorization[F, U, Auth] { def isAuthorized( toAuth: authentication.SecuredRequest[F, U, Auth] ): OptionT[F, authentication.SecuredRequest[F, U, Auth]] = OptionT { role.fetchInfo(toAuth.identity).map { extractedRole => if (enum.contains(extractedRole) && authorized.contains(extractedRole)) Some(toAuth) else None } } } object BasicRBAC { def apply[F[_], R: ClassTag, U, Auth](roles: R*)( implicit enum: SimpleAuthEnum[R, String], role: AuthorizationInfo[F, R, U], F: MonadError[F, Throwable] ): BasicRBAC[F, R, U, Auth] = fromGroup[F, R, U, Auth](AuthGroup(roles: _*)) def fromGroup[F[_], R: ClassTag, U, Auth](valueSet: AuthGroup[R])( implicit role: AuthorizationInfo[F, R, U], enum: SimpleAuthEnum[R, String], F: MonadError[F, Throwable] ): BasicRBAC[F, R, U, Auth] = new BasicRBAC[F, R, U, Auth](valueSet) {} def all[F[_], R: ClassTag, U, Auth]( implicit enum: SimpleAuthEnum[R, String], role: AuthorizationInfo[F, R, U], F: MonadError[F, Throwable] ): BasicRBAC[F, R, U, Auth] = new BasicRBAC[F, R, U, Auth](enum.viewAll) {} }
Example 45
Source File: HierarchyAuth.scala From tsec with MIT License | 5 votes |
package tsec.authorization import cats.MonadError import cats.data.OptionT import cats.syntax.functor._ import tsec.authentication sealed abstract case class HierarchyAuth[F[_], R, U, Auth](authLevel: R)( implicit role: AuthorizationInfo[F, R, U], enum: SimpleAuthEnum[R, Int], F: MonadError[F, Throwable] ) extends Authorization[F, U, Auth] { def isAuthorized( toAuth: authentication.SecuredRequest[F, U, Auth] ): OptionT[F, authentication.SecuredRequest[F, U, Auth]] = OptionT { role.fetchInfo(toAuth.identity).map { authRole => val intRepr = enum.getRepr(authRole) if (0 <= intRepr && intRepr <= enum.getRepr(authLevel) && enum.contains(authRole)) Some(toAuth) else None } } } object HierarchyAuth { def apply[F[_], R, U, Auth](auth: R)( implicit role: AuthorizationInfo[F, R, U], e: SimpleAuthEnum[R, Int], F: MonadError[F, Throwable] ): F[HierarchyAuth[F, R, U, Auth]] = if (e.getRepr(auth) < 0) F.raiseError[HierarchyAuth[F, R, U, Auth]](InvalidAuthLevelError) else F.pure(new HierarchyAuth[F, R, U, Auth](auth) {}) }
Example 46
Source File: RepoConfigAlg.scala From scala-steward with Apache License 2.0 | 5 votes |
package org.scalasteward.core.repoconfig import better.files.File import cats.data.OptionT import cats.implicits._ import io.chrisdavenport.log4cats.Logger import io.circe.config.parser import org.scalasteward.core.application.Config import org.scalasteward.core.data.Update import org.scalasteward.core.io.{FileAlg, WorkspaceAlg} import org.scalasteward.core.repoconfig.RepoConfigAlg._ import org.scalasteward.core.util.MonadThrowable import org.scalasteward.core.vcs.data.Repo final class RepoConfigAlg[F[_]](implicit config: Config, fileAlg: FileAlg[F], logger: Logger[F], workspaceAlg: WorkspaceAlg[F], F: MonadThrowable[F] ) { def readRepoConfigOrDefault(repo: Repo): F[RepoConfig] = readRepoConfig(repo).flatMap { config => config.map(F.pure).getOrElse(defaultRepoConfig) } val defaultRepoConfig: F[RepoConfig] = OptionT .fromOption[F](config.defaultRepoConfigFile) .flatMap(readRepoConfigFromFile) .getOrElse(RepoConfig.empty) def readRepoConfig(repo: Repo): F[Option[RepoConfig]] = workspaceAlg .repoDir(repo) .flatMap(dir => readRepoConfigFromFile(dir / repoConfigBasename).value) private def readRepoConfigFromFile(configFile: File): OptionT[F, RepoConfig] = OptionT(fileAlg.readFile(configFile)).map(parseRepoConfig).flatMapF { case Right(repoConfig) => logger.info(s"Parsed $repoConfig").as(repoConfig.some) case Left(errorMsg) => logger.info(errorMsg).as(none[RepoConfig]) } } object RepoConfigAlg { val repoConfigBasename: String = ".scala-steward.conf" def parseRepoConfig(input: String): Either[String, RepoConfig] = parser.decode[RepoConfig](input).leftMap { error => s"Failed to parse $repoConfigBasename: ${error.getMessage}" } def configToIgnoreFurtherUpdates(update: Update): String = update match { case s: Update.Single => s"""updates.ignore = [ { groupId = "${s.groupId}", artifactId = "${s.artifactId.name}" } ]""" case g: Update.Group => s"""updates.ignore = [ { groupId = "${g.groupId}" } ]""" } }
Example 47
Source File: PartialStatelessJWTAuth.scala From tsec with MIT License | 5 votes |
package tsec.authentication.internal import java.time.Instant import cats.data.OptionT import cats.effect.Sync import cats.syntax.all._ import io.circe.parser.decode import io.circe.syntax._ import io.circe.{Decoder, Encoder} import org.http4s._ import tsec.authentication._ import tsec.common._ import tsec.jws.mac._ import tsec.jwt.algorithms.JWTMacAlgo import tsec.jwt.{JWTClaims, JWTPrinter} import tsec.mac.jca._ import scala.concurrent.duration._ def discard(authenticator: AugmentedJWT[A, I]): F[AugmentedJWT[A, I]] = for { now <- F.delay(Instant.now) jwt <- JWTMac .build[F, A]( authenticator.jwt.body .withExpiry(now) .withJwtID(SecureRandomId.Interactive.generate), signingKey ) } yield AugmentedJWT(authenticator.id, jwt, authenticator.identity, now, authenticator.lastTouched) }
Example 48
Source File: StatelessJWTAuth.scala From tsec with MIT License | 5 votes |
package tsec.authentication.internal import java.time.Instant import cats.data.OptionT import cats.effect.Sync import cats.syntax.all._ import io.circe.syntax._ import io.circe.{Decoder, Encoder} import org.http4s._ import tsec.authentication._ import tsec.common._ import tsec.jws.mac._ import tsec.jwt.algorithms.JWTMacAlgo import tsec.jwt.JWTClaims import tsec.mac.MAC import tsec.mac.jca._ import scala.concurrent.duration._ private[tsec] abstract class StatelessJWTAuth[F[_], V: Decoder: Encoder.AsObject, A: JWTMacAlgo]( val expiry: FiniteDuration, val maxIdle: Option[FiniteDuration], signingKey: MacSigningKey[A] )(implicit F: Sync[F], cv: JWSMacCV[F, A]) extends JWTAuthenticator[F, V, V, A] { private[tsec] def verifyLastTouched(body: JWTMac[A], now: Instant): F[Option[Instant]] def parseRaw(raw: String, request: Request[F]): OptionT[F, SecuredRequest[F, V, AugmentedJWT[A, V]]] = OptionT( (for { now <- F.delay(Instant.now()) extracted <- cv.verifyAndParse(raw, signingKey, now) jwtid <- cataOption(extracted.id) body <- extracted.body.asF[F, V] expiry <- cataOption(extracted.body.expiration) lastTouched <- verifyLastTouched(extracted, now) augmented = AugmentedJWT( SecureRandomId.coerce(jwtid), extracted, body, expiry, lastTouched ) refreshed <- refresh(augmented) } yield SecuredRequest(request, body, refreshed).some) .handleError(_ => None) ) def create(body: V): F[AugmentedJWT[A, V]] = for { now <- F.delay(Instant.now()) jwtId <- SecureRandomId.Interactive.generateF[F] expiryTime = now.plusSeconds(expiry.toSeconds) lastTouched = touch(now) claims = JWTClaims( issuedAt = touch(now), jwtId = Some(jwtId), expiration = Some(expiryTime), customFields = body.asJsonObject.toList ) out <- JWTMac.build[F, A](claims, signingKey) } yield AugmentedJWT(jwtId, out, body, expiryTime, lastTouched) def update(authenticator: AugmentedJWT[A, V]): F[AugmentedJWT[A, V]] = F.pure(authenticator) def renew(authenticator: AugmentedJWT[A, V]): F[AugmentedJWT[A, V]] = for { now <- F.delay(Instant.now()) updatedExpiry = now.plusSeconds(expiry.toSeconds) authBody = authenticator.jwt.body lastTouched = touch(now) jwt <- JWTMac.build( authBody.withIATOption(lastTouched).withExpiry(updatedExpiry), signingKey ) } yield AugmentedJWT(authenticator.id, jwt, authenticator.identity, updatedExpiry, lastTouched) def discard(authenticator: AugmentedJWT[A, V]): F[AugmentedJWT[A, V]] = F.pure(authenticator.copy(jwt = JWTMac.buildToken[A](JWSMacHeader[A], JWTClaims(), MAC[A](Array.empty[Byte])))) def afterBlock(response: Response[F], authenticator: AugmentedJWT[A, V]): OptionT[F, Response[F]] = OptionT.pure[F](embed(response, authenticator)) }
Example 49
Source File: StatefulJWTAuth.scala From tsec with MIT License | 5 votes |
package tsec.authentication.internal import java.time.Instant import cats.data.OptionT import cats.effect.Sync import cats.syntax.all._ import org.http4s._ import tsec.authentication._ import tsec.common._ import tsec.jws.mac._ import tsec.jwt._ import tsec.jwt.algorithms.JWTMacAlgo import tsec.mac.jca._ import scala.concurrent.duration.FiniteDuration private[tsec] abstract class StatefulJWTAuth[F[_], I, V, A: JWTMacAlgo]( val expiry: FiniteDuration, val maxIdle: Option[FiniteDuration], tokenStore: BackingStore[F, SecureRandomId, AugmentedJWT[A, I]], identityStore: IdentityStore[F, I, V], signingKey: MacSigningKey[A] )(implicit F: Sync[F], cv: JWSMacCV[F, A]) extends JWTAuthenticator[F, I, V, A] { private[tsec] def verifyAndRefresh( raw: String, retrieved: AugmentedJWT[A, I], now: Instant ): F[AugmentedJWT[A, I]] def parseRaw(raw: String, request: Request[F]): OptionT[F, SecuredRequest[F, V, AugmentedJWT[A, I]]] = OptionT( (for { now <- F.delay(Instant.now()) extracted <- cv.verifyAndParse(raw, signingKey, now) id <- cataOption(extracted.id) retrieved <- tokenStore.get(SecureRandomId(id)).orAuthFailure refreshed <- verifyAndRefresh(raw, retrieved, now) identity <- identityStore.get(retrieved.identity).orAuthFailure } yield SecuredRequest(request, identity, refreshed).some) .handleError(_ => None) ) def create(body: I): F[AugmentedJWT[A, I]] = for { cookieId <- F.delay(SecureRandomId.Interactive.generate) now <- F.delay(Instant.now()) newExpiry = now.plusSeconds(expiry.toSeconds) claims = JWTClaims( issuedAt = Some(now), jwtId = Some(cookieId), expiration = Some(newExpiry) ) signed <- JWTMac.build[F, A](claims, signingKey) created <- tokenStore.put(AugmentedJWT(cookieId, signed, body, newExpiry, touch(now))) } yield created def renew(authenticator: AugmentedJWT[A, I]): F[AugmentedJWT[A, I]] = F.delay(Instant.now()).flatMap { now => val updatedExpiry = now.plusSeconds(expiry.toSeconds) val newBody = authenticator.jwt.body.withExpiry(updatedExpiry) for { reSigned <- JWTMac.build[F, A](newBody, signingKey) updated <- tokenStore .update(authenticator.copy(jwt = reSigned, expiry = updatedExpiry, lastTouched = touch(now))) } yield updated } def update(authenticator: AugmentedJWT[A, I]): F[AugmentedJWT[A, I]] = tokenStore.update(authenticator) def discard(authenticator: AugmentedJWT[A, I]): F[AugmentedJWT[A, I]] = tokenStore.delete(SecureRandomId.coerce(authenticator.id)).map(_ => authenticator) def afterBlock(response: Response[F], authenticator: AugmentedJWT[A, I]): OptionT[F, Response[F]] = OptionT.pure[F](response) }
Example 50
Source File: SecuredRequestHandler.scala From tsec with MIT License | 5 votes |
package tsec.authentication import cats.ApplicativeError import cats.MonadError import cats.data.{Kleisli, OptionT} import cats.syntax.all._ import org.http4s._ import org.log4s._ import tsec.authorization._ sealed abstract class SecuredRequestHandler[F[_], Identity, User, Auth]( val authenticator: Authenticator[F, Identity, User, Auth] )(implicit F: MonadError[F, Throwable], ME: MonadError[Kleisli[OptionT[F, ?], Request[F], ?], Throwable]) { private[this] val cachedUnauthorized: Response[F] = Response[F](Status.Unauthorized) private[this] val defaultNotAuthenticated: Request[F] => F[Response[F]] = _ => F.pure(cachedUnauthorized) private[tsec] def default[F[_], Identity, User, Auth]( authenticator: Authenticator[F, Identity, User, Auth] )(implicit F: MonadError[F, Throwable]): SecuredRequestHandler[F, Identity, User, Auth] = new SecuredRequestHandler[F, Identity, User, Auth](authenticator) {} }
Example 51
package jbok.evm import cats.data.OptionT import cats.effect.Sync import cats.implicits._ import jbok.common.log.Logger def run[F[_]: Sync](context: ProgramContext[F]): F[ProgramResult[F]] = { val state = ProgramState[F](context) OptionT.fromOption[F](PrecompiledContracts.runOptionally(state.config.preCompiledContracts, context)).getOrElseF { run(state).map { finalState => ProgramResult[F]( finalState.returnData, finalState.gas, finalState.world, finalState.addressesToDelete, finalState.logs, finalState.internalTxs, finalState.gasRefund, finalState.error, finalState.reverted ) } } } private def run[F[_]: Sync](state: ProgramState[F]): F[ProgramState[F]] = { val byte = state.program.getByte(state.pc) state.config.byteToOpCode.get(byte) match { case Some(opCode) => for { newState <- opCode.execute(state) _ <- Logger[F].trace( s"$opCode | pc: ${newState.pc} | depth: ${newState.env.callDepth} | gas: ${newState.gas} | stack: ${newState.stack}") s <- if (newState.halted || newState.reverted) newState.pure[F] else run(newState) } yield s case None => state.withError(InvalidOpCode(byte)).halt.pure[F] } } }
Example 52
Source File: TransactionService.scala From iotchain with MIT License | 5 votes |
package jbok.app.service import cats.data.OptionT import cats.effect.Sync import cats.implicits._ import jbok.codec.rlp.RlpEncoded import jbok.core.ledger.History import jbok.core.models.{Receipt, SignedTransaction} import jbok.core.pool.TxPool import jbok.core.api.{BlockTag, TransactionAPI} import scodec.bits.ByteVector final class TransactionService[F[_]](history: History[F], txPool: TxPool[F], helper: ServiceHelper[F])(implicit F: Sync[F]) extends TransactionAPI[F] { override def getTx(hash: ByteVector): F[Option[SignedTransaction]] = (for { loc <- OptionT(history.getTransactionLocation(hash)) block <- OptionT(history.getBlockByHash(loc.blockHash)) stx <- OptionT.fromOption[F](block.body.transactionList.lift(loc.txIndex)) } yield stx).value override def getPendingTx(hash: ByteVector): F[Option[SignedTransaction]] = txPool.getPendingTransactions.map(_.keys.toList.find(_.hash == hash)) override def getReceipt(hash: ByteVector): F[Option[Receipt]] = (for { loc <- OptionT(history.getTransactionLocation(hash)) block <- OptionT(history.getBlockByHash(loc.blockHash)) _ <- OptionT.fromOption[F](block.body.transactionList.lift(loc.txIndex)) receipts <- OptionT(history.getReceiptsByHash(loc.blockHash)) receipt <- OptionT.fromOption[F](receipts.lift(loc.txIndex)) } yield receipt).value override def getTxByBlockHashAndIndex(hash: ByteVector, index: Int): F[Option[SignedTransaction]] = (for { block <- OptionT(history.getBlockByHash(hash)) stx <- OptionT.fromOption[F](block.body.transactionList.lift(index)) } yield stx).value override def getTxByBlockTagAndIndex(tag: BlockTag, index: Int): F[Option[SignedTransaction]] = (for { block <- OptionT(helper.resolveBlock(tag)) stx <- OptionT.fromOption[F](block.body.transactionList.lift(index)) } yield stx).value override def sendTx(stx: SignedTransaction): F[ByteVector] = txPool.addOrUpdateTransaction(stx).as(stx.hash) override def sendRawTx(data: RlpEncoded): F[ByteVector] = for { stx <- F.fromEither(data.decoded[SignedTransaction]) _ <- txPool.addOrUpdateTransaction(stx) } yield stx.hash }
Example 53
Source File: StoreUpdateService.scala From iotchain with MIT License | 5 votes |
package jbok.app.service import cats.data.OptionT import cats.effect.{Sync, Timer} import cats.implicits._ import fs2._ import jbok.app.service.store.{BlockStore, TransactionStore} import jbok.common.log.Logger import jbok.common.math.N import jbok.core.ledger.History import spire.compat._ import scala.concurrent.duration._ final class StoreUpdateService[F[_]](history: History[F], blockStore: BlockStore[F], txStore: TransactionStore[F])(implicit F: Sync[F], T: Timer[F]) { private[this] val log = Logger[F] def findForkPoint(start: N): F[N] = for { hash1 <- blockStore.getBlockHashByNumber(start) hash2 <- history.getHashByBlockNumber(start) number <- (hash1, hash2) match { case (Some(h1), Some(h2)) if h1 == h2 => F.pure(start) case (Some(_), Some(_)) => findForkPoint(start - 1) case _ => F.raiseError(new Exception(s"fatal error")) } } yield number private def delRange(start: N, end: N): F[Unit] = List.range(start, end + 1).traverse_ { number => blockStore.delByBlockNumber(number) >> txStore.delByBlockNumber(number) } private def syncRange(start: N, end: N): F[Unit] = List.range(start, end + 1).traverse_ { number => syncBlock(number) >> syncTransactions(number) } private def syncBlock(number: N): F[Unit] = for { header <- history.getBlockHeaderByNumber(number) _ <- header.fold(F.unit)(header => blockStore.insert(header.number, header.hash)) } yield () private def syncTransactions(number: N): F[Unit] = (for { hash <- OptionT(history.getHashByBlockNumber(number)) block <- OptionT(history.getBlockByHash(hash)) receipts <- OptionT(history.getReceiptsByHash(hash)) _ <- OptionT.liftF(txStore.insertBlockTransactions(block, receipts)) } yield ()).value.void def sync: F[Unit] = for { currentOpt <- blockStore.getBestBlockNumber fork <- currentOpt.fold(N(0).pure[F])(current => findForkPoint(current)) best <- history.getBestBlockNumber _ <- log.i(s"current: ${fork}, best: ${best}") _ <- if (fork == best) { F.unit } else { delRange(fork, best) >> syncRange(fork, best) } } yield () val stream: Stream[F, Unit] = Stream.eval(log.i(s"starting App/StoreUpdateService")) ++ Stream.repeatEval(sync).metered(10.seconds) }
Example 54
Source File: HmacAuthMiddleware.scala From iotchain with MIT License | 5 votes |
package jbok.network.http.server.middleware import java.time.{Duration, Instant} import cats.data.{Kleisli, OptionT} import cats.effect.Sync import jbok.network.http.server.authentication.HMAC import org.http4s.headers.Authorization import org.http4s.util.CaseInsensitiveString import org.http4s.{AuthScheme, Credentials, HttpRoutes, Request, Response, Status} import tsec.mac.jca.{HMACSHA256, MacSigningKey} import scala.concurrent.duration.{FiniteDuration, _} sealed abstract class HmacAuthError(val message: String) extends Exception(message) object HmacAuthError { case object NoAuthHeader extends HmacAuthError("Could not find an Authorization header") case object NoDatetimeHeader extends HmacAuthError("Could not find an X-Datetime header") case object BadMAC extends HmacAuthError("Bad MAC") case object InvalidMacFormat extends HmacAuthError("The MAC is not a valid Base64 string") case object InvalidDatetime extends HmacAuthError("The datetime is not a valid UTC datetime string") case object Timeout extends HmacAuthError("The request time window is closed") } object HmacAuthMiddleware { val defaultDuration: FiniteDuration = 5.minutes private def verifyFromHeader[F[_]]( req: Request[F], key: MacSigningKey[HMACSHA256], duration: FiniteDuration ): Either[HmacAuthError, Unit] = for { authHeader <- req.headers .get(Authorization) .flatMap { t => t.credentials match { case Credentials.Token(scheme, token) if scheme == AuthScheme.Bearer => Some(token) case _ => None } } .toRight(HmacAuthError.NoAuthHeader) datetimeHeader <- req.headers .get(CaseInsensitiveString("X-Datetime")) .toRight(HmacAuthError.NoDatetimeHeader) instant <- HMAC.http.verifyFromHeader( req.method.name, req.uri.renderString, datetimeHeader.value, authHeader, key ) _ <- Either.cond( Instant.now().isBefore(instant.plus(Duration.ofNanos(duration.toNanos))), (), HmacAuthError.Timeout ) } yield () def apply[F[_]: Sync](key: MacSigningKey[HMACSHA256], duration: FiniteDuration = defaultDuration)(routes: HttpRoutes[F]): HttpRoutes[F] = Kleisli { req: Request[F] => verifyFromHeader(req, key, duration) match { case Left(error) => OptionT.some[F](Response[F](Status.Forbidden).withEntity(error.message)) case Right(_) => routes(req) } } }
Example 55
Source File: HttpErrorHandler.scala From codepropertygraph with Apache License 2.0 | 5 votes |
package io.shiftleft.cpgserver.route import cats.data.{Kleisli, OptionT} import cats.effect.IO import org.http4s.{HttpRoutes, Request, Response} trait HttpErrorHandler { def handle(routes: HttpRoutes[IO]): HttpRoutes[IO] } object HttpErrorHandler { def apply(routes: HttpRoutes[IO])(handler: PartialFunction[Throwable, IO[Response[IO]]]): HttpRoutes[IO] = { Kleisli { req: Request[IO] => OptionT { routes.run(req).value.handleErrorWith { e => if (handler.isDefinedAt(e)) handler(e).map(Option(_)) else IO.raiseError(e) } } } } }
Example 56
Source File: QMails.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.store.queries import cats.data.OptionT import docspell.common._ import docspell.store.impl.Column import docspell.store.impl.Implicits._ import docspell.store.records._ import doobie._ import doobie.implicits._ object QMails { def delete(coll: Ident, mailId: Ident): ConnectionIO[Int] = (for { m <- OptionT(findMail(coll, mailId)) k <- OptionT.liftF(RSentMailItem.deleteMail(mailId)) n <- OptionT.liftF(RSentMail.delete(m._1.id)) } yield k + n).getOrElse(0) def findMail(coll: Ident, mailId: Ident): ConnectionIO[Option[(RSentMail, Ident)]] = { val iColl = RItem.Columns.cid.prefix("i") val mId = RSentMail.Columns.id.prefix("m") val (cols, from) = partialFind val cond = Seq(mId.is(mailId), iColl.is(coll)) selectSimple(cols, from, and(cond)).query[(RSentMail, Ident)].option } def findMails(coll: Ident, itemId: Ident): ConnectionIO[Vector[(RSentMail, Ident)]] = { val iColl = RItem.Columns.cid.prefix("i") val tItem = RSentMailItem.Columns.itemId.prefix("t") val mCreated = RSentMail.Columns.created.prefix("m") val (cols, from) = partialFind val cond = Seq(tItem.is(itemId), iColl.is(coll)) (selectSimple(cols, from, and(cond)) ++ orderBy(mCreated.f) ++ fr"DESC") .query[(RSentMail, Ident)] .to[Vector] } private def partialFind: (Seq[Column], Fragment) = { val iId = RItem.Columns.id.prefix("i") val tItem = RSentMailItem.Columns.itemId.prefix("t") val tMail = RSentMailItem.Columns.sentMailId.prefix("t") val mId = RSentMail.Columns.id.prefix("m") val mUser = RSentMail.Columns.uid.prefix("m") val uId = RUser.Columns.uid.prefix("u") val uLogin = RUser.Columns.login.prefix("u") val cols = RSentMail.Columns.all.map(_.prefix("m")) :+ uLogin val from = RSentMail.table ++ fr"m INNER JOIN" ++ RSentMailItem.table ++ fr"t ON" ++ tMail.is(mId) ++ fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ tItem.is(iId) ++ fr"INNER JOIN" ++ RUser.table ++ fr"u ON" ++ uId.is(mUser) (cols, from) } }
Example 57
Source File: OJoex.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.backend.ops import scala.concurrent.ExecutionContext import cats.data.OptionT import cats.effect._ import cats.implicits._ import docspell.common.{Ident, NodeType} import docspell.joexapi.client.JoexClient import docspell.store.Store import docspell.store.records.RNode trait OJoex[F[_]] { def notifyAllNodes: F[Unit] def cancelJob(job: Ident, worker: Ident): F[Boolean] } object OJoex { def apply[F[_]: Sync](client: JoexClient[F], store: Store[F]): Resource[F, OJoex[F]] = Resource.pure[F, OJoex[F]](new OJoex[F] { def notifyAllNodes: F[Unit] = for { nodes <- store.transact(RNode.findAll(NodeType.Joex)) _ <- nodes.toList.traverse(n => client.notifyJoexIgnoreErrors(n.url)) } yield () def cancelJob(job: Ident, worker: Ident): F[Boolean] = (for { node <- OptionT(store.transact(RNode.findById(worker))) cancel <- OptionT.liftF(client.cancelJob(node.url, job)) } yield cancel.success).getOrElse(false) }) def create[F[_]: ConcurrentEffect]( ec: ExecutionContext, store: Store[F] ): Resource[F, OJoex[F]] = JoexClient.resource(ec).flatMap(client => apply(client, store)) }
Example 58
Source File: OJob.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.backend.ops import cats.data.OptionT import cats.effect._ import cats.implicits._ import docspell.backend.ops.OJob.{CollectiveQueueState, JobCancelResult} import docspell.common.{Ident, JobState} import docspell.store.Store import docspell.store.queries.QJob import docspell.store.records.{RJob, RJobLog} trait OJob[F[_]] { def queueState(collective: Ident, maxResults: Int): F[CollectiveQueueState] def cancelJob(id: Ident, collective: Ident): F[JobCancelResult] } object OJob { sealed trait JobCancelResult object JobCancelResult { case object Removed extends JobCancelResult case object CancelRequested extends JobCancelResult case object JobNotFound extends JobCancelResult def removed: JobCancelResult = Removed def cancelRequested: JobCancelResult = CancelRequested def jobNotFound: JobCancelResult = JobNotFound } case class JobDetail(job: RJob, logs: Vector[RJobLog]) case class CollectiveQueueState(jobs: Vector[JobDetail]) { def queued: Vector[JobDetail] = jobs.filter(r => JobState.queued.contains(r.job.state)) def done: Vector[JobDetail] = jobs.filter(r => JobState.done.contains(r.job.state)) def running: Vector[JobDetail] = jobs.filter(_.job.state == JobState.Running) } def apply[F[_]: Sync]( store: Store[F], joex: OJoex[F] ): Resource[F, OJob[F]] = Resource.pure[F, OJob[F]](new OJob[F] { def queueState(collective: Ident, maxResults: Int): F[CollectiveQueueState] = store .transact(QJob.queueStateSnapshot(collective).take(maxResults.toLong)) .map(t => JobDetail(t._1, t._2)) .compile .toVector .map(CollectiveQueueState) def cancelJob(id: Ident, collective: Ident): F[JobCancelResult] = { def remove(job: RJob): F[JobCancelResult] = store.transact(RJob.delete(job.id)) *> JobCancelResult.removed.pure[F] def tryCancel(job: RJob): F[JobCancelResult] = job.worker match { case Some(worker) => for { flag <- joex.cancelJob(job.id, worker) res <- if (flag) JobCancelResult.cancelRequested.pure[F] else remove(job) } yield res case None => remove(job) } (for { job <- OptionT(store.transact(RJob.findByIdAndGroup(id, collective))) result <- OptionT.liftF( if (job.isInProgress) tryCancel(job) else remove(job) ) } yield result) .getOrElse(JobCancelResult.jobNotFound) } }) }
Example 59
Source File: OUserTask.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.backend.ops import cats.data.OptionT import cats.effect._ import cats.implicits._ import fs2.Stream import docspell.common._ import docspell.store.queue.JobQueue import docspell.store.usertask._ import io.circe.Encoder trait OUserTask[F[_]] { def executeNow[A](account: AccountId, task: UserTask[A])(implicit E: Encoder[A] ): F[Unit] } object OUserTask { def apply[F[_]: Effect]( store: UserTaskStore[F], queue: JobQueue[F], joex: OJoex[F] ): Resource[F, OUserTask[F]] = Resource.pure[F, OUserTask[F]](new OUserTask[F] { def executeNow[A](account: AccountId, task: UserTask[A])(implicit E: Encoder[A] ): F[Unit] = for { ptask <- task.encode.toPeriodicTask(account) job <- ptask.toJob _ <- queue.insert(job) _ <- joex.notifyAllNodes } yield () def getScanMailbox(account: AccountId): Stream[F, UserTask[ScanMailboxArgs]] = store .getByName[ScanMailboxArgs](account, ScanMailboxArgs.taskName) def findScanMailbox( id: Ident, account: AccountId ): OptionT[F, UserTask[ScanMailboxArgs]] = OptionT(getScanMailbox(account).find(_.id == id).compile.last) def deleteTask(account: AccountId, id: Ident): F[Unit] = (for { _ <- store.getByIdRaw(account, id) _ <- OptionT.liftF(store.deleteTask(account, id)) } yield ()).getOrElse(()) def submitScanMailbox( account: AccountId, task: UserTask[ScanMailboxArgs] ): F[Unit] = for { _ <- store.updateTask[ScanMailboxArgs](account, task) _ <- joex.notifyAllNodes } yield () def getNotifyDueItems(account: AccountId): Stream[F, UserTask[NotifyDueItemsArgs]] = store .getByName[NotifyDueItemsArgs](account, NotifyDueItemsArgs.taskName) def findNotifyDueItems( id: Ident, account: AccountId ): OptionT[F, UserTask[NotifyDueItemsArgs]] = OptionT(getNotifyDueItems(account).find(_.id == id).compile.last) def submitNotifyDueItems( account: AccountId, task: UserTask[NotifyDueItemsArgs] ): F[Unit] = for { _ <- store.updateTask[NotifyDueItemsArgs](account, task) _ <- joex.notifyAllNodes } yield () }) }
Example 60
Source File: SentMailRoutes.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.restserver.routes import cats.data.OptionT import cats.effect._ import cats.implicits._ import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.backend.ops.OMail.Sent import docspell.common._ import docspell.restapi.model._ import emil.javamail.syntax._ import org.http4s._ import org.http4s.circe.CirceEntityEncoder._ import org.http4s.dsl.Http4sDsl object SentMailRoutes { def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = { val dsl = new Http4sDsl[F] {} import dsl._ HttpRoutes.of { case GET -> Root / "item" / Ident(id) => for { all <- backend.mail.getSentMailsForItem(user.account, id) resp <- Ok(SentMails(all.map(convert).toList)) } yield resp case GET -> Root / "mail" / Ident(mailId) => (for { mail <- backend.mail.getSentMail(user.account, mailId) resp <- OptionT.liftF(Ok(convert(mail))) } yield resp).getOrElseF(NotFound()) case DELETE -> Root / "mail" / Ident(mailId) => for { n <- backend.mail.deleteSentMail(user.account, mailId) resp <- Ok(BasicResult(n > 0, s"Mails deleted: $n")) } yield resp } } def convert(s: Sent): SentMail = SentMail( s.id, s.senderLogin, s.connectionName, s.recipients.map(_.asUnicodeString), s.subject, s.body, s.created ) }
Example 61
Source File: FullTextIndexRoutes.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.restserver.routes import cats.data.OptionT import cats.effect._ import cats.implicits._ import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.common._ import docspell.restserver.Config import docspell.restserver.conv.Conversions import org.http4s._ import org.http4s.circe.CirceEntityEncoder._ import org.http4s.dsl.Http4sDsl object FullTextIndexRoutes { def secured[F[_]: Effect]( cfg: Config, backend: BackendApp[F], user: AuthToken ): HttpRoutes[F] = if (!cfg.fullTextSearch.enabled) notFound[F] else { val dsl = Http4sDsl[F] import dsl._ HttpRoutes.of { case POST -> Root / "reIndex" => for { res <- backend.fulltext.reindexCollective(user.account).attempt resp <- Ok(Conversions.basicResult(res, "Full-text index will be re-created.")) } yield resp } } def open[F[_]: Effect](cfg: Config, backend: BackendApp[F]): HttpRoutes[F] = if (!cfg.fullTextSearch.enabled) notFound[F] else { val dsl = Http4sDsl[F] import dsl._ HttpRoutes.of { case POST -> Root / "reIndexAll" / Ident(id) => for { res <- if (id.nonEmpty && id == cfg.fullTextSearch.recreateKey) backend.fulltext.reindexAll.attempt else Left(new Exception("The provided key is invalid.")).pure[F] resp <- Ok(Conversions.basicResult(res, "Full-text index will be re-created.")) } yield resp } } private def notFound[F[_]: Effect]: HttpRoutes[F] = HttpRoutes(_ => OptionT.pure(Response.notFound[F])) }
Example 62
Source File: Migration.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.joex.fts import cats.Traverse import cats.data.{Kleisli, OptionT} import cats.effect._ import cats.implicits._ import docspell.common._ import docspell.ftsclient._ import docspell.joex.Config import docspell.store.records.RFtsMigration import docspell.store.{AddResult, Store} case class Migration[F[_]]( version: Int, engine: Ident, description: String, task: FtsWork[F] ) object Migration { def apply[F[_]: Effect]( cfg: Config.FullTextSearch, fts: FtsClient[F], store: Store[F], logger: Logger[F] ): Kleisli[F, List[Migration[F]], Unit] = { val ctx = FtsContext(cfg, store, fts, logger) Kleisli(migs => Traverse[List].sequence(migs.map(applySingle[F](ctx))).map(_ => ())) } def applySingle[F[_]: Effect](ctx: FtsContext[F])(m: Migration[F]): F[Unit] = { val insertRecord: F[Option[RFtsMigration]] = for { rec <- RFtsMigration.create(m.version, m.engine, m.description) res <- ctx.store.add( RFtsMigration.insert(rec), RFtsMigration.exists(m.version, m.engine) ) ret <- res match { case AddResult.Success => rec.some.pure[F] case AddResult.EntityExists(_) => None.pure[F] case AddResult.Failure(ex) => Effect[F].raiseError(ex) } } yield ret (for { _ <- OptionT.liftF(ctx.logger.info(s"Apply ${m.version}/${m.description}")) rec <- OptionT(insertRecord) res <- OptionT.liftF(m.task.run(ctx).attempt) ret <- OptionT.liftF(res match { case Right(()) => ().pure[F] case Left(ex) => ctx.logger.error(ex)( s"Applying index migration ${m.version}/${m.description} failed" ) *> ctx.store.transact(RFtsMigration.deleteById(rec.id)) *> Effect[F] .raiseError[Unit]( ex ) }) } yield ret).getOrElseF( ctx.logger.info(s"Migration ${m.version}/${m.description} already applied.") ) } }
Example 63
Source File: DummyCpgProviderSpec.scala From codepropertygraph with Apache License 2.0 | 5 votes |
package io.shiftleft.cpgserver.cpg import java.util.UUID import scala.concurrent.ExecutionContext import cats.data.OptionT import cats.effect.{ContextShift, IO} import org.scalatest.concurrent.Eventually import io.shiftleft.codepropertygraph.Cpg import io.shiftleft.cpgserver.BaseSpec import io.shiftleft.cpgserver.query.CpgOperationResult import scala.concurrent.duration._ import scala.language.postfixOps class DummyCpgProviderSpec extends BaseSpec with Eventually { private implicit val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global) private def withNewCpgProvider[T](f: DummyCpgProvider => T): T = { f(new DummyCpgProvider) } "Creating a CPG" should { "return a UUID referencing the eventual CPG" in withNewCpgProvider { cpgProvider => noException should be thrownBy cpgProvider.createCpg(Set.empty).unsafeRunSync() } } "Retrieving a CPG" should { "return a success if the CPG was created successfully" in withNewCpgProvider { cpgProvider => val cpgId = cpgProvider.createCpg(Set.empty).unsafeRunSync() eventually(timeout(10 seconds), interval(1 seconds)) { cpgProvider.retrieveCpg(cpgId).value.unsafeRunSync() shouldBe defined } } "return an empty OptionT if the CPG does not exist" in withNewCpgProvider { cpgProvider => cpgProvider.retrieveCpg(UUID.randomUUID) shouldBe OptionT.none[IO, CpgOperationResult[Cpg]] } } }
Example 64
Source File: DummyBackingStore.scala From iotchain with MIT License | 5 votes |
package jbok.network.http.server.authentication import cats.data.OptionT import cats.effect.IO import tsec.authentication.BackingStore import scala.collection.mutable object DummyBackingStore { def apply[I, V](getId: V => I): BackingStore[IO, I, V] = new BackingStore[IO, I, V] { private val storageMap = mutable.HashMap.empty[I, V] def put(elem: V): IO[V] = { val map = storageMap.put(getId(elem), elem) if (map.isEmpty) IO.pure(elem) else IO.raiseError(new IllegalArgumentException) } def get(id: I): OptionT[IO, V] = OptionT.fromOption[IO](storageMap.get(id)) def update(v: V): IO[V] = { storageMap.update(getId(v), v) IO.pure(v) } def delete(id: I): IO[Unit] = storageMap.remove(id) match { case Some(_) => IO.unit case None => IO.raiseError(new IllegalArgumentException) } } }
Example 65
Source File: SwaggerRoute.scala From codepropertygraph with Apache License 2.0 | 5 votes |
package io.shiftleft.cpgserver.route import java.util.concurrent.Executors import scala.concurrent.ExecutionContext import cats.data.OptionT import cats.effect.{Blocker, ContextShift, IO} import io.circe.generic.auto._ import io.circe.syntax._ import org.http4s._ import org.http4s.circe._ import org.http4s.dsl.io._ import org.http4s.headers.Location import org.webjars.WebJarAssetLocator import io.shiftleft.cpgserver.route.CpgRoute.ApiError final class SwaggerRoute { private val blockingEc = ExecutionContext.fromExecutor(Executors.newSingleThreadExecutor) private val blocker = Blocker.liftExecutionContext(blockingEc) private implicit val blockingCs: ContextShift[IO] = IO.contextShift(blockingEc) private val swaggerUiVersion = IO { new WebJarAssetLocator().getWebJars.get("swagger-ui") } private val swaggerUiResources = swaggerUiVersion.map { ver => s"/META-INF/resources/webjars/swagger-ui/$ver" } private val swaggerUiPath = Path("swagger-ui") val routes: HttpRoutes[IO] = HttpRoutes.of { case GET -> Root / ("swagger-ui" | "docs") => PermanentRedirect(Location(Uri.unsafeFromString("swagger-ui/index.html"))) // TODO discuss with jacob: according to scalac this is unreachable... commenting for now since it probably never worked anyway case req @ GET -> (Root | `swaggerUiPath`) / "swagger.yaml" => StaticFile .fromResource("/swagger.yaml", blocker, Some(req)) .getOrElseF(InternalServerError(ApiError("Swagger documentation is missing.").asJson)) case req @ GET -> path if path.startsWith(swaggerUiPath) => { val file = path.toList.tail.mkString("/", "/", "") match { case f if f == "/index.html" => StaticFile.fromResource[IO]("/swagger-ui/index.html", blocker, Some(req)) case f => OptionT.liftF(swaggerUiResources).flatMap { resources => StaticFile.fromResource[IO](resources + f, blocker, Some(req)) } } file.getOrElseF(InternalServerError(ApiError(s"Requested file [$file] is missing.").asJson)) } } } object SwaggerRoute { def apply(): SwaggerRoute = new SwaggerRoute }
Example 66
Source File: ServerAmmoniteExecutor.scala From codepropertygraph with Apache License 2.0 | 5 votes |
package io.shiftleft.cpgserver.query import cats.data.OptionT import cats.effect.{Blocker, ContextShift, IO} import io.shiftleft.codepropertygraph.Cpg import io.shiftleft.console.scripting.AmmoniteExecutor import java.util.UUID import java.util.concurrent.{ConcurrentHashMap, Executors} import scala.collection.concurrent.Map import scala.concurrent.ExecutionContext import scala.jdk.CollectionConverters._ abstract class ServerAmmoniteExecutor(implicit cs: ContextShift[IO]) extends AmmoniteExecutor { private val blocker: Blocker = Blocker.liftExecutionContext(ExecutionContext.fromExecutor(Executors.newFixedThreadPool(2))) private val queryResultMap: Map[UUID, CpgOperationResult[String]] = new ConcurrentHashMap[UUID, CpgOperationResult[String]].asScala private val uuidProvider = IO { UUID.randomUUID } def executeQuery(cpg: Cpg, query: String): IO[UUID] = { for { resultUuid <- uuidProvider _ <- blocker .blockOn(runQuery(query, cpg)) .runAsync { case Right(result) => IO(queryResultMap.put(resultUuid, CpgOperationSuccess(result.toString))).map(_ => ()) case Left(ex) => IO(queryResultMap.put(resultUuid, CpgOperationFailure(ex))).map(_ => ()) } .toIO } yield resultUuid } def retrieveQueryResult(queryId: UUID): OptionT[IO, CpgOperationResult[String]] = { OptionT.fromOption(queryResultMap.get(queryId)) } def executeQuerySync(cpg: Cpg, query: String): IO[CpgOperationResult[String]] = { for { result <- runQuery(query, cpg) .map(v => CpgOperationSuccess(v.toString)) .handleErrorWith(err => IO(CpgOperationFailure(err))) } yield result } }
Example 67
Source File: DummyCpgProvider.scala From codepropertygraph with Apache License 2.0 | 5 votes |
package io.shiftleft.cpgserver.cpg import java.util.UUID import java.util.concurrent.{ConcurrentHashMap, Executors} import scala.jdk.CollectionConverters._ import scala.collection.concurrent.Map import scala.concurrent.ExecutionContext import cats.data.OptionT import cats.effect.{Blocker, ContextShift, IO} import io.shiftleft.codepropertygraph.Cpg import io.shiftleft.codepropertygraph.generated.nodes.NewMethod import io.shiftleft.cpgserver.query.{CpgOperationFailure, CpgOperationResult, CpgOperationSuccess} import io.shiftleft.passes.{CpgPass, DiffGraph} import io.shiftleft.semanticcpg.language._ class DummyCpgProvider(implicit cs: ContextShift[IO]) extends CpgProvider { private val blocker: Blocker = Blocker.liftExecutionContext(ExecutionContext.fromExecutor(Executors.newFixedThreadPool(2))) private val cpgMap: Map[UUID, CpgOperationResult[Cpg]] = new ConcurrentHashMap[UUID, CpgOperationResult[Cpg]].asScala private val uuidProvider = IO(UUID.randomUUID) private class MyPass(cpg: Cpg) extends CpgPass(cpg) { override def run(): Iterator[DiffGraph] = { implicit val diffGraph: DiffGraph.Builder = DiffGraph.newBuilder NewMethod(name = "main", isExternal = false).start.store Iterator(diffGraph.build()) } } override def createCpg(filenames: Set[String]): IO[UUID] = { val cpg = new Cpg for { cpgId <- uuidProvider _ <- blocker .blockOn(IO(new MyPass(cpg).createAndApply())) .runAsync { case Right(_) => IO(cpgMap.put(cpgId, CpgOperationSuccess(cpg))).map(_ => ()) case Left(ex) => IO(cpgMap.put(cpgId, CpgOperationFailure(ex))).map(_ => ()) } .toIO } yield cpgId } override def retrieveCpg(uuid: UUID): OptionT[IO, CpgOperationResult[Cpg]] = { OptionT.fromOption(cpgMap.get(uuid)) } }
Example 68
Source File: ConsRecordToActionHeader.scala From kafka-journal with MIT License | 5 votes |
package com.evolutiongaming.kafka.journal.conversions import cats.data.OptionT import cats.implicits._ import com.evolutiongaming.catshelper.MonadThrowable import com.evolutiongaming.kafka.journal._ import com.evolutiongaming.kafka.journal.util.CatsHelper._ import com.evolutiongaming.skafka.Header import scodec.bits.ByteVector trait ConsRecordToActionHeader[F[_]] { def apply(consRecord: ConsRecord): OptionT[F, ActionHeader] } object ConsRecordToActionHeader { implicit def apply[F[_] : MonadThrowable](implicit fromBytes: FromBytes[F, Option[ActionHeader]] ): ConsRecordToActionHeader[F] = { consRecord: ConsRecord => { def header = consRecord .headers .find { _.key === ActionHeader.key } def actionHeader(header: Header) = { val byteVector = ByteVector.view(header.value) fromBytes(byteVector).adaptError { case e => JournalError(s"ConsRecordToActionHeader failed for $consRecord: $e", e) } } for { header <- header.toOptionT[F] actionHeader <- actionHeader(header).toOptionT } yield actionHeader } } }
Example 69
Source File: ConsRecordToActionRecord.scala From kafka-journal with MIT License | 5 votes |
package com.evolutiongaming.kafka.journal.conversions import java.time.Instant import cats.data.OptionT import cats.implicits._ import com.evolutiongaming.catshelper.MonadThrowable import com.evolutiongaming.kafka.journal._ import com.evolutiongaming.kafka.journal.util.CatsHelper._ trait ConsRecordToActionRecord[F[_]] { def apply(consRecord: ConsRecord): OptionT[F, ActionRecord[Action]] } object ConsRecordToActionRecord { implicit def apply[F[_] : MonadThrowable](implicit consRecordToActionHeader: ConsRecordToActionHeader[F], headerToTuple: HeaderToTuple[F], ): ConsRecordToActionRecord[F] = { consRecord: ConsRecord => { def action(key: Key, timestamp: Instant, header: ActionHeader) = { def append(header: ActionHeader.Append) = { consRecord .value .traverse { value => val headers = consRecord.headers .filter { _.key =!= ActionHeader.key } .traverse { header => headerToTuple(header) } for { headers <- headers } yield { val payload = value.value Action.append(key, timestamp, header, payload, headers.toMap) } } } header match { case header: ActionHeader.Append => append(header).toOptionT case header: ActionHeader.Mark => Action.mark(key, timestamp, header).pure[OptionT[F, *]] case header: ActionHeader.Delete => Action.delete(key, timestamp, header).pure[OptionT[F, *]] case header: ActionHeader.Purge => Action.purge(key, timestamp, header).pure[OptionT[F, *]] } } val result = for { id <- consRecord.key.toOptionT[F] timestampAndType <- consRecord.timestampAndType.toOptionT[F] header <- consRecordToActionHeader(consRecord) key = Key(id = id.value, topic = consRecord.topic) timestamp = timestampAndType.timestamp action <- action(key, timestamp, header) } yield { val partitionOffset = PartitionOffset(consRecord) ActionRecord(action, partitionOffset) } result .value .adaptError { case e => JournalError(s"ConsRecordToActionRecord failed for $consRecord: $e", e) } .toOptionT } } }
Example 70
Source File: CatsHelper.scala From kafka-journal with MIT License | 5 votes |
package com.evolutiongaming.kafka.journal.util import cats.data.OptionT import cats.effect._ import cats.implicits._ import cats.kernel.CommutativeMonoid import cats.{Applicative, ApplicativeError, CommutativeApplicative} import com.evolutiongaming.kafka.journal.util.Fail.implicits._ object CatsHelper { implicit class CommutativeApplicativeOps(val self: CommutativeApplicative.type) extends AnyVal { def commutativeMonoid[F[_] : CommutativeApplicative, A: CommutativeMonoid]: CommutativeMonoid[F[A]] = { new CommutativeMonoid[F[A]] { def empty = { Applicative[F].pure(CommutativeMonoid[A].empty) } def combine(x: F[A], y: F[A]) = { Applicative[F].map2(x, y)(CommutativeMonoid[A].combine) } } } } implicit class FOpsCatsHelper[F[_], A](val self: F[A]) extends AnyVal { def error[E](implicit F: ApplicativeError[F, E]): F[Option[E]] = { self.redeem[Option[E]](_.some, _ => none[E]) } } implicit class FOptionOpsCatsHelper[F[_], A](val self: F[Option[A]]) extends AnyVal { def toOptionT: OptionT[F, A] = OptionT(self) } implicit class ResourceOpsCatsHelper[F[_], A](val self: Resource[F, A]) extends AnyVal { def start[B](use: A => F[B])(implicit F: Concurrent[F]): F[Fiber[F, B]] = { StartResource(self)(use) } } implicit class OptionOpsCatsHelper[A](val self: Option[A]) extends AnyVal { def getOrError[F[_]: Applicative : Fail](name: => String): F[A] = { self.fold { s"$name is not defined".fail[F, A] } { a => a.pure[F] } } } }
Example 71
Source File: MigrationAlg.scala From scala-steward with Apache License 2.0 | 5 votes |
package org.scalasteward.core.scalafix import better.files.File import cats.data.OptionT import cats.effect.Sync import cats.implicits._ import io.circe.config.parser.decode import org.scalasteward.core.data.{Update, Version} import org.scalasteward.core.io.FileAlg import org.scalasteward.core.util.{ApplicativeThrowable, MonadThrowable} trait MigrationAlg { def findMigrations(update: Update): List[Migration] } object MigrationAlg { def create[F[_]](extraMigrations: Option[File])(implicit fileAlg: FileAlg[F], F: Sync[F] ): F[MigrationAlg] = loadMigrations(extraMigrations).map { migrations => new MigrationAlg { override def findMigrations(update: Update): List[Migration] = findMigrationsImpl(migrations, update) } } def loadMigrations[F[_]]( extraMigrations: Option[File] )(implicit fileAlg: FileAlg[F], F: MonadThrowable[F]): F[List[Migration]] = for { default <- fileAlg .readResource("scalafix-migrations.conf") .flatMap(decodeMigrations[F](_, "default")) maybeExtra <- OptionT(extraMigrations.flatTraverse(fileAlg.readFile)) .semiflatMap(decodeMigrations[F](_, "extra")) .value migrations = maybeExtra match { case Some(extra) if extra.disableDefaults => extra.migrations case Some(extra) => default.migrations ++ extra.migrations case None => default.migrations } } yield migrations private def decodeMigrations[F[_]](content: String, tpe: String)(implicit F: ApplicativeThrowable[F] ): F[ScalafixMigrations] = F.fromEither(decode[ScalafixMigrations](content)) .adaptErr(new Throwable(s"Failed to load $tpe Scalafix migrations", _)) private def findMigrationsImpl( givenMigrations: List[Migration], update: Update ): List[Migration] = givenMigrations.filter { migration => update.groupId === migration.groupId && migration.artifactIds.exists(re => update.artifactIds.exists(artifactId => re.r.findFirstIn(artifactId.name).isDefined) ) && Version(update.currentVersion) < migration.newVersion && Version(update.newerVersions.head) >= migration.newVersion } }