Skip to content

Commit

Permalink
Fix #113
Browse files Browse the repository at this point in the history
  • Loading branch information
paulpdaniels committed Jan 29, 2021
1 parent 1f9c518 commit 5a750ac
Show file tree
Hide file tree
Showing 7 changed files with 637 additions and 53 deletions.
4 changes: 2 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ name in ThisBuild := "zio-slack"
organization in ThisBuild := "com.github.dapperware"


val mainScala = "2.12.10"
val allScala = Seq("2.13.1", mainScala)
val mainScala = "2.12.12"
val allScala = Seq("2.13.4", mainScala)

inThisBuild(
List(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.github.dapperware.slack.api

import com.github.dapperware.slack.models.AuthIdentity
import com.github.dapperware.slack.{ SlackEnv, SlackError, SlackExtractors }
import com.github.dapperware.slack.models.AuthIdentity
import com.github.dapperware.slack.{ SlackEnv, SlackError, SlackExtractors }
import zio.ZIO
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package com.github.dapperware.slack.api

import com.github.dapperware.slack.models.{ Attachment, Block, UpdateResponse }
import com.github.dapperware.slack.{ SlackEnv, SlackError }
import com.github.dapperware.slack.{ SlackEnv, SlackError }
import com.github.dapperware.slack.models.{ Attachment, Block, UpdateResponse }
import io.circe.Json
import io.circe.syntax._
import zio.ZIO
Expand Down
112 changes: 73 additions & 39 deletions client/src/main/scala/com/github/dapperware/slack/models/Block.scala
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ case class ButtonElement(text: PlainTextObject,
case class StaticSelectElement(placeholder: PlainTextObject,
action_id: String,
options: Seq[OptionObject],
option_groups: Seq[OptionGroupObject],
option_groups: Option[Seq[OptionGroupObject]] = None,
initial_option: Option[Either[OptionObject, OptionGroupObject]] = None,
confirm: Option[ConfirmationObject] = None)
extends BlockElement
Expand All @@ -182,10 +182,19 @@ case class UserSelectElement(placeholder: PlainTextObject,
action_id: String,
initial_user: Option[String] = None,
confirm: Option[ConfirmationObject] = None)
extends BlockElement {
extends BlockElement
with InputBlockElement {
override val `type`: String = "users_select"
}

case class MultiUsersSelectElement(
placeholder: PlainTextObject,
action_id: String
) extends BlockElement
with InputBlockElement {
override val `type`: String = "multi_users_select"
}

case class ChannelSelectElement(placeholder: PlainTextObject,
action_id: String,
initial_channel: Option[String] = None,
Expand All @@ -204,6 +213,18 @@ case class ConversationSelectElement(placeholder: PlainTextObject,
override val `type`: String = "conversations_select"
}

case class MultiConversationsSelectElement(placeholder: PlainTextObject,
action_id: String,
initial_conversations: Option[List[String]] = None,
default_to_current_conversation: Option[Boolean] = None,
confirm: Option[ConfirmationObject] = None,
max_selected_items: Option[Int] = None,
response_url_enabled: Option[Boolean] = None)
extends BlockElement
with InputBlockElement {
override val `type`: String = "multi_conversations_select"
}

case class OverflowElement(action_id: String, options: Seq[OptionObject], confirm: Option[ConfirmationObject] = None)
extends BlockElement {
override val `type`: String = "overflow"
Expand All @@ -225,29 +246,33 @@ object BlockElement {
implicit val optionGrpObjFmt = deriveCodec[OptionGroupObject]
implicit val confirmObjFmt = deriveCodec[ConfirmationObject]

implicit val eitherOptFmt = eitherObjectFormat[OptionObject, OptionGroupObject]("text", "label")
implicit val buttonElementFmt = deriveCodec[ButtonElement]
implicit val imageElementFmt = deriveCodec[ImageElement]
implicit val staticMenuElementFmt = deriveCodec[StaticSelectElement]
implicit val extMenuElementFmt = deriveCodec[ExternalSelectElement]
implicit val userMenuElementFmt = deriveCodec[UserSelectElement]
implicit val channelMenuElementFmt = deriveCodec[ChannelSelectElement]
implicit val conversationMenuElementFmt = deriveCodec[ConversationSelectElement]
implicit val overflowElementFmt = deriveCodec[OverflowElement]
implicit val datePickerElementFmt = deriveCodec[DatePickerElement]
implicit val eitherOptFmt = eitherObjectFormat[OptionObject, OptionGroupObject]("text", "label")
implicit val buttonElementFmt = deriveCodec[ButtonElement]
implicit val imageElementFmt = deriveCodec[ImageElement]
implicit val staticMenuElementFmt = deriveCodec[StaticSelectElement]
implicit val extMenuElementFmt = deriveCodec[ExternalSelectElement]
implicit val userMenuElementFmt = deriveCodec[UserSelectElement]
implicit val multiUsersSelectElementFmt = deriveCodec[MultiUsersSelectElement]
implicit val channelMenuElementFmt = deriveCodec[ChannelSelectElement]
implicit val conversationMenuElementFmt = deriveCodec[ConversationSelectElement]
implicit val multiConversationMenuElementFmt = deriveCodec[MultiConversationsSelectElement]
implicit val overflowElementFmt = deriveCodec[OverflowElement]
implicit val datePickerElementFmt = deriveCodec[DatePickerElement]

private val elemWrites = new Encoder[BlockElement] {
def apply(element: BlockElement): Json = {
val json = element match {
case elem: ButtonElement => elem.asJson
case elem: ImageElement => elem.asJson
case elem: StaticSelectElement => elem.asJson
case elem: ExternalSelectElement => elem.asJson
case elem: UserSelectElement => elem.asJson
case elem: ChannelSelectElement => elem.asJson
case elem: ConversationSelectElement => elem.asJson
case elem: OverflowElement => elem.asJson
case elem: DatePickerElement => elem.asJson
case elem: ButtonElement => elem.asJson
case elem: ImageElement => elem.asJson
case elem: StaticSelectElement => elem.asJson
case elem: ExternalSelectElement => elem.asJson
case elem: UserSelectElement => elem.asJson
case elem: MultiUsersSelectElement => elem.asJson
case elem: ChannelSelectElement => elem.asJson
case elem: ConversationSelectElement => elem.asJson
case elem: MultiConversationsSelectElement => elem.asJson
case elem: OverflowElement => elem.asJson
case elem: DatePickerElement => elem.asJson
}
Json.obj("type" -> element.`type`.asJson).deepMerge(json)
}
Expand All @@ -258,16 +283,18 @@ object BlockElement {
for {
value <- c.downField("type").as[String]
result <- value match {
case "button" => c.as[ButtonElement]
case "image" => c.as[ImageElement]
case "static_select" => c.as[StaticSelectElement]
case "external_select" => c.as[ExternalSelectElement]
case "users_select" => c.as[UserSelectElement]
case "conversations_select" => c.as[ConversationSelectElement]
case "channels_select" => c.as[ChannelSelectElement]
case "overflow" => c.as[OverflowElement]
case "datepicker" => c.as[DatePickerElement]
case other => Left(DecodingFailure(s"Invalid element type: $other", List.empty))
case "button" => c.as[ButtonElement]
case "image" => c.as[ImageElement]
case "static_select" => c.as[StaticSelectElement]
case "external_select" => c.as[ExternalSelectElement]
case "users_select" => c.as[UserSelectElement]
case "multi_users_select" => c.as[MultiUsersSelectElement]
case "conversations_select" => c.as[ConversationSelectElement]
case "multi_conversations_select" => c.as[MultiConversationsSelectElement]
case "channels_select" => c.as[ChannelSelectElement]
case "overflow" => c.as[OverflowElement]
case "datepicker" => c.as[DatePickerElement]
case other => Left(DecodingFailure(s"Invalid element type: $other", List.empty))
}
} yield result
}
Expand All @@ -280,10 +307,13 @@ object InputBlockElement {

implicit val encoder: Encoder[InputBlockElement] = Encoder.AsObject.instance[InputBlockElement] { ibe =>
val json = ibe match {
case i: PlainTextInput => i.asJsonObject
case i: StaticSelectElement => i.asJsonObject
case i: DatePickerElement => i.asJsonObject
case i: ConversationSelectElement => i.asJsonObject
case i: PlainTextInput => i.asJsonObject
case i: StaticSelectElement => i.asJsonObject
case i: DatePickerElement => i.asJsonObject
case i: ConversationSelectElement => i.asJsonObject
case i: MultiConversationsSelectElement => i.asJsonObject
case i: UserSelectElement => i.asJsonObject
case i: MultiUsersSelectElement => i.asJsonObject
}

json.add("type", ibe.`type`.asJson)
Expand All @@ -293,10 +323,14 @@ object InputBlockElement {

implicit val decoder: Decoder[InputBlockElement] = Decoder.instance[InputBlockElement] { c =>
typeDecoder(c).flatMap {
case "plain_text_input" => c.as[PlainTextInput]
case "static_select" => c.as[StaticSelectElement]
case "datepicker" => c.as[DatePickerElement]
case "conversations_select" => c.as[ConversationSelectElement]
case "plain_text_input" => c.as[PlainTextInput]
case "static_select" => c.as[StaticSelectElement]
case "datepicker" => c.as[DatePickerElement]
case "conversations_select" => c.as[ConversationSelectElement]
case "multi_conversations_select" => c.as[MultiConversationsSelectElement]
case "users_select" => c.as[UserSelectElement]
case "multi_users_select" => c.as[MultiUsersSelectElement]
case t => Left(DecodingFailure(s"Unknown input block element type $t", c.history))
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package com.github.dapperware.slack.models

import io.circe.Codec
import cats.Applicative.ops.toAllApplicativeOps
import io.circe.{ Codec, Decoder }
import io.circe.generic.semiauto._

case class View(
final case class View(
id: String,
team_id: String,
`type`: String,
Expand All @@ -25,10 +26,10 @@ case class View(
)

object View {
implicit val viewCodec: Codec[View] = deriveCodec[View]
implicit val viewCodec: Decoder[View] = deriveDecoder[View]
}

case class ViewPayload(
final case class ViewPayload(
`type`: String,
title: PlainTextObject,
blocks: Seq[Block],
Expand All @@ -42,17 +43,74 @@ case class ViewPayload(
submit_disabled: Option[Boolean] = None
)

case class ViewState(values: Map[String, Map[String, ViewStateValue]]) {
final case class ViewState(values: Map[String, Map[String, ViewStateValue]]) {
def getValue(block: String, action: String): Option[ViewStateValue] =
values.get(block).flatMap(_.get(action))
}

case class ViewStateValue(`type`: String, value: String)
sealed trait ViewStateValue {
def `type`: String
}

case class PlainTextValue(value: Option[String]) extends ViewStateValue {
val `type` = "plain_text_input"
}

case class SelectedOption(text: PlainTextObject, value: String)

case class StaticSelectValue(selected_option: Option[SelectedOption]) extends ViewStateValue {
override val `type`: String = "static_select"
}

case class MultiStaticSelectValue(selected_options: List[SelectedOption]) extends ViewStateValue {
override val `type`: String = "multi_static_select"
}

case class MultiConversationsValue(selected_conversations: List[String]) extends ViewStateValue {
override val `type`: String = "multi_conversations_select"
}

case class ConversationsSelectValue(selected_conversation: Option[String]) extends ViewStateValue {
override val `type`: String = "conversations_select"
}

case class DatepickerValue(selected_date: Option[String]) extends ViewStateValue {
override val `type`: String = "datepicker"
}

case class MultiUsersSelectValue(selected_users: List[String]) extends ViewStateValue {
override val `type`: String = "multi_users_select"
}

case class UsersSelectValue(selected_user: Option[String]) extends ViewStateValue {
override val `type`: String = "users_select"
}

object ViewState {
implicit val viewStateChildCodec: Codec.AsObject[ViewStateValue] = deriveCodec[ViewStateValue]
private val typeDecoder: Decoder[String] = Decoder.decodeString.at("type")
private implicit val selectedOptionDecoder: Decoder[SelectedOption] = deriveDecoder[SelectedOption]
private implicit val plainTextDecoder: Decoder[PlainTextValue] = deriveDecoder
private implicit val staticSelectDecoder: Decoder[StaticSelectValue] = deriveDecoder
private implicit val multiStaticSelectDecoder: Decoder[MultiStaticSelectValue] = deriveDecoder
private implicit val datepickerDecoder: Decoder[DatepickerValue] = deriveDecoder
private implicit val multiUserSelectDecoder: Decoder[MultiUsersSelectValue] = deriveDecoder
private implicit val usersSelectDecoder: Decoder[UsersSelectValue] = deriveDecoder
private implicit val multiConversationsSelectDecoder: Decoder[MultiConversationsValue] = deriveDecoder
private implicit val conversationsSelectDecoder: Decoder[ConversationsSelectValue] = deriveDecoder

implicit val viewStateChildDecoder: Decoder[ViewStateValue] = typeDecoder.flatMap {
case "plain_text_input" => Decoder[PlainTextValue].widen
case "static_select" => Decoder[StaticSelectValue].widen
case "multi_static_select" => Decoder[MultiStaticSelectValue].widen
case "datepicker" => Decoder[DatepickerValue].widen
case "multi_users_select" => Decoder[MultiUsersSelectValue].widen
case "users_select" => Decoder[UsersSelectValue].widen
case "multi_conversations_select" => Decoder[MultiConversationsValue].widen
case "conversations_select" => Decoder[ConversationsSelectValue].widen
case t => Decoder.failedWithMessage(s"Unknown view type $t is not supported")
}

implicit val viewStateDecoder: Codec.AsObject[ViewState] = deriveCodec[ViewState]
implicit val viewStateDecoder: Decoder[ViewState] = deriveDecoder[ViewState]
}

object ViewPayload {
Expand Down
Loading

0 comments on commit 5a750ac

Please sign in to comment.