1package gocb
2
3import (
4	"encoding/json"
5	"time"
6
7	"github.com/google/uuid"
8)
9
10// DiagConnState represents the state of a connection in a diagnostics report.
11type DiagConnState int
12
13const (
14	// DiagStateOk indicates that the connection state is ok.
15	DiagStateOk = DiagConnState(0)
16
17	// DiagStateDisconnected indicates that the connection is disconnected.
18	DiagStateDisconnected = DiagConnState(1)
19)
20
21func diagStateString(state DiagConnState) string {
22	switch state {
23	case DiagStateOk:
24		return "ok"
25	case DiagStateDisconnected:
26		return "disconnected"
27	}
28	return "?"
29}
30
31// DiagnosticEntry represents a single entry in a diagnostics report.
32type DiagnosticEntry struct {
33	Service      ServiceType
34	State        DiagConnState
35	LocalAddr    string
36	RemoteAddr   string
37	LastActivity time.Time
38}
39
40// DiagnosticReport encapsulates the results of a Diagnostics operation.
41type DiagnosticReport struct {
42	ConfigRev int64
43	Services  []DiagnosticEntry
44}
45
46type jsonDiagnosticEntry struct {
47	State          string `json:"state"`
48	Remote         string `json:"remote"`
49	Local          string `json:"local"`
50	LastActivityUs uint64 `json:"last_activity_us"`
51}
52
53type jsonDiagnosticReport struct {
54	Version   int                              `json:"version"`
55	Id        string                           `json:"id"`
56	ConfigRev int                              `json:"config_rev"`
57	Sdk       string                           `json:"sdk"`
58	Services  map[string][]jsonDiagnosticEntry `json:"services"`
59}
60
61// MarshalJSON generates a JSON representation of this diagnostics report.
62func (report *DiagnosticReport) MarshalJSON() ([]byte, error) {
63	jsonReport := jsonDiagnosticReport{
64		Version:  1,
65		Id:       uuid.New().String(),
66		Services: make(map[string][]jsonDiagnosticEntry),
67	}
68
69	for _, service := range report.Services {
70		serviceStr := diagServiceString(service.Service)
71		stateStr := diagStateString(service.State)
72
73		jsonReport.Services[serviceStr] = append(jsonReport.Services[serviceStr], jsonDiagnosticEntry{
74			State:          stateStr,
75			Remote:         service.RemoteAddr,
76			Local:          service.LocalAddr,
77			LastActivityUs: uint64(time.Now().Sub(service.LastActivity).Nanoseconds()),
78		})
79	}
80
81	return json.Marshal(&jsonReport)
82}
83
84// Diagnostics returns information about the internal state of the SDK.
85//
86// Experimental: This API is subject to change at any time.
87func (bucket *Bucket) Diagnostics() (*DiagnosticReport, error) {
88	agentReport, err := bucket.client.Diagnostics()
89	if err != nil {
90		return nil, err
91	}
92
93	report := &DiagnosticReport{
94		ConfigRev: agentReport.ConfigRev,
95	}
96	for _, conn := range agentReport.MemdConns {
97		state := DiagStateDisconnected
98		if conn.LocalAddr != "" {
99			state = DiagStateOk
100		}
101
102		report.Services = append(report.Services, DiagnosticEntry{
103			Service:      MemdService,
104			State:        state,
105			LocalAddr:    conn.LocalAddr,
106			RemoteAddr:   conn.RemoteAddr,
107			LastActivity: conn.LastActivity,
108		})
109	}
110
111	return report, nil
112}
113