Skip to content

Commit

Permalink
Merge 3ecc8b8 into 9550d17
Browse files Browse the repository at this point in the history
  • Loading branch information
voropaevp committed Nov 16, 2022
2 parents 9550d17 + 3ecc8b8 commit 6added9
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 7 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
Version 2.1.0 (2022-05-31)
--------------------------
Bump specs2 to 4.15.0 (#198)
Bump http4s to 0.23.12 (#197)
Bump scala-collection-compat to 2.7.0 (#196)
Bump circe to 0.14.2 (#195)
Bump cats-effect to 3.3.12 (#194)
Bump jackson-databind to 2.13.3 (#192)
Bump json-schema-validator to 1.0.70 (#193)
Replace deprecated scalaj http client (#190)
Use jsonschema v4 text from iglu-scala-core (#186)
Split out a iglu-scala-client-data module (#187)

Version 2.0.0 (2022-03-28)
--------------------------
Bump jackson-databind to 2.13.1 (#185)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ Iglu Scala Client is used extensively in **[Snowplow][snowplow-repo]** to valida

## Installation

The latest version of Iglu Scala Client is 2.0.0, which currently works with Scala 2.12 and 2.13.
The latest version of Iglu Scala Client is 2.1.0, which currently works with Scala 2.12 and 2.13.

If you're using SBT, add the following lines to your build file:

```scala
val igluClient = "com.snowplowanalytics" %% "iglu-scala-client" % "2.0.0"
val igluClient = "com.snowplowanalytics" %% "iglu-scala-client" % "2.1.0"
```

## API
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ object RegistryLookup {
implicit class LookupOps(val repositoryRef: Registry) extends AnyVal with Serializable {
def lookupSchema[F[_]: RegistryLookup](schemaKey: SchemaKey): F[Either[RegistryError, Json]] =
RegistryLookup[F].lookup(repositoryRef, schemaKey)

def list[F[_]: RegistryLookup](
vendor: String,
name: String,
model: Int
): F[Either[RegistryError, SchemaList]] =
RegistryLookup[F].list(repositoryRef, vendor: String, name: String, model: Int)
}

implicit def ioLookupInstance[F[_]](implicit F: Sync[F]): RegistryLookup[F] =
Expand All @@ -98,7 +105,10 @@ object RegistryLookup {
): F[Either[RegistryError, SchemaList]] =
registry match {
case Registry.Http(_, connection) => httpList(connection, vendor, name, model)
case _ => F.pure(RegistryError.NotFound.asLeft)
case Registry.Embedded(_, base) =>
val path = toSubpath(base, vendor, name)
Sync[F].delay(Utils.unsafeEmbeddedList(path, model))
case _ => F.pure(RegistryError.NotFound.asLeft)
}
}

Expand Down Expand Up @@ -128,6 +138,9 @@ object RegistryLookup {
case Registry.Http(_, connection) =>
val subpath = toSubpath(connection.uri.toString, vendor, name, model)
Utils.stringToUri(subpath).flatMap(Utils.unsafeHttpList(_, connection.apikey))
case Registry.Embedded(_, base) =>
val path = toSubpath(base, vendor, name)
Utils.unsafeEmbeddedList(path, model)
case _ =>
RegistryError.NotFound.asLeft
}
Expand All @@ -151,6 +164,13 @@ object RegistryLookup {
): String =
s"${prefix.stripSuffix("/")}/schemas/$vendor/$name/jsonschema/$model"

private def toSubpath(
prefix: String,
vendor: String,
name: String
): String =
s"${prefix.stripSuffix("/")}/schemas/$vendor/$name/jsonschema"

/**
* Retrieves an Iglu Schema from the Embedded Iglu Repo as a JSON
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ package com.snowplowanalytics.iglu.client
package resolver.registries

// Java
import java.io.InputStream
import com.snowplowanalytics.iglu.core.{SchemaKey, SchemaVer}

import java.io.{File, InputStream}
import java.net.URI
import java.net.http.HttpResponse.BodyHandlers
import java.net.http.{HttpClient, HttpRequest, HttpResponse}
import java.time.Duration
import scala.util.matching.Regex

// Scala
import scala.io.Source
Expand All @@ -29,6 +32,7 @@ import cats.effect.Sync
import cats.syntax.either._
import cats.syntax.option._
import cats.syntax.show._
import cats.syntax.traverse._

// circe
import io.circe.parser.parse
Expand Down Expand Up @@ -72,6 +76,61 @@ private[registries] object Utils {
repoFailure(e).asLeft
}

def unsafeEmbeddedList(path: String, modelMatch: Int): Either[RegistryError, SchemaList] =
try {
val d =
new File(
getClass.getResource(path).getPath
) // this will throw NPE for missing entry in embedded repos
val schemaFileRegex: Regex = (".*/schemas/?" + // path to file
"([a-zA-Z0-9-_.]+)/" + // Vendor
"([a-zA-Z0-9-_]+)/" + // Name
"([a-zA-Z0-9-_]+)/" + // Format
"([1-9][0-9]*)-(\\d+)-(\\d+)$").r // MODEL, REVISION and ADDITION

def getFolderContent(d: File): List[String] = {
d.listFiles
.filter(_.isFile)
.toList
.filter(_.getName.startsWith(s"${modelMatch.toString}-"))
.map(_.getAbsolutePath)
}

val content =
if (d.exists & d.isDirectory)
getFolderContent(d)
else
List.empty[String]

content
.traverse {
case schemaFileRegex(vendor, name, format, model, revision, addition)
if model == modelMatch.toString =>
SchemaKey(
vendor = vendor,
name = name,
format = format,
version = SchemaVer
.Full(model = model.toInt, revision = revision.toInt, addition = addition.toInt)
).asRight
case f => RegistryError.RepoFailure(s"Corrupted schema file name at $f").asLeft
}
.map(_.sortBy(_.version))
.flatMap(s =>
if (s.isEmpty)
RegistryError.NotFound.asLeft
else
s.asRight
)
.map(SchemaList.apply)
} catch {
case NonFatal(e) =>
e match {
case _: NullPointerException => RegistryError.NotFound.asLeft
case _ => repoFailure(e).asLeft
}
}

/** Not-RT analog of [[RegistryLookup.embeddedLookup]] */
def unsafeEmbeddedLookup(path: String): Either[RegistryError, Json] =
try {
Expand All @@ -84,7 +143,10 @@ private[registries] object Utils {
result
} catch {
case NonFatal(e) =>
repoFailure(e).asLeft
e match {
case _: NullPointerException => RegistryError.NotFound.asLeft
case _ => repoFailure(e).asLeft
}
}

/** Non-RT analog of [[RegistryLookup.httpList]] */
Expand Down Expand Up @@ -156,6 +218,7 @@ private[registries] object Utils {
RegistryError.RepoFailure(failure.show)

private[resolver] def repoFailure(failure: Throwable): RegistryError =
RegistryError.RepoFailure(failure.getMessage)

RegistryError.RepoFailure(
if (failure.getMessage != null) failure.getMessage else "Unhandled error"
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package com.snowplowanalytics.iglu.client.resolver.registries
// Cats
import cats.effect.IO
import cats.effect.testing.specs2.CatsEffect
import com.snowplowanalytics.iglu.core.SchemaList

// circe
import io.circe.literal._
Expand All @@ -38,6 +39,7 @@ class EmbeddedSpec extends Specification with CatsEffect {
retrieving an existent JSON Schema from an embedded RepositoryRef should work $e3
requesting a non-existent JSON Schema from an embedded RepositoryRef should return None $e4
requesting a corrupted JSON Schema from an embedded RepositoryRef should return an appropriate Failure $e5
Schema list should work for embedded repo $e6
"""

val AcmeConfig =
Expand Down Expand Up @@ -119,4 +121,37 @@ class EmbeddedSpec extends Specification with CatsEffect {
.map(result => result must beLeft)
}

def e6 = {
val schemaList = SchemaList(
List(
SchemaKey(
"com.snowplowanalytics.iglu-test",
"test-embedded-list",
"jsonschema",
SchemaVer.Full(1, 0, 0)
),
SchemaKey(
"com.snowplowanalytics.iglu-test",
"test-embedded-list",
"jsonschema",
SchemaVer.Full(1, 0, 1)
),
SchemaKey(
"com.snowplowanalytics.iglu-test",
"test-embedded-list",
"jsonschema",
SchemaVer.Full(1, 2, 0)
),
SchemaKey(
"com.snowplowanalytics.iglu-test",
"test-embedded-list",
"jsonschema",
SchemaVer.Full(1, 2, 11)
)
)
)
SpecHelpers.EmbeddedTest
.list[IO]("com.snowplowanalytics.iglu-test", "test-embedded-list", 1)
.map(result => result must beRight(schemaList))
}
}

0 comments on commit 6added9

Please sign in to comment.