Skip to content
This repository has been archived by the owner on Nov 30, 2022. It is now read-only.

[WIP] - New Versioning #284

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
version = "2.5.2"
style = defaultWithAlign
maxColumn = 100
maxColumn = 120

continuationIndent.callSite = 2

Expand Down
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ lazy val commonSettings = Seq(
%%("specs2-core", V.specs2) % Test,
%%("specs2-scalacheck", V.specs2) % Test,
%%("doobie-specs2", V.doobie) % Test,
"org.specs2" %% "specs2-cats" % V.specs2 % Test,
"io.chrisdavenport" %% "cats-scalacheck" % V.catsScalacheck % Test,
"io.chrisdavenport" %% "testcontainers-specs2" % "0.2.0-M2" % Test,
"org.testcontainers" % "postgresql" % "1.14.2" % Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
CREATE TABLE protocols (
id CHARACTER VARYING(255) NOT NULL,
version INTEGER NOT NULL,
version VARCHAR(20) NOT NULL CHECK (version ~ '^(\d+\.)?(\d+\.)?(\*|\d+)$'),
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 did this here because of the tests, but I guess the correct way would be to create a migration?

protocol BYTEA,
PRIMARY KEY (id, version)
);
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ CREATE TYPE idl AS ENUM ('avro', 'protobuf', 'mu', 'openapi', 'scala');
CREATE TABLE metaprotocols (
id CHARACTER VARYING(255) PRIMARY KEY,
idl_name idl NOT NULL,
version INTEGER NOT NULL
version VARCHAR(20) NOT NULL CHECK (version ~ '^(\d+\.)?(\d+\.)?(\*|\d+)$')
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same as above

);
4 changes: 1 addition & 3 deletions src/main/scala/higherkindness/compendium/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,7 @@ object CompendiumStreamApp {
metadataConf: CompendiumMetadataConfig
): Stream[F, MetadataStorage[F]] =
Stream.eval(
Migrations.metadataLocation.flatMap(l =>
Migrations.makeMigrations(metadataConf.storage, l :: Nil)
)
Migrations.metadataLocation.flatMap(l => Migrations.makeMigrations(metadataConf.storage, l :: Nil))
) >>
Stream.resource(createTransactor(metadataConf.storage).map(PgMetadataStorage[F](_)))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package higherkindness.compendium.core

import cats.effect.Sync
import cats.implicits._
import higherkindness.compendium.core.refinements._
import higherkindness.compendium.core.refinements.ProtocolId
import higherkindness.compendium.metadata.MetadataStorage
import higherkindness.compendium.models._
import higherkindness.compendium.storage.Storage
Expand Down Expand Up @@ -47,8 +47,10 @@ object CompendiumService {
idlName: IdlName
): F[ProtocolVersion] =
for {
_ <- ProtocolUtils[F].validateProtocol(protocol, idlName)
version <- MetadataStorage[F].store(id, idlName)
_ <- ProtocolUtils[F].validateProtocol(protocol, idlName)
existingVersion <- MetadataStorage[F].versionOf(id)
newVersion = existingVersion.getOrElse(ProtocolVersion.initial)
version <- MetadataStorage[F].store(id, newVersion, idlName)
_ <- Storage[F].store(id, version, protocol)
} yield version

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@

package higherkindness.compendium.core.doobie

import cats.instances.all._
import cats.implicits._
import doobie.util.{Get, Put}
import doobie.util.meta.Meta
import higherkindness.compendium.core.refinements.{ProtocolId, ProtocolVersion}
import higherkindness.compendium.models.{IdlName, Protocol}
import higherkindness.compendium.core.refinements.ProtocolId
import higherkindness.compendium.models._

object implicits {

Expand All @@ -33,6 +33,7 @@ object implicits {

implicit val IdlNamesMeta: Meta[IdlName] = Meta[String].timap(IdlName.withName)(_.entryName)

implicit val protocolVersionPut: Put[ProtocolVersion] = Put[Int].contramap(_.value)
implicit val protocolVersionGet: Get[ProtocolVersion] = Get[Int].temap(ProtocolVersion.from)
implicit val protocolVersionPut: Put[ProtocolVersion] = Put[String].contramap(_.show)
implicit val protocolVersionGet: Get[ProtocolVersion] =
Get[String].temap(ProtocolVersion.fromString(_).leftMap(_.msg))
}
17 changes: 9 additions & 8 deletions src/main/scala/higherkindness/compendium/core/refinements.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import eu.timepit.refined.boolean.{And, AnyOf}
import eu.timepit.refined.char.LetterOrDigit
import eu.timepit.refined.collection.{Forall, MaxSize}
import eu.timepit.refined.generic.Equal
import eu.timepit.refined.numeric.Positive
import higherkindness.compendium.models.{ProtocolIdError, ProtocolVersionError}
import eu.timepit.refined.string.MatchesRegex
import higherkindness.compendium.models._
import shapeless.{::, HNil}

object refinements {
Expand All @@ -37,19 +37,20 @@ object refinements {
type ProtocolIdConstraints = And[MaxProtocolIdSize, ValidProtocolIdChars]

type ProtocolId = String Refined ProtocolIdConstraints

object ProtocolId extends RefinedTypeOps[ProtocolId, String] {
def parseOrRaise[F[_]: Sync](id: String): F[ProtocolId] =
F.fromEither(ProtocolId.from(id).leftMap(ProtocolIdError))
}

type ProtocolVersion = Int Refined Positive
/** An String that matches with format xx.yy.zz, xx.yy, xx */
type ProtocolVersionRefined =
String Refined MatchesRegex[W.`"""^(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)$"""`.T]
object ProtocolVersionRefined extends RefinedTypeOps[ProtocolVersionRefined, String] {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe we don't need this anymore... I have to give it a deeper look


object ProtocolVersion extends RefinedTypeOps[ProtocolVersion, Int] {
def parseOrRaise[F[_]: Sync](version: String): F[ProtocolVersion] =
for {
number <- F.delay(version.toInt)
protoVersion <- F.fromEither(ProtocolVersion.from(number).leftMap(ProtocolVersionError))
} yield protoVersion
versionRefined <- F.fromEither(ProtocolVersionRefined.from(version).leftMap(ProtocolVersionError))
protocolVersion <- F.fromEither(ProtocolVersion.fromString(versionRefined.value))
} yield protocolVersion
}
}
17 changes: 7 additions & 10 deletions src/main/scala/higherkindness/compendium/http/QueryParams.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,10 @@

package higherkindness.compendium.http

import higherkindness.compendium.core.refinements.ProtocolVersion
import higherkindness.compendium.core.refinements.ProtocolVersionRefined
import higherkindness.compendium.models.IdlName
import org.http4s.QueryParamDecoder
import org.http4s.dsl.impl.{
OptionalValidatingQueryParamDecoderMatcher,
ValidatingQueryParamDecoderMatcher
}
import org.http4s.dsl.impl.{OptionalValidatingQueryParamDecoderMatcher, ValidatingQueryParamDecoderMatcher}

object QueryParams {

Expand All @@ -32,10 +29,10 @@ object QueryParams {
object TargetParam extends ValidatingQueryParamDecoderMatcher[IdlName]("target")
object IdlNameParam extends ValidatingQueryParamDecoderMatcher[IdlName]("idlName")

implicit val versionQueryParamDecoder: QueryParamDecoder[ProtocolVersion] =
QueryParamDecoder.fromUnsafeCast[ProtocolVersion](param =>
ProtocolVersion.unsafeFrom(param.value.toInt)
)("ProtocolVersion")
implicit val versionQueryParamDecoder: QueryParamDecoder[ProtocolVersionRefined] =
QueryParamDecoder.fromUnsafeCast[ProtocolVersionRefined](param => ProtocolVersionRefined.unsafeFrom(param.value))(
"ProtocolVersion"
)

object ProtoVersion extends OptionalValidatingQueryParamDecoderMatcher[ProtocolVersion]("version")
object ProtoVersion extends OptionalValidatingQueryParamDecoderMatcher[ProtocolVersionRefined]("version")
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ object RootService {
F.fromValidated(idlNameValidated.leftMap(errs => UnknownIdlName(errs.toList.mkString)))

def versionValidation(
maybeVersionValidated: Option[ValidatedNel[ParseFailure, ProtocolVersion]]
maybeVersionValidated: Option[ValidatedNel[ParseFailure, ProtocolVersionRefined]]
): F[Option[ProtocolVersion]] =
maybeVersionValidated.traverse { validated =>
val validation = validated.leftMap(errs => ProtocolVersionError(errs.toList.mkString))
val validation = validated
.leftMap(errs => ProtocolVersionError(errs.toList.mkString))
.andThen(v => ProtocolVersion.fromString(v.value).toValidated)
F.fromValidated(validation)
}

Expand All @@ -53,7 +55,7 @@ object RootService {
idlName <- idlValidation(idlNameValidated)
protocol <- req.as[Protocol]
version <- CompendiumService[F].storeProtocol(protocolId, protocol, idlName)
response <- Created(version.value)
response <- Created(version.show)
} yield response.putHeaders(Location(req.uri.withPath(s"${req.uri.path}")))

case GET -> Root / "protocol" / id :? ProtoVersion(maybeVersionValidated) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@

package higherkindness.compendium.metadata

import higherkindness.compendium.models.{IdlName, ProtocolMetadata}
import higherkindness.compendium.core.refinements.{ProtocolId, ProtocolVersion}
import higherkindness.compendium.models._
import higherkindness.compendium.core.refinements.ProtocolId

trait MetadataStorage[F[_]] {
def store(id: ProtocolId, idlName: IdlName): F[ProtocolVersion]
def store(id: ProtocolId, protocolVersion: ProtocolVersion, idlName: IdlName): F[ProtocolVersion]
def retrieve(id: ProtocolId): F[ProtocolMetadata]
def exists(id: ProtocolId): F[Boolean]
def ping: F[Boolean]
def versionOf(id: ProtocolId): F[Option[ProtocolVersion]]
}

object MetadataStorage {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,37 @@ import cats.effect.Async
import doobie.implicits._
import doobie.util.transactor.Transactor
import higherkindness.compendium.core.doobie.implicits._
import higherkindness.compendium.core.refinements.{ProtocolId, ProtocolVersion}
import higherkindness.compendium.core.refinements.ProtocolId
import higherkindness.compendium.models.ProtocolVersion
import higherkindness.compendium.metadata.MetadataStorage
import higherkindness.compendium.models.{IdlName, ProtocolMetadata, ProtocolNotFound}
import higherkindness.compendium.models._

object PgMetadataStorage {

def apply[F[_]: Async](xa: Transactor[F]): MetadataStorage[F] =
new MetadataStorage[F] {

override def store(id: ProtocolId, idlName: IdlName): F[ProtocolVersion] =
def store(
id: ProtocolId,
protocolVersion: ProtocolVersion,
idlName: IdlName
): F[ProtocolVersion] =
Queries
.store(id, idlName.entryName)
.store(id, protocolVersion, idlName.entryName)
.withUniqueGeneratedKeys[ProtocolVersion]("version")
.transact(xa)

override def retrieve(id: ProtocolId): F[ProtocolMetadata] =
def retrieve(id: ProtocolId): F[ProtocolMetadata] =
F.handleErrorWith(Queries.retrieve(id).unique.transact(xa)) { e =>
F.raiseError(ProtocolNotFound(e.getMessage))
}

override def exists(id: ProtocolId): F[Boolean] =
def exists(id: ProtocolId): F[Boolean] =
Queries.exists(id).unique.transact(xa)

override def ping: F[Boolean] = Queries.checkConn.unique.transact(xa)
def ping: F[Boolean] = Queries.checkConn.unique.transact(xa)

def versionOf(id: ProtocolId): F[Option[ProtocolVersion]] =
Queries.checkVersion.option(id).transact(xa)
}
}
15 changes: 10 additions & 5 deletions src/main/scala/higherkindness/compendium/metadata/pg/Queries.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
package higherkindness.compendium.metadata.pg

import doobie.implicits._
import doobie.{Query0, Update0}
import doobie._
import higherkindness.compendium.core.doobie.implicits._
import higherkindness.compendium.core.refinements.ProtocolId
import higherkindness.compendium.models.ProtocolMetadata
import higherkindness.compendium.models._

object Queries {

Expand All @@ -29,11 +29,11 @@ object Queries {
SELECT exists (SELECT true FROM metaprotocols WHERE id=$id)
""".query[Boolean]

def store(id: ProtocolId, idl_name: String): Update0 =
def store(id: ProtocolId, protocol_version: ProtocolVersion, idl_name: String): Update0 =
sql"""
INSERT INTO metaprotocols (id, idl_name, version)
VALUES ($id, $idl_name::idl, 1)
ON CONFLICT (id) DO UPDATE SET version = metaprotocols.version + 1
VALUES ($id, $idl_name::idl, $protocol_version)
ON CONFLICT (id) DO UPDATE SET version = ${protocol_version.incRevision}
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 Revision is updated by default, but this can be changed

RETURNING version
""".update

Expand All @@ -44,4 +44,9 @@ object Queries {

def checkConn: Query0[Boolean] =
sql"SELECT exists (SELECT 1)".query[Boolean]

val checkVersion: Query[ProtocolId, ProtocolVersion] =
Query(
"SELECT version from metaprotocols WHERE id = ?"
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@

package higherkindness.compendium.models

import higherkindness.compendium.core.refinements.{ProtocolId, ProtocolVersion}
import higherkindness.compendium.core.refinements.ProtocolId

case class ProtocolMetadata(id: ProtocolId, idlName: IdlName, version: ProtocolVersion)
Loading