From 267a22417ad623f6926861d320abcfd19690a79c Mon Sep 17 00:00:00 2001 From: Konstantin Kolmogortsev Date: Thu, 21 Nov 2024 21:48:10 +0300 Subject: [PATCH] Add custom websocket event type support --- .../scala/muffin/model/websocket/domain.scala | 116 ++++++++++-------- .../src/test/scala/muffin/api/ApiTest.scala | 8 +- .../websocket/EntityTypeParsingTest.scala | 18 +-- .../interop/json/circe/CirceApiTest.scala | 4 +- .../muffin/interop/json/zio/ZioApiTest.scala | 4 +- 5 files changed, 83 insertions(+), 67 deletions(-) diff --git a/modules/core/src/main/scala/muffin/model/websocket/domain.scala b/modules/core/src/main/scala/muffin/model/websocket/domain.scala index 8646db1..0120c73 100644 --- a/modules/core/src/main/scala/muffin/model/websocket/domain.scala +++ b/modules/core/src/main/scala/muffin/model/websocket/domain.scala @@ -1,5 +1,7 @@ package muffin.model.websocket +import scala.util._ + import muffin.model.Post object domain { @@ -20,65 +22,77 @@ object domain { data: A ) - enum EventType { - case Hello - case Posted - case AddedToTeam - case AuthenticationChallenge - case ChannelConverted - case ChannelCreated - case ChannelDeleted - case ChannelMemberUpdated - case ChannelUpdated - case ChannelViewed - case ConfigChanged - case DeleteTeam - case DirectAdded - case EmojiAdded - case EphemeralMessage - case GroupAdded - case LeaveTeam - case LicenseChanged - case MemberroleUpdated - case NewUser - case PluginDisabled - case PluginEnabled - case PluginStatusesChanged - case PostDeleted - case PostEdited - case PostUnread - case PreferenceChanged - case PreferencesChanged - case PreferencesDeleted - case ReactionAdded - case ReactionRemoved - case Response - case RoleUpdated - case StatusChange - case Typing - case UpdateTeam - case UserAdded - case UserRemoved - case UserRoleUpdated - case UserUpdated - case DialogOpened - case ThreadUpdated - case ThreadFollowChanged - case ThreadReadChanged + sealed trait EventType { + def repr: String } object EventType { + enum KnownEventType extends EventType { + case Hello + case Posted + case AddedToTeam + case AuthenticationChallenge + case ChannelConverted + case ChannelCreated + case ChannelDeleted + case ChannelMemberUpdated + case ChannelUpdated + case ChannelViewed + case ConfigChanged + case DeleteTeam + case DirectAdded + case EmojiAdded + case EphemeralMessage + case GroupAdded + case LeaveTeam + case LicenseChanged + case MemberroleUpdated + case NewUser + case PluginDisabled + case PluginEnabled + case PluginStatusesChanged + case PostDeleted + case PostEdited + case PostUnread + case PreferenceChanged + case PreferencesChanged + case PreferencesDeleted + case ReactionAdded + case ReactionRemoved + case Response + case RoleUpdated + case StatusChange + case Typing + case UpdateTeam + case UserAdded + case UserRemoved + case UserRoleUpdated + case UserUpdated + case DialogOpened + case ThreadUpdated + case ThreadFollowChanged + case ThreadReadChanged + + def repr: String = this.toString + } + def fromSnakeCase(s: String): EventType = { val tokens = s.split("_").toList.map(_.capitalize) - EventType.valueOf( - tokens.foldLeft(new StringBuilder(tokens.length)) { - (builder, token) => builder.addAll(token) - } - .toString() - ) + Try( + KnownEventType.valueOf( + tokens.foldLeft(new StringBuilder(tokens.length)) { + (builder, token) => builder.addAll(token) + } + .result() + ) + ) match { + case Success(tpe) => tpe + case Failure(_) => CustomEventType(s) + } } + case class CustomEventType(repr: String) extends EventType } case class PostedEventData(channelName: String, channelType: ChannelType, senderName: String, post: Post) diff --git a/modules/core/src/test/scala/muffin/api/ApiTest.scala b/modules/core/src/test/scala/muffin/api/ApiTest.scala index cfebaa5..5a84b71 100644 --- a/modules/core/src/test/scala/muffin/api/ApiTest.scala +++ b/modules/core/src/test/scala/muffin/api/ApiTest.scala @@ -291,7 +291,7 @@ trait ApiTest[To[_], From[_]](integration: String, codecSupport: CodecSupport[To .websocket() .flatMap( _.addListener[domain.TestObject]( - EventType.Hello, + EventType.KnownEventType.Hello, event => listenedEvent.complete(event).void ) .connect() @@ -310,7 +310,7 @@ trait ApiTest[To[_], From[_]](integration: String, codecSupport: CodecSupport[To .websocket() .flatMap( _.addListener[String]( - EventType.Hello, + EventType.KnownEventType.Hello, event => listenedEvents.offer(domain.TestObject.default) ) @@ -325,7 +325,7 @@ trait ApiTest[To[_], From[_]](integration: String, codecSupport: CodecSupport[To .websocket() .flatMap( _.addListener[domain.TestObject]( - EventType.Hello, + EventType.KnownEventType.Hello, event => listenedEvents.offer(event) ) .connect() @@ -347,7 +347,7 @@ trait ApiTest[To[_], From[_]](integration: String, codecSupport: CodecSupport[To .websocket() .flatMap( _.addListener[PostedEventData]( - EventType.Posted, + EventType.KnownEventType.Posted, event => listenedEvent.complete(event).void ) .connect() diff --git a/modules/core/src/test/scala/muffin/model/websocket/EntityTypeParsingTest.scala b/modules/core/src/test/scala/muffin/model/websocket/EntityTypeParsingTest.scala index e2b2b82..2b166ec 100644 --- a/modules/core/src/test/scala/muffin/model/websocket/EntityTypeParsingTest.scala +++ b/modules/core/src/test/scala/muffin/model/websocket/EntityTypeParsingTest.scala @@ -1,7 +1,6 @@ package muffin.model.websocket import scala.concurrent.Future -import scala.util.Try import muffin.api.ApiTestSupport import muffin.model.websocket.domain.* @@ -61,16 +60,19 @@ class EntityTypeParsingTest() extends ApiTestSupport { val result: List[EventType] = rawTypes.map(EventType.fromSnakeCase) - Future.successful(assert(result.length == EventType.values.length)) + Future.successful(assert(result.length == EventType.KnownEventType.values.length)) } - Scenario(s"Parse from snake case, incorrect kebab case raw types $integration") { - val rawTypes = List( - "added-to-team" - ) + Scenario(s"Parse from snake case custom types $integration") { + val rawType = "everything you want here" - val result = rawTypes.map(tpe => Try(EventType.fromSnakeCase(tpe))) + val result = EventType.fromSnakeCase(rawType) - Future.successful(assert(result.head.isFailure)) + Future.successful( + result match { + case _: EventType.KnownEventType => assert(false, "must be a custom type") + case EventType.CustomEventType(_) => assert(true) + } + ) } } diff --git a/modules/integration/circe-json-interop/src/test/scala/muffin/interop/json/circe/CirceApiTest.scala b/modules/integration/circe-json-interop/src/test/scala/muffin/interop/json/circe/CirceApiTest.scala index 9c2443c..ecce515 100644 --- a/modules/integration/circe-json-interop/src/test/scala/muffin/interop/json/circe/CirceApiTest.scala +++ b/modules/integration/circe-json-interop/src/test/scala/muffin/interop/json/circe/CirceApiTest.scala @@ -63,10 +63,10 @@ class CirceApiTest extends ApiTest[Encoder, Decoder]("circe", codec) { .map(postingEvent => List( Event( - EventType.Hello, + EventType.KnownEventType.Hello, RawJson.from(Encoder[domain.TestObject].apply(domain.TestObject.default).toString) ), - Event(EventType.Posted, RawJson.from(postingEvent)) + Event(EventType.KnownEventType.Posted, RawJson.from(postingEvent)) ) ) diff --git a/modules/integration/zio-json-interop/src/test/scala/muffin/interop/json/zio/ZioApiTest.scala b/modules/integration/zio-json-interop/src/test/scala/muffin/interop/json/zio/ZioApiTest.scala index 8b6af4b..b145682 100644 --- a/modules/integration/zio-json-interop/src/test/scala/muffin/interop/json/zio/ZioApiTest.scala +++ b/modules/integration/zio-json-interop/src/test/scala/muffin/interop/json/zio/ZioApiTest.scala @@ -62,10 +62,10 @@ class ZioApiTest extends ApiTest[JsonEncoder, JsonDecoder]("zio", codec) { private val events = loadResource("websockets/posting/postingWithFileIds.json").map(postingEvent => List( Event( - EventType.Hello, + EventType.KnownEventType.Hello, RawJson.from(domain.TestObject.default.toJson) ), - Event(EventType.Posted, RawJson.from(postingEvent)) + Event(EventType.KnownEventType.Posted, RawJson.from(postingEvent)) ) ) }