diff --git a/build.sbt b/build.sbt index be995d3ed..13e561c2a 100644 --- a/build.sbt +++ b/build.sbt @@ -28,18 +28,19 @@ lazy val root = lazy val library = project .in(file("modules/library")) + .disablePlugins(RevolverPlugin) .settings(stdSettings("zio-elasticsearch")) .settings(scalacOptions += "-language:higherKinds") .configs(IntegrationTest) .settings( Defaults.itSettings, libraryDependencies ++= List( - "com.softwaremill.sttp.client3" %% "zio" % "3.8.3", - "com.softwaremill.sttp.client3" %% "zio-json" % "3.8.3", + "com.softwaremill.sttp.client3" %% "zio" % "3.8.5", + "com.softwaremill.sttp.client3" %% "zio-json" % "3.8.5", "dev.zio" %% "zio-json" % "0.3.0", "dev.zio" %% "zio-prelude" % "1.0.0-RC16", - "dev.zio" %% "zio-schema" % "0.3.1", - "dev.zio" %% "zio-schema-json" % "0.3.1", + "dev.zio" %% "zio-schema" % "0.4.1", + "dev.zio" %% "zio-schema-json" % "0.4.1", "org.apache.commons" % "commons-lang3" % "3.12.0", "dev.zio" %% "zio-test" % "2.0.5" % Tests, "dev.zio" %% "zio-test-sbt" % "2.0.5" % Tests @@ -51,9 +52,17 @@ lazy val example = project .in(file("modules/example")) .settings(stdSettings("example")) + .settings(scalacOptions += "-language:higherKinds") .settings( libraryDependencies ++= List( - "dev.zio" %% "zio" % "2.0.5" + "dev.zio" %% "zio" % "2.0.5", + "dev.zio" %% "zio-config" % "3.0.6", + "dev.zio" %% "zio-config-magnolia" % "3.0.6", + "dev.zio" %% "zio-config-typesafe" % "3.0.6", + "dev.zio" %% "zio-http" % "0.0.3", + "dev.zio" %% "zio-json" % "0.3.0", + "dev.zio" %% "zio-schema" % "0.4.1", + "dev.zio" %% "zio-schema-json" % "0.4.1" ) ) .dependsOn(library) @@ -65,6 +74,7 @@ lazy val docs = project .in(file("modules/docs")) .enablePlugins(MdocPlugin, DocusaurusPlugin, ScalaUnidocPlugin) + .disablePlugins(RevolverPlugin) .dependsOn(library) .settings( publish / skip := true, diff --git a/modules/example/README.md b/modules/example/README.md new file mode 100644 index 000000000..b16a9b3f0 --- /dev/null +++ b/modules/example/README.md @@ -0,0 +1,22 @@ +# ZIO Elasticsearch Example Application + +This application represents an example of usage `zio-elasticsearch` library for **Elasticsearch 7.x**. + +### Running + +- Run the Elasticsearch 7.x service (one can be found as part of the `docker-compose.yml` file in the root of this + repository) +- Start the application by running the following command: + ```shell + ./sbt "~example/reStart" + ``` +- Check whether the application is running [here](http://localhost:9000/health) +- Explore endpoints using Postman collection (`zio-elasticsearch-example.postman_collection.json`) + +### Description + +On the application startup - a **"repositories"** index will be deleted, and immediately re-created with the mapping +definition given in the `resources/mapping.json` file. + +After successfully starting the application, you can test the exposed ZIO Elasticsearch library's API through exposed HTTP +endpoints. diff --git a/modules/example/src/main/resources/application.conf b/modules/example/src/main/resources/application.conf new file mode 100644 index 000000000..b0601b6da --- /dev/null +++ b/modules/example/src/main/resources/application.conf @@ -0,0 +1,8 @@ +http { + port = 9000 +} + +elasticsearch { + host = "localhost" + port = 9200 +} diff --git a/modules/example/src/main/resources/mapping.json b/modules/example/src/main/resources/mapping.json new file mode 100644 index 000000000..29952b023 --- /dev/null +++ b/modules/example/src/main/resources/mapping.json @@ -0,0 +1,38 @@ +{ + "settings": { + "index": { + "number_of_shards": 1 + } + }, + "mappings": { + "_routing": { + "required": true + }, + "properties": { + "id": { + "type": "keyword" + }, + "organization": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "url": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "lastCommitAt": { + "type": "date" + }, + "stars": { + "type": "integer" + }, + "forks": { + "type": "integer" + } + } + } +} diff --git a/modules/example/src/main/scala/example/GitHubRepo.scala b/modules/example/src/main/scala/example/GitHubRepo.scala new file mode 100644 index 000000000..a5e7ccf09 --- /dev/null +++ b/modules/example/src/main/scala/example/GitHubRepo.scala @@ -0,0 +1,23 @@ +package example + +import zio.json.{DeriveJsonEncoder, JsonEncoder} +import zio.schema.{DeriveSchema, Schema} + +import java.time.LocalDateTime + +final case class GitHubRepo( + id: Option[String], + organization: String, + name: String, + url: String, + description: Option[String], + lastCommitAt: LocalDateTime, + stars: Int, + forks: Int +) + +object GitHubRepo { + implicit val schema: Schema[GitHubRepo] = DeriveSchema.gen[GitHubRepo] + + implicit val encoder: JsonEncoder[GitHubRepo] = DeriveJsonEncoder.gen[GitHubRepo] +} diff --git a/modules/example/src/main/scala/example/Main.scala b/modules/example/src/main/scala/example/Main.scala new file mode 100644 index 000000000..e5789bbcc --- /dev/null +++ b/modules/example/src/main/scala/example/Main.scala @@ -0,0 +1,58 @@ +package example + +import example.api.{HealthCheck, Repositories} +import example.config.{AppConfig, ElasticsearchConfig, HttpConfig} +import sttp.client3.httpclient.zio.HttpClientZioBackend +import zio._ +import zio.config.getConfig +import zio.elasticsearch.{ElasticConfig, ElasticExecutor, ElasticRequest} +import zio.http.{Server, ServerConfig} + +import scala.io.Source + +object Main extends ZIOAppDefault { + + override def run: Task[ExitCode] = { + val elasticConfigLive = ZLayer(getConfig[ElasticsearchConfig].map(es => ElasticConfig(es.host, es.port))) + + (prepare *> runServer).provide( + AppConfig.live, + elasticConfigLive, + ElasticExecutor.live, + HttpClientZioBackend.layer() + ) + } + + private[this] def prepare: RIO[ElasticExecutor, Unit] = { + val deleteIndex: RIO[ElasticExecutor, Unit] = + for { + _ <- ZIO.logInfo(s"Deleting index '$Index'...") + _ <- ElasticRequest.deleteIndex(Index).execute + } yield () + + val createIndex: RIO[ElasticExecutor, Unit] = + for { + _ <- ZIO.logInfo(s"Creating index '$Index'...") + mapping <- ZIO.attempt(Source.fromURL(getClass.getResource("/mapping.json")).mkString) + _ <- ElasticRequest.createIndex(Index, Some(mapping)).execute + } yield () + + deleteIndex *> createIndex + } + + private[this] def runServer: RIO[HttpConfig with ElasticExecutor, ExitCode] = { + val serverConfigLive = ZLayer(getConfig[HttpConfig].map(http => ServerConfig.default.port(http.port))) + + (for { + http <- getConfig[HttpConfig] + _ <- ZIO.logInfo(s"Starting an HTTP service on port: ${http.port}") + routes = HealthCheck.Route ++ Repositories.Routes + _ <- Server.serve(routes) + } yield ExitCode.success).provideSome( + RepositoriesElasticsearch.live, + Server.live, + serverConfigLive + ) + } + +} diff --git a/modules/example/src/main/scala/example/RepositoriesElasticsearch.scala b/modules/example/src/main/scala/example/RepositoriesElasticsearch.scala new file mode 100644 index 000000000..a68c5fdcb --- /dev/null +++ b/modules/example/src/main/scala/example/RepositoriesElasticsearch.scala @@ -0,0 +1,58 @@ +package example + +import zio._ +import zio.elasticsearch.ElasticError.DocumentRetrievingError.DocumentNotFound +import zio.elasticsearch.{DocumentId, ElasticExecutor, ElasticRequest, Routing} + +final case class RepositoriesElasticsearch(executor: ElasticExecutor) { + + def findById(organization: String, id: String): Task[Option[GitHubRepo]] = + for { + routing <- routingOf(organization) + req = ElasticRequest.getById[GitHubRepo](Index, DocumentId(id)).routing(routing) + res <- executor.execute(req) + } yield res.toOption + + def create(repository: GitHubRepo): Task[Option[DocumentId]] = + for { + routing <- routingOf(repository.organization) + req = ElasticRequest.create(Index, repository).routing(routing) + res <- executor.execute(req) + } yield res + + def upsert(id: String, repository: GitHubRepo): Task[Unit] = + for { + routing <- routingOf(repository.organization) + req = ElasticRequest.upsert(Index, DocumentId(id), repository).routing(routing) + _ <- executor.execute(req) + } yield () + + def remove(organization: String, id: String): Task[Either[DocumentNotFound.type, Unit]] = + for { + routing <- routingOf(organization) + req = ElasticRequest.deleteById(Index, DocumentId(id)).routing(routing) + res <- executor.execute(req) + } yield res + + private def routingOf(value: String): IO[IllegalArgumentException, Routing.Type] = + Routing.make(value).toZIO.mapError(e => new IllegalArgumentException(e)) + +} + +object RepositoriesElasticsearch { + + def findById(organization: String, id: String): RIO[RepositoriesElasticsearch, Option[GitHubRepo]] = + ZIO.serviceWithZIO[RepositoriesElasticsearch](_.findById(organization, id)) + + def create(repository: GitHubRepo): RIO[RepositoriesElasticsearch, Option[DocumentId]] = + ZIO.serviceWithZIO[RepositoriesElasticsearch](_.create(repository)) + + def upsert(id: String, repository: GitHubRepo): RIO[RepositoriesElasticsearch, Unit] = + ZIO.serviceWithZIO[RepositoriesElasticsearch](_.upsert(id, repository)) + + def remove(organization: String, id: String): RIO[RepositoriesElasticsearch, Either[DocumentNotFound.type, Unit]] = + ZIO.serviceWithZIO[RepositoriesElasticsearch](_.remove(organization, id)) + + lazy val live: URLayer[ElasticExecutor, RepositoriesElasticsearch] = + ZLayer.fromFunction(RepositoriesElasticsearch(_)) +} diff --git a/modules/example/src/main/scala/example/api/HealthCheck.scala b/modules/example/src/main/scala/example/api/HealthCheck.scala new file mode 100644 index 000000000..a8b41054b --- /dev/null +++ b/modules/example/src/main/scala/example/api/HealthCheck.scala @@ -0,0 +1,13 @@ +package example.api + +import zio.http._ +import zio.http.model.Method +import zio.json.ast.Json._ + +object HealthCheck { + + final val Route: Http[Any, Nothing, Any, Response] = Http.collect { case Method.GET -> !! / "health" => + Response.json(Obj("name" -> Str("zio-elasticsearch-example"), "status" -> Str("up")).toJson) + } + +} diff --git a/modules/example/src/main/scala/example/api/Repositories.scala b/modules/example/src/main/scala/example/api/Repositories.scala new file mode 100644 index 000000000..f32d4b2de --- /dev/null +++ b/modules/example/src/main/scala/example/api/Repositories.scala @@ -0,0 +1,86 @@ +package example.api + +import example.{GitHubRepo, RepositoriesElasticsearch} +import zio.ZIO +import zio.elasticsearch.DocumentId +import zio.http._ +import zio.http.model.Method +import zio.http.model.Status._ +import zio.json.EncoderOps +import zio.schema.codec.JsonCodec + +object Repositories { + + private final val BasePath = !! / "api" / "repositories" + + final val Routes: Http[RepositoriesElasticsearch, Nothing, Request, Response] = + Http.collectZIO[Request] { + case Method.GET -> BasePath => + ZIO.succeed(Response.text("TODO: Get a list of repositories").setStatus(NotImplemented)) + + case Method.GET -> BasePath / organization / id => + RepositoriesElasticsearch + .findById(organization, id) + .map { + case Some(r) => + Response.json(r.toJson) + case None => + Response.json(ErrorResponse.fromReasons(s"Repository $id does not exist.").toJson).setStatus(NotFound) + } + .orDie + + case req @ Method.POST -> BasePath => + req.body.asString + .map(JsonCodec.JsonDecoder.decode[GitHubRepo](GitHubRepo.schema, _)) + .flatMap { + case Left(e) => + ZIO.succeed(Response.json(ErrorResponse.fromReasons(e.message).toJson).setStatus(BadRequest)) + case Right(repo) => + RepositoriesElasticsearch.create(repo).map { + case Some(id) => + Response.json(repo.copy(id = Some(DocumentId.unwrap(id))).toJson).setStatus(Created) + case None => + Response.json(ErrorResponse.fromReasons("Failed to create repository.").toJson).setStatus(BadRequest) + } + } + .orDie + + case req @ Method.PUT -> BasePath / id => + req.body.asString + .map(JsonCodec.JsonDecoder.decode[GitHubRepo](GitHubRepo.schema, _)) + .flatMap { + case Left(e) => + ZIO.succeed(Response.json(ErrorResponse.fromReasons(e.message).toJson).setStatus(BadRequest)) + case Right(repo) if repo.id.exists(_ != id) => + ZIO.succeed( + Response + .json( + ErrorResponse.fromReasons("The ID provided in the path does not match the ID from the body.").toJson + ) + .setStatus(BadRequest) + ) + case Right(repo) => + (RepositoriesElasticsearch + .upsert(id, repo.copy(id = Some(id))) *> RepositoriesElasticsearch.findById( + repo.organization, + id + )).map { + case Some(updated) => Response.json(updated.toJson) + case None => Response.json(ErrorResponse.fromReasons("Operation failed.").toJson).setStatus(BadRequest) + } + } + .orDie + + case Method.DELETE -> BasePath / organization / id => + RepositoriesElasticsearch + .remove(organization, id) + .map { + case Right(_) => + Response.status(NoContent) + case Left(_) => + Response.json(ErrorResponse.fromReasons(s"Repository $id does not exist.").toJson).setStatus(NotFound) + } + .orDie + } + +} diff --git a/modules/example/src/main/scala/example/api/package.scala b/modules/example/src/main/scala/example/api/package.scala new file mode 100644 index 000000000..8970f5550 --- /dev/null +++ b/modules/example/src/main/scala/example/api/package.scala @@ -0,0 +1,23 @@ +package example + +import zio.Chunk +import zio.json._ + +package object api { + + final case class ErrorResponseData(body: Chunk[String]) + + object ErrorResponseData { + implicit val encoder: JsonEncoder[ErrorResponseData] = DeriveJsonEncoder.gen[ErrorResponseData] + } + + final case class ErrorResponse(errors: ErrorResponseData) + + object ErrorResponse { + implicit val encoder: JsonEncoder[ErrorResponse] = DeriveJsonEncoder.gen[ErrorResponse] + + def fromReasons(reasons: String*): ErrorResponse = + new ErrorResponse(ErrorResponseData(Chunk.fromIterable(reasons))) + } + +} diff --git a/modules/example/src/main/scala/example/config/AppConfig.scala b/modules/example/src/main/scala/example/config/AppConfig.scala new file mode 100644 index 000000000..48d5f5162 --- /dev/null +++ b/modules/example/src/main/scala/example/config/AppConfig.scala @@ -0,0 +1,16 @@ +package example.config + +import zio.Layer +import zio.config.ReadError +import zio.config.magnolia.descriptor +import zio.config.syntax._ +import zio.config.typesafe.TypesafeConfig + +final case class AppConfig(http: HttpConfig, elasticsearch: ElasticsearchConfig) + +object AppConfig { + lazy val live: Layer[ReadError[String], ElasticsearchConfig with HttpConfig] = { + val config = TypesafeConfig.fromResourcePath(descriptor[AppConfig]) + config.narrow(_.elasticsearch) >+> config.narrow(_.http) + } +} diff --git a/modules/example/src/main/scala/example/config/ElasticsearchConfig.scala b/modules/example/src/main/scala/example/config/ElasticsearchConfig.scala new file mode 100644 index 000000000..53239ac01 --- /dev/null +++ b/modules/example/src/main/scala/example/config/ElasticsearchConfig.scala @@ -0,0 +1,3 @@ +package example.config + +final case class ElasticsearchConfig(host: String, port: Int) diff --git a/modules/example/src/main/scala/example/config/HttpConfig.scala b/modules/example/src/main/scala/example/config/HttpConfig.scala new file mode 100644 index 000000000..acc50df07 --- /dev/null +++ b/modules/example/src/main/scala/example/config/HttpConfig.scala @@ -0,0 +1,3 @@ +package example.config + +final case class HttpConfig(port: Int) diff --git a/modules/example/src/main/scala/example/package.scala b/modules/example/src/main/scala/example/package.scala new file mode 100644 index 000000000..2659cd008 --- /dev/null +++ b/modules/example/src/main/scala/example/package.scala @@ -0,0 +1,5 @@ +import zio.elasticsearch.IndexName + +package object example { + val Index: IndexName.Type = IndexName("repositories") +} diff --git a/modules/example/zio-elasticsearch-example.postman_collection.json b/modules/example/zio-elasticsearch-example.postman_collection.json new file mode 100644 index 000000000..3e09d1d36 --- /dev/null +++ b/modules/example/zio-elasticsearch-example.postman_collection.json @@ -0,0 +1,405 @@ +{ + "info": { + "_postman_id": "7bf079a3-55fa-4833-9627-648026c5fda4", + "name": "zio-elasticsearch-example", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "1136886" + }, + "item": [ + { + "name": "es", + "item": [ + { + "name": "Create \"repositories\" index", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"settings\": {\n \"index\": {\n \"number_of_shards\": 1\n }\n },\n \"mappings\": {\n \"_routing\": {\n \"required\": true\n },\n \"properties\": {\n \"id\": {\n \"type\": \"keyword\"\n },\n \"organization\": {\n \"type\": \"keyword\"\n },\n \"name\": {\n \"type\": \"keyword\"\n },\n \"url\": {\n \"type\": \"keyword\"\n },\n \"description\": {\n \"type\": \"text\"\n },\n \"lastCommitAt\": {\n \"type\": \"date\"\n },\n \"stars\": {\n \"type\": \"integer\"\n },\n \"forks\": {\n \"type\": \"integer\"\n }\n }\n }\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ELASTICSEARCH_HOST}}:{{ELASTICSEARCH_PORT}}/repositories", + "protocol": "http", + "host": [ + "{{ELASTICSEARCH_HOST}}" + ], + "port": "{{ELASTICSEARCH_PORT}}", + "path": [ + "repositories" + ] + } + }, + "response": [] + }, + { + "name": "Delete \"repositories\" index", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "http://{{ELASTICSEARCH_HOST}}:{{ELASTICSEARCH_PORT}}/repositories", + "protocol": "http", + "host": [ + "{{ELASTICSEARCH_HOST}}" + ], + "port": "{{ELASTICSEARCH_PORT}}", + "path": [ + "repositories" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Checking app health", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:{{HTTP_PORT}}/health", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "{{HTTP_PORT}}", + "path": [ + "health" + ] + } + }, + "response": [] + }, + { + "name": "Creating a repository (scurl-detector)", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"organization\": \"lambdaworks\",\n \"name\": \"scrul-detector\",\n \"url\": \"https://github.com/lambdaworks/scurl-detector\",\n \"description\": \"Scala library that detects and extracts URLs from text.\",\n \"lastCommitAt\": \"2022-12-01T14:27:11.436\",\n \"stars\": 14,\n \"forks\": 1\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:{{HTTP_PORT}}/api/repositories", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "{{HTTP_PORT}}", + "path": [ + "api", + "repositories" + ] + } + }, + "response": [] + }, + { + "name": "Creating a repository with invalid body", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"organization\": \"lambdaworks\",\n \"name\": \"We build awesome digital products\",\n \"url\": \"https://lambdaworks.io\",\n \"lastCommitAt\": \"No commit date!\",\n \"stars\": 9999\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:{{HTTP_PORT}}/api/repositories", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "{{HTTP_PORT}}", + "path": [ + "api", + "repositories" + ] + } + }, + "response": [] + }, + { + "name": "Creating a repository (scountries)", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"organization\": \"lambdaworks\",\n \"name\": \"scountries\",\n \"url\": \"https://github.com/lambdaworks/scountries\",\n \"description\": \"Scala library that provides an enumeration of ISO 3166 codes for countries, along with their subdivisions.\",\n \"lastCommitAt\": \"2022-12-08T19:10:46.016\",\n \"stars\": 16,\n \"forks\": 1\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:{{HTTP_PORT}}/api/repositories", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "{{HTTP_PORT}}", + "path": [ + "api", + "repositories" + ] + } + }, + "response": [] + }, + { + "name": "Retrieving an existing repository", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:{{HTTP_PORT}}/api/repositories/lambdaworks/lw-zio-elasticsearch", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "{{HTTP_PORT}}", + "path": [ + "api", + "repositories", + "lambdaworks", + "lw-zio-elasticsearch" + ] + } + }, + "response": [] + }, + { + "name": "Retrieving a non-existing repository (wrong ID)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:{{HTTP_PORT}}/api/repositories/lambdaworks/lw-scurl-detector", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "{{HTTP_PORT}}", + "path": [ + "api", + "repositories", + "lambdaworks", + "lw-scurl-detector" + ] + } + }, + "response": [] + }, + { + "name": "Retrieving a non-existing repository (wrong organization)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:{{HTTP_PORT}}/api/repositories/zio/lw-zio-elasticsearch", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "{{HTTP_PORT}}", + "path": [ + "api", + "repositories", + "zio", + "lw-zio-elasticsearch" + ] + } + }, + "response": [] + }, + { + "name": "Deleting an existing repository", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "http://localhost:{{HTTP_PORT}}/api/repositories/lambdaworks/lw-zio-elasticsearch", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "{{HTTP_PORT}}", + "path": [ + "api", + "repositories", + "lambdaworks", + "lw-zio-elasticsearch" + ] + } + }, + "response": [] + }, + { + "name": "Deleting a non-existing repository", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "http://localhost:{{HTTP_PORT}}/api/repositories/lambdaworks/lw-scurl-detector", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "{{HTTP_PORT}}", + "path": [ + "api", + "repositories", + "lambdaworks", + "lw-scurl-detector" + ] + } + }, + "response": [] + }, + { + "name": "Creating/updating a repository", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"organization\": \"lambdaworks\",\n \"name\": \"zio-elasticsearch\",\n \"url\": \"https://github.com/lambdaworks/zio-elasticsearch\",\n \"description\": \"ZIO Elasticsearch is a type-safe, testable and streaming-friendly ZIO native Elasticsearch client.\",\n \"lastCommitAt\": \"2022-12-27T15:58:30.996\",\n \"stars\": 21,\n \"forks\": 5\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:{{HTTP_PORT}}/api/repositories/lw-zio-elasticsearch", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "{{HTTP_PORT}}", + "path": [ + "api", + "repositories", + "lw-zio-elasticsearch" + ] + } + }, + "response": [] + }, + { + "name": "Creating/updating a repository (wrong IDs)", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"zio-elasticsearch\",\n \"organization\": \"lambdaworks\",\n \"name\": \"zio-elasticsearch\",\n \"url\": \"https://github.com/lambdaworks/zio-elasticsearch\",\n \"description\": \"ZIO Elasticsearch is a type-safe, testable and streaming-friendly ZIO native Elasticsearch client.\",\n \"lastCommitAt\": \"2022-12-27T15:58:30.996\",\n \"stars\": 21,\n \"forks\": 5\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:{{HTTP_PORT}}/api/repositories/lw-zio-elasticsearch", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "{{HTTP_PORT}}", + "path": [ + "api", + "repositories", + "lw-zio-elasticsearch" + ] + } + }, + "response": [] + }, + { + "name": "Updating a repository with invalid body", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"organization\": \"lambdaworks\",\n \"url\": \"https://github.com/lambdaworks/zio-elasticsearch\",\n \"lastCommitAt\": \"2022-12-27T15:58:30.996\",\n \"stars\": 21,\n \"forks\": 5\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:{{HTTP_PORT}}/api/repositories/lw-zio-elasticsearch", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "{{HTTP_PORT}}", + "path": [ + "api", + "repositories", + "lw-zio-elasticsearch" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "ELASTICSEARCH_HOST", + "value": "localhost", + "type": "string" + }, + { + "key": "ELASTICSEARCH_PORT", + "value": "9200", + "type": "string" + }, + { + "key": "HTTP_PORT", + "value": "9000", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/project/plugins.sbt b/project/plugins.sbt index 2bccc0767..8170e6d9b 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,5 @@ addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.10.4") +addSbtPlugin("com.github.sbt" % "sbt-unidoc" % "0.5.0") +addSbtPlugin("io.spray" % "sbt-revolver" % "0.9.1") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.0") addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.3.6") -addSbtPlugin("com.github.sbt" % "sbt-unidoc" % "0.5.0")