diff --git a/src/main/scala/com/particeep/api/ControlCapability.scala b/src/main/scala/com/particeep/api/ControlCapability.scala new file mode 100644 index 0000000..00a2aa2 --- /dev/null +++ b/src/main/scala/com/particeep/api/ControlCapability.scala @@ -0,0 +1,66 @@ +package com.particeep.api + +import com.particeep.api.core.{ ApiCredential, EntityClient, WSClient, WithCredentials, WithWS } +import com.particeep.api.models.{ ErrorResult, PaginatedSequence, TableSearch } +import com.particeep.api.models.control.{ Control, ControlBlockUpdate, ControlCreation, ControlUpdate, ControlView, ControlViewSearchCriteria, EventControl } +import com.particeep.api.utils.LangUtils +import play.api.libs.json.{ JsNull, Json } + +import scala.concurrent.{ ExecutionContext, Future } + +trait ControlCapability { + self: WSClient => + + val control = new ControlClient(this) + + def control(credentials: ApiCredential): ControlClient = new ControlClient(this, Some(credentials)) +} + +object ControlClient { + private val endPoint: String = "/control" +} + +class ControlClient(val ws: WSClient, val credentials: Option[ApiCredential] = None) extends WithWS + with WithCredentials + with EntityClient { + + import ControlClient._ + + def byIds(ids: String, timeout: Long = defaultTimeOut)(implicit exec: ExecutionContext): Future[Either[ErrorResult, List[ControlView]]] = { + ws.get[List[ControlView]](s"$endPoint/", timeout, List("ids" -> ids)) + } + + def audit(id: String, entity_type: String, timeout: Long = defaultTimeOut)(implicit exec: ExecutionContext): Future[Either[ErrorResult, List[EventControl]]] = { + ws.get[List[EventControl]](s"$endPoint/$entity_type/$id/audit", timeout) + } + + def create(user_id: String, timeout: Long = defaultTimeOut, control_creation: ControlCreation)(implicit exec: ExecutionContext): Future[Either[ErrorResult, Control]] = { + ws.put[Control](s"$endPoint/assign_to/$user_id", timeout, Json.toJson(control_creation)) + } + + def update(id: String, timeout: Long = defaultTimeOut, control_update: ControlUpdate)(implicit exec: ExecutionContext): Future[Either[ErrorResult, Control]] = { + ws.post[Control](s"$endPoint/$id", timeout, Json.toJson(control_update)) + } + + def updateBlock( + id: String, + block_id: String, + timeout: Long = defaultTimeOut, + control_block_update: ControlBlockUpdate + )(implicit exec: ExecutionContext): Future[Either[ErrorResult, Control]] = { + ws.post[Control](s"$endPoint/$id/block/$block_id", timeout, Json.toJson(control_block_update)) + } + + def publish(id: String, timeout: Long = defaultTimeOut)(implicit exec: ExecutionContext): Future[Either[ErrorResult, Control]] = { + ws.post[Control](s"$endPoint/$id/publish", timeout, JsNull) + } + + def search( + criteria: ControlViewSearchCriteria, + table_criteria: TableSearch, + timeout: Long = defaultTimeOut + )(implicit exec: ExecutionContext): Future[Either[ErrorResult, PaginatedSequence[ControlView]]] = { + val params_query = LangUtils.productToQueryString(criteria) ++ LangUtils.productToQueryString(table_criteria) + ws.get[PaginatedSequence[ControlView]](s"$endPoint/search", timeout, params_query) + } +} \ No newline at end of file diff --git a/src/main/scala/com/particeep/api/ParticeepApi.scala b/src/main/scala/com/particeep/api/ParticeepApi.scala index 89adac4..0cfc37f 100644 --- a/src/main/scala/com/particeep/api/ParticeepApi.scala +++ b/src/main/scala/com/particeep/api/ParticeepApi.scala @@ -38,7 +38,8 @@ object ParticeepApi { with WalletCapability with WalletSepaCapability with WebHookCapability - with PhoneMessagingCapability { + with PhoneMessagingCapability + with ControlCapability { self: WSClient => } diff --git a/src/main/scala/com/particeep/api/models/control/Control.scala b/src/main/scala/com/particeep/api/models/control/Control.scala new file mode 100644 index 0000000..40bfc67 --- /dev/null +++ b/src/main/scala/com/particeep/api/models/control/Control.scala @@ -0,0 +1,24 @@ +package com.particeep.api.models.control + +import com.particeep.api.models.enums.ControlStatus +import play.api.libs.json.{ JsObject, Json, OFormat } + +import java.time.OffsetDateTime + +final case class Control( + id: String, + created_at: OffsetDateTime, + created_by: String, + assigned_to: String, + level: Int, + target_id: String, + target_type: String, + target_entity: JsObject, + blocks: List[ControlBlock], + status: ControlStatus, + comment: Option[String] +) + +object Control { + implicit val controlFormat: OFormat[Control] = Json.format[Control] +} \ No newline at end of file diff --git a/src/main/scala/com/particeep/api/models/control/ControlBlock.scala b/src/main/scala/com/particeep/api/models/control/ControlBlock.scala new file mode 100644 index 0000000..b0c52c2 --- /dev/null +++ b/src/main/scala/com/particeep/api/models/control/ControlBlock.scala @@ -0,0 +1,18 @@ +package com.particeep.api.models.control + +import com.particeep.api.models.enums.{ ControlBlockStatus, ControlBlockType } +import play.api.libs.json.{ Json, OFormat } + +final case class ControlBlock( + id: String, + block_type: ControlBlockType, + path: String, + status: ControlBlockStatus, + target_id: Option[String], + comment: Option[String], + doc_ids: Seq[String] +) + +object ControlBlock { + implicit val control_block_format: OFormat[ControlBlock] = Json.format[ControlBlock] +} \ No newline at end of file diff --git a/src/main/scala/com/particeep/api/models/control/ControlBlockUpdate.scala b/src/main/scala/com/particeep/api/models/control/ControlBlockUpdate.scala new file mode 100644 index 0000000..505d38c --- /dev/null +++ b/src/main/scala/com/particeep/api/models/control/ControlBlockUpdate.scala @@ -0,0 +1,14 @@ +package com.particeep.api.models.control + +import com.particeep.api.models.enums.ControlBlockStatus +import play.api.libs.json.{ Json, OFormat } + +final case class ControlBlockUpdate( + doc_ids: Seq[String], + status: Option[ControlBlockStatus], + comment: Option[String] +) + +object ControlBlockUpdate { + implicit val control_block_update_format: OFormat[ControlBlockUpdate] = Json.format[ControlBlockUpdate] +} \ No newline at end of file diff --git a/src/main/scala/com/particeep/api/models/control/ControlCreation.scala b/src/main/scala/com/particeep/api/models/control/ControlCreation.scala new file mode 100644 index 0000000..f29673b --- /dev/null +++ b/src/main/scala/com/particeep/api/models/control/ControlCreation.scala @@ -0,0 +1,13 @@ +package com.particeep.api.models.control + +import play.api.libs.json.{ JsObject, Json, OFormat } + +final case class ControlCreation( + target_id: String, + target_type: String, + target_entity: JsObject +) + +object ControlCreation { + implicit val control_creation_format: OFormat[ControlCreation] = Json.format[ControlCreation] +} \ No newline at end of file diff --git a/src/main/scala/com/particeep/api/models/control/ControlUpdate.scala b/src/main/scala/com/particeep/api/models/control/ControlUpdate.scala new file mode 100644 index 0000000..9cedaa1 --- /dev/null +++ b/src/main/scala/com/particeep/api/models/control/ControlUpdate.scala @@ -0,0 +1,11 @@ +package com.particeep.api.models.control + +import play.api.libs.json.{ Json, OFormat } + +final case class ControlUpdate( + comment: Option[String] = None +) + +object ControlUpdate { + implicit val control_update_format: OFormat[ControlUpdate] = Json.format[ControlUpdate] +} \ No newline at end of file diff --git a/src/main/scala/com/particeep/api/models/control/ControlView.scala b/src/main/scala/com/particeep/api/models/control/ControlView.scala new file mode 100644 index 0000000..b4db3d4 --- /dev/null +++ b/src/main/scala/com/particeep/api/models/control/ControlView.scala @@ -0,0 +1,35 @@ +package com.particeep.api.models.control + +import com.particeep.api.models.enums.ControlStatus +import play.api.libs.json.{ Json, OFormat } + +import java.time.OffsetDateTime + +final case class ControlView( + id: String, + created_at: OffsetDateTime, + updated_at: OffsetDateTime, + deleted_at: Option[OffsetDateTime], + created_by: String, + assigned_to: String, + level: Int, + target_id: String, + target_type: String, + blocks: List[ControlBlock], + status: ControlStatus, + comment: Option[String] +) + +object ControlView { + implicit val control_view_format: OFormat[ControlView] = Json.format[ControlView] +} + +final case class ControlViewSearchCriteria( + created_after: Option[OffsetDateTime], + created_before: Option[OffsetDateTime], + level: Option[Int], + target_id: Option[String], + target_type: Option[String], + status: Option[ControlStatus], + ids: Option[String] +) \ No newline at end of file diff --git a/src/main/scala/com/particeep/api/models/control/EventControl.scala b/src/main/scala/com/particeep/api/models/control/EventControl.scala new file mode 100644 index 0000000..0a48754 --- /dev/null +++ b/src/main/scala/com/particeep/api/models/control/EventControl.scala @@ -0,0 +1,17 @@ +package com.particeep.api.models.control + +import play.api.libs.json.{ JsValue, Json, OFormat } + +final case class EventControl( + id: String, + name: String, + created_at: Long, + created_by: String, + entity_id: String, + payload: JsValue, + tags: Set[String] +) + +object EventControl { + implicit val eventFormat: OFormat[EventControl] = Json.format[EventControl] +} \ No newline at end of file diff --git a/src/main/scala/com/particeep/api/models/enums/ControlBlockStatus.scala b/src/main/scala/com/particeep/api/models/enums/ControlBlockStatus.scala new file mode 100644 index 0000000..f50c3e1 --- /dev/null +++ b/src/main/scala/com/particeep/api/models/enums/ControlBlockStatus.scala @@ -0,0 +1,12 @@ +package com.particeep.api.models.enums + +sealed trait ControlBlockStatus extends Product with Serializable with Enum + +object ControlBlockStatus extends EnumHelper[ControlBlockStatus] { + + case object ONGOING extends ControlBlockStatus { val name: String = "ONGOING" } + case object VALIDATED extends ControlBlockStatus { val name: String = "VALIDATED" } + case object REJECTED extends ControlBlockStatus { val name: String = "REJECTED" } + + val values: Set[ControlBlockStatus] = Set(ONGOING, VALIDATED, REJECTED) +} diff --git a/src/main/scala/com/particeep/api/models/enums/ControlBlockType.scala b/src/main/scala/com/particeep/api/models/enums/ControlBlockType.scala new file mode 100644 index 0000000..658bd98 --- /dev/null +++ b/src/main/scala/com/particeep/api/models/enums/ControlBlockType.scala @@ -0,0 +1,27 @@ +package com.particeep.api.models.enums + +import play.api.libs.json._ + +trait ControlBlockType extends Product with Serializable with Enum + +object ControlBlockType { + + private[this] def enumReads: Reads[ControlBlockType] = new Reads[ControlBlockType] { + def reads(json: JsValue): JsResult[ControlBlockType] = { + json + .validate[ControlControlBlockType] + .orElse(json.validate[TransactionControlBlockType]) + .orElse(JsError(s"Can't parse json into ControlBlockType, no implementation found for $json")) + } + } + + private[this] def enumWrites: Writes[ControlBlockType] = new Writes[ControlBlockType] { + def writes(v: ControlBlockType): JsValue = v match { + case blockType: ControlControlBlockType => Json.toJson(blockType) + case blockType: TransactionControlBlockType => Json.toJson(blockType) + case _ => throw new IllegalStateException(s"Can't serialize case class to json, no implementation found for $v") + } + } + + implicit val controlBlockTypeFormat: Format[ControlBlockType] = Format(enumReads, enumWrites) +} diff --git a/src/main/scala/com/particeep/api/models/enums/ControlControlBlockType.scala b/src/main/scala/com/particeep/api/models/enums/ControlControlBlockType.scala new file mode 100644 index 0000000..3897aff --- /dev/null +++ b/src/main/scala/com/particeep/api/models/enums/ControlControlBlockType.scala @@ -0,0 +1,9 @@ +package com.particeep.api.models.enums + +sealed trait ControlControlBlockType extends ControlBlockType + +object ControlControlBlockType extends EnumHelper[ControlControlBlockType] { + case object COMMENT extends ControlControlBlockType { val name: String = "COMMENT" } + + override def values: Set[ControlControlBlockType] = Set(COMMENT) +} diff --git a/src/main/scala/com/particeep/api/models/enums/ControlStatus.scala b/src/main/scala/com/particeep/api/models/enums/ControlStatus.scala new file mode 100644 index 0000000..aee620c --- /dev/null +++ b/src/main/scala/com/particeep/api/models/enums/ControlStatus.scala @@ -0,0 +1,13 @@ +package com.particeep.api.models.enums + +sealed trait ControlStatus extends Product with Serializable with Enum + +object ControlStatus extends EnumHelper[ControlStatus] { + + case object ONGOING extends ControlStatus { val name: String = "ONGOING" } + case object VALIDATED extends ControlStatus { val name: String = "VALIDATED" } + case object REJECTED extends ControlStatus { val name: String = "REJECTED" } + case object SUCCEED extends ControlStatus { val name: String = "SUCCEED" } + + val values: Set[ControlStatus] = Set(ONGOING, VALIDATED, REJECTED, SUCCEED) +} diff --git a/src/main/scala/com/particeep/api/models/enums/TransactionControlBlockType.scala b/src/main/scala/com/particeep/api/models/enums/TransactionControlBlockType.scala new file mode 100644 index 0000000..eca2f99 --- /dev/null +++ b/src/main/scala/com/particeep/api/models/enums/TransactionControlBlockType.scala @@ -0,0 +1,29 @@ +package com.particeep.api.models.enums + +sealed trait TransactionControlBlockType extends ControlBlockType + +object TransactionControlBlockType extends EnumHelper[TransactionControlBlockType] { + + case object INVESTOR extends TransactionControlBlockType { val name: String = "INVESTOR" } + case object CO_ISSUER extends TransactionControlBlockType { val name: String = "CO_ISSUER" } + case object PARTNER extends TransactionControlBlockType { val name: String = "PARTNER" } + case object USUFRUCTUARY extends TransactionControlBlockType { val name: String = "USUFRUCTUARY" } + case object BANK_ACCOUNT extends TransactionControlBlockType { val name: String = "BANK_ACCOUNT" } + case object TRANSACTION extends TransactionControlBlockType { val name: String = "TRANSACTION" } + case object DOCUMENTS extends TransactionControlBlockType { val name: String = "DOCUMENTS" } + case object QUESTION extends TransactionControlBlockType { val name: String = "QUESTION" } + + val values: Set[TransactionControlBlockType] = { + Set( + INVESTOR, + CO_ISSUER, + PARTNER, + USUFRUCTUARY, + BANK_ACCOUNT, + TRANSACTION, + DOCUMENTS, + QUESTION + ) + } + +}