Skip to content

Commit

Permalink
move datanode to internal/node
Browse files Browse the repository at this point in the history
Clues has grown organically into a split between the api that places data in the right spot (clues) and the structure which contains the data (nodes). As we move more behavior into subpackages, and as we look forward to future subpackage additions, it makes the most sense to move the node systems into an internal package so that they can be generically utilized across all clues subpackages without issues like import cycles.

This movement is 80% copy/paste, 19% renaming and reorganization, and 1% interface/import adaptation. No logical changes were intended.

    copy/paste: datanode.go has moved into /internal/node/node.go. otel.go has moved into /internal/node/otel.go.
    reorganization: datanode.go has been broken into multiple files. Discrete concerns (comments, agents, otel) all exist as various files within /node. Generic and basic functionality stays in /node/node.go.
    adaptation: OTELConfig now has a user-facing config builder in /clues/otel.go, while the core otel config struct still lives in /internal/node/otel.go.
  • Loading branch information
ryanfkeepers committed Nov 27, 2024
1 parent f551729 commit 869138e
Show file tree
Hide file tree
Showing 17 changed files with 1,017 additions and 1,034 deletions.
5 changes: 3 additions & 2 deletions clog/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"reflect"

"github.com/alcionai/clues"
"github.com/alcionai/clues/internal/node"
"github.com/alcionai/clues/internal/stringify"
otellog "go.opentelemetry.io/otel/log"
"go.uber.org/zap"
Expand Down Expand Up @@ -86,15 +87,15 @@ func (b builder) log(l logLevel, msg string) {
for k, v := range cv {
zsl = zsl.With(k, v)

attr := clues.NewAttribute(k, v)
attr := node.NewAttribute(k, v)
record.AddAttributes(attr.KV())
}

// plus any values added using builder.With()
for k, v := range b.with {
zsl.With(k, v)

attr := clues.NewAttribute(stringify.Fmt(k)[0], v)
attr := node.NewAttribute(stringify.Fmt(k)[0], v)
record.AddAttributes(attr.KV())
}

Expand Down
4 changes: 2 additions & 2 deletions clog/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ func runErrorLogs(

func TestGetValue(t *testing.T) {
var (
p1 int = 1
ps string = "ptr"
p1 = 1
ps = "ptr"
pn any
)

Expand Down
2 changes: 0 additions & 2 deletions clog/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ import (
// consts
// ---------------------------------------------------

const clogLogFileEnv = "CLOG_LOG_FILE"

type logLevel string

const (
Expand Down
97 changes: 55 additions & 42 deletions clues.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,42 @@ package clues
import (
"context"

"github.com/alcionai/clues/internal/node"
"github.com/alcionai/clues/internal/stringify"
)

// ---------------------------------------------------------------------------
// persistent client initialization
// ---------------------------------------------------------------------------

// Initialize will spin up any persistent clients that are held by clues,
// such as OTEL communication. Clues will use these optimistically in the
// background to provide additional telemetry hook-ins.
// InitializeOTEL will spin up the OTEL clients that are held by clues,
// Clues will eagerly use these clients in the background to provide
// additional telemetry hook-ins.
//
// Clues will operate as expected in the event of an error, or if initialization
// is not called. This is a purely optional step.
func Initialize(
// Clues will operate as expected in the event of an error, or if OTEL is not
// initialized. This is a purely optional step.
func InitializeOTEL(
ctx context.Context,
serviceName string,
config OTELConfig,
) (context.Context, error) {
nc := nodeFromCtx(ctx)
nc := node.FromCtx(ctx)

err := nc.init(ctx, serviceName, config)
err := nc.InitOTEL(ctx, serviceName, config.toInternalConfig())
if err != nil {
return ctx, err
}

return setNodeInCtx(ctx, nc), nil
return node.EmbedInCtx(ctx, nc), nil
}

// Close will flush all buffered data waiting to be read. If Initialize was not
// called, this call is a no-op. Should be called in a defer after initializing.
func Close(ctx context.Context) error {
nc := nodeFromCtx(ctx)
nc := node.FromCtx(ctx)

if nc.otel != nil {
err := nc.otel.close(ctx)
if nc.OTEL != nil {
err := nc.OTEL.Close(ctx)
if err != nil {
return Wrap(err, "closing otel client")
}
Expand All @@ -46,29 +47,40 @@ func Close(ctx context.Context) error {
return nil
}

// ---------------------------------------------------------------------------
// data access
// ---------------------------------------------------------------------------

// In retrieves the clues structured data from the context.
// TODO: turn return an interface instead of a node, have nodes
// and errors both comply with that wrapper.
func In(ctx context.Context) *node.Node {
return node.FromCtx(ctx)
}

// ---------------------------------------------------------------------------
// key-value metadata
// ---------------------------------------------------------------------------

// Add adds all key-value pairs to the clues.
func Add(ctx context.Context, kvs ...any) context.Context {
nc := nodeFromCtx(ctx)
return setNodeInCtx(ctx, nc.addValues(stringify.Normalize(kvs...)))
nc := node.FromCtx(ctx)
return node.EmbedInCtx(ctx, nc.AddValues(stringify.Normalize(kvs...)))
}

// AddMap adds a shallow clone of the map to a namespaced set of clues.
func AddMap[K comparable, V any](
ctx context.Context,
m map[K]V,
) context.Context {
nc := nodeFromCtx(ctx)
nc := node.FromCtx(ctx)

kvs := make([]any, 0, len(m)*2)
for k, v := range m {
kvs = append(kvs, k, v)
}

return setNodeInCtx(ctx, nc.addValues(stringify.Normalize(kvs...)))
return node.EmbedInCtx(ctx, nc.AddValues(stringify.Normalize(kvs...)))
}

// ---------------------------------------------------------------------------
Expand All @@ -82,25 +94,25 @@ func AddMap[K comparable, V any](
// reference is returned mostly as a quality-of-life step
// so that callers don't need to declare the map outside of
// this call.
func InjectTrace[C traceMapCarrierBase](
func InjectTrace[C node.TraceMapCarrierBase](
ctx context.Context,
mapCarrier C,
) C {
nodeFromCtx(ctx).
injectTrace(ctx, asTraceMapCarrier(mapCarrier))
node.FromCtx(ctx).
InjectTrace(ctx, node.AsTraceMapCarrier(mapCarrier))

return mapCarrier
}

// ReceiveTrace extracts the current trace details from the
// headers and adds them to the context. If otel is not
// initialized, no-ops.
func ReceiveTrace[C traceMapCarrierBase](
func ReceiveTrace[C node.TraceMapCarrierBase](
ctx context.Context,
mapCarrier C,
) context.Context {
return nodeFromCtx(ctx).
receiveTrace(ctx, asTraceMapCarrier(mapCarrier))
return node.FromCtx(ctx).
ReceiveTrace(ctx, node.AsTraceMapCarrier(mapCarrier))
}

// AddSpan stacks a clues node onto this context and uses the provided
Expand All @@ -114,27 +126,28 @@ func AddSpan(
name string,
kvs ...any,
) context.Context {
nc := nodeFromCtx(ctx)
nc := node.FromCtx(ctx)

var node *dataNode
var spanned *node.Node

if len(kvs) > 0 {
ctx, node = nc.addSpan(ctx, name)
node.id = name
node = node.addValues(stringify.Normalize(kvs...))
ctx, spanned = nc.AddSpan(ctx, name)
spanned.ID = name
spanned = spanned.AddValues(stringify.Normalize(kvs...))
} else {
ctx, node = nc.addSpan(ctx, name)
node = node.trace(name)
ctx, spanned = nc.AddSpan(ctx, name)
spanned = spanned.AppendToTree(name)
}

return setNodeInCtx(ctx, node)
return node.EmbedInCtx(ctx, spanned)
}

// CloseSpan closes the current span in the clues node. Should only be called
// following a `clues.AddSpan()` call.
func CloseSpan(ctx context.Context) context.Context {
nc := nodeFromCtx(ctx).closeSpan(ctx)
return setNodeInCtx(ctx, nc)
return node.EmbedInCtx(
ctx,
node.FromCtx(ctx).CloseSpan(ctx))
}

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -167,10 +180,10 @@ func AddComment(
msg string,
vs ...any,
) context.Context {
nc := nodeFromCtx(ctx)
nn := nc.addComment(1, msg, vs...)
nc := node.FromCtx(ctx)
nn := nc.AddComment(1, msg, vs...)

return setNodeInCtx(ctx, nn)
return node.EmbedInCtx(ctx, nn)
}

// ---------------------------------------------------------------------------
Expand All @@ -194,27 +207,27 @@ func AddAgent(
ctx context.Context,
name string,
) context.Context {
nc := nodeFromCtx(ctx)
nn := nc.addAgent(name)
nc := node.FromCtx(ctx)
nn := nc.AddAgent(name)

return setNodeInCtx(ctx, nn)
return node.EmbedInCtx(ctx, nn)
}

// Relay adds all key-value pairs to the provided agent. The agent will
// record those values to the dataNode in which it was created. All relayed
// record those values to the node in which it was created. All relayed
// values are namespaced to the owning agent.
func Relay(
ctx context.Context,
agent string,
vs ...any,
) {
nc := nodeFromCtx(ctx)
ag, ok := nc.agents[agent]
nc := node.FromCtx(ctx)
ag, ok := nc.Agents[agent]

if !ok {
return
}

// set values, not add. We don't want agents to own a full clues tree.
ag.data.setValues(stringify.Normalize(vs...))
ag.Data.SetValues(stringify.Normalize(vs...))
}
8 changes: 4 additions & 4 deletions clues_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,9 @@ func TestAddSpan(t *testing.T) {
ctx := context.Background()

if init {
ictx, err := clues.Initialize(ctx, test.name, clues.OTELConfig{
GRPCEndpoint: "localhost:4317",
})
ocfg := clues.OTELConfig{GRPCEndpoint: "localhost:4317"}

ictx, err := clues.InitializeOTEL(ctx, test.name, ocfg)
if err != nil {
t.Error("initializing clues", err)
return
Expand Down Expand Up @@ -326,7 +326,7 @@ func TestImmutableCtx(t *testing.T) {
}

pre = clues.In(testCtx)
if _, ok := preMap["k"]; ok {
if _, ok := pre.Map()["k"]; ok {
t.Errorf("previous map within ctx should not have been mutated by addition")
}

Expand Down
Loading

0 comments on commit 869138e

Please sign in to comment.