Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update realm and schema module to rely on cats-effect #4194

Merged
merged 17 commits into from
Sep 1, 2023

Conversation

imsdu
Copy link
Contributor

@imsdu imsdu commented Aug 23, 2023

No description provided.

@imsdu imsdu marked this pull request as draft August 24, 2023 08:20
@imsdu imsdu force-pushed the 4162-investigate-monix-bio branch from a00b83d to 02ca26d Compare August 25, 2023 09:00
@@ -49,6 +50,13 @@ class RealmsRoutes(identities: Identities, realms: Realms, aclCheck: AclCheck)(i
RealmSearchParams(None, deprecated, rev, createdBy, updatedBy)
}

private def emitFetch(io: IO[RealmResource]): Route = emit(io.attemptNarrow[RealmRejection])
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.attemptNarrow allows to fallback to a IO[Either[RealmRejection, A]]


import scala.reflect.ClassTag

trait MigrateEffectSyntax {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allow to go to BIO to IO and IO to BIO


import scala.concurrent.duration._

object KamonMonitoringCats {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KamonMonitoring adapted to CE IO

import scala.io.{Codec, Source}
import scala.jdk.CollectionConverters._

trait CatsEffectsClasspathResourceUtils {
Copy link
Contributor Author

@imsdu imsdu Aug 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClasspathResourceUtils for CE IO

import java.nio.charset.StandardCharsets
import java.util.Base64

sealed trait CatsResponseToJsonLd {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To serialize both success and error channels in the API
Adapted from ResponseToJsonLd


object DeltaDirectives extends DeltaDirectives

trait DeltaDirectives extends UriDirectives {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delta directives for CE IO

private def openIdAlreadyExists(uri: Uri) =
(_: Label, _: Uri) => IO.raiseError(RealmOpenIdConfigAlreadyExists(label, uri))

group("Evaluate a create command") {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idea copied from fs2, the output looks like this:

ch.epfl.bluebrain.nexus.delta.sdk.schemas.SchemasImplSuite:
  + Creating a schema - Succeed with the id present in the payload 0.829s
  + Creating a schema - Fail with different ids on the payload and passed 0.008s
  + Updating a schema - Succeed 0.042s
  + Updating a schema - Fail if the schema does not exist 0.009s
  + Refreshing a schema - succeed 0.037s
  + Refreshing a schema - Fail if it does not exist 0.006s

import scala.reflect.ClassTag
import scala.util.control.NonFatal

trait CatsEffectAssertions { self: Assertions =>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taken from munit cats-effects

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am missing what is blocking us from just using munit-cats-effect directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not maintained by CE2 and it relies on a outdated version of MUnit

@imsdu imsdu changed the title Update realm module to rely on cats-effect Update realm and schema module to rely on cats-effect Aug 28, 2023
import fs2.Stream
import monix.bio.{Task, UIO}

import java.time.Instant
import java.util.UUID

class ElemRoutesSpec extends BaseRouteSpec with CirceLiteral {
class ElemRoutesSpec extends BaseRouteSpec with CirceLiteral with IOFromMap {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Temporary, we can change this back later

@imsdu imsdu marked this pull request as ready for review August 31, 2023 09:16
(parameter("rev".as[Int].?) & pathEndOrSingleSlash & entity(as[Json])) {
case (None, source) =>
// Create a schema with id segment
emitMetadata(Created, schemas.create(id, ref, source).flatTap(index))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is much clearer, thanks

Comment on lines -139 to +143
operationName(s"$prefixSegment/schemas/{org}/{project}/{id}/source") {
authorizeFor(ref, Read).apply {
implicit val source: Printer = sourcePrinter
val sourceIO = schemas.fetch(id, ref).map(_.value.source)
emit(sourceIO.leftWiden[SchemaRejection].rejectOn[SchemaNotFound])
}
authorizeFor(ref, Read).apply {
emitSource(schemas.fetch(id, ref))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's really nice to hide this printer boilerplate

Comment on lines +84 to +86
private def emitTags(io: IO[SchemaResource]): Route =
emit(io.map(_.value.tags).attemptNarrow[SchemaRejection].rejectOn[SchemaNotFound])

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love the rejectOn being hidden here, although it's probably just nicer if we removed rejectOn :)

Copy link
Contributor Author

@imsdu imsdu Aug 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It felt better than having it separated from attemptNarrow
And we can't get rid of the rejectOn for now

@@ -17,6 +17,15 @@ trait IOUtils {
clock.realTime(TimeUnit.MILLISECONDS).map(Instant.ofEpochMilli)
}

object IOUtils extends IOUtils
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider calling Clock.now or similar, the important thing here is that you're getting the current time

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IOUtils is gonna be replaced with IOInstant progressively

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but IOInstant.get is less readable than something like now or Clock.now where the intention is clearer

If you imported get then you would have to rename it everywhere, so calling it now instead of get helps

// format: off
def create(c: CreateRealm) =
state.fold {
openIdExists(c.label, c.openIdConfig) >> (wellKnown(c.openIdConfig), IOUtils.instant).mapN {
openIdExists(c.label, c.openIdConfig) >> (wellKnown(c.openIdConfig), IOInstant.get).mapN {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be just now with the rename I suggested earlier and an import of the method

Comment on lines 99 to 109
group("Fail with realm not found") {
List(
None -> UpdateRealm(label, 1, name, wellKnownUri, None, None, subject),
None -> DeprecateRealm(label, 1, subject)
).foreach { case (state, cmd) =>
test(s"for a ${cmd.getClass.getSimpleName} command when the state does not exist") {
evaluate(wkResolution, (_, _) => IO.unit)(state, cmd).intercept[RealmNotFound]
}
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a group


abstract class Rejection extends Exception with Product with Serializable {

def reason: String
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the message in exception should probably be this reason, perhaps even reason should be an argument. It's definitely worth thinking about this now before everything is using it and it's hard to change

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason is currently brought up all the way to the API to serialize the errors so it is difficult to change reason for message

Comment on lines 9 to 17
protected def group(name: String)(thunk: => Unit): Unit = {
val countBefore = munitTestsBuffer.size
val _ = thunk
val countAfter = munitTestsBuffer.size
val countRegistered = countAfter - countBefore
val registered = munitTestsBuffer.toList.drop(countBefore)
(0 until countRegistered).foreach(_ => munitTestsBuffer.remove(countBefore))
registered.foreach(t => munitTestsBuffer += t.withName(s"$name - ${t.name}"))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just want to know that individual tests can still run in intellij easily? As in, click the test name and then run

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's possible. It's also not possible to run a group like this. And to run a single test (from the CLI for instance) you need to specify "{groupName} - {testName}" to run an individual test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I never use this feature maybe with a munit extension it does

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible to run a single normal munit test, I just need that to still be possible

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +9 to +18
def assertSome(value: A): Unit =
assert(option.contains(value), s"$value was not found in the option, it contains '${option.getOrElse("None")}'")
def assertNone(): Unit =
assert(
option.isEmpty,
s"The option is not empty, it contains ${option.getOrElse(new IllegalStateException("Should not happen))}."))}"
)

def assertSome(): Unit =
assert(option.isDefined, s"The option is empty.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if I like these methods. It feels like these failure messages won't be helpful

@imsdu imsdu merged commit dbc2c36 into BlueBrain:master Sep 1, 2023
5 checks passed
@imsdu imsdu deleted the 4162-investigate-monix-bio branch September 1, 2023 08:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants