abdolence / slack-morphism

Type-safe and reactive Slack client with blocks templating DSL and rate control for Scala

Home Page:https://slack.abdolence.dev/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Files API support

AlexisBRENON opened this issue · comments

I try to upload the result of a spark job a CSV excerpt of a slack dataframe to a slack channel.

I think the best solution would be to upload it as a file, through the files.upload method

As described in the "Low-level HTTP API to Slack Web API" section, I declared my request and response objects:

package org.latestbit.slack.morphism.client.reqresp.files

import org.latestbit.slack.morphism.common.{SlackChannelId, SlackTs}
import org.latestbit.slack.morphism.files.SlackFile

case class SlackApiFilesUploadRequest(channels: Option[Seq[SlackChannelId]] = None,
                                      file: Option[String] = None,
                                      content: Option[String] = None,
                                      filename: Option[String] = None,
                                      filetype: Option[String] = None,
                                      initialComment: Option[String] = None,
                                      thread_ts: Option[SlackTs] = None,
                                      title: Option[String] = None
                                     )

case class SlackApiFilesUploadResponse(ts: SlackTs, file: SlackFile)

With SlackFile defined as:

package org.latestbit.slack.morphism.files

import org.latestbit.slack.morphism.common.{SlackChannelId, SlackUserId}

case class SlackFile(id: Option[String],
                     created: Option[Long],
                     timestamp: Option[Long],
                     name: Option[String],
                     title: Option[String],
                     mimetype: Option[String],
                     filetype: Option[String],
                     pretty_type: Option[String],
                     user: Option[String],
                     editable: Option[Boolean],
                     size: Option[Long],
                     mode: Option[String],
                     is_external: Option[Boolean],
                     external_type: Option[String],
                     is_public: Option[Boolean],
                     public_url_shared: Option[Boolean],
                     display_as_bot: Option[Boolean],
                     username: Option[String],
                     url_private: Option[String],
                     url_private_download: Option[String],
                     thumb_64: Option[String],
                     thumb_80: Option[String],
                     thumb_360: Option[String],
                     thumb_360_w: Option[Int],
                     thumb_360_h: Option[Int],
                     thumb_160: Option[String],
                     thumb_360_gif: Option[String],
                     image_exif_rotation: Option[Int],
                     original_w: Option[Int],
                     original_h: Option[Int],
                     deanimate_gif: Option[String],
                     pjpeg: Option[String],
                     permalink: Option[String],
                     permalink_public: Option[String],
                     comments_count: Option[Int],
                     is_starred: Option[Boolean],
                     shares: Option[SlackShares],
                     channels: Option[Seq[SlackChannelId]],
                     groups: Option[Seq[String]],
                     ims: Option[Seq[String]],
                     has_rich_preview: Option[Boolean]
                    )

case class SlackShares(public: Map[SlackChannelId, Seq[SlackShare]])

case class SlackShare(reply_user: Seq[SlackUserId],
                      reply_users_count: Int,
                      reply_count: Int,
                      ts: String,
                      thread_ts: String,
                      latest_reply: String,
                      channel_name: String,
                      team_id: String)

And declare a package method to use them:

package org.latestbit.slack.morphism.client.reqresp

import io.circe.{Decoder, Encoder}
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
import org.latestbit.slack.morphism.client.{SlackApiClientBackend, SlackApiClientError, SlackApiClientT, SlackApiToken}

import scala.concurrent.Future

package object files {
  def upload(client: SlackApiClientT[Future])(
    req: SlackApiFilesUploadRequest,
  )(implicit
    slackApiToken: SlackApiToken,
    backendType: SlackApiClientBackend.BackendType[Future]
            ): Future[Either[SlackApiClientError, SlackApiFilesUploadResponse]] = {
    implicit val rqstEncoder: Encoder[SlackApiFilesUploadRequest] = deriveEncoder[SlackApiFilesUploadRequest]
    implicit val rspDecoder: Decoder[SlackApiFilesUploadResponse] = deriveDecoder[SlackApiFilesUploadResponse]
    client.http.post[SlackApiFilesUploadRequest, SlackApiFilesUploadResponse](
      methodUri = "files.upload",
      req = req,
    )
  }
}

However, when compiling this package.scala file I get the following error:

.../src/main/scala/org/latestbit/slack/morphism/client/reqresp/files/package.scala:16:82
could not find Lazy implicit value of type io.circe.generic.encoding.DerivedAsObjectEncoder[org.latestbit.slack.morphism.client.reqresp.files.SlackApiFilesUploadRequest]
    implicit val rqstEncoder: Encoder[SlackApiFilesUploadRequest] = deriveEncoder[SlackApiFilesUploadRequest]

.../src/main/scala/org/latestbit/slack/morphism/client/reqresp/files/package.scala:16:82
not enough arguments for method deriveEncoder: (implicit encode: shapeless.Lazy[io.circe.generic.encoding.DerivedAsObjectEncoder[org.latestbit.slack.morphism.client.reqresp.files.SlackApiFilesUploadRequest]])io.circe.Encoder.AsObject[org.latestbit.slack.morphism.client.reqresp.files.SlackApiFilesUploadRequest].
Unspecified value parameter encode.
    implicit val rqstEncoder: Encoder[SlackApiFilesUploadRequest] = deriveEncoder[SlackApiFilesUploadRequest]

.../src/main/scala/org/latestbit/slack/morphism/client/reqresp/files/package.scala:17:82
could not find Lazy implicit value of type io.circe.generic.decoding.DerivedDecoder[org.latestbit.slack.morphism.client.reqresp.files.SlackApiFilesUploadResponse]
    implicit val rspDecoder: Decoder[SlackApiFilesUploadResponse] = deriveDecoder[SlackApiFilesUploadResponse]

.../src/main/scala/org/latestbit/slack/morphism/client/reqresp/files/package.scala:17:82
not enough arguments for method deriveDecoder: (implicit decode: shapeless.Lazy[io.circe.generic.decoding.DerivedDecoder[org.latestbit.slack.morphism.client.reqresp.files.SlackApiFilesUploadResponse]])io.circe.Decoder[org.latestbit.slack.morphism.client.reqresp.files.SlackApiFilesUploadResponse].
Unspecified value parameter decode.
    implicit val rspDecoder: Decoder[SlackApiFilesUploadResponse] = deriveDecoder[SlackApiFilesUploadResponse]

Is there any plan to support the files.[...] methods?
Is there any workaround to make it work for the moment?

Hey, as a first glance, try this:

  • extend your object files with org.latestbit.slack.morphism.codecs.CirceCodecs to bring all existing JSON codecs
  • you need to define encoder/decoders for all of the new classes that you have in SlackApiFilesUploadRequest/SlackApiFilesUploadResponse (in their fields), so you need to define Circe encoder/decoders for SlackShare, etc as well

I'll think about to support it natively, for now try those

All info I just gave were relavant for Slack Web API that supports JSON requests.
Now I checked https://api.slack.com/methods/files.upload, and unfortunately, it won't work, because this method requires:
multipart/form-data or application/x-www-form-urlencoded, so it needs to be handled a bit differently and Circe codecs won't help for the request part.

This method does not currently accept application/json

So, give me time to dig into it more.

Started working on this, it is needed handled a bit differently than other WebAPI methods

In the latest snapshot (3.1.0-SNAPSHOT) there is a working version of files.upload method. I think in a few days it will be released with some other Files API methods. I tested it a bit, seems working fine now.

v3.1.0 released with the support of Files API.

Wow ! What a responsiveness. Thanks you !