-
Notifications
You must be signed in to change notification settings - Fork 154
Open
Description
Hi
I have a question about API for custom fields, API doesn't return anything and I can't understand why
There is a scala-cli script (project.scala):
//> using scala 3.6.4
//> using toolkit typelevel:0.1.29
//> using dep org.typelevel::log4cats-core:2.7.0
//> using dep org.typelevel::log4cats-slf4j:2.7.0
//> using dep ch.qos.logback:logback-classic:1.5.18
import cats.effect.kernel.Async
import cats.effect.{ExitCode, IO}
import cats.syntax.all.*
import com.monovore.decline.Opts
import com.monovore.decline.effect.CommandIOApp
import io.circe.syntax.*
import io.circe.{Decoder, Encoder, Json, JsonObject}
import org.http4s.circe.*
import org.http4s.circe.CirceEntityCodec.*
import org.http4s.client.Client
import org.http4s.client.middleware.Logger as LoggerMiddleware
import org.http4s.ember.client.EmberClientBuilder
import org.http4s.headers.Accept
import org.http4s.{
EntityEncoder,
Header,
Headers,
MediaType,
Method,
Request,
Uri
}
import org.typelevel.ci.CIString
import org.typelevel.log4cats.slf4j.Slf4jFactory
import org.typelevel.log4cats.{Logger, LoggerFactory}
trait Auth[F[_]]:
def login(host: String, user: String, password: String): F[String]
object Auth:
def make[F[_]: Logger: Async](client: Client[F]): Auth[F] = new Auth[F]:
override def login(
host: String,
user: String,
password: String
): F[String] =
val body = JsonObject(
"account" -> user.asJson,
"password" -> password.asJson,
"rememberMe" -> true.asJson
).asJson
val req = Request[F](
method = Method.POST,
uri = Uri
.unsafeFromString(s"https://$host")
.withPath("/api/v1/open/auth/login"),
headers = Headers(Accept(MediaType.application.json))
).withEntity(body)
client
.expect[Json](req)(jsonOf[F, Json])
.flatTap: json =>
Logger[F].info(s"got response $json")
.flatMap: json =>
Async[F].fromEither(
json.hcursor.downField("token").as[String]
)
trait RestClient[F[_]]:
def get[A: Decoder](path: String): F[A]
def post[A: Decoder, B: Encoder: Decoder](path: String, body: B)(using
EntityEncoder[F, B]
): F[A]
object RestClient:
def make[F[_]: Logger: Async](
client: Client[F],
token: String,
host: String
): RestClient[F] = new RestClient[F]:
override def get[A: Decoder](path: String): F[A] =
client.expect(mkRequest(path, Method.GET))(jsonOf[F, A])
override def post[A: Decoder, B: Encoder: Decoder](path: String, body: B)(
using EntityEncoder[F, B]
): F[A] =
client.expect(mkRequest(path, Method.POST).withEntity(body.asJson))(
jsonOf[F, A]
)
private def mkRequest(path: String, method: Method): Request[F] =
Request[F](
method = method,
uri = Uri
.unsafeFromString(s"https://$host")
.withPath(path),
headers = Headers(
Header.Raw(CIString("X-Docspell-Auth"), token),
Accept(MediaType.application.json)
)
)
object MainApp
extends CommandIOApp(
name = "customfields",
header = "test customfields api",
helpFlag = true,
version = "0.0.0"
):
override def main: Opts[IO[ExitCode]] =
given LoggerFactory[IO] = Slf4jFactory.create[IO]
given Logger[IO] = LoggerFactory[IO].getLogger
val hostOpt =
Opts.option[String]("host", help = "Docspell host")
val userOpt =
Opts.option[String]("user", help = "Docspell username")
val passwordOpt =
Opts.option[String]("password", help = "Docspell password")
(hostOpt, userOpt, passwordOpt).mapN:
case (host, user, password) =>
EmberClientBuilder
.default[IO]
.build
.map: client =>
LoggerMiddleware[IO](
logHeaders = true,
logBody = true,
logAction = Some((msg: String) => Logger[IO].info(msg))
)(client)
.use: client =>
val auth = Auth.make[IO](client = client)
for
token <- auth.login(host = host, user = user, password = password)
rest = RestClient
.make[IO](token = token, host = host, client = client)
_ <- rest.post[Json, Json](
"/api/v1/sec/customfield",
JsonObject(
"name" -> Json.fromString("asn"),
"ftype" -> Json.fromString("text"),
"label" -> Json.fromString("ASN")
).asJson
)
_ <- rest.get[Json]("/api/v1/sec/customfield?q=asn")
yield ExitCode.SuccessWhen I run it:
scala run project.scala -- --host myhost.com --user mycollective/myuser --password mypasswordI receive an empty collection for custom fields "{"items":[]}":
11:44:29.405 [io-compute-3] INFO <empty>.MainApp -- HTTP/1.1 POST https://myhost.com/api/v1/open/auth/login Headers(Content-Length: 56, Accept: application/json, Content-Type: application/json, Accept: application/json) body="{"account":"mycolletive/myuser","password":"mypassword","rememberMe":true}"
11:44:29.534 [io-compute-3] INFO <empty>.MainApp -- HTTP/1.1 200 OK Headers(Server: nginx, Date: Wed, 07 May 2025 09:44:29 GMT, Content-Type: application/json, Content-Length: 294, Connection: keep-alive, Keep-Alive: timeout=20, Set-Cookie: <REDACTED>, Set-Cookie: <REDACTED>) body="{"collective":"mycollective","user":"myuser","success":true,"message":"Login successful","token":"mytoken","validMs":300000,"requireSecondFactor":false}"
11:44:29.535 [io-compute-3] INFO <empty>.MainApp -- got response {
"collective" : "mycollective",
"user" : "myuser",
"success" : true,
"message" : "Login successful",
"token" : "mytoken",
"validMs" : 300000,
"requireSecondFactor" : false
}
11:44:29.541 [io-compute-6] INFO <empty>.MainApp -- HTTP/1.1 POST https://myhost.com/api/v1/sec/customfield Headers(Content-Length: 43, X-Docspell-Auth: mytoken, Accept: application/json, Content-Type: application/json, Accept: application/json) body="{"name":"asn","ftype":"text","label":"ASN"}"
11:44:29.573 [io-compute-5] INFO <empty>.MainApp -- HTTP/1.1 200 OK Headers(Server: nginx, Date: Wed, 07 May 2025 09:44:29 GMT, Content-Type: application/json, Content-Length: 47, Connection: keep-alive, Keep-Alive: timeout=20) body="{"success":true,"message":"New field created."}"
11:44:29.577 [io-compute-6] INFO <empty>.MainApp -- HTTP/1.1 GET https://myhost.com/api/v1/sec/customfield?q=asn Headers(X-Docspell-Auth: mytoken, Accept application/json, Accept: application/json) body=""
11:44:29.598 [io-compute-2] INFO <empty>.MainApp -- HTTP/1.1 200 OK Headers(Server: nginx, Date: Wed, 07 May 2025 09:44:29 GMT, Content-Type: application/json, Content-Length: 12, Connection: keep-alive, Keep-Alive: timeout=20) body="{"items":[]}"
Metadata
Metadata
Assignees
Labels
No labels