No "groupName" property present in competing consumer streams (HTTP API). Only present in info document. Surely a mistake?

When sending an HTTP GET request to /subscriptions/{streamId}/{groupName} the returned feed does not contain a groupName property.

Although this property can be retrieved by sending an HTTP GET request to **/subscriptions/{streamId}/{groupName}/info **it adds extra hassle.

This is especially apparent when you want to construct a valid Subscription object which would obviously have streamId and **groupName **properties.

Lets say you have an interface:

interface SubscriptionRepository
{
Subscription find(string streamId, string groupName);

add(Subscription subscription)
}

``

As you can see I must supply the streamId and groupName when retrieving a Subscription.

This would return a fully constructed instance of Subscription. The problem is I need to make two HTTP requests in order to do this.

One to get the subscription and its events (/subscriptions/{streamId}/{groupName}), another to get the group name (/subscriptions/{streamId}/{groupName}/info).

A valid instance of Subscription is important because if I want to pass this object around to say the SubscriptionRepository::add() method it would need

the groupName property in order to successfully send a POST or PUT request to /subscriptions/{streamId}/{groupName}.

This is causing other annoyances as well. I’m creating a browser library which allows you to easily browse through a feed which allows the events to be lazy loaded among other stuff

and this tiny little property missing in the feed is causing all sorts of headaches.

I’d have thought the groupName property would be treated as a first class citizen of the competing subscription feed since it is required if you want to create, retrieve, update or delete

the subscription group.

I know the property can be resolved by getting the last segment of the id property of the feed but that just seems as though it could be brittle and cause breakages in the future

if I depended on that.

Why is it like this? Surely groupName should be returned with each feed or at least if you specify the embed mode to “rich” which gives you piles of data except this key property.

➜ eventstore git:(release-v4.0.0) curl http://127.0.0.1:2113/subscriptions
[
  {
    "links": [
      {
        "href":
"http://127.0.0.1:2113/subscriptions/%24statistics-127.0.0.1%3A2113/shitbird/info",
        "rel": "detail"
      }
    ],
    "eventStreamId": "$statistics-127.0.0.1:2113",
    "groupName": "shitbird",
    "parkedMessageUri":
"http://127.0.0.1:2113/streams/%24persistentsubscription-%24statistics-127.0.0.1%3A2113::shitbird-parked",
    "getMessagesUri":
"http://127.0.0.1:2113/subscriptions/%24statistics-127.0.0.1%3A2113/shitbird/1",
    "status": "Live",
    "averageItemsPerSecond": 0.0,
    "totalItemsProcessed": 0,
    "lastProcessedEventNumber": -1,
    "lastKnownEventNumber": -1,
    "connectionCount": 0,
    "totalInFlightMessages": 0
  }
]

This gives you all subscriptions (and includes group names ... top level).

➜ eventstore git:(release-v4.0.0) curl
http://127.0.0.1:2113/subscriptions/%24statistics-127.0.0.1%3A2113/shitbird/info
{
  "links": [
    {
      "href": "http://127.0.0.1:2113/subscriptions/%24statistics-127.0.0.1%3A2113/shitbird/info",
      "rel": "detail"
    },
    {
      "href": "http://127.0.0.1:2113/subscriptions/%24statistics-127.0.0.1%3A2113/shitbird/replayParked",
      "rel": "replayParked"
    }
  ],
  "config": {
    "resolveLinktos": false,
    "startFrom": 0,
    "messageTimeoutMilliseconds": 10000,
    "extraStatistics": false,
    "maxRetryCount": 10,
    "liveBufferSize": 500,
    "bufferSize": 500,
    "readBatchSize": 20,
    "preferRoundRobin": true,
    "checkPointAfterMilliseconds": 1000,
    "minCheckPointCount": 10,
    "maxCheckPointCount": 500,
    "maxSubscriberCount": 10,
    "namedConsumerStrategy": "RoundRobin"
  },
  "eventStreamId": "$statistics-127.0.0.1:2113",
  "groupName": "shitbird",
  "status": "Live",
  "averageItemsPerSecond": 0.0,
  "parkedMessageUri":
"http://127.0.0.1:2113/streams/%24persistentsubscription-%24statistics-127.0.0.1%3A2113::shitbird-parked",
  "getMessagesUri":
"http://127.0.0.1:2113/subscriptions/%24statistics-127.0.0.1%3A2113/shitbird/1",
  "totalItemsProcessed": 0,
  "countSinceLastMeasurement": 0,
  "lastProcessedEventNumber": -1,
  "lastKnownEventNumber": -1,
  "readBufferCount": 0,
  "liveBufferCount": 0,
  "retryBufferCount": 0,
  "totalInFlightMessages": 0,
  "connections": []
}

This gives you details on a subscription (note you should follow the
rel link from the first uri). Also includes a groupname. This gives
you as well the uris of where the stream actually is.

The problem is you are trying to bookmark instead of following links.
Why you are going to the link of "getMessagesUri":
"http://127.0.0.1:2113/subscriptions/%24statistics-127.0.0.1%3A2113/shitbird/1",
before having walked the surrounding stuff I am not sure, maybe you
can explain?

Greg

I’m sort of skipping the initial entry point and making it so I can just jump straight into a subscription feed and then use the links in it the navigate.

If there are 5,000 subscriptions and I want to starting browsing the “marketing” one does that mean I should go to the entry point, iterate through

all the subscriptions looking for one where groupName == “marketing” and go from there? It just seems inefficient.

To add we reserve the ability to change uris such as
http://127.0.0.1:2113/subscriptions/%24statistics-127.0.0.1%3A2113/shitbird/1
which is why we provide such a structure.

If you are bookmarking then you already know the group name.
Understand that bookmarking also causes possible version conflicts.

Yeah I was just sort of banking on you not changing the URI’s which might be considered foolish.

If I’m bookmarking then yes I know the group name. The problem is as I pass the streamId and groupName into my browser they

will be used to generate the URL and down at that level I am just working with HTTP and the responses it sends back. Since there is no groupName

in any response (unless I make a request to /info) it makes things difficult.

When the response comes back I parse it and it eventually returns a Subscription object. I can’t construct a valid Subscription object without the

groupName.

I can probably come up with another method for somehow passing the supplied groupName into my factory when it’s creating the Subscription

from the response****but it would all be so much more nicer if the property was just returned with the feed. I’m still confused as to why it’s not.

Why not give yourself at least one level of isolation and hit /info
first then take the uri for messages from it? Then you have a penalty
on create but also get benefit as you are abstracted from bookmarking
the actual uri (one less change in the future). You also get the
parked message uri etc as a bonus.

Seems like a good idea.

I could cache the info document so when navigating through a feed it only gets the info document the first time.

Do you think the property will be returned with a competing consumer feed in future versions?

Not really as the competing feed is trying to follow the atom format
and technically does include it for those who really want it and are
bookmarking (see self uri).

The right way to subscribe to stream s/group g (if somewhat
bookmarking) is /subscriptions/s/g/info then use the get uri from
there. Ideally its to use subscriptions as a discovery point (but
everything is a trade off:))

Yeah I get that. Thanks very much.