Files
tutti/packages/agent/store-sqlite/activity_replication_conformance_test.go
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

525 lines
22 KiB
Go

package storesqlite_test
import (
"context"
"database/sql"
"encoding/json"
"errors"
"fmt"
"path/filepath"
"testing"
activityreplication "github.com/tutti-os/tutti/packages/agent/activity-replication"
"github.com/tutti-os/tutti/packages/agent/activity-replication/conformance"
storesqlite "github.com/tutti-os/tutti/packages/agent/store-sqlite"
_ "modernc.org/sqlite"
)
type projectionDescriptor struct {
workspaceID string
entityType activityreplication.EntityType
key activityreplication.EntityKey
targetScope *activityreplication.TargetScope
sessionScope *activityreplication.SessionScope
}
type sqliteProjectionBuilder struct {
db *sql.DB
store *storesqlite.Store
descriptors []projectionDescriptor
}
func TestSQLiteCanonicalProjectionConformance(t *testing.T) {
t.Parallel()
for _, fixture := range conformance.ProjectionFixtures() {
fixture := fixture
t.Run(fixture.Name, func(t *testing.T) {
t.Parallel()
builder := newSQLiteProjectionBuilder(t)
if err := conformance.RunProjection(context.Background(), builder, fixture); err != nil {
t.Fatal(err)
}
})
}
}
func newSQLiteProjectionBuilder(t *testing.T) *sqliteProjectionBuilder {
t.Helper()
db, err := sql.Open("sqlite", filepath.Join(t.TempDir(), "activity.db"))
if err != nil {
t.Fatalf("open SQLite: %v", err)
}
t.Cleanup(func() { _ = db.Close() })
db.SetMaxOpenConns(1)
if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil {
t.Fatalf("enable foreign keys: %v", err)
}
store := storesqlite.New(db, storesqlite.Options{})
if err := store.Migrate(context.Background()); err != nil {
t.Fatalf("Migrate() error = %v", err)
}
return &sqliteProjectionBuilder{db: db, store: store}
}
func (b *sqliteProjectionBuilder) Reset(ctx context.Context) error {
for _, table := range []string{
"workspace_agent_messages", "workspace_agent_interactions", "workspace_agent_turns",
"workspace_agent_sessions", "agent_targets",
} {
if _, err := b.db.ExecContext(ctx, "DELETE FROM "+table); err != nil {
return fmt.Errorf("clear %s: %w", table, err)
}
}
b.descriptors = nil
return nil
}
func (b *sqliteProjectionBuilder) Seed(ctx context.Context, seeds []conformance.CanonicalSeed) error {
for _, seed := range seeds {
mutation := seedWireMutation(seed)
if err := activityreplication.ValidateMutation(mutation); err != nil {
return err
}
if mutation.Operation != activityreplication.OperationUpsert {
return errors.New("SQLite projection fixture seeds must be upserts")
}
if err := b.seedMutation(ctx, mutation); err != nil {
return fmt.Errorf("seed %s: %w", mutation.EntityType, err)
}
b.descriptors = append(b.descriptors, projectionDescriptor{
workspaceID: mutation.WorkspaceID, entityType: mutation.EntityType, key: mutation.Key,
targetScope: cloneTargetScope(mutation.TargetScope), sessionScope: cloneSessionScope(mutation.SessionScope),
})
}
return nil
}
func seedWireMutation(seed conformance.CanonicalSeed) activityreplication.Mutation {
sourceDevice := ""
if seed.TargetScope != nil {
sourceDevice = seed.TargetScope.OwnerDeviceID
} else if seed.SessionScope != nil {
sourceDevice = seed.SessionScope.SourceDeviceID
}
return activityreplication.Mutation{
SchemaVersion: activityreplication.SchemaVersion, MutationID: "canonical-seed", TransactionID: "canonical-seed",
SourceDeviceID: sourceDevice, WorkspaceID: seed.WorkspaceID, EntityType: seed.EntityType,
Operation: activityreplication.OperationUpsert, Key: seed.Key,
Target: seed.Target, TargetScope: seed.TargetScope, Session: seed.Session, SessionScope: seed.SessionScope,
Turn: seed.Turn, Interaction: seed.Interaction, Message: seed.Message,
}
}
func (b *sqliteProjectionBuilder) Build(ctx context.Context) (activityreplication.ChangeBatch, error) {
mutations := make([]activityreplication.Mutation, 0, len(b.descriptors))
for index, descriptor := range b.descriptors {
mutation, found, err := b.buildMutation(ctx, descriptor)
if err != nil {
return activityreplication.ChangeBatch{}, err
}
if !found {
return activityreplication.ChangeBatch{}, fmt.Errorf("canonical %s snapshot is missing", descriptor.entityType)
}
mutation.SchemaVersion = activityreplication.SchemaVersion
mutation.MutationID = fmt.Sprintf("sqlite-projection-%d", index+1)
mutation.TransactionID = "sqlite-projection"
mutations = append(mutations, mutation)
}
return activityreplication.ChangeBatch{
SchemaVersion: activityreplication.SchemaVersion,
ProjectionEpoch: activityreplication.ProjectionEpoch,
Mutations: mutations,
}, nil
}
func (b *sqliteProjectionBuilder) seedMutation(ctx context.Context, mutation activityreplication.Mutation) error {
switch mutation.EntityType {
case activityreplication.EntityTarget:
target := mutation.Target
if _, err := b.store.PutAgentTarget(ctx, storesqlite.Target{
ID: target.ID, Provider: target.Provider, LaunchRefJSON: string(target.LaunchRef), Name: target.Name,
IconKey: dereference(target.IconKey), IconURL: target.IconURL, MaskIconURL: target.MaskIconURL, HeroImageURL: target.HeroImageURL,
Enabled: target.Enabled, Source: target.Source, SortOrder: int(target.SortOrder), CreatedAtUnixMS: target.CreatedAtUnixMS,
}); err != nil {
return err
}
_, err := b.db.ExecContext(ctx, `UPDATE agent_targets SET created_at_ms=?,updated_at_ms=? WHERE id=?`,
target.CreatedAtUnixMS, target.UpdatedAtUnixMS, target.ID)
return err
case activityreplication.EntitySession:
session := mutation.Session
settings, err := decodeObject(session.Settings)
if err != nil {
return err
}
runtimeContext, err := decodeObject(session.InternalRuntimeContext)
if err != nil {
return err
}
_, capabilities, err := storesqlite.DecodeSessionMetadataJSON(session.SessionMetadata)
if err != nil {
return err
}
result, err := b.store.ReportSessionState(ctx, storesqlite.SessionStateReport{
WorkspaceID: session.WorkspaceID, AgentSessionID: session.AgentSessionID, Kind: session.Kind,
RootAgentSessionID: dereference(session.RootAgentSessionID), RootTurnID: dereference(session.RootTurnID),
ParentAgentSessionID: dereference(session.ParentAgentSessionID), ParentTurnID: dereference(session.ParentTurnID),
ParentToolCallID: dereference(session.ParentToolCallID), Origin: session.Origin, UserID: session.UserID,
AgentTargetID: dereference(session.AgentTargetID), Provider: session.Provider, ProviderSessionID: session.ProviderSessionID,
Model: session.Model, Settings: settings, Capabilities: capabilities, RuntimeContext: runtimeContext, Cwd: session.CWD, Title: session.Title,
OccurredAtUnixMS: session.LastEventAtUnixMS, StartedAtUnixMS: session.StartedAtUnixMS,
EndedAtUnixMS: session.EndedAtUnixMS, CreatedAtUnixMS: session.CreatedAtUnixMS,
})
if err != nil {
return err
}
if !result.Accepted {
return errors.New("SQLite canonical store rejected session seed")
}
_, err = b.db.ExecContext(ctx, `UPDATE workspace_agent_sessions SET rail_section_kind=?,rail_project_path=?,rail_section_key=?,message_version=0,last_event_at_unix_ms=?,pinned_at_unix_ms=?,deleted_at_unix_ms=?,created_at_unix_ms=?,updated_at_unix_ms=?,active_turn_id=NULL WHERE workspace_id=? AND agent_session_id=?`,
session.RailSectionKind, session.RailProjectPath, session.RailSectionKey, session.LastEventAtUnixMS,
session.PinnedAtUnixMS, session.DeletedAtUnixMS, session.CreatedAtUnixMS, session.UpdatedAtUnixMS,
session.WorkspaceID, session.AgentSessionID)
return err
case activityreplication.EntityTurn:
turn := mutation.Turn
_, accepted, err := b.store.RecordTurnTransition(ctx, storesqlite.TurnTransition{
WorkspaceID: turn.WorkspaceID, AgentSessionID: turn.AgentSessionID, TurnID: turn.TurnID,
Phase: turn.Phase, Outcome: dereference(turn.Outcome), Origin: turn.Origin,
SourceGoalOperationID: dereference(turn.SourceGoalOperationID), SourceGoalRevision: dereferenceInt64(turn.SourceGoalRevision),
SourceGoalRepairEpoch: dereferenceInt64(turn.SourceGoalRepairEpoch), StartedAtUnixMS: turn.StartedAtUnixMS,
SettledAtUnixMS: dereferenceInt64(turn.SettledAtUnixMS), OccurredAtUnixMS: turn.UpdatedAtUnixMS,
})
if err != nil {
return err
}
if !accepted {
return errors.New("SQLite canonical store rejected turn seed")
}
_, err = b.db.ExecContext(ctx, `UPDATE workspace_agent_turns SET backfilled=?,created_at_unix_ms=?,updated_at_unix_ms=? WHERE workspace_id=? AND agent_session_id=? AND turn_id=?`,
turn.Backfilled, turn.CreatedAtUnixMS, turn.UpdatedAtUnixMS, turn.WorkspaceID, turn.AgentSessionID, turn.TurnID)
if err != nil {
return err
}
activeTurnID := any(turn.TurnID)
if turn.Phase == storesqlite.TurnPhaseSettled {
activeTurnID = nil
}
_, err = b.db.ExecContext(ctx, `UPDATE workspace_agent_sessions SET active_turn_id=? WHERE workspace_id=? AND agent_session_id=?`,
activeTurnID, turn.WorkspaceID, turn.AgentSessionID)
return err
case activityreplication.EntityInteraction:
interaction := mutation.Interaction
input, err := decodeObject(interaction.Input)
if err != nil {
return err
}
output, err := decodeObject(interaction.Output)
if err != nil {
return err
}
metadata, err := decodeObject(interaction.Metadata)
if err != nil {
return err
}
_, result, err := b.store.UpsertInteraction(ctx, storesqlite.InteractionUpsert{
WorkspaceID: interaction.WorkspaceID, AgentSessionID: interaction.AgentSessionID, RequestID: interaction.RequestID,
TurnID: interaction.TurnID, Kind: interaction.Kind, Status: interaction.Status, ToolName: interaction.ToolName,
Input: input, Output: output, Metadata: metadata, OccurredAtUnixMS: interaction.UpdatedAtUnixMS,
})
if err != nil {
return err
}
if result == storesqlite.InteractionTransitionConflict {
return errors.New("SQLite canonical store rejected interaction seed")
}
_, err = b.db.ExecContext(ctx, `UPDATE workspace_agent_interactions SET created_at_unix_ms=?,updated_at_unix_ms=? WHERE workspace_id=? AND agent_session_id=? AND turn_id=? AND request_id=?`,
interaction.CreatedAtUnixMS, interaction.UpdatedAtUnixMS, interaction.WorkspaceID, interaction.AgentSessionID,
interaction.TurnID, interaction.RequestID)
return err
case activityreplication.EntityMessage:
message := mutation.Message
payload, err := decodeObject(message.Payload)
if err != nil {
return err
}
result, err := b.store.ReportSessionMessages(ctx, storesqlite.SessionMessageReport{
WorkspaceID: message.WorkspaceID, AgentSessionID: message.AgentSessionID,
Messages: []storesqlite.MessageUpdate{{
MessageID: message.MessageID, TurnID: dereference(message.TurnID), Role: message.Role, Kind: message.Kind,
Status: message.Status, Payload: payload, OccurredAtUnixMS: message.OccurredAtUnixMS,
StartedAtUnixMS: message.StartedAtUnixMS, CompletedAtUnixMS: message.CompletedAtUnixMS,
}},
})
if err != nil {
return err
}
if result.AcceptedCount != 1 {
return fmt.Errorf("SQLite canonical store accepted %d messages, want 1", result.AcceptedCount)
}
_, err = b.db.ExecContext(ctx, `UPDATE workspace_agent_messages SET id=?,version=?,semantics_json=?,deleted_at_unix_ms=?,created_at_unix_ms=?,updated_at_unix_ms=? WHERE workspace_id=? AND agent_session_id=? AND message_id=?`,
message.ID, message.Version, string(message.Semantics), message.DeletedAtUnixMS, message.CreatedAtUnixMS,
message.UpdatedAtUnixMS, message.WorkspaceID, message.AgentSessionID, message.MessageID)
if err != nil {
return err
}
_, err = b.db.ExecContext(ctx, `UPDATE workspace_agent_sessions SET message_version=?,last_event_at_unix_ms=?,updated_at_unix_ms=? WHERE workspace_id=? AND agent_session_id=?`,
message.Version, message.UpdatedAtUnixMS, message.UpdatedAtUnixMS, message.WorkspaceID, message.AgentSessionID)
return err
default:
return fmt.Errorf("unsupported SQLite projection seed entity %q", mutation.EntityType)
}
}
func (b *sqliteProjectionBuilder) buildMutation(ctx context.Context, descriptor projectionDescriptor) (activityreplication.Mutation, bool, error) {
mutation := activityreplication.Mutation{
WorkspaceID: descriptor.workspaceID, SourceDeviceID: sourceDeviceID(descriptor), EntityType: descriptor.entityType,
Operation: activityreplication.OperationUpsert, Key: descriptor.key,
TargetScope: cloneTargetScope(descriptor.targetScope), SessionScope: cloneSessionScope(descriptor.sessionScope),
}
switch descriptor.entityType {
case activityreplication.EntityTarget:
stored, err := b.store.GetAgentTarget(ctx, descriptor.key.AgentTargetID)
if errors.Is(err, storesqlite.ErrAgentTargetNotFound) {
return activityreplication.Mutation{}, false, nil
}
if err != nil {
return activityreplication.Mutation{}, false, err
}
mutation.Target = &activityreplication.Target{
ID: stored.ID, Provider: stored.Provider, LaunchRef: json.RawMessage(stored.LaunchRefJSON), Name: stored.Name,
IconKey: nullable(stored.IconKey), IconURL: stored.IconURL, MaskIconURL: stored.MaskIconURL, HeroImageURL: stored.HeroImageURL,
Enabled: stored.Enabled, Source: stored.Source, SortOrder: int64(stored.SortOrder),
CreatedAtUnixMS: stored.CreatedAtUnixMS, UpdatedAtUnixMS: stored.UpdatedAtUnixMS,
}
case activityreplication.EntitySession:
stored, found, err := b.store.GetSession(ctx, descriptor.workspaceID, descriptor.key.AgentSessionID)
if err != nil || !found {
return activityreplication.Mutation{}, found, err
}
metadata, err := storesqlite.EncodeSessionMetadataJSON(stored.Metadata, stored.Capabilities)
if err != nil {
return activityreplication.Mutation{}, false, err
}
settings, err := marshalObject(stored.Settings)
if err != nil {
return activityreplication.Mutation{}, false, err
}
runtimeContext, err := marshalObject(stored.InternalRuntimeContext)
if err != nil {
return activityreplication.Mutation{}, false, err
}
var railKind, railProjectPath string
var deletedAt int64
if err := b.db.QueryRowContext(ctx, `SELECT rail_section_kind,rail_project_path,deleted_at_unix_ms FROM workspace_agent_sessions WHERE workspace_id=? AND agent_session_id=?`,
descriptor.workspaceID, descriptor.key.AgentSessionID).Scan(&railKind, &railProjectPath, &deletedAt); err != nil {
return activityreplication.Mutation{}, false, err
}
mutation.Session = &activityreplication.Session{
WorkspaceID: stored.WorkspaceID, AgentSessionID: stored.ID, Kind: stored.Kind,
RootAgentSessionID: nullable(stored.RootAgentSessionID), RootTurnID: nullable(stored.RootTurnID),
ParentAgentSessionID: nullable(stored.ParentAgentSessionID), ParentTurnID: nullable(stored.ParentTurnID),
ParentToolCallID: nullable(stored.ParentToolCallID), Origin: stored.Origin, UserID: stored.UserID,
AgentTargetID: nullable(stored.AgentTargetID), Provider: stored.Provider, ProviderSessionID: stored.ProviderSessionID,
Model: stored.Model, Settings: settings, SessionMetadata: metadata, InternalRuntimeContext: runtimeContext,
CWD: stored.Cwd, RailSectionKind: railKind, RailProjectPath: railProjectPath, RailSectionKey: stored.RailSectionKey,
Title: stored.Title, MessageVersion: stored.MessageVersion, LastEventAtUnixMS: stored.LastEventUnixMS,
StartedAtUnixMS: stored.StartedAtUnixMS, EndedAtUnixMS: stored.EndedAtUnixMS, PinnedAtUnixMS: stored.PinnedAtUnixMS,
DeletedAtUnixMS: deletedAt, CreatedAtUnixMS: stored.CreatedAtUnixMS, UpdatedAtUnixMS: stored.UpdatedAtUnixMS,
ActiveTurnID: nullable(stored.ActiveTurnID),
}
case activityreplication.EntityTurn:
stored, found, err := b.store.GetTurn(ctx, descriptor.workspaceID, descriptor.key.AgentSessionID, descriptor.key.TurnID)
if err != nil || !found {
return activityreplication.Mutation{}, found, err
}
mutation.Turn = turnSnapshot(stored)
case activityreplication.EntityInteraction:
items, err := b.store.ListSessionInteractions(ctx, storesqlite.ListSessionInteractionsInput{
WorkspaceID: descriptor.workspaceID, AgentSessionID: descriptor.key.AgentSessionID,
})
if err != nil {
return activityreplication.Mutation{}, false, err
}
for _, stored := range items {
if stored.TurnID == descriptor.key.TurnID && stored.RequestID == descriptor.key.RequestID {
mutation.Interaction, err = interactionSnapshot(stored)
return mutation, err == nil, err
}
}
return activityreplication.Mutation{}, false, nil
case activityreplication.EntityMessage:
page, found, err := b.store.ListSessionMessages(ctx, storesqlite.ListSessionMessagesInput{
WorkspaceID: descriptor.workspaceID, AgentSessionID: descriptor.key.AgentSessionID, Limit: 100,
})
if err != nil || !found {
return activityreplication.Mutation{}, found, err
}
for _, stored := range page.Messages {
if stored.MessageID == descriptor.key.MessageID {
mutation.Message, err = messageSnapshot(descriptor.workspaceID, stored)
return mutation, err == nil, err
}
}
return activityreplication.Mutation{}, false, nil
default:
return activityreplication.Mutation{}, false, fmt.Errorf("unsupported SQLite projection entity %q", descriptor.entityType)
}
return mutation, true, nil
}
func turnSnapshot(stored storesqlite.Turn) *activityreplication.Turn {
return &activityreplication.Turn{
WorkspaceID: stored.WorkspaceID, AgentSessionID: stored.AgentSessionID, TurnID: stored.TurnID,
IdentityAnchorTurnID: nullable(stored.IdentityAnchorTurnID),
Phase: stored.Phase, Outcome: nullable(stored.Outcome), Error: structuredResult(stored.ErrorMessage, stored.ErrorCode),
FileChanges: rawObject(stored.FileChanges), CompletedCommand: structuredResult(stored.CompletedCommandKind, stored.CompletedCommandStatus),
Backfilled: stored.Backfilled, Origin: stored.Origin, SourceGoalOperationID: nullable(stored.SourceGoalOperationID),
SourceGoalRevision: nullableInt64(stored.SourceGoalOperationID, stored.SourceGoalRevision),
SourceGoalRepairEpoch: nullableInt64(stored.SourceGoalOperationID, stored.SourceGoalRepairEpoch),
StartedAtUnixMS: stored.StartedAtUnixMS, SettledAtUnixMS: nullableInt64(stored.Outcome, stored.SettledAtUnixMS),
CreatedAtUnixMS: stored.CreatedAtUnixMS, UpdatedAtUnixMS: stored.UpdatedAtUnixMS,
RootProviderTurnID: nullable(stored.RootProviderTurnID),
ProviderTurnBindingJSON: rawJSONOrEmptyObject(stored.ProviderTurnBindingJSON),
RootProviderTurnPhase: nullable(stored.RootProviderTurnPhase),
RootProviderTurnOutcome: nullable(stored.RootProviderTurnOutcome),
RootProviderTurnError: structuredResult(stored.RootProviderTurnErrorMessage, stored.RootProviderTurnErrorCode),
RootProviderTurnCompletedCommand: structuredResult(stored.RootProviderTurnCompletedCommandKind, stored.RootProviderTurnCompletedCommandStatus),
RootProviderTurnUpdatedAtUnixMS: stored.RootProviderTurnUpdatedAtUnixMS,
}
}
func interactionSnapshot(stored storesqlite.Interaction) (*activityreplication.Interaction, error) {
input, err := marshalObject(stored.Input)
if err != nil {
return nil, err
}
output, err := marshalObject(stored.Output)
if err != nil {
return nil, err
}
metadata, err := marshalObject(stored.Metadata)
if err != nil {
return nil, err
}
return &activityreplication.Interaction{
WorkspaceID: stored.WorkspaceID, AgentSessionID: stored.AgentSessionID, RequestID: stored.RequestID,
TurnID: stored.TurnID, Kind: stored.Kind, Status: stored.Status, ToolName: stored.ToolName,
Input: input, Output: output, Metadata: metadata, CreatedAtUnixMS: stored.CreatedAtUnixMS, UpdatedAtUnixMS: stored.UpdatedAtUnixMS,
}, nil
}
func messageSnapshot(workspaceID string, stored storesqlite.Message) (*activityreplication.Message, error) {
semantics, err := json.Marshal(stored.Semantics)
if err != nil {
return nil, err
}
payload, err := marshalObject(stored.Payload)
if err != nil {
return nil, err
}
return &activityreplication.Message{
ID: stored.ID, WorkspaceID: workspaceID, AgentSessionID: stored.AgentSessionID, MessageID: stored.MessageID,
Version: stored.Version, TurnID: nullable(stored.TurnID), Role: stored.Role, Kind: stored.Kind, Status: stored.Status,
Semantics: semantics, Payload: payload, OccurredAtUnixMS: stored.OccurredAtUnixMS,
StartedAtUnixMS: stored.StartedAtUnixMS, CompletedAtUnixMS: stored.CompletedAtUnixMS,
CreatedAtUnixMS: stored.CreatedAtUnixMS, UpdatedAtUnixMS: stored.UpdatedAtUnixMS,
}, nil
}
func sourceDeviceID(descriptor projectionDescriptor) string {
if descriptor.targetScope != nil {
return descriptor.targetScope.OwnerDeviceID
}
if descriptor.sessionScope != nil {
return descriptor.sessionScope.SourceDeviceID
}
return ""
}
func decodeObject(raw json.RawMessage) (map[string]any, error) {
if len(raw) == 0 || string(raw) == "null" {
return nil, nil
}
var value map[string]any
if err := json.Unmarshal(raw, &value); err != nil {
return nil, err
}
return value, nil
}
func marshalObject(value map[string]any) (json.RawMessage, error) {
if value == nil {
return json.RawMessage(`{}`), nil
}
return json.Marshal(value)
}
func rawObject(value map[string]any) json.RawMessage {
if len(value) == 0 {
return nil
}
raw, _ := json.Marshal(value)
return raw
}
func rawJSONOrEmptyObject(value json.RawMessage) json.RawMessage {
if len(value) == 0 {
return json.RawMessage(`{}`)
}
return append(json.RawMessage(nil), value...)
}
func structuredResult(first, second string) json.RawMessage {
if first == "" && second == "" {
return nil
}
raw, _ := json.Marshal(map[string]string{"kind": first, "status": second})
return raw
}
func dereference(value *string) string {
if value == nil {
return ""
}
return *value
}
func nullable(value string) *string {
if value == "" {
return nil
}
return &value
}
func dereferenceInt64(value *int64) int64 {
if value == nil {
return 0
}
return *value
}
func nullableInt64(presence string, value int64) *int64 {
if presence == "" {
return nil
}
return &value
}
func cloneTargetScope(scope *activityreplication.TargetScope) *activityreplication.TargetScope {
if scope == nil {
return nil
}
copy := *scope
return &copy
}
func cloneSessionScope(scope *activityreplication.SessionScope) *activityreplication.SessionScope {
if scope == nil {
return nil
}
copy := *scope
return &copy
}