1 /*
2  *  Make.org Core API
3  *  Copyright (C) 2020 Make.org
4  *
5  * This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU Affero General Public License as
7  *  published by the Free Software Foundation, either version 3 of the
8  *  License, or (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU Affero General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Affero General Public License
16  *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  *
18  */
19 
20 package org.make.api.technical.graphql
21 
22 import cats.data.Validated.Valid
23 import cats.data.{NonEmptyList, Validated}
24 import cats.implicits._
25 import cats.{Id, Show, Traverse}
26 import org.make.core.StringValue
27 import zio.query.{CompletedRequestMap, DataSource}
28 import zio.{Chunk, Task}
29 
30 import scala.concurrent.{ExecutionContext, Future}
31 
32 object DataSourceHelper {
33   def createDataSource[F[_]: Traverse, EntityId <: StringValue, Entity, Request <: IdsRequest[F, EntityId, Entity]](
34     name: String,
35     findFromIds: Seq[EntityId] => ExecutionContext => Future[Map[EntityId, Entity]]
36   ): DataSource[Any, Request] = {
37     DataSource.Batched.make(name) { chunk =>
38       val resultMap = CompletedRequestMap.empty
39       val ids: Chunk[EntityId] = chunk.flatMap(_.ids.toIterable)
40       val entities: Task[Map[EntityId, Entity]] = Task.fromFuture(ec => findFromIds(ids)(ec))
41       implicit val showId: Show[EntityId] = Show.show[EntityId](_.value)
42 
43       entities.fold(
44         err => chunk.foldLeft(resultMap) { case (map, req) => map.insert(req)(Left(err)) }, { result =>
45           chunk.foldLeft(resultMap) {
46             case (map, req) =>
47               val res: Either[Exception, F[Entity]] = Traverse[F]
48                 .traverse(req.ids) { id =>
49                   result.get(id) match {
50                     case Some(value) => Valid(value)
51                     case None        => Validated.invalidNel(id)
52                   }
53                 }
54                 .toEither
55                 .leftMap(invalids => DataSourceIdsNotFound(name, invalids))
56               map.insert(req)(res)
57           }
58         }
59       )
60     }
61   }
62 
63   def one[EntityId <: StringValue, Entity, Request <: IdsRequest[Id, EntityId, Entity]](
64     name: String,
65     findFromIds: Seq[EntityId] => ExecutionContext => Future[Map[EntityId, Entity]]
66   ): DataSource[Any, Request] = {
67     createDataSource[Id, EntityId, Entity, Request](name, findFromIds)
68   }
69 
70   def seq[EntityId <: StringValue, Entity, Request <: IdsRequest[List, EntityId, Entity]](
71     name: String,
72     findFromIds: Seq[EntityId] => ExecutionContext => Future[Map[EntityId, Entity]]
73   ): DataSource[Any, Request] = {
74     createDataSource[List, EntityId, Entity, Request](name, findFromIds)
75   }
76 
77 }
78 
79 final case class DataSourceIdsNotFound[EntityId: Show](name: String, idsNotFound: NonEmptyList[EntityId])
80     extends Exception(s"DataSource $name could not find entities with ids: ${idsNotFound.mkString_(", ")}")
Line Stmt Id Pos Tree Symbol Tests Code
37 42378 1363 - 2357 Apply zio.query.DataSource.Batched.make zio.query.DataSource.Batched.make[Any, Request](name)(((chunk: zio.Chunk[Request]) => { val resultMap: zio.query.CompletedRequestMap = zio.query.CompletedRequestMap.empty; val ids: zio.Chunk[EntityId] = chunk.flatMap[EntityId](((x$1: Request) => cats.implicits.toFoldableOps[F, EntityId](x$1.ids)(evidence$1).toIterable)); val entities: zio.Task[Map[EntityId,Entity]] = zio.Task.fromFuture[Map[EntityId,Entity]](((ec: scala.concurrent.ExecutionContext) => findFromIds.apply(ids).apply(ec))); implicit val showId: cats.Show[EntityId] = cats.Show.show[EntityId](((x$2: EntityId) => x$2.value)); entities.fold[zio.query.CompletedRequestMap](((err: Throwable) => chunk.foldLeft[zio.query.CompletedRequestMap](resultMap)(((x0$1: zio.query.CompletedRequestMap, x1$1: Request) => scala.Tuple2.apply[zio.query.CompletedRequestMap, Request](x0$1, x1$1) match { case (_1: zio.query.CompletedRequestMap, _2: Request): (zio.query.CompletedRequestMap, Request)((map @ _), (req @ _)) => map.insert[Throwable, F[Entity]](req)(scala.`package`.Left.apply[Throwable, Nothing](err)) }))), ((result: Map[EntityId,Entity]) => chunk.foldLeft[zio.query.CompletedRequestMap](resultMap)(((x0$2: zio.query.CompletedRequestMap, x1$2: Request) => scala.Tuple2.apply[zio.query.CompletedRequestMap, Request](x0$2, x1$2) match { case (_1: zio.query.CompletedRequestMap, _2: Request): (zio.query.CompletedRequestMap, Request)((map @ _), (req @ _)) => { val res: Either[Exception,F[Entity]] = cats.implicits.catsSyntaxEither[cats.data.NonEmptyList[EntityId], F[Entity]](cats.Traverse.apply[F](evidence$1).traverse[[+A]cats.data.Validated[cats.data.NonEmptyList[EntityId],A], EntityId, Entity](req.ids)(((id: EntityId) => result.get(id) match { case (value: Entity): Some[Entity]((value @ _)) => cats.data.Validated.Valid.apply[Entity](value) case scala.None => cats.data.Validated.invalidNel[EntityId, Nothing](id) }))(data.this.Validated.catsDataApplicativeErrorForValidated[cats.data.NonEmptyList[EntityId]](data.this.NonEmptyList.catsDataSemigroupForNonEmptyList[EntityId])).toEither).leftMap[org.make.api.technical.graphql.DataSourceIdsNotFound[EntityId]](((invalids: cats.data.NonEmptyList[EntityId]) => DataSourceIdsNotFound.apply[EntityId](name, invalids)(showId))); map.insert[Throwable, F[Entity]](req)(res) } }))))(zio.this.CanFail.canFail[Any]) }))
38 44178 1426 - 1451 Select zio.query.CompletedRequestMap.empty zio.query.CompletedRequestMap.empty
39 41559 1485 - 1516 Apply zio.ChunkLike.flatMap chunk.flatMap[EntityId](((x$1: Request) => cats.implicits.toFoldableOps[F, EntityId](x$1.ids)(evidence$1).toIterable))
39 36071 1499 - 1504 Select org.make.api.technical.graphql.IdsRequest.ids x$1.ids
39 49677 1499 - 1515 Select cats.Foldable.Ops.toIterable cats.implicits.toFoldableOps[F, EntityId](x$1.ids)(evidence$1).toIterable
40 37682 1589 - 1609 Apply scala.Function1.apply findFromIds.apply(ids).apply(ec)
40 50170 1567 - 1610 Apply zio.Task.fromFuture zio.Task.fromFuture[Map[EntityId,Entity]](((ec: scala.concurrent.ExecutionContext) => findFromIds.apply(ids).apply(ec)))
41 34783 1655 - 1683 Apply cats.Show.show cats.Show.show[EntityId](((x$2: EntityId) => x$2.value))
41 42584 1675 - 1682 Select org.make.core.StringValue.value x$2.value
43 51267 1691 - 2351 ApplyToImplicitArgs zio.ZIO.fold entities.fold[zio.query.CompletedRequestMap](((err: Throwable) => chunk.foldLeft[zio.query.CompletedRequestMap](resultMap)(((x0$1: zio.query.CompletedRequestMap, x1$1: Request) => scala.Tuple2.apply[zio.query.CompletedRequestMap, Request](x0$1, x1$1) match { case (_1: zio.query.CompletedRequestMap, _2: Request): (zio.query.CompletedRequestMap, Request)((map @ _), (req @ _)) => map.insert[Throwable, F[Entity]](req)(scala.`package`.Left.apply[Throwable, Nothing](err)) }))), ((result: Map[EntityId,Entity]) => chunk.foldLeft[zio.query.CompletedRequestMap](resultMap)(((x0$2: zio.query.CompletedRequestMap, x1$2: Request) => scala.Tuple2.apply[zio.query.CompletedRequestMap, Request](x0$2, x1$2) match { case (_1: zio.query.CompletedRequestMap, _2: Request): (zio.query.CompletedRequestMap, Request)((map @ _), (req @ _)) => { val res: Either[Exception,F[Entity]] = cats.implicits.catsSyntaxEither[cats.data.NonEmptyList[EntityId], F[Entity]](cats.Traverse.apply[F](evidence$1).traverse[[+A]cats.data.Validated[cats.data.NonEmptyList[EntityId],A], EntityId, Entity](req.ids)(((id: EntityId) => result.get(id) match { case (value: Entity): Some[Entity]((value @ _)) => cats.data.Validated.Valid.apply[Entity](value) case scala.None => cats.data.Validated.invalidNel[EntityId, Nothing](id) }))(data.this.Validated.catsDataApplicativeErrorForValidated[cats.data.NonEmptyList[EntityId]](data.this.NonEmptyList.catsDataSemigroupForNonEmptyList[EntityId])).toEither).leftMap[org.make.api.technical.graphql.DataSourceIdsNotFound[EntityId]](((invalids: cats.data.NonEmptyList[EntityId]) => DataSourceIdsNotFound.apply[EntityId](name, invalids)(showId))); map.insert[Throwable, F[Entity]](req)(res) } }))))(zio.this.CanFail.canFail[Any])
43 33221 1704 - 1704 TypeApply zio.CanFail.canFail zio.this.CanFail.canFail[Any]
44 43665 1768 - 1794 Apply zio.query.CompletedRequestMap.insert map.insert[Throwable, F[Entity]](req)(scala.`package`.Left.apply[Throwable, Nothing](err))
44 35820 1721 - 1796 Apply zio.Chunk.foldLeft chunk.foldLeft[zio.query.CompletedRequestMap](resultMap)(((x0$1: zio.query.CompletedRequestMap, x1$1: Request) => scala.Tuple2.apply[zio.query.CompletedRequestMap, Request](x0$1, x1$1) match { case (_1: zio.query.CompletedRequestMap, _2: Request): (zio.query.CompletedRequestMap, Request)((map @ _), (req @ _)) => map.insert[Throwable, F[Entity]](req)(scala.`package`.Left.apply[Throwable, Nothing](err)) }))
44 48093 1784 - 1793 Apply scala.util.Left.apply scala.`package`.Left.apply[Throwable, Nothing](err)
45 42059 1820 - 2333 Apply zio.Chunk.foldLeft chunk.foldLeft[zio.query.CompletedRequestMap](resultMap)(((x0$2: zio.query.CompletedRequestMap, x1$2: Request) => scala.Tuple2.apply[zio.query.CompletedRequestMap, Request](x0$2, x1$2) match { case (_1: zio.query.CompletedRequestMap, _2: Request): (zio.query.CompletedRequestMap, Request)((map @ _), (req @ _)) => { val res: Either[Exception,F[Entity]] = cats.implicits.catsSyntaxEither[cats.data.NonEmptyList[EntityId], F[Entity]](cats.Traverse.apply[F](evidence$1).traverse[[+A]cats.data.Validated[cats.data.NonEmptyList[EntityId],A], EntityId, Entity](req.ids)(((id: EntityId) => result.get(id) match { case (value: Entity): Some[Entity]((value @ _)) => cats.data.Validated.Valid.apply[Entity](value) case scala.None => cats.data.Validated.invalidNel[EntityId, Nothing](id) }))(data.this.Validated.catsDataApplicativeErrorForValidated[cats.data.NonEmptyList[EntityId]](data.this.NonEmptyList.catsDataSemigroupForNonEmptyList[EntityId])).toEither).leftMap[org.make.api.technical.graphql.DataSourceIdsNotFound[EntityId]](((invalids: cats.data.NonEmptyList[EntityId]) => DataSourceIdsNotFound.apply[EntityId](name, invalids)(showId))); map.insert[Throwable, F[Entity]](req)(res) } }))
48 35527 1980 - 1980 ApplyToImplicitArgs cats.data.ValidatedInstances.catsDataApplicativeErrorForValidated data.this.Validated.catsDataApplicativeErrorForValidated[cats.data.NonEmptyList[EntityId]](data.this.NonEmptyList.catsDataSemigroupForNonEmptyList[EntityId])
48 49110 1971 - 1978 Select org.make.api.technical.graphql.IdsRequest.ids req.ids
48 42345 1980 - 1980 TypeApply cats.data.NonEmptyListInstances.catsDataSemigroupForNonEmptyList data.this.NonEmptyList.catsDataSemigroupForNonEmptyList[EntityId]
49 41597 2006 - 2020 Apply scala.collection.MapOps.get result.get(id)
50 33740 2069 - 2081 Apply cats.data.Validated.Valid.apply cats.data.Validated.Valid.apply[Entity](value)
51 50207 2122 - 2146 Apply cats.data.ValidatedFunctions.invalidNel cats.data.Validated.invalidNel[EntityId, Nothing](id)
54 48129 1933 - 2210 Select cats.data.Validated.toEither cats.Traverse.apply[F](evidence$1).traverse[[+A]cats.data.Validated[cats.data.NonEmptyList[EntityId],A], EntityId, Entity](req.ids)(((id: EntityId) => result.get(id) match { case (value: Entity): Some[Entity]((value @ _)) => cats.data.Validated.Valid.apply[Entity](value) case scala.None => cats.data.Validated.invalidNel[EntityId, Nothing](id) }))(data.this.Validated.catsDataApplicativeErrorForValidated[cats.data.NonEmptyList[EntityId]](data.this.NonEmptyList.catsDataSemigroupForNonEmptyList[EntityId])).toEither
55 44724 2248 - 2285 ApplyToImplicitArgs org.make.api.technical.graphql.DataSourceIdsNotFound.apply DataSourceIdsNotFound.apply[EntityId](name, invalids)(showId)
55 35860 1933 - 2286 Apply cats.syntax.EitherOps.leftMap cats.implicits.catsSyntaxEither[cats.data.NonEmptyList[EntityId], F[Entity]](cats.Traverse.apply[F](evidence$1).traverse[[+A]cats.data.Validated[cats.data.NonEmptyList[EntityId],A], EntityId, Entity](req.ids)(((id: EntityId) => result.get(id) match { case (value: Entity): Some[Entity]((value @ _)) => cats.data.Validated.Valid.apply[Entity](value) case scala.None => cats.data.Validated.invalidNel[EntityId, Nothing](id) }))(data.this.Validated.catsDataApplicativeErrorForValidated[cats.data.NonEmptyList[EntityId]](data.this.NonEmptyList.catsDataSemigroupForNonEmptyList[EntityId])).toEither).leftMap[org.make.api.technical.graphql.DataSourceIdsNotFound[EntityId]](((invalids: cats.data.NonEmptyList[EntityId]) => DataSourceIdsNotFound.apply[EntityId](name, invalids)(showId)))
56 49629 2301 - 2321 Apply zio.query.CompletedRequestMap.insert map.insert[Throwable, F[Entity]](req)(res)
67 48582 2592 - 2658 ApplyToImplicitArgs org.make.api.technical.graphql.DataSourceHelper.createDataSource DataSourceHelper.this.createDataSource[cats.Id, EntityId, Entity, Request](name, findFromIds)(cats.this.Invariant.catsInstancesForId)
67 35564 2639 - 2639 Select cats.Invariant.catsInstancesForId cats.this.Invariant.catsInstancesForId
74 44759 2944 - 2944 Select cats.instances.ListInstances.catsStdInstancesForList cats.implicits.catsStdInstancesForList
74 36916 2895 - 2963 ApplyToImplicitArgs org.make.api.technical.graphql.DataSourceHelper.createDataSource DataSourceHelper.this.createDataSource[List, EntityId, Entity, Request](name, findFromIds)(cats.implicits.catsStdInstancesForList)