Skip to content

Commit

Permalink
Allow building caches based on the resolver's content (close #207)
Browse files Browse the repository at this point in the history
I just took the code from

then branched off master, and fixed the compilation errors

Co-authored-by: Ian Streeter <ian@snowplowanalytics.com>
Co-authored-by:  Piotr Poniedziałek <pondzix@gmail.com>
Co-authored-by: Leigh-Anne Mathieson <leigh-anne@snowplowanalytics.com>
  • Loading branch information
4 people committed Nov 21, 2022
1 parent fb0f1b4 commit 857a8e4
Show file tree
Hide file tree
Showing 14 changed files with 1,469 additions and 93 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ project/plugins/project/

# Vagrant
.vagrant

.bsp/
.idea/
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,8 @@ package com.snowplowanalytics.iglu.client
import cats.Monad
import cats.data.EitherT
import cats.effect.{Clock, IO}

import io.circe.Json

import io.circe.{DecodingFailure, Json}
import com.snowplowanalytics.iglu.core.SelfDescribingData

import resolver.{InitListCache, InitSchemaCache}
import resolver.registries.{Registry, RegistryLookup}

Expand All @@ -42,7 +39,7 @@ final case class Client[F[_], A](resolver: Resolver[F], validator: Validator[A])
for {
schema <- EitherT(resolver.lookupSchema(instance.schema))
schemaValidation = validator.validateSchema(schema)
_ <- EitherT.fromEither(schemaValidation).leftMap(_.toClientError)
_ <- EitherT.fromEither[F](schemaValidation).leftMap(_.toClientError)
validation = validator.validate(instance.data, schema)
_ <- EitherT.fromEither[F](validation).leftMap(_.toClientError)
} yield ()
Expand All @@ -54,8 +51,10 @@ object Client {
val IgluCentral: Client[IO, Json] =
Client[IO, Json](Resolver(List(Registry.IgluCentral), None), CirceValidator)

def parseDefault[F[_]: Monad: InitSchemaCache: InitListCache](json: Json) =
EitherT(Resolver.parse(json)).map { resolver =>
def parseDefault[F[_]: Monad: InitSchemaCache: InitListCache](
json: Json
): EitherT[F, DecodingFailure, Client[F, Json]] =
EitherT(Resolver.parse[F](json)).map { resolver =>
Client(resolver, CirceValidator)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2014-2022 Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package com.snowplowanalytics.iglu.client

import cats.Monad
import cats.data.EitherT
import cats.effect.Clock
import cats.implicits._
import com.snowplowanalytics.iglu.client.resolver.registries.RegistryLookup
import com.snowplowanalytics.iglu.client.resolver.{InitListCache, InitSchemaCache}
import com.snowplowanalytics.iglu.client.validator.CirceValidator.WithCaching.{
InitValidatorCache,
SchemaEvaluationCache,
SchemaEvaluationKey,
SchemaEvaluationResult
}
import com.snowplowanalytics.iglu.core.SelfDescribingData
import com.snowplowanalytics.lrumap.CreateLruMap
import io.circe.{DecodingFailure, Json}

/**
* Client using 'lookupSchemaResult' resolver method (as opposed to old client relying on plain `lookupSchema` method)
* which enables validator taking advantage of caching.
* Should provide significant performance boost for the 'check' operation when called frequently.
*/
final class IgluCirceClient[F[_]] private (
resolver: Resolver[F],
schemaEvaluationCache: SchemaEvaluationCache[F]
) {
def check(
instance: SelfDescribingData[Json]
)(implicit
M: Monad[F],
L: RegistryLookup[F],
C: Clock[F]
): EitherT[F, ClientError, Unit] =
for {
resolverResult <- EitherT(resolver.lookupSchemaResult(instance.schema))
validation =
CirceValidator.WithCaching.validate(schemaEvaluationCache)(instance.data, resolverResult)
_ <- EitherT(validation).leftMap(_.toClientError)
} yield ()
}

object IgluCirceClient {

def parseDefault[F[_]: Monad: InitSchemaCache: InitListCache: InitValidatorCache](
json: Json
): EitherT[F, DecodingFailure, IgluCirceClient[F]] =
for {
config <- EitherT.fromEither[F](Resolver.parseConfig(json))
resolver <- Resolver.fromConfig[F](config)
client <- EitherT.liftF(fromResolver(resolver, config.cacheSize))
} yield client

def fromResolver[F[_]: Monad: InitValidatorCache](
resolver: Resolver[F],
cacheSize: Int
): F[IgluCirceClient[F]] = {
schemaEvaluationCache[F](cacheSize).map { cache =>
new IgluCirceClient(resolver, cache)
}
}

private def schemaEvaluationCache[F[_]: InitValidatorCache](
cacheSize: Int
): F[SchemaEvaluationCache[F]] =
CreateLruMap[F, SchemaEvaluationKey, SchemaEvaluationResult].create(cacheSize)

}

0 comments on commit 857a8e4

Please sign in to comment.