5c8092176692158fd36e4303fddb2cac792841f5 — Sam Whited 9 months ago e8c09b3
design: add MAM design doc

Fixes #110

Signed-off-by: Sam Whited <sam@samwhited.com>
1 files changed, 125 insertions(+), 0 deletions(-)

A design/110_mam.md
A design/110_mam.md => design/110_mam.md +125 -0
@@ 0,0 1,125 @@
# Title

**Author(s):** Sam Whited <sam@samwhited.com>  
**Last updated:** 2021-02-23  
**Discussion:** https://mellium.im/issue/110

## Abstract

An API to support history retrieval via [XEP-0313: Message Archive

[XEP-0313]: https://xmpp.org/extensions/xep-0313.html

## Requirements

- Ability to process messages from an archive in order in a seemingly
  synchronous fashion
- The ability to process messages from an archive asynchronously using a
  user-defined handler that is not part of this package
- Pagination in both directions
- Support for non-standard form fields

## Proposal

Implementing the current proposal would add three types, 11 methods, and 2
functions that would need to remain backwards compatible once we reach 1.0.

// The namespace used by this package, provided as a convenience.
const NS = `urn:xmpp:mam:2`

type Query struct{
  ID      string
  With    jid.JID
  Start   time.Time
  End     time.Time
  Before  string
  After   string
  IDs     []string
  Field   []form.Field
  Max     uint64
  Reverse bool

// TokenReader implements xmlstream.Marshaler.
func (*Query) TokenReader() xml.TokenReader {}

// WriteXML implements xmlstream.WriterTo.
func (*Query) WriteXML(w xmlstream.TokenWriter) (int, error) {}

// MarshalXML implements xml.Marshaler.
func (*Query) MarshalXML(e *xml.Encoder, _ xml.StartElement) error {}

// UnmarshalXML implements xml.Unmarshaler.
func (*Query) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {}

// Handle returns an option that registers a Handler for archive responses.
func Handle(h *Handler) mux.Option

// Get can be called to send a query where the responses should be handled by a
// user defined handler.
// It will block until all messages sent in response have been received and
// processed.
func Get(ctx context.Context, q Query) error {}

// Handler listens for incoming responses from an archive and matches them to
// outgoing requests sent with Get or GetIQ.
type Handler struct{}

// HandleMessage implements mux.MessageHandler.
func (h *Handler) HandleMessage(msg stanza.Message, t xmlstream.TokenReadEncoder) error {}

// Get requests history just like the Query function, except that it associates
// responses to the original query and allows the user to iterate through them
// as they come in.
// Unlike most iterators, archive history may arrive interspersed with other
// traffic, causing the Iter to block waiting for the next response from the
// archive.
// Care should be taken not to block processing of the stream.
// When the iter is closed if any subsequent responses are received they are
// sent to the normal serve handler for processing.
func (Handler) Get(ctx context.Context, q Query) *Iter

// Iter is used to iterate through MAM responses.
type Iter struct{}

// Next returns true if we expect more results from the iterator.
// When Next is called it blocks until one of three things happens: the next
// response is received, the context used when creating the iter is canceled,
// the original queries result is sent back indicating that no more responses
// are expected.
// Next automatically fetches the next (or previous) page if the end of the
// current page has been reached.
func (*Iter) Next() bool {}

// Current returns the most resent response from the archive.
func (*Iter) Current() (Result, xml.TokenReader) {}

// Close stops the iterator from receiving further archive responses.
// It does not cancel the current query.
// Future responses will be sent to the normal handler and processed just like
// any other incoming message where they may be processed or ignored.
func (*Iter) Close() error {}

// Err returns any error that was encountered while iterating.
func (*Iter) Err() error {}

// Set returns information about the finished query.
func (*Iter) Set() paging.Set {}

## Open Questions

- How can we verify that `urn:xmpp:mam:2#extended` is in the disco features
  before allowing the user to make requests that include extended querying?
- Instead can we discover the form first and check if it has these fields (which
  presumably also means it supports `<flip-page/>` as well)?
- Does it matter if you include `<after>` in Result Set management, but also set
  `after-id` in the MAM form? Does the latest ID win?
- How do we query for metadata?
- Can we recommend a good querying strategy for clients in this package and make
  it the default or a special easy to use function?