[management] network map from nmap data type (#6919)

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>
Co-authored-by: Dmitri Dolguikh <dmitri.external@netbird.io>
This commit is contained in:
Pascal Fischer
2026-08-27 11:28:05 +02:00
committed by GitHub
co-authored by Dmitri Dolguikh
parent 473392a935
commit e06c17cf59
338 changed files with 24867 additions and 3799 deletions
@@ -0,0 +1,47 @@
package networkmap_sqlite
import (
"context"
"reflect"
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
)
const (
GetAccountSettingsQuery = `
select settings_peer_login_expiration_enabled as peer_login_expiration_enabled,
settings_peer_login_expiration as peer_login_expiration,
settings_peer_inactivity_expiration_enabled as peer_inactivity_expiration_enabled,
settings_peer_inactivity_expiration as peer_inactivity_expiration,
settings_dns_domain as dns_domain,
settings_ipv6_enabled_groups as ipv6_enabled_groups,
settings_routing_peer_dns_resolution_enabled as routing_peer_dns_resolution_enabled,
settings_lazy_connection_enabled as lazy_connection_enabled,
settings_auto_update_version as auto_update_version,
settings_auto_update_always as auto_update_always,
settings_metrics_push_enabled as metrics_push_enabled
from accounts
where id=?
`
)
func (sc *SqliteStoreConn) GetAccountSettings(ctx context.Context, accountId string) (nmdata.AccountSettingsInfo, error) {
rows, err := sc.Conn.QueryContext(ctx, GetAccountSettingsQuery, accountId)
if err != nil {
return nmdata.AccountSettingsInfo{}, err
}
a, err := CollectOneRowForSqlite[networkmapdb.Account](rows)
if err != nil {
return nmdata.AccountSettingsInfo{}, err
}
settingsInfo := nmdata.AccountSettingsInfo{}
err = networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&a), reflect.ValueOf(&settingsInfo))
if err != nil {
return nmdata.AccountSettingsInfo{}, err
}
return settingsInfo, nil
}
@@ -0,0 +1,32 @@
package networkmap_sqlite
import (
"context"
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
"github.com/netbirdio/netbird/shared/management/networkmap"
)
const (
GetAccountZonesQuery = `
select zones.id as id, domain, not enable_search_domain as search_domain_disabled, distribution_groups,
r.name as record_name, r.type as record_type, 'IN' record_class, r.ttl as record_ttl, r.content as record_rdata
from zones
left join records as r on r.zone_id = zones.id
where zones.account_id=? and zones.enabled
`
)
func (sc *SqliteStoreConn) GetAppliedZoneCandidates(ctx context.Context, accountId string) ([]networkmap.AppliedZoneCandidate, error) {
rows, err := sc.Conn.QueryContext(ctx, GetAccountZonesQuery, accountId)
if err != nil {
return nil, err
}
zones, err := CollectRowsForSqlite[networkmapdb.Zone](rows)
if err != nil {
return nil, err
}
return networkmapdb.ZonesToAppliedZoneCandidates(zones)
}
@@ -0,0 +1,42 @@
package networkmap_sqlite
import (
"context"
"encoding/json"
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
)
const (
GetDnsSettingsQuery = `
select dns_settings_disabled_management_groups
from accounts
where id=?
`
)
func (sc *SqliteStoreConn) GetDnsSettings(ctx context.Context, accountId string) (nmdata.DNSSettings, error) {
rows, err := sc.Conn.QueryContext(ctx, GetDnsSettingsQuery, accountId)
if err != nil {
return nmdata.DNSSettings{}, err
}
defer rows.Close()
var value nmdata.DNSSettings
var settings []byte
rows.Next()
if err := rows.Scan(&settings); err != nil {
return value, err
}
if settings == nil {
return nmdata.DNSSettings{}, nil
}
if err := json.Unmarshal(settings, &value.DisabledManagementGroups); err != nil {
return value, err
}
return value, nil
}
@@ -0,0 +1,24 @@
package networkmap_sqlite
import (
"context"
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
)
const (
GetDomainsQuery = `
select domain, target_cluster
from domains
where account_id=? and domain<>'' and target_cluster<>''
`
)
func (sc *SqliteStoreConn) GetDomains(ctx context.Context, accountId string) ([]networkmapdb.Domain, error) {
rows, err := sc.Conn.QueryContext(ctx, GetDomainsQuery, accountId)
if err != nil {
return nil, err
}
return CollectRowsForSqlite[networkmapdb.Domain](rows)
}
@@ -0,0 +1,70 @@
package networkmap_sqlite
import (
"context"
"database/sql"
"reflect"
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
)
const (
GetGroupsQuery = `
select groups.id, groups.name, groups.public_id, groups.resources, gp.peer_id
from groups
left join group_peers gp on gp.group_id=groups.id and gp.account_id=?
where groups.account_id=?
`
)
// we also return a resource-to-group index.
// an alternative is to add json indexes, query this directly. Not sure how expensive
// json indexes are. TODO (dmitri) verify and maybe change the implementation here.
func (sc *SqliteStoreConn) GetGroups(ctx context.Context, accountId string) ([]nmdata.Group, map[string]map[string]any, error) {
rows, err := sc.Conn.QueryContext(ctx, GetGroupsQuery, accountId, accountId)
if err != nil {
return nil, nil, err
}
groups, err := CollectRowsForSqlite[group](rows)
toret := make([]nmdata.Group, 0, len(groups))
resourceToGroupIdx := make(map[string]map[string]any)
for _, g := range groups {
if len(toret) > 0 && toret[len(toret)-1].ID == g.ID && g.PeerID.Valid {
toret[len(toret)-1].Peers = append(toret[len(toret)-1].Peers, g.PeerID.String)
continue
}
dg := nmdata.Group{}
err := networkmapdb.FromSqlTypesToSharedTypes(
reflect.ValueOf(&g), reflect.ValueOf(&dg))
if err != nil {
return nil, nil, err
}
if g.PeerID.Valid {
dg.Peers = append(dg.Peers, g.PeerID.String)
}
toret = append(toret, dg)
for _, resource := range dg.Resources {
if _, ok := resourceToGroupIdx[resource.ID]; !ok {
resourceToGroupIdx[resource.ID] = make(map[string]any)
}
resourceToGroupIdx[resource.ID][g.ID] = struct{}{}
}
}
return toret, resourceToGroupIdx, err
}
type group struct {
ID string
Name sql.NullString
PublicID sql.NullString
Resources []byte `nmap:"json"`
PeerID sql.NullString `nmap:"skip"`
}
@@ -0,0 +1,30 @@
package networkmap_sqlite
import (
"context"
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
)
const (
GetNameserversQuery = `
select id, public_id, name, description, name_servers, groups, "primary", domains, enabled, search_domains_enabled
from name_server_groups
where account_id=?
`
)
func (sc *SqliteStoreConn) GetNameServerGroups(ctx context.Context, accountId string) ([]nmdata.NameServerGroup, error) {
rows, err := sc.Conn.QueryContext(ctx, GetNameserversQuery, accountId)
if err != nil {
return nil, err
}
nsgroups, err := CollectRowsForSqlite[networkmapdb.NameserverGroup](rows)
if err != nil {
return nil, err
}
return networkmapdb.ConvertAllToSharedTypes[networkmapdb.NameserverGroup, nmdata.NameServerGroup](nsgroups)
}
@@ -0,0 +1,38 @@
package networkmap_sqlite
import (
"context"
"reflect"
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
)
const (
GetNetworkQuery = `
select network_identifier as identifier, network_net as net, network_net_v6 as net_v6, network_dns as dns, network_serial as serial
from accounts
where id=?
`
)
func (sc *SqliteStoreConn) GetNetwork(ctx context.Context, accountId string) (nmdata.Network, error) {
rows, err := sc.Conn.QueryContext(ctx, GetNetworkQuery, accountId)
if err != nil {
return nmdata.Network{}, err
}
n, err := CollectOneRowForSqlite[networkmapdb.AccountNetwork](rows)
if err != nil {
return nmdata.Network{}, err
}
toret := nmdata.Network{}
err = networkmapdb.FromSqlTypesToSharedTypes(
reflect.ValueOf(&n), reflect.ValueOf(&toret))
if err != nil {
return nmdata.Network{}, err
}
return toret, nil
}
@@ -0,0 +1,30 @@
package networkmap_sqlite
import (
"context"
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
)
const (
GetNetworkResourcesQuery = `
select id, network_id, account_id, public_id, name, description, type, domain, prefix, enabled
from network_resources
where account_id=?
`
)
func (sc *SqliteStoreConn) GetNetworkResources(ctx context.Context, accountId string) ([]nmdata.NetworkResource, error) {
rows, err := sc.Conn.QueryContext(ctx, GetNetworkResourcesQuery, accountId)
if err != nil {
return nil, err
}
netresorces, err := CollectRowsForSqlite[networkmapdb.Networkresource](rows)
if err != nil {
return nil, err
}
return networkmapdb.ConvertAllToSharedTypes[networkmapdb.Networkresource, nmdata.NetworkResource](netresorces)
}
@@ -0,0 +1,74 @@
package networkmap_sqlite
import (
"context"
"database/sql"
"fmt"
"reflect"
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
)
const (
GetNetworkRouterQuery = `
select public_id, peer, network_id, masquerade, metric, enabled, peer_groups, group_peers.peer_id
from network_routers, json_each(peer_groups)
left join group_peers on group_peers.account_id=? and group_peers.group_id=json_each.value
where network_routers.account_id=?
`
)
func (sc *SqliteStoreConn) GetNetworkRouters(ctx context.Context, accountId string) (map[string]map[string]*nmdata.NetworkRouter, error) {
rows, err := sc.Conn.QueryContext(ctx, GetNetworkRouterQuery, accountId, accountId)
if err != nil {
return nil, err
}
routers, err := CollectRowsForSqlite[networkrouter](rows)
if err != nil {
return nil, err
}
toret := make(map[string]map[string]*nmdata.NetworkRouter)
for _, router := range routers {
if !router.Enabled.Bool {
continue
}
networkId := router.NetworkID.String
if networkId == "" {
return nil, fmt.Errorf("router with public_id %s doesn't have network_id set", router.PublicID.String)
}
nmdatarouter := nmdata.NetworkRouter{}
err := networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&router), reflect.ValueOf(&nmdatarouter))
if err != nil {
return nil, err
}
if toret[networkId] == nil {
toret[networkId] = make(map[string]*nmdata.NetworkRouter)
}
if router.Peer.String != "" {
toret[networkId][router.Peer.String] = &nmdatarouter
continue
}
if router.PeerViaGroups.String != "" {
toret[networkId][router.PeerViaGroups.String] = &nmdatarouter
}
}
return toret, nil
}
type networkrouter struct {
PublicID sql.NullString
Peer sql.NullString `nmap:"skip"`
NetworkID sql.NullString `nmap:"skip"`
Masquerade sql.NullBool
Metric sql.NullInt64
Enabled sql.NullBool
PeerGroups []byte `nmap:"json"`
PeerViaGroups sql.NullString `nmap:"skip"`
}
@@ -0,0 +1,35 @@
package networkmap_sqlite
import (
"context"
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
)
const (
GetNetworksQuery = `
select id, public_id
from networks where account_id=?
`
)
func (sc *SqliteStoreConn) GetNetworkXIDToPublicIdMap(ctx context.Context, accountId string) (map[string]string, error) {
rows, err := sc.Conn.QueryContext(ctx, GetNetworksQuery, accountId)
if err != nil {
return nil, err
}
networks, err := CollectRowsForSqlite[networkmapdb.Network](rows)
if err != nil {
return nil, err
}
toret := make(map[string]string)
for _, n := range networks {
if n.PublicID.Valid {
toret[n.ID] = n.PublicID.String
}
}
return toret, nil
}
@@ -0,0 +1,33 @@
package networkmap_sqlite
import (
"context"
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
)
const (
GetPeersQuery = `
select id, key, ssh_key, dns_label, extra_dns_labels, user_id, ssh_enabled, login_expiration_enabled, last_login, ip, ipv6,
peer_status_requires_approval, peer_status_connected, proxy_meta_embedded, proxy_meta_cluster,
meta_wt_version, meta_go_os, meta_os_version, meta_kernel_version, meta_network_addresses, meta_files, meta_capabilities, meta_flags, meta_sync_message_version,
location_country_code, location_city_name, location_connection_ip
from peers
where account_id = ?
`
)
func (sc *SqliteStoreConn) GetPeers(ctx context.Context, accountId string) ([]nmdata.Peer, map[string][]*nmdata.Peer, error) {
rows, err := sc.Conn.QueryContext(ctx, GetPeersQuery, accountId)
if err != nil {
return nil, nil, err
}
peers, err := CollectRowsForSqlite[networkmapdb.Peer](rows)
if err != nil {
return nil, nil, err
}
return networkmapdb.ConvertToNmdataPeers(peers)
}
@@ -0,0 +1,33 @@
package networkmap_sqlite
import (
"context"
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
)
const (
GetPoliciesQuery = `
select p.id, p.public_id, p.enabled, p.source_posture_checks, pr.enabled as rule_enabled, pr.action, pr.protocol, pr.bidirectional,
pr.sources, pr.destinations, pr.source_resource, pr.destination_resource, pr.ports, pr.port_ranges,
pr.authorized_groups, pr.authorized_user
from policies as p
left join policy_rules as pr on p.id = pr.policy_id
where account_id=?
`
)
func (sc *SqliteStoreConn) GetPolicies(ctx context.Context, accountId string) ([]nmdata.Policy, map[string]map[string]any, map[string]map[string]any, error) {
rows, err := sc.Conn.QueryContext(ctx, GetPoliciesQuery, accountId)
if err != nil {
return nil, nil, nil, err
}
policies, err := CollectRowsForSqlite[networkmapdb.Policy](rows)
if err != nil {
return nil, nil, nil, err
}
return networkmapdb.ConvertToNmdataPolicy(policies)
}
@@ -0,0 +1,43 @@
package networkmap_sqlite
import (
"context"
"reflect"
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
)
const (
GetPostureChecksQuery = `
select id, public_id, checks
from posture_checks
where account_id=?
`
)
func (sc *SqliteStoreConn) GetPostureChecks(ctx context.Context, accountId string) ([]nmdata.PostureChecks, map[string]string, error) {
rows, err := sc.Conn.QueryContext(ctx, GetPostureChecksQuery, accountId)
if err != nil {
return nil, nil, err
}
checks, err := CollectRowsForSqlite[networkmapdb.PostureChecks](rows)
if err != nil {
return nil, nil, err
}
toret := make([]nmdata.PostureChecks, 0, len(checks))
idToPublicIDIdx := make(map[string]string)
for _, c := range checks {
checks := nmdata.PostureChecks{}
err := networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&c), reflect.ValueOf(&checks))
if err != nil {
return nil, nil, err
}
toret = append(toret, checks)
idToPublicIDIdx[checks.ID] = c.PublicID.String
}
return toret, idToPublicIDIdx, nil
}
@@ -0,0 +1,32 @@
package networkmap_sqlite
import (
"context"
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
)
const (
GetRoutesQuery = `
select id, account_id, public_id, network, domains, keep_route, net_id, description,
peer, peer as peer_id, peer_groups, network_type, masquerade, metric, enabled,
groups, access_control_groups, skip_auto_apply
from routes
where account_id=?
`
)
func (sc *SqliteStoreConn) GetRoutes(ctx context.Context, accountId string) ([]nmdata.Route, error) {
rows, err := sc.Conn.QueryContext(ctx, GetRoutesQuery, accountId)
if err != nil {
return nil, err
}
routes, err := CollectRowsForSqlite[networkmapdb.Route](rows)
if err != nil {
return nil, err
}
return networkmapdb.ConvertAllToSharedTypes[networkmapdb.Route, nmdata.Route](routes)
}
@@ -0,0 +1,89 @@
package networkmap_sqlite
import (
"context"
"database/sql"
"encoding/json"
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
)
const (
GetServicesQuery = `
select enabled, private, access_groups, proxy_cluster, domain
from services
where account_id=?
`
GetProxyTargetedDomainResourcesQuery = `
select t.target_id
from targets as t
join services as s on s.id = t.service_id
where s.account_id=? and s.enabled and not coalesce(s.terminated, false)
and t.enabled and t.target_type='domain' and t.target_id is not null
`
)
func (sc *SqliteStoreConn) GetPrivateServices(ctx context.Context, accountId string) ([]networkmapdb.Service, error) {
rows, err := sc.Conn.QueryContext(ctx, GetServicesQuery, accountId)
if err != nil {
return nil, err
}
services, err := CollectRowsForSqlite[service](rows)
if err != nil {
return nil, err
}
toret := make([]networkmapdb.Service, 0, len(services))
for _, service := range services {
acg := []string{}
if service.AccessGroups != nil {
if err := json.Unmarshal(service.AccessGroups, &acg); err != nil {
return nil, err
}
}
s := networkmapdb.Service{
Enabled: service.Enabled,
Private: service.Private,
AccessGroups: acg,
ProxyCluster: service.ProxyCluster,
Domain: service.Domain,
}
toret = append(toret, s)
}
return toret, nil
}
func (sc *SqliteStoreConn) GetProxyTargetedDomainResourceIDs(ctx context.Context, accountId string) (map[string]struct{}, error) {
rows, err := sc.Conn.QueryContext(ctx, GetProxyTargetedDomainResourcesQuery, accountId)
if err != nil {
return nil, err
}
defer rows.Close()
toret := make(map[string]struct{})
for rows.Next() {
var id string
err := rows.Scan(&id)
if err != nil {
return nil, err
}
toret[id] = struct{}{}
}
if err := rows.Err(); err != nil {
return nil, err
}
return toret, nil
}
type service struct {
Enabled sql.NullBool
Private sql.NullBool
AccessGroups []byte
ProxyCluster sql.NullString
Domain sql.NullString
}
@@ -0,0 +1,148 @@
package networkmap_sqlite
import (
"context"
"errors"
"fmt"
"net/url"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"database/sql"
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
)
var ErrNoRows = errors.New("no rows in result set")
type SqliteStore struct {
Db *sql.DB
}
type sqliteInterface interface {
QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
}
type SqliteStoreConn struct {
Conn sqliteInterface
}
func NewSqliteStore(storeFile, dataDir string) (*SqliteStore, error) {
dbfile := storeFile
if envFile, ok := os.LookupEnv("NB_STORE_ENGINE_SQLITE_FILE"); ok && envFile != "" {
dbfile = envFile
}
// Separate file path from any SQLite URI query parameters (e.g., "store.db?mode=rwc")
filePath, query, hasQuery := strings.Cut(dbfile, "?")
connStr := filePath
if filePath != ":memory:" && !filepath.IsAbs(filePath) {
connStr = filepath.Join(dataDir, filePath)
}
// Compose query parameters. User-provided ?_busy_timeout (or its mattn alias
// ?_timeout) overrides our default; otherwise inject 30s so SQLite waits at
// most that long on a lock instead of blocking the only Go-side connection.
// mattn/go-sqlite3 applies PRAGMA from the DSN on every fresh connection, so
// the value survives ConnMaxIdleTime/ConnMaxLifetime recycling. cache=shared
// stays the default on non-Windows for the same reason as before.
parsed, _ := url.ParseQuery(query)
var defaults []string
if parsed.Get("_busy_timeout") == "" && parsed.Get("_timeout") == "" {
defaults = append(defaults, "_busy_timeout=30000")
}
if !hasQuery && runtime.GOOS != "windows" {
// To avoid `The process cannot access the file because it is being used by another process` on Windows
defaults = append(defaults, "cache=shared")
}
parts := defaults
if hasQuery {
parts = append(parts, query)
}
if len(parts) > 0 {
connStr += "?" + strings.Join(parts, "&")
}
db, err := sql.Open("sqlite3", connStr)
if err != nil {
return nil, err
}
return &SqliteStore{Db: db}, nil
}
func (s *SqliteStore) BeginTx(ctx context.Context) (networkmapdb.NetworkMapDBStoreConn, error) {
tx, err := s.Db.BeginTx(ctx, &sql.TxOptions{ReadOnly: true, Isolation: sql.LevelRepeatableRead})
if err != nil {
return nil, err
}
return &SqliteStoreConn{Conn: tx}, nil
}
func (s *SqliteStore) Exec(_ context.Context, query string, args ...any) error {
_, err := s.Db.Exec(query, args...)
return err
}
func (sc *SqliteStoreConn) RollbackTx(ctx context.Context) error {
tx, ok := sc.Conn.(*sql.Tx)
if !ok {
return fmt.Errorf("expected an sql.Tx got %s", reflect.TypeOf(sc.Conn).Kind())
}
return tx.Rollback()
}
func (sc *SqliteStoreConn) CommitTx(ctx context.Context) error {
tx, ok := sc.Conn.(*sql.Tx)
if !ok {
return fmt.Errorf("expected an sql.Tx got %s", reflect.TypeOf(sc.Conn).Kind())
}
return tx.Commit()
}
func (s *SqliteStore) UsingConn() *SqliteStoreConn {
return &SqliteStoreConn{Conn: s.Db}
}
func CollectOneRowForSqlite[T any](rows *sql.Rows) (T, error) {
defer rows.Close()
var r T
if !rows.Next() {
if err := rows.Err(); err != nil {
return r, err
}
return r, ErrNoRows
}
err := rows.Scan(networkmapdb.StructFields(&r)...)
if err != nil {
return r, err
}
return r, nil
}
func CollectRowsForSqlite[T any](rows *sql.Rows) ([]T, error) {
defer rows.Close()
toret := make([]T, 0)
for rows.Next() {
var r T
err := rows.Scan(networkmapdb.StructFields(&r)...)
if err != nil {
return nil, err
}
toret = append(toret, r)
}
if err := rows.Err(); err != nil {
return nil, err
}
return toret, nil
}
@@ -0,0 +1,84 @@
package networkmap_sqlite
import (
"context"
"database/sql"
"encoding/json"
)
const (
GetAllowedUserIdsQuery = `
select id, auto_groups
from users
where account_id=? and not blocked and not is_service_user
`
GetAllGroupIdQuery = `
select id from groups
where account_id=? and name='All'
`
)
func (sc *SqliteStoreConn) GetAllowedUsers(ctx context.Context, accountId string) (map[string]struct{}, map[string][]string, error) {
rows, err := sc.Conn.QueryContext(ctx, GetAllowedUserIdsQuery, accountId)
if err != nil {
return nil, nil, err
}
users, err := CollectRowsForSqlite[user](rows)
if err != nil {
return nil, nil, err
}
rows, err = sc.Conn.QueryContext(ctx, GetAllGroupIdQuery, accountId)
if err != nil {
return nil, nil, err
}
allGroupIds, err := collectAllGroupIds(rows)
if err != nil {
return nil, nil, err
}
userIdIdx := make(map[string]struct{})
groupIdToUserIds := make(map[string][]string)
for _, user := range users {
autogroups := make([]string, 0)
if err := json.Unmarshal(user.AutoGroups, &autogroups); err != nil {
return nil, nil, err
}
userIdIdx[user.ID] = struct{}{}
for _, groupId := range autogroups {
groupIdToUserIds[groupId] = append(groupIdToUserIds[groupId], user.ID)
}
for _, allgid := range allGroupIds {
groupIdToUserIds[allgid] = append(groupIdToUserIds[allgid], user.ID)
}
}
return userIdIdx, groupIdToUserIds, nil
}
func collectAllGroupIds(rows *sql.Rows) ([]string, error) {
defer rows.Close()
var toret []string
for rows.Next() {
var id string
err := rows.Scan(&id)
if err != nil {
return nil, err
}
toret = append(toret, id)
}
if err := rows.Err(); err != nil {
return nil, err
}
return toret, nil
}
type user struct {
ID string
AutoGroups []byte
}