[ 
https://issues.apache.org/jira/browse/JAMES-3581?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Benoit Tellier updated JAMES-3581:
----------------------------------
    Description: 
h2. Why?

JMAP specifications defines the minimal features to get a basic email 
reading/writing experience.

However, as part of our job at Linagora, writing a collaborative email suite, 
we need advanced collaborative features that uses custom, off-spec, JMAP 
extensions.

We thus want to implement these collaborative features as JMAP extensions 
(because JMAP is so much of a nice protocol!).

We were able to:

 - Write custom methods pretty much directly.

 - We needed to contribute modular definition of session "capabilities" to be 
advertising our custom ones.

But we are missing the push for custom extensions.

h2. What?

As an extension developper I want to be able to register my type states and 
have states matching my format:

{code:java}
object IntState {
  def parse(string: String): Either[IllegalArgumentException, IntState] = 
Try(Integer.parseInt(string))
    .toEither
    .map(IntState(_))
    .left.map(new IllegalArgumentException(_))
}

case class IntState(i: Int) extends State {
  override def serialize: String = i.toString
}

case object CustomTypeName extends TypeName {
  override val asString: String = "MyTypeName"

  override def parse(string: String): Option[TypeName] = string match {
    case CustomTypeName.asString => Some(CustomTypeName)
    case _ => None
  }

  override def parseState(string: String): Either[IllegalArgumentException, 
IntState] = IntState.parse(string)
}
{code}

Given this, I want to be injecting my extensions to the push in the guice 
module definition:

{code:java}
    Multibinder.newSetBinder(binder(), classOf[TypeName])
      .addBinding()
      .toInstance(CustomTypeName)
    Multibinder.newSetBinder(binder(), classOf[GuiceProbe])
      .addBinding()
      .to(classOf[JmapEventBusProbe])
{code}

Doing so should allow me to emit 'custon' state change push notifications:

{code:java}
    val stateChangeEvent: StateChangeEvent = StateChangeEvent(eventId = 
CustomMethodContract.eventId, username = BOB, map = Map(CustomTypeName -> 
intState))
    SMono(jmapEventBus.dispatch(stateChangeEvent, 
AccountIdRegistrationKey(accountId))).block()
{code}

Which allows generating state change events PUSH notifications carried over via 
webSocket or SSE for your extensions :-)

h2. How?

Modularize the definition of State (extensions might not want to have it backed 
by a UUID), allow creating custom type states (parse them, and from them be 
able to parse the associated state). We also need to transport these additional 
type states as part of our event system.

h2. Definition of done

Write an integration test where you emit a custom type state with an exotic 
state implementation (meaning not backed by a UUID) and have the StateChange 
event carried over to the client, for instance using WebSocket.

h2. Proof of concept

Quan had been putting this PR together: 
https://github.com/apache/james-project/pull/391


  was:
= Why?

JMAP specifications defines the minimal features to get a basic email 
reading/writing experience.

However, as part of our job at Linagora, writing a collaborative email suite, 
we need advanced collaborative features that uses custom, off-spec, JMAP 
extensions.

We thus want to implement these collaborative features as JMAP extensions 
(because JMAP is so much of a nice protocol!).

We were able to:

 - Write custom methods pretty much directly.

 - We needed to contribute modular definition of session "capabilities" to be 
advertising our custom ones.

But we are missing the push for custom extensions.

= What?

As an extension developper I want to be able to register my type states and 
have states matching my format:

{code:java}
object IntState {
  def parse(string: String): Either[IllegalArgumentException, IntState] = 
Try(Integer.parseInt(string))
    .toEither
    .map(IntState(_))
    .left.map(new IllegalArgumentException(_))
}

case class IntState(i: Int) extends State {
  override def serialize: String = i.toString
}

case object CustomTypeName extends TypeName {
  override val asString: String = "MyTypeName"

  override def parse(string: String): Option[TypeName] = string match {
    case CustomTypeName.asString => Some(CustomTypeName)
    case _ => None
  }

  override def parseState(string: String): Either[IllegalArgumentException, 
IntState] = IntState.parse(string)
}
{code}

Given this, I want to be injecting my extensions to the push in the guice 
module definition:

{code:java}
    Multibinder.newSetBinder(binder(), classOf[TypeName])
      .addBinding()
      .toInstance(CustomTypeName)
    Multibinder.newSetBinder(binder(), classOf[GuiceProbe])
      .addBinding()
      .to(classOf[JmapEventBusProbe])
{code}

Doing so should allow me to emit 'custon' state change push notifications:

{code:java}
    val stateChangeEvent: StateChangeEvent = StateChangeEvent(eventId = 
CustomMethodContract.eventId, username = BOB, map = Map(CustomTypeName -> 
intState))
    SMono(jmapEventBus.dispatch(stateChangeEvent, 
AccountIdRegistrationKey(accountId))).block()
{code}

Which allows generating state change events PUSH notifications carried over via 
webSocket or SSE for your extensions :-)

= How?

Modularize the definition of State (extensions might not want to have it backed 
by a UUID), allow creating custom type states (parse them, and from them be 
able to parse the associated state). We also need to transport these additional 
type states as part of our event system.

= Definition of done

Write an integration test where you emit a custom type state with an exotic 
state implementation (meaning not backed by a UUID) and have the StateChange 
event carried over to the client, for instance using WebSocket.

= Proof of concept

Quan had been putting this PR together: 
https://github.com/apache/james-project/pull/391



> As a JMAP extension writer, I want to PUSH state changes for my extensions
> --------------------------------------------------------------------------
>
>                 Key: JAMES-3581
>                 URL: https://issues.apache.org/jira/browse/JAMES-3581
>             Project: James Server
>          Issue Type: Improvement
>          Components: JMAP
>    Affects Versions: 3.6.0
>            Reporter: Benoit Tellier
>            Assignee: Antoine Duprat
>            Priority: Major
>             Fix For: 3.7.0
>
>
> h2. Why?
> JMAP specifications defines the minimal features to get a basic email 
> reading/writing experience.
> However, as part of our job at Linagora, writing a collaborative email suite, 
> we need advanced collaborative features that uses custom, off-spec, JMAP 
> extensions.
> We thus want to implement these collaborative features as JMAP extensions 
> (because JMAP is so much of a nice protocol!).
> We were able to:
>  - Write custom methods pretty much directly.
>  - We needed to contribute modular definition of session "capabilities" to be 
> advertising our custom ones.
> But we are missing the push for custom extensions.
> h2. What?
> As an extension developper I want to be able to register my type states and 
> have states matching my format:
> {code:java}
> object IntState {
>   def parse(string: String): Either[IllegalArgumentException, IntState] = 
> Try(Integer.parseInt(string))
>     .toEither
>     .map(IntState(_))
>     .left.map(new IllegalArgumentException(_))
> }
> case class IntState(i: Int) extends State {
>   override def serialize: String = i.toString
> }
> case object CustomTypeName extends TypeName {
>   override val asString: String = "MyTypeName"
>   override def parse(string: String): Option[TypeName] = string match {
>     case CustomTypeName.asString => Some(CustomTypeName)
>     case _ => None
>   }
>   override def parseState(string: String): Either[IllegalArgumentException, 
> IntState] = IntState.parse(string)
> }
> {code}
> Given this, I want to be injecting my extensions to the push in the guice 
> module definition:
> {code:java}
>     Multibinder.newSetBinder(binder(), classOf[TypeName])
>       .addBinding()
>       .toInstance(CustomTypeName)
>     Multibinder.newSetBinder(binder(), classOf[GuiceProbe])
>       .addBinding()
>       .to(classOf[JmapEventBusProbe])
> {code}
> Doing so should allow me to emit 'custon' state change push notifications:
> {code:java}
>     val stateChangeEvent: StateChangeEvent = StateChangeEvent(eventId = 
> CustomMethodContract.eventId, username = BOB, map = Map(CustomTypeName -> 
> intState))
>     SMono(jmapEventBus.dispatch(stateChangeEvent, 
> AccountIdRegistrationKey(accountId))).block()
> {code}
> Which allows generating state change events PUSH notifications carried over 
> via webSocket or SSE for your extensions :-)
> h2. How?
> Modularize the definition of State (extensions might not want to have it 
> backed by a UUID), allow creating custom type states (parse them, and from 
> them be able to parse the associated state). We also need to transport these 
> additional type states as part of our event system.
> h2. Definition of done
> Write an integration test where you emit a custom type state with an exotic 
> state implementation (meaning not backed by a UUID) and have the StateChange 
> event carried over to the client, for instance using WebSocket.
> h2. Proof of concept
> Quan had been putting this PR together: 
> https://github.com/apache/james-project/pull/391



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to