Skip to content
This repository has been archived by the owner on Oct 25, 2024. It is now read-only.

Commit

Permalink
Merge pull request #16 from traveltime-dev/feature/user-defined-eleme…
Browse files Browse the repository at this point in the history
…nt-writes

User-defined Element types and writes. Enable cross-publishing for Scala 2.13
  • Loading branch information
PKJonas authored Jun 22, 2021
2 parents b13d879 + c1a2a2c commit 357135c
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 29 deletions.
2 changes: 2 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ sonatypeRepository := "https://s01.oss.sonatype.org/service/local"

name := "scala-slack-client"

crossScalaVersions := Seq("2.13.6", "2.12.14")

scalacOptions ++= Seq(
"-feature",
"-deprecation",
Expand Down
3 changes: 2 additions & 1 deletion src/main/scala/com/traveltime/slack/ApiSlackClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.traveltime.slack
import com.traveltime.slack.HooksSlackClient.Error
import com.traveltime.slack.dto.{InteractiveMessage, SlackFile}
import com.traveltime.slack.dto.InteractiveMessage.Channel
import play.api.libs.json.Writes

import scala.concurrent.Future

Expand All @@ -12,5 +13,5 @@ trait ApiSlackClient {
val multipartFormDataContentType = "multipart/form-data"

def uploadFile(channels: Seq[Channel], authToken: String, slackFile: SlackFile): Future[Either[Error, Unit]]
def sendInteractiveMessage(interactiveMessage: InteractiveMessage, authToken: String): Future[Either[Error, Unit]]
def sendInteractiveMessage[A : Writes](interactiveMessage: InteractiveMessage[A], authToken: String): Future[Either[Error, Unit]]
}
5 changes: 2 additions & 3 deletions src/main/scala/com/traveltime/slack/SlackHttpClient.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.traveltime.slack

import java.nio.charset.StandardCharsets.UTF_8

import com.traveltime.slack.HooksSlackClient.{Error, HookMessage}
import com.traveltime.slack.dto.InteractiveMessage.Channel
import com.traveltime.slack.dto.{InteractiveMessage, SlackFile}
Expand All @@ -10,7 +9,7 @@ import com.traveltime.slack.util.PayloadMapper
import com.traveltime.slack.util.RequestHelpers._
import dispatch.{url => Url}
import org.asynchttpclient.request.body.multipart.{FilePart, StringPart}
import play.api.libs.json.{JsValue, Json}
import play.api.libs.json.{JsValue, Json, Writes}

import scala.concurrent.Future

Expand All @@ -23,7 +22,7 @@ object SlackHttpClient extends HooksSlackClient with ApiSlackClient {
sendPost(url, body, Map.empty)
}

def sendInteractiveMessage(interactiveMessage: InteractiveMessage, authToken: String): Future[Either[Error, Unit]] =
def sendInteractiveMessage[A : Writes](interactiveMessage: InteractiveMessage[A], authToken: String): Future[Either[Error, Unit]] =
sendPost(
postMessageUrl,
Json.toJson(interactiveMessage),
Expand Down
3 changes: 1 addition & 2 deletions src/main/scala/com/traveltime/slack/dto/Button.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.traveltime.slack.dto

import com.traveltime.slack.dto.Button.{ActionId, ButtonStyle}
import com.traveltime.slack.dto.InteractiveMessage.Element

/**
* [[https://api.slack.com/reference/block-kit/block-elements#button]]
Expand All @@ -13,7 +12,7 @@ case class Button(
url: Option[String] = None,
value: Option[String] = None,
confirm: Option[ConfirmDialog] = None
) extends Element
)

object Button {
case class ActionId(id: String)
Expand Down
12 changes: 5 additions & 7 deletions src/main/scala/com/traveltime/slack/dto/InteractiveMessage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@ package com.traveltime.slack.dto

import com.traveltime.slack.dto.InteractiveMessage.{Block, Channel}

case class InteractiveMessage(channel: Channel, blocks: Vector[Block])
case class InteractiveMessage[A](channel: Channel, blocks: Vector[Block[A]])

object InteractiveMessage {
/**
* This can be either user id or channel id
*/
case class Channel(id: String)

trait Element

sealed abstract class Block(val blockType: String)
case object Divider extends Block("divider")
case class Section(textObject: TextObject) extends Block("section")
case class Actions(elements: Vector[Element]) extends Block("actions")
sealed abstract class Block[+A](val blockType: String)
case object Divider extends Block[Nothing]("divider")
case class Section(textObject: TextObject) extends Block[Nothing]("section")
case class Actions[A](elements: Vector[A]) extends Block[A]("actions")
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package com.traveltime.slack.json

import com.traveltime.slack.dto.InteractiveMessage._
import com.traveltime.slack.dto.TextObject.{Mrkdwn, PlainText, TextType}
import com.traveltime.slack.dto.{Button, ConfirmDialog, InteractiveMessage, TextObject}
Expand Down Expand Up @@ -37,7 +38,7 @@ object InteractiveMessageWrites {
(__ \ "deny").write[TextObject]
)(unlift(ConfirmDialog.unapply))

val buttonWrites: Writes[Button] = (button: Button) => JsObject(
implicit val buttonWrites: Writes[Button] = (button: Button) => JsObject(
Map(
"type" -> Some(JsString("button")),
"text" -> Some(textObjectWrites.writes(button.textObject)),
Expand All @@ -46,29 +47,31 @@ object InteractiveMessageWrites {
"value" -> button.value.map(JsString),
"style" -> button.style.map(style => JsString(style.literal)),
"confirm" -> button.confirm.map(confirmDialogWrites.writes)
).collect{ case (key, Some(value)) => (key, value) }
).collect { case (key, Some(value)) => (key, value) }
)

val elementsWrites: Writes[Element] = Writes[Element] {
case e: Button => buttonWrites.writes(e)
def actionsWrites[A: Writes]: Writes[Actions[A]] = (actions: Actions[A]) => {
val w = implicitly[Writes[A]]
JsObject(Map(
"type" -> JsString(actions.blockType),
"elements" -> JsArray(actions.elements.map(w.writes))
))
}

val actionsWrites: Writes[Actions] = (actions: Actions) => JsObject(Map(
"type" -> JsString(actions.blockType),
"elements" -> JsArray(actions.elements.map(elementsWrites.writes))
))

val dividerWrites: Writes[Divider.type] = (_: InteractiveMessage.Divider.type) =>
val dividerWrites: Writes[Divider.type] = (_: Divider.type) =>
JsObject(Map("type" -> JsString("divider")))

implicit val blockWrites: Writes[Block] = Writes[Block] {
implicit def blockWrites[A: Writes]: Writes[Block[A]] = Writes[Block[A]] {
case b: Divider.type => dividerWrites.writes(b)
case b: Section => sectionWrites.writes(b)
case b: Actions => actionsWrites.writes(b)
case b: Actions[A] =>
val w = implicitly[Writes[A]]
actionsWrites(w).writes(b)
}

implicit val interactiveMessageWrites: Writes[InteractiveMessage] = (
(__ \ "channel").write[String].contramap[Channel](_.id) and
(__ \ "blocks").write[Vector[Block]]
)(unlift(InteractiveMessage.unapply))
implicit def interactiveMessageWrites[A: Writes]: Writes[InteractiveMessage[A]] =
(
(__ \ "channel").write[String].contramap[Channel](_.id) and
(__ \ "blocks").write[Vector[Block[A]]]
) (unlift(InteractiveMessage.unapply[A]))
}

0 comments on commit 357135c

Please sign in to comment.