diff --git a/build.sbt b/build.sbt index d2aacd64..5f62893e 100644 --- a/build.sbt +++ b/build.sbt @@ -270,7 +270,7 @@ lazy val webapp = project stShortModuleNames := true, webpackBundlingMode := BundlingMode.LibraryAndApplication(), // BundlingMode.Application, Compile / scalaJSModuleInitializers += { - org.scalajs.linker.interface.ModuleInitializer.mainMethod("fmgp.webapp.App", "main") + org.scalajs.linker.interface.ModuleInitializer.mainMethod("io.iohk.atala.mediator.App", "main") }, ) diff --git a/mediator/src/main/scala/io/iohk/atala/mediator/actions/ProtocolExecute.scala b/mediator/src/main/scala/io/iohk/atala/mediator/actions/ProtocolExecute.scala index f87f4476..4d4501a6 100644 --- a/mediator/src/main/scala/io/iohk/atala/mediator/actions/ProtocolExecute.scala +++ b/mediator/src/main/scala/io/iohk/atala/mediator/actions/ProtocolExecute.scala @@ -10,6 +10,7 @@ import fmgp.did.comm.protocol.trustping2.* import io.iohk.atala.mediator.* import io.iohk.atala.mediator.comm.* import io.iohk.atala.mediator.db.* +import io.iohk.atala.mediator.protocols.NullProtocolExecuter import zio.* import zio.json.* //TODO pick a better name // maybe "Protocol" only @@ -38,14 +39,14 @@ case class ProtocolExecuterCollection[-R](executers: ProtocolExecuter[R]*) exten plaintextMessage: PlaintextMessage, ): ZIO[R1, MediatorError, Option[EncryptedMessage]] = selectExecutersFor(plaintextMessage.`type`) match - case None => NullProtocolExecute.execute(plaintextMessage) + case None => NullProtocolExecuter.execute(plaintextMessage) case Some(px) => px.execute(plaintextMessage) override def program[R1 <: R]( plaintextMessage: PlaintextMessage, ): ZIO[R1, MediatorError, Action] = selectExecutersFor(plaintextMessage.`type`) match - case None => NullProtocolExecute.program(plaintextMessage) + case None => NullProtocolExecuter.program(plaintextMessage) case Some(px) => px.program(plaintextMessage) } @@ -133,52 +134,3 @@ trait ProtocolExecuterWithServices[-R <: ProtocolExecuter.Services] extends Prot // context: Context ): ZIO[R1, MediatorError, Action] } - -object NullProtocolExecute extends ProtocolExecuter[Any] { - - override def suportedPIURI = Seq() - override def program[R1 <: Any](plaintextMessage: PlaintextMessage) = - ZIO.fail(MissingProtocolError(plaintextMessage.`type`)) -} - -object BasicMessageExecuter extends ProtocolExecuter[Any] { - - override def suportedPIURI: Seq[PIURI] = Seq(BasicMessage.piuri) - override def program[R1 <: Any](plaintextMessage: PlaintextMessage) = for { - job <- BasicMessage.fromPlaintextMessage(plaintextMessage) match - case Left(error) => ZIO.fail(MediatorDidError(FailToParse(error))) - case Right(bm) => Console.printLine(bm.toString).mapError(ex => MediatorThrowable(ex)) - } yield NoReply -} - -class TrustPingExecuter extends ProtocolExecuterWithServices[ProtocolExecuter.Services] { - - override def suportedPIURI: Seq[PIURI] = Seq(TrustPing.piuri, TrustPingResponse.piuri) - - override def program[R1 <: Agent]( - plaintextMessage: PlaintextMessage - ): ZIO[R1, MediatorError, Action] = { - // the val is from the match to be definitely stable - val piuriTrustPing = TrustPing.piuri - val piuriTrustPingResponse = TrustPingResponse.piuri - - plaintextMessage.`type` match - case `piuriTrustPing` => - TrustPing.fromPlaintextMessage(plaintextMessage) match - case Left(error) => ZIO.fail(MediatorDidError(FailToParse(error))) - case Right(ping: TrustPingWithOutRequestedResponse) => ZIO.logInfo(ping.toString()) *> ZIO.succeed(NoReply) - case Right(ping: TrustPingWithRequestedResponse) => - for { - _ <- ZIO.logInfo(ping.toString()) - agent <- ZIO.service[Agent] - ret = ping.makeRespond - } yield Reply(ret.toPlaintextMessage) - case `piuriTrustPingResponse` => - for { - job <- TrustPingResponse.fromPlaintextMessage(plaintextMessage) match - case Left(error) => ZIO.fail(MediatorDidError(FailToParse(error))) - case Right(ping) => ZIO.logInfo(ping.toString()) - } yield NoReply - } - -} diff --git a/mediator/src/main/scala/io/iohk/atala/mediator/protocols/BasicMessageExecuter.scala b/mediator/src/main/scala/io/iohk/atala/mediator/protocols/BasicMessageExecuter.scala new file mode 100644 index 00000000..a430cef9 --- /dev/null +++ b/mediator/src/main/scala/io/iohk/atala/mediator/protocols/BasicMessageExecuter.scala @@ -0,0 +1,18 @@ +package io.iohk.atala.mediator.protocols + +import fmgp.crypto.error.FailToParse +import fmgp.did.comm.{PIURI, PlaintextMessage} +import fmgp.did.comm.protocol.basicmessage2.BasicMessage +import io.iohk.atala.mediator.{MediatorDidError, MediatorThrowable} +import io.iohk.atala.mediator.actions.{NoReply, ProtocolExecuter} +import zio.{Console, ZIO} + +object BasicMessageExecuter extends ProtocolExecuter[Any] { + + override def suportedPIURI: Seq[PIURI] = Seq(BasicMessage.piuri) + override def program[R1 <: Any](plaintextMessage: PlaintextMessage) = for { + job <- BasicMessage.fromPlaintextMessage(plaintextMessage) match + case Left(error) => ZIO.fail(MediatorDidError(FailToParse(error))) + case Right(bm) => Console.printLine(bm.toString).mapError(ex => MediatorThrowable(ex)) + } yield NoReply +} diff --git a/mediator/src/main/scala/io/iohk/atala/mediator/protocols/NullProtocolExecuter.scala b/mediator/src/main/scala/io/iohk/atala/mediator/protocols/NullProtocolExecuter.scala new file mode 100644 index 00000000..657c1b96 --- /dev/null +++ b/mediator/src/main/scala/io/iohk/atala/mediator/protocols/NullProtocolExecuter.scala @@ -0,0 +1,13 @@ +package io.iohk.atala.mediator.protocols + +import fmgp.did.comm.PlaintextMessage +import io.iohk.atala.mediator.MissingProtocolError +import io.iohk.atala.mediator.actions.ProtocolExecuter +import zio.ZIO + +object NullProtocolExecuter extends ProtocolExecuter[Any] { + + override def suportedPIURI = Seq() + override def program[R1 <: Any](plaintextMessage: PlaintextMessage) = + ZIO.fail(MissingProtocolError(plaintextMessage.`type`)) +} diff --git a/mediator/src/main/scala/io/iohk/atala/mediator/protocols/TrustPingExecuter.scala b/mediator/src/main/scala/io/iohk/atala/mediator/protocols/TrustPingExecuter.scala new file mode 100644 index 00000000..49c27510 --- /dev/null +++ b/mediator/src/main/scala/io/iohk/atala/mediator/protocols/TrustPingExecuter.scala @@ -0,0 +1,46 @@ +package io.iohk.atala.mediator.protocols + +import fmgp.crypto.error.FailToParse +import fmgp.did.Agent +import fmgp.did.comm.{PIURI, PlaintextMessage} +import fmgp.did.comm.protocol.trustping2.{ + TrustPing, + TrustPingResponse, + TrustPingWithOutRequestedResponse, + TrustPingWithRequestedResponse +} +import io.iohk.atala.mediator.{MediatorDidError, MediatorError} +import io.iohk.atala.mediator.actions.{Action, NoReply, ProtocolExecuter, ProtocolExecuterWithServices, Reply} +import zio.ZIO + +class TrustPingExecuter extends ProtocolExecuterWithServices[ProtocolExecuter.Services] { + + override def suportedPIURI: Seq[PIURI] = Seq(TrustPing.piuri, TrustPingResponse.piuri) + + override def program[R1 <: Agent]( + plaintextMessage: PlaintextMessage + ): ZIO[R1, MediatorError, Action] = { + // the val is from the match to be definitely stable + val piuriTrustPing = TrustPing.piuri + val piuriTrustPingResponse = TrustPingResponse.piuri + + plaintextMessage.`type` match + case `piuriTrustPing` => + TrustPing.fromPlaintextMessage(plaintextMessage) match + case Left(error) => ZIO.fail(MediatorDidError(FailToParse(error))) + case Right(ping: TrustPingWithOutRequestedResponse) => ZIO.logInfo(ping.toString()) *> ZIO.succeed(NoReply) + case Right(ping: TrustPingWithRequestedResponse) => + for { + _ <- ZIO.logInfo(ping.toString()) + agent <- ZIO.service[Agent] + ret = ping.makeRespond + } yield Reply(ret.toPlaintextMessage) + case `piuriTrustPingResponse` => + for { + job <- TrustPingResponse.fromPlaintextMessage(plaintextMessage) match + case Left(error) => ZIO.fail(MediatorDidError(FailToParse(error))) + case Right(ping) => ZIO.logInfo(ping.toString()) + } yield NoReply + } + +} diff --git a/webapp/src/main/scala/fmgp/webapp/MyRouter.scala b/webapp/src/main/scala/fmgp/webapp/MyRouter.scala deleted file mode 100644 index 29094868..00000000 --- a/webapp/src/main/scala/fmgp/webapp/MyRouter.scala +++ /dev/null @@ -1,104 +0,0 @@ -package fmgp.webapp - -import com.raquo.laminar.api.L.{_, given} -import com.raquo.waypoint._ -import org.scalajs.dom -import upickle.default._ - -object MyRouter { - sealed abstract class Page( - val title: String, - val icon: String // https://fonts.google.com/icons?selected=Material+Icons+Outlined - ) - - // case object HomePage extends Page("Home", "home") - // case class OOBPage(query_oob: String) extends Page("OutOfBand", "app_shortcut") - // case object DocPage extends Page("Doc", "menu_book") - // case object AgentKeysPage extends Page("AgentKeys", "key") - // // case object DIDPage extends Page("DID", "visibility") - // case object AgentDBPage extends Page("MessageDB", "folder_open") - // case class ResolverPage(did: String) extends Page("Resolver", "dns") - // case object EncryptPage extends Page("Encrypt", "enhanced_encryption") - // case object DecryptPage extends Page("Decrypt", "email") - // case object BasicMessagePage extends Page("BasicMessage", "message") - // case object TrustPingPage extends Page("TrustPing", "network_ping`") - // case object TapIntoStreamPage extends Page("TapIntoStream", "chat") - // case object DAppStorePage extends Page("DAppStore", "share") - case object MediatorPage extends Page("Mediator", "diversity_3") - - // given homePageRW: ReadWriter[HomePage.type] = macroRW - // given oobPageRW: ReadWriter[OOBPage] = macroRW - // given docPageRW: ReadWriter[DocPage.type] = macroRW - // given keysPageRW: ReadWriter[AgentKeysPage.type] = macroRW - // given agentDBPageRW: ReadWriter[AgentDBPage.type] = macroRW - // given resolverPageRW: ReadWriter[ResolverPage] = macroRW - // given encryptPageRW: ReadWriter[EncryptPage.type] = macroRW - // given decryptPageRW: ReadWriter[DecryptPage.type] = macroRW - // given basicMessagePageRW: ReadWriter[BasicMessagePage.type] = macroRW - // given trustPingPageRW: ReadWriter[TrustPingPage.type] = macroRW - // given tapIntoStreamPageRW: ReadWriter[TapIntoStreamPage.type] = macroRW - // given dAppStorePageRW: ReadWriter[DAppStorePage.type] = macroRW - given mediatorPageRW: ReadWriter[MediatorPage.type] = macroRW - - given rw: ReadWriter[Page] = macroRW - - private val routes = List( - // // http://localhost:8080/?_oob=eyJ0eXBlIjoiaHR0cHM6Ly9kaWRjb21tLm9yZy9vdXQtb2YtYmFuZC8yLjAvaW52aXRhdGlvbiIsImlkIjoiNTk5ZjM2MzgtYjU2My00OTM3LTk0ODctZGZlNTUwOTlkOTAwIiwiZnJvbSI6ImRpZDpleGFtcGxlOnZlcmlmaWVyIiwiYm9keSI6eyJnb2FsX2NvZGUiOiJzdHJlYW1saW5lZC12cCIsImFjY2VwdCI6WyJkaWRjb21tL3YyIl19fQ - // Route.onlyQuery[OOBPage, String]( // OOB - // encode = page => page.query_oob, - // decode = arg => OOBPage(query_oob = arg), - // pattern = (root / endOfSegments) ? (param[String]("_oob")), - // Router.localFragmentBasePath - // ), - // Route[ResolverPage, String]( - // encode = page => page.did, - // decode = arg => ResolverPage(did = arg), - // pattern = root / "resolver" / segment[String] / endOfSegments, - // Router.localFragmentBasePath - // ), - // Route.static(HomePage, root / endOfSegments, Router.localFragmentBasePath), - // Route.static(DocPage, root / "doc" / endOfSegments, Router.localFragmentBasePath), - // Route.static(AgentKeysPage, root / "agentkeys" / endOfSegments, Router.localFragmentBasePath), - // Route.static(AgentDBPage, root / "db" / endOfSegments, Router.localFragmentBasePath), - // Route.static(EncryptPage, root / "encrypt" / endOfSegments, Router.localFragmentBasePath), - // Route.static(DecryptPage, root / "decrypt" / endOfSegments, Router.localFragmentBasePath), - // Route.static(BasicMessagePage, root / "basicmessage" / endOfSegments, Router.localFragmentBasePath), - // Route.static(TrustPingPage, root / "trustping" / endOfSegments, Router.localFragmentBasePath), - // Route.static(TapIntoStreamPage, root / "stream" / endOfSegments, Router.localFragmentBasePath), - // Route.static(DAppStorePage, root / "dapp" / endOfSegments, Router.localFragmentBasePath), - // Route.static(MediatorPage, root / "mediator" / endOfSegments, Router.localFragmentBasePath), - Route.static(MediatorPage, root / endOfSegments, Router.localFragmentBasePath), - ) - - val router = new Router[Page]( - routes = routes, - getPageTitle = _.title, // displayed in the browser tab next to favicon - serializePage = page => write(page)(rw), // serialize page data for storage in History API log - deserializePage = pageStr => read(pageStr)(rw), // deserialize the above - // routeFallback = { (_: String) => HomePage }, - routeFallback = { (_: String) => MediatorPage }, - )( - popStateEvents = windowEvents(_.onPopState), // this is how Waypoint avoids an explicit dependency on Laminar - owner = unsafeWindowOwner // this router will live as long as the window - ) - - // Note: for fragment ('#') URLs this isn't actually needed. - // See https://github.com/raquo/Waypoint docs for why this modifier is useful in general. - def navigateTo(page: Page): Binder[HtmlElement] = Binder { el => - - val isLinkElement = el.ref.isInstanceOf[dom.html.Anchor] - - if (isLinkElement) { - el.amend(href(router.absoluteUrlForPage(page))) - } - - // If element is a link and user is holding a modifier while clicking: - // - Do nothing, browser will open the URL in new tab / window / etc. depending on the modifier key - // Otherwise: - // - Perform regular pushState transition - (onClick - .filter(ev => !(isLinkElement && (ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.altKey))) - .preventDefault - --> (_ => router.pushState(page))).bind(el) - } -} diff --git a/webapp/src/main/scala/fmgp/webapp/App.scala b/webapp/src/main/scala/io/iohk/atala/mediator/App.scala similarity index 97% rename from webapp/src/main/scala/fmgp/webapp/App.scala rename to webapp/src/main/scala/io/iohk/atala/mediator/App.scala index afbd0456..855a3a29 100644 --- a/webapp/src/main/scala/fmgp/webapp/App.scala +++ b/webapp/src/main/scala/io/iohk/atala/mediator/App.scala @@ -1,4 +1,4 @@ -package fmgp.webapp +package io.iohk.atala.mediator import scala.scalajs.js.annotation._ diff --git a/webapp/src/main/scala/fmgp/webapp/AppUtils.scala b/webapp/src/main/scala/io/iohk/atala/mediator/AppUtils.scala similarity index 99% rename from webapp/src/main/scala/fmgp/webapp/AppUtils.scala rename to webapp/src/main/scala/io/iohk/atala/mediator/AppUtils.scala index 41bc4d21..fad32c87 100644 --- a/webapp/src/main/scala/fmgp/webapp/AppUtils.scala +++ b/webapp/src/main/scala/io/iohk/atala/mediator/AppUtils.scala @@ -1,4 +1,4 @@ -package fmgp.webapp +package io.iohk.atala.mediator import scala.scalajs.js.annotation.JSExportTopLevel import scala.scalajs.js.annotation.JSExport diff --git a/webapp/src/main/scala/fmgp/webapp/Global.scala b/webapp/src/main/scala/io/iohk/atala/mediator/Global.scala similarity index 94% rename from webapp/src/main/scala/fmgp/webapp/Global.scala rename to webapp/src/main/scala/io/iohk/atala/mediator/Global.scala index 90963d88..e793705e 100644 --- a/webapp/src/main/scala/fmgp/webapp/Global.scala +++ b/webapp/src/main/scala/io/iohk/atala/mediator/Global.scala @@ -1,4 +1,4 @@ -package fmgp.webapp +package io.iohk.atala.mediator import scala.scalajs.js import scala.scalajs.js.annotation.JSExport diff --git a/webapp/src/main/scala/fmgp/webapp/MediatorInfo.scala b/webapp/src/main/scala/io/iohk/atala/mediator/MediatorInfo.scala similarity index 94% rename from webapp/src/main/scala/fmgp/webapp/MediatorInfo.scala rename to webapp/src/main/scala/io/iohk/atala/mediator/MediatorInfo.scala index f6937e82..428afd62 100644 --- a/webapp/src/main/scala/fmgp/webapp/MediatorInfo.scala +++ b/webapp/src/main/scala/io/iohk/atala/mediator/MediatorInfo.scala @@ -1,4 +1,4 @@ -package fmgp.webapp +package io.iohk.atala.mediator import org.scalajs.dom import com.raquo.laminar.api.L._ @@ -17,7 +17,7 @@ object MediatorInfo { goal = Some("RequestMediate"), accept = Some(Seq("didcomm/v2")), ) - val qrCodeData = OutOfBandPlaintext.from(invitation.toPlaintextMessage).makeURI("https://did.fmgp.app/#/") + val qrCodeData = OutOfBandPlaintext.from(invitation.toPlaintextMessage).makeURI("#/") val divQRCode = div() { diff --git a/webapp/src/main/scala/io/iohk/atala/mediator/MyRouter.scala b/webapp/src/main/scala/io/iohk/atala/mediator/MyRouter.scala new file mode 100644 index 00000000..3f8f8ba2 --- /dev/null +++ b/webapp/src/main/scala/io/iohk/atala/mediator/MyRouter.scala @@ -0,0 +1,56 @@ +package io.iohk.atala.mediator + +import com.raquo.laminar.api.L.{_, given} +import com.raquo.waypoint._ +import org.scalajs.dom +import upickle.default._ + +object MyRouter { + sealed abstract class Page( + val title: String, + val icon: String // https://fonts.google.com/icons?selected=Material+Icons+Outlined + ) + + case object MediatorPage extends Page("Mediator", "diversity_3") + + given mediatorPageRW: ReadWriter[MediatorPage.type] = macroRW + + given rw: ReadWriter[Page] = macroRW + + private val routes = List( + // // http://localhost:8080/?_oob=eyJ0eXBlIjoiaHR0cHM6Ly9kaWRjb21tLm9yZy9vdXQtb2YtYmFuZC8yLjAvaW52aXRhdGlvbiIsImlkIjoiNTk5ZjM2MzgtYjU2My00OTM3LTk0ODctZGZlNTUwOTlkOTAwIiwiZnJvbSI6ImRpZDpleGFtcGxlOnZlcmlmaWVyIiwiYm9keSI6eyJnb2FsX2NvZGUiOiJzdHJlYW1saW5lZC12cCIsImFjY2VwdCI6WyJkaWRjb21tL3YyIl19fQ + Route.static(MediatorPage, root / endOfSegments, Router.localFragmentBasePath), + ) + + val router = new Router[Page]( + routes = routes, + getPageTitle = _.title, // displayed in the browser tab next to favicon + serializePage = page => write(page)(rw), // serialize page data for storage in History API log + deserializePage = pageStr => read(pageStr)(rw), // deserialize the above + // routeFallback = { (_: String) => HomePage }, + routeFallback = { (_: String) => MediatorPage }, + )( + popStateEvents = windowEvents(_.onPopState), // this is how Waypoint avoids an explicit dependency on Laminar + owner = unsafeWindowOwner // this router will live as long as the window + ) + + // Note: for fragment ('#') URLs this isn't actually needed. + // See https://github.com/raquo/Waypoint docs for why this modifier is useful in general. + def navigateTo(page: Page): Binder[HtmlElement] = Binder { el => + + val isLinkElement = el.ref.isInstanceOf[dom.html.Anchor] + + if (isLinkElement) { + el.amend(href(router.absoluteUrlForPage(page))) + } + + // If element is a link and user is holding a modifier while clicking: + // - Do nothing, browser will open the URL in new tab / window / etc. depending on the modifier key + // Otherwise: + // - Perform regular pushState transition + (onClick + .filter(ev => !(isLinkElement && (ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.altKey))) + .preventDefault + --> (_ => router.pushState(page))).bind(el) + } +}