Allow stream lookup by subject.

Allow an API endpoint and public API to lookup a stream by subject. The subject needs to be an exact match or a subset. If the subject is considered a filtered subject for the stream that will also be returned.

Signed-off-by: Derek Collison <derek@nats.io>
This commit is contained in:
Derek Collison
2020-11-14 13:35:05 -08:00
parent c851b39b79
commit 5ff28b6087
5 changed files with 190 additions and 13 deletions

View File

@@ -75,6 +75,10 @@ const (
JSApiStreamInfo = "$JS.API.STREAM.INFO.*"
JSApiStreamInfoT = "$JS.API.STREAM.INFO.%s"
// JSApiStreamLookup is for obtaining a stream by a target subject.
// Will return JSON response.
JSApiStreamLookup = "$JS.API.STREAM.LOOKUP"
// JSApiStreamDelete is the endpoint to delete streams.
// Will return JSON response.
JSApiStreamDelete = "$JS.API.STREAM.DELETE.*"
@@ -112,7 +116,7 @@ const (
JSApiConsumerCreate = "$JS.API.CONSUMER.CREATE.*"
JSApiConsumerCreateT = "$JS.API.CONSUMER.CREATE.%s"
// JSApiDurableCreate is the endpoint to create ephemeral consumers for streams.
// JSApiDurableCreate is the endpoint to create durable consumers for streams.
// You need to include the stream and consumer name in the subject.
JSApiDurableCreate = "$JS.API.CONSUMER.DURABLE.CREATE.*.*"
JSApiDurableCreateT = "$JS.API.CONSUMER.DURABLE.CREATE.%s.%s"
@@ -262,6 +266,15 @@ type JSApiStreamInfoResponse struct {
const JSApiStreamInfoResponseType = "io.nats.jetstream.api.v1.stream_info_response"
// JSApiStreamLookupResponse.
type JSApiStreamLookupResponse struct {
ApiResponse
Stream string `json:"stream"`
Filtered bool `json:"is_filtered"`
}
const JSApiStreamLookupResponseType = "io.nats.jetstream.api.v1.stream_lookup_response"
// Maximum entries we will return for streams or consumers lists.
// TODO(dlc) - with header or request support could request chunked response.
const JSApiNamesLimit = 1024
@@ -480,6 +493,7 @@ var allJsExports = []string{
JSApiStreams,
JSApiStreamList,
JSApiStreamInfo,
JSApiStreamLookup,
JSApiStreamDelete,
JSApiStreamPurge,
JSApiStreamSnapshot,
@@ -509,6 +523,7 @@ func (s *Server) setJetStreamExportSubs() error {
{JSApiStreams, s.jsStreamNamesRequest},
{JSApiStreamList, s.jsStreamListRequest},
{JSApiStreamInfo, s.jsStreamInfoRequest},
{JSApiStreamLookup, s.jsStreamLookupRequest},
{JSApiStreamDelete, s.jsStreamDeleteRequest},
{JSApiStreamPurge, s.jsStreamPurgeRequest},
{JSApiStreamSnapshot, s.jsStreamSnapshotRequest},
@@ -953,6 +968,44 @@ func (s *Server) jsStreamInfoRequest(sub *subscription, c *client, subject, repl
s.sendAPIResponse(c, subject, reply, string(msg), s.jsonResponse(resp))
}
// Request to lookup a stream by target subject.
func (s *Server) jsStreamLookupRequest(sub *subscription, c *client, subject, reply string, msg []byte) {
if c == nil || c.acc == nil {
return
}
var resp = JSApiStreamLookupResponse{ApiResponse: ApiResponse{Type: JSApiStreamLookupResponseType}}
if !c.acc.JetStreamEnabled() {
resp.Error = jsNotEnabledErr
s.sendAPIResponse(c, subject, reply, string(msg), s.jsonResponse(&resp))
return
}
if isEmptyRequest(msg) {
resp.Error = &ApiError{Code: 400, Description: "subject required"}
s.sendAPIResponse(c, subject, reply, string(msg), s.jsonResponse(&resp))
return
}
subj := string(msg)
if !IsValidSubject(subj) {
resp.Error = &ApiError{Code: 400, Description: "subject argument is not a valid subject"}
s.sendAPIResponse(c, subject, reply, string(msg), s.jsonResponse(&resp))
return
}
// Lookup our stream.
mset, filtered, err := c.acc.LookupStreamBySubject(subj)
if err != nil {
resp.Error = jsNotFoundError(err)
s.sendAPIResponse(c, subject, reply, string(msg), s.jsonResponse(&resp))
return
}
resp.Stream = mset.Name()
resp.Filtered = filtered
s.sendAPIResponse(c, subject, reply, string(msg), s.jsonResponse(resp))
}
func isEmptyRequest(req []byte) bool {
if len(req) == 0 {
return true