[ADDED] Accessors for JS API level and IsSysAccount (#2060)

Signed-off-by: Piotr Piotrowski <piotr@synadia.com>
This commit is contained in:
Piotr Piotrowski
2026-04-13 13:17:41 +02:00
committed by GitHub
parent 7a2bb4ab8d
commit b6d0226cf7
2 changed files with 120 additions and 1 deletions
+42
View File
@@ -925,6 +925,14 @@ type ServerInfo struct {
Cluster string `json:"cluster,omitempty"`
ConnectURLs []string `json:"connect_urls,omitempty"`
LameDuckMode bool `json:"ldm,omitempty"`
// JetStream indicates whether the server has JetStream enabled.
JetStream bool `json:"jetstream,omitempty"`
// IsSystemAccount indicates whether the connected client's account
// is the system account.
IsSystemAccount bool `json:"acc_is_sys,omitempty"`
// JSApiLevel is the JetStream API level advertised by the server.
// Requires nats-server v2.12.0 or later; older servers will report 0.
JSApiLevel int `json:"api_lvl,omitempty"`
}
const (
@@ -2641,6 +2649,40 @@ func (nc *Conn) ConnectedClusterName() string {
return nc.info.Cluster
}
// ConnectedServerJetStream reports whether the connected server has
// JetStream enabled and, if so, its API level. The API level is
// advertised by nats-server v2.12.0 or later; older servers will
// report 0 even when JetStream is enabled.
func (nc *Conn) ConnectedServerJetStream() (bool, int) {
if nc == nil {
return false, 0
}
nc.mu.RLock()
defer nc.mu.RUnlock()
if nc.status != CONNECTED {
return false, 0
}
return nc.info.JetStream, nc.info.JSApiLevel
}
// IsSystemAccount reports whether the connected client's account
// is the system account.
func (nc *Conn) IsSystemAccount() bool {
if nc == nil {
return false
}
nc.mu.RLock()
defer nc.mu.RUnlock()
if nc.status != CONNECTED {
return false
}
return nc.info.IsSystemAccount
}
// Low level setup for structs, etc
func (nc *Conn) setup() {
nc.subs = make(map[int64]*Subscription)
+78 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2012-2023 The NATS Authors
// Copyright 2012-2026 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -19,6 +19,7 @@ import (
"errors"
"fmt"
"math"
"os"
"regexp"
"runtime"
"strings"
@@ -147,6 +148,13 @@ func TestConnectedServer(t *testing.T) {
if cname == "" {
t.Fatalf("Expected a connected server cluster name, got %s", cname)
}
jsEnabled, _ := nc.ConnectedServerJetStream()
if jsEnabled {
t.Fatalf("Expected JetStream to be disabled")
}
if nc.IsSystemAccount() {
t.Fatalf("Expected non-system account")
}
nc.Close()
u = nc.ConnectedUrl()
@@ -165,6 +173,75 @@ func TestConnectedServer(t *testing.T) {
if cname != "" {
t.Fatalf("Expected a nil connect server cluster, got %s", cname)
}
jsEnabled, _ = nc.ConnectedServerJetStream()
if jsEnabled {
t.Fatalf("Expected JetStream to be disabled after close")
}
if nc.IsSystemAccount() {
t.Fatalf("Expected non-system account after close")
}
}
func TestConnectedServerJetStream(t *testing.T) {
s := RunBasicJetStreamServer()
defer s.Shutdown()
nc, err := nats.Connect(s.ClientURL())
if err != nil {
t.Fatalf("Error connecting: %v", err)
}
defer nc.Close()
jsEnabled, jsApiLevel := nc.ConnectedServerJetStream()
if !jsEnabled {
t.Fatalf("Expected JetStream to be enabled")
}
if jsApiLevel == 0 {
t.Fatalf("Expected non-zero JetStream API level")
}
}
func TestIsSystemAccount(t *testing.T) {
conf := createConfFile(t, []byte(`
listen: 127.0.0.1:-1
accounts: {
SYS: {
users: [ {user: "sys", password: "pass"} ]
}
APP: {
users: [ {user: "app", password: "pass"} ]
}
}
system_account: SYS
`))
defer os.Remove(conf)
s, _ := RunServerWithConfig(conf)
defer s.Shutdown()
nc, err := nats.Connect(s.ClientURL(), nats.UserInfo("sys", "pass"))
if err != nil {
t.Fatalf("Error connecting: %v", err)
}
defer nc.Close()
// IsSystemAccount is sent by the server in an async INFO after connect.
checkFor(t, time.Second, 15*time.Millisecond, func() error {
if !nc.IsSystemAccount() {
return fmt.Errorf("expected system account")
}
return nil
})
nc2, err := nats.Connect(s.ClientURL(), nats.UserInfo("app", "pass"))
if err != nil {
t.Fatalf("Error connecting: %v", err)
}
defer nc2.Close()
if nc2.IsSystemAccount() {
t.Fatalf("Expected non-system account")
}
}
func TestMultipleClose(t *testing.T) {