diff --git a/CHANGELOG b/CHANGELOG index a2c7ce9..45820e3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +Version 4.0.1 (2024-11-19) +-------------------------- +Consider all `ClientFailure`s as not found in `lookupSchemasUntil` (#260) + Version 4.0.0 (2024-09-30) -------------------------- Add caching to lookupSchemasUntil function (#258) diff --git a/README.md b/README.md index c83307b..7e4dd3e 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,12 @@ Iglu Scala Client is used extensively in **[Snowplow][snowplow-repo]** to valida ## Installation -The latest version of Iglu Scala Client is 4.0.0, which works with Scala 2.12, 2.13, and 3. +The latest version of Iglu Scala Client is 4.0.1, which works with Scala 2.12, 2.13, and 3. If you're using SBT, add the following lines to your build file: ```scala -val igluClient = "com.snowplowanalytics" %% "iglu-scala-client" % "4.0.0" +val igluClient = "com.snowplowanalytics" %% "iglu-scala-client" % "4.0.1" ``` ## API diff --git a/modules/core/src/main/scala/com.snowplowanalytics.iglu/client/resolver/Resolver.scala b/modules/core/src/main/scala/com.snowplowanalytics.iglu/client/resolver/Resolver.scala index 092e278..7b0a683 100644 --- a/modules/core/src/main/scala/com.snowplowanalytics.iglu/client/resolver/Resolver.scala +++ b/modules/core/src/main/scala/com.snowplowanalytics.iglu/client/resolver/Resolver.scala @@ -190,14 +190,19 @@ final case class Resolver[F[_]](repos: List[Registry], cache: Option[ResolverCac /** * If Iglu Central or any of its mirrors doesn't have a schema, * it should be considered NotFound, even if one of them returned an error. + * All 4xx (`ClientFailure`) are also considered NotFound. */ private[resolver] def isNotFound(error: ResolutionError): Boolean = { val (igluCentral, custom) = error.value.partition { case (repo, _) => allIgluCentral.contains(repo) } (igluCentral.isEmpty || igluCentral.values.exists( - _.errors.exists(_ == RegistryError.NotFound) - )) && custom.values.flatMap(_.errors).forall(_ == RegistryError.NotFound) + _.errors.exists(e => + e == RegistryError.NotFound || e.isInstanceOf[RegistryError.ClientFailure] + ) + )) && custom.values + .flatMap(_.errors) + .forall(e => e == RegistryError.NotFound || e.isInstanceOf[RegistryError.ClientFailure]) } /** diff --git a/modules/core/src/test/scala/com.snowplowanalytics.iglu.client/resolver/ResolverSpec.scala b/modules/core/src/test/scala/com.snowplowanalytics.iglu.client/resolver/ResolverSpec.scala index d064dff..43432f8 100644 --- a/modules/core/src/test/scala/com.snowplowanalytics.iglu.client/resolver/ResolverSpec.scala +++ b/modules/core/src/test/scala/com.snowplowanalytics.iglu.client/resolver/ResolverSpec.scala @@ -90,13 +90,16 @@ class ResolverSpec extends Specification with CatsEffect { result from SchemaListLike should contain the exact schemaKey provided $e13 isNotFound should return true if custom repo and Iglu Central repos don't have a schema $e14 - return true if one Iglu Central repo returns an error and the other one NotFound $e15 - return false if custom repo returns an error $e16 - return true if there is no custom repo, one Iglu Central repo returns an error and the other one NotFound $e17 - return false if there is no custom repo and Iglu Central ones return an error $e18 - return true if there is just one custom repo that returns NotFound $e19 - return false if there is just one custom repo that returns an error $e20 - return true if one Iglu Central repo returns 2 errors and the other one returns one error and one NotFound $e21 + return true if one Iglu Central repo returns a RepoFailure and the other one NotFound $e15 + return false if custom repo returns a RepoFailure $e16 + return true if custom repo returns a ClientFailure $e17 + return true if there is no custom repo, one Iglu Central repo returns an error and the other one NotFound $e18 + return false if there is no custom repo and Iglu Central ones return a RepoFailure $e19 + return true if there is no custom repo and Iglu Central ones return a ClientFailure $e20 + return true if there is just one custom repo that returns NotFound $e21 + return false if there is just one custom repo that returns a RepoFailure $e22 + return true if there is just one custom repo that returns a ClientFailure $e23 + return true if one Iglu Central repo returns 2 errors and the other one returns one error and one NotFound $e24 """ import ResolverSpec._ @@ -556,7 +559,7 @@ class ResolverSpec extends Specification with CatsEffect { Instant.now() ), Repos.custom.config.name -> LookupHistory( - Set(RegistryError.ClientFailure("Something went wrong")), + Set(RegistryError.RepoFailure("Something went wrong")), 1, Instant.now() ) @@ -567,6 +570,34 @@ class ResolverSpec extends Specification with CatsEffect { } def e17 = { + val resolver: Resolver[Id] = + Resolver + .init[Id](0, None, SpecHelpers.IgluCentral, SpecHelpers.IgluCentralMirror, Repos.custom) + + val resolutionError = ResolutionError( + SortedMap( + SpecHelpers.IgluCentral.config.name -> LookupHistory( + Set(RegistryError.NotFound), + 1, + Instant.now() + ), + SpecHelpers.IgluCentralMirror.config.name -> LookupHistory( + Set(RegistryError.NotFound), + 1, + Instant.now() + ), + Repos.custom.config.name -> LookupHistory( + Set(RegistryError.ClientFailure("402")), + 1, + Instant.now() + ) + ) + ) + + resolver.isNotFound(resolutionError) should beTrue + } + + def e18 = { val resolver: Resolver[Id] = Resolver.init[Id](0, None, SpecHelpers.IgluCentral, SpecHelpers.IgluCentralMirror) @@ -588,7 +619,7 @@ class ResolverSpec extends Specification with CatsEffect { resolver.isNotFound(resolutionError) should beTrue } - def e18 = { + def e19 = { val resolver: Resolver[Id] = Resolver.init[Id](0, None, SpecHelpers.IgluCentral, SpecHelpers.IgluCentralMirror) @@ -600,7 +631,7 @@ class ResolverSpec extends Specification with CatsEffect { Instant.now() ), SpecHelpers.IgluCentralMirror.config.name -> LookupHistory( - Set(RegistryError.ClientFailure("Network issue")), + Set(RegistryError.RepoFailure("Network issue")), 1, Instant.now() ) @@ -610,7 +641,29 @@ class ResolverSpec extends Specification with CatsEffect { resolver.isNotFound(resolutionError) should beFalse } - def e19 = { + def e20 = { + val resolver: Resolver[Id] = + Resolver.init[Id](0, None, SpecHelpers.IgluCentral, SpecHelpers.IgluCentralMirror) + + val resolutionError = ResolutionError( + SortedMap( + SpecHelpers.IgluCentral.config.name -> LookupHistory( + Set(RegistryError.RepoFailure("Problem")), + 1, + Instant.now() + ), + SpecHelpers.IgluCentralMirror.config.name -> LookupHistory( + Set(RegistryError.ClientFailure("403 Forbidden")), + 1, + Instant.now() + ) + ) + ) + + resolver.isNotFound(resolutionError) should beTrue + } + + def e21 = { val resolver: Resolver[Id] = Resolver.init[Id](0, None, Repos.custom) @@ -623,14 +676,14 @@ class ResolverSpec extends Specification with CatsEffect { resolver.isNotFound(resolutionError) should beTrue } - def e20 = { + def e22 = { val resolver: Resolver[Id] = Resolver.init[Id](0, None, Repos.custom) val resolutionError = ResolutionError( SortedMap( Repos.custom.config.name -> LookupHistory( - Set(RegistryError.ClientFailure("Boom")), + Set(RegistryError.RepoFailure("Boom")), 1, Instant.now() ) @@ -640,7 +693,24 @@ class ResolverSpec extends Specification with CatsEffect { resolver.isNotFound(resolutionError) should beFalse } - def e21 = { + def e23 = { + val resolver: Resolver[Id] = + Resolver.init[Id](0, None, Repos.custom) + + val resolutionError = ResolutionError( + SortedMap( + Repos.custom.config.name -> LookupHistory( + Set(RegistryError.ClientFailure("401")), + 1, + Instant.now() + ) + ) + ) + + resolver.isNotFound(resolutionError) should beTrue + } + + def e24 = { val resolver: Resolver[Id] = Resolver .init[Id](0, None, SpecHelpers.IgluCentral, SpecHelpers.IgluCentralMirror, Repos.custom)