Allow users of the library to send tracing headers with the events

This commit is contained in:
Christian Schaible 2024-07-30 14:55:33 +02:00
parent 23f99b0668
commit 92fb9923a7
7 changed files with 250 additions and 56 deletions

View file

@ -60,6 +60,39 @@ type AuditApi interface {
routableIdentifier *RoutableIdentifier,
) error
// LogWithTrace is a convenience method that validates, serializes and sends data over the wire.
// If the transactional outbox patter should be used, the ValidateAndSerialize method
// and Send method can be called manually.
//
// Parameters:
// * ctx - the context object
// * event - the auditV1.AuditEvent
// * visibility - route the event only internally or to the customer (no routing in the legacy solution)
// * routableIdentifier - the identifier of the object
// * traceParent - optional trace parent
// * traceState - optional trace state
//
// It may return one of the following errors:
//
// - ErrUnknownSingularType - if the routableIdentifier type is unknown
// - ErrUnknownPluralType - if the routableIdentifier type is unknown
// - ErrEventNil - if event is nil
// - ErrObjectIdentifierNil - if the object identifier is nil
// - ErrObjectIdentifierVisibilityMismatch - if object identifier and visibility are not in a valid state
// - ErrUnsupportedObjectIdentifierType - if an unsupported object identifier type was provided
// - ErrAttributeIdentifierInvalid - if identifier in a checked attribute and the object identifier do not match
// - ErrAttributeTypeInvalid - if the type from checked attribute and the type from object identifier do not match
// - protovalidate.ValidationError - if schema validation errors have been detected
// - protobuf serialization errors - if the event couldn't be serialized
LogWithTrace(
ctx context.Context,
event *auditV1.AuditLogEntry,
visibility auditV1.Visibility,
routableIdentifier *RoutableIdentifier,
traceParent *string,
traceState *string,
) error
// ValidateAndSerialize validates and serializes the event into a byte representation.
// The result has to be sent explicitly by calling the Send method.
ValidateAndSerialize(
@ -68,6 +101,16 @@ type AuditApi interface {
routableIdentifier *RoutableIdentifier,
) (*CloudEvent, error)
// ValidateAndSerializeWithTrace validates and serializes the event into a byte representation.
// The result has to be sent explicitly by calling the Send method.
ValidateAndSerializeWithTrace(
event *auditV1.AuditLogEntry,
visibility auditV1.Visibility,
routableIdentifier *RoutableIdentifier,
traceParent *string,
traceState *string,
) (*CloudEvent, error)
// Send the serialized content as byte array to the audit log system.
// It may return one of the following errors:
//

View file

@ -79,7 +79,20 @@ func (a *LegacyAuditApi) Log(
routableIdentifier *RoutableIdentifier,
) error {
cloudEvent, err := a.ValidateAndSerialize(event, visibility, routableIdentifier)
return a.LogWithTrace(ctx, event, visibility, routableIdentifier, nil, nil)
}
// LogWithTrace implements AuditApi.LogWithTrace
func (a *LegacyAuditApi) LogWithTrace(
ctx context.Context,
event *auditV1.AuditLogEntry,
visibility auditV1.Visibility,
routableIdentifier *RoutableIdentifier,
traceParent *string,
traceState *string,
) error {
cloudEvent, err := a.ValidateAndSerializeWithTrace(event, visibility, routableIdentifier, traceParent, traceState)
if err != nil {
return err
}
@ -94,6 +107,18 @@ func (a *LegacyAuditApi) ValidateAndSerialize(
visibility auditV1.Visibility,
routableIdentifier *RoutableIdentifier,
) (*CloudEvent, error) {
return a.ValidateAndSerializeWithTrace(event, visibility, routableIdentifier, nil, nil)
}
// ValidateAndSerializeWithTrace implements AuditApi.ValidateAndSerializeWithTrace.
// It serializes the event into the byte representation of the legacy audit log system.
func (a *LegacyAuditApi) ValidateAndSerializeWithTrace(
event *auditV1.AuditLogEntry,
visibility auditV1.Visibility,
routableIdentifier *RoutableIdentifier,
traceParent *string,
traceState *string,
) (*CloudEvent, error) {
routableEvent, err := validateAndSerializePartially(a.validator, event, visibility, routableIdentifier)
if err != nil {
@ -121,11 +146,10 @@ func (a *LegacyAuditApi) ValidateAndSerialize(
dataContentType: ContentTypeCloudEventsProtobuf,
dataType: fmt.Sprintf("%v", routableEvent.ProtoReflect().Descriptor().FullName()),
// TODO check if this is correct
subject: event.ProtoPayload.ResourceName,
data: legacyBytes,
// TODO set trace fields
traceParent: nil,
traceState: nil,
subject: event.ProtoPayload.ResourceName,
data: legacyBytes,
traceParent: traceParent,
traceState: traceState,
}
return &message, nil
}

View file

@ -41,6 +41,8 @@ func TestLegacyAuditApi(t *testing.T) {
assert.NoError(t, err)
topicSubscriptionTopicPattern := "audit-log/>"
traceParent := "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
traceState := "rojo=00f067aa0ba902b7,congo=t61rcWkgMzE"
// Check logging of organization events
t.Run("Log public organization event", func(t *testing.T) {
@ -69,17 +71,19 @@ func TestLegacyAuditApi(t *testing.T) {
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
assert.NoError(t, (*auditApi).Log(
assert.NoError(t, (*auditApi).LogWithTrace(
ctx,
event,
visibility,
NewRoutableIdentifier(objectIdentifier),
&traceParent,
&traceState,
))
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
assert.NoError(t, err)
validateSentMessage(t, topicName, message, event)
validateSentMessage(t, topicName, message, event, &traceParent, &traceState)
})
t.Run("Log private organization event", func(t *testing.T) {
@ -106,17 +110,19 @@ func TestLegacyAuditApi(t *testing.T) {
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
assert.NoError(t, (*auditApi).Log(
assert.NoError(t, (*auditApi).LogWithTrace(
ctx,
event,
visibility,
NewRoutableIdentifier(objectIdentifier),
&traceParent,
&traceState,
))
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
assert.NoError(t, err)
validateSentMessage(t, topicName, message, event)
validateSentMessage(t, topicName, message, event, &traceParent, &traceState)
})
// Check logging of folder events
@ -144,17 +150,19 @@ func TestLegacyAuditApi(t *testing.T) {
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
assert.NoError(t, (*auditApi).Log(
assert.NoError(t, (*auditApi).LogWithTrace(
ctx,
event,
visibility,
NewRoutableIdentifier(objectIdentifier),
&traceParent,
&traceState,
))
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
assert.NoError(t, err)
validateSentMessage(t, topicName, message, event)
validateSentMessage(t, topicName, message, event, &traceParent, &traceState)
})
t.Run("Log private folder event", func(t *testing.T) {
@ -181,17 +189,19 @@ func TestLegacyAuditApi(t *testing.T) {
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
assert.NoError(t, (*auditApi).Log(
assert.NoError(t, (*auditApi).LogWithTrace(
ctx,
event,
visibility,
NewRoutableIdentifier(objectIdentifier),
&traceParent,
&traceState,
))
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
assert.NoError(t, err)
validateSentMessage(t, topicName, message, event)
validateSentMessage(t, topicName, message, event, &traceParent, &traceState)
})
// Check logging of project events
@ -219,17 +229,19 @@ func TestLegacyAuditApi(t *testing.T) {
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
assert.NoError(t, (*auditApi).Log(
assert.NoError(t, (*auditApi).LogWithTrace(
ctx,
event,
visibility,
NewRoutableIdentifier(objectIdentifier),
&traceParent,
&traceState,
))
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
assert.NoError(t, err)
validateSentMessage(t, topicName, message, event)
validateSentMessage(t, topicName, message, event, &traceParent, &traceState)
})
t.Run("Log private project event", func(t *testing.T) {
@ -256,17 +268,19 @@ func TestLegacyAuditApi(t *testing.T) {
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
assert.NoError(t, (*auditApi).Log(
assert.NoError(t, (*auditApi).LogWithTrace(
ctx,
event,
visibility,
NewRoutableIdentifier(objectIdentifier),
&traceParent,
&traceState,
))
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
assert.NoError(t, err)
validateSentMessage(t, topicName, message, event)
validateSentMessage(t, topicName, message, event, &traceParent, &traceState)
})
// Check logging of system events
@ -294,11 +308,13 @@ func TestLegacyAuditApi(t *testing.T) {
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
assert.NoError(t,
(*auditApi).Log(
(*auditApi).LogWithTrace(
ctx,
event,
visibility,
RoutableSystemIdentifier,
nil,
nil,
))
// Receive the event from solace
@ -307,9 +323,8 @@ func TestLegacyAuditApi(t *testing.T) {
// Check topic name
assert.Equal(t, topicName, *message.Properties.To)
// Check topic name
assert.Equal(t, topicName, *message.Properties.To)
assert.Nil(t, message.ApplicationProperties["cloudEvents:traceparent"])
assert.Nil(t, message.ApplicationProperties["cloudEvents:tracestate"])
// Check deserialized message
var auditEvent LegacyAuditEvent
@ -349,23 +364,35 @@ func TestLegacyAuditApi(t *testing.T) {
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
assert.NoError(t, (*auditApi).Log(
assert.NoError(t, (*auditApi).LogWithTrace(
ctx,
event,
visibility,
NewRoutableIdentifier(objectIdentifier),
&traceParent,
&traceState,
))
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
assert.NoError(t, err)
validateSentMessageWithDetails(t, topicName, message, event)
validateSentMessageWithDetails(t, topicName, message, event, &traceParent, &traceState)
})
}
func validateSentMessage(t *testing.T, topicName string, message *amqp.Message, event *auditV1.AuditLogEntry) {
// Check topic name
func validateSentMessage(
t *testing.T,
topicName string,
message *amqp.Message,
event *auditV1.AuditLogEntry,
traceParent *string,
traceState *string,
) {
// Check message properties
assert.Equal(t, topicName, *message.Properties.To)
assert.Equal(t, *traceParent, message.ApplicationProperties["cloudEvents:traceparent"])
assert.Equal(t, *traceState, message.ApplicationProperties["cloudEvents:tracestate"])
// Check deserialized message
var auditEvent LegacyAuditEvent
@ -405,9 +432,19 @@ func validateSentMessage(t *testing.T, topicName string, message *amqp.Message,
assert.Equal(t, event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent, auditEvent.UserAgent)
}
func validateSentMessageWithDetails(t *testing.T, topicName string, message *amqp.Message, event *auditV1.AuditLogEntry) {
func validateSentMessageWithDetails(
t *testing.T,
topicName string,
message *amqp.Message,
event *auditV1.AuditLogEntry,
traceParent *string,
traceState *string,
) {
// Check topic name
assert.Equal(t, topicName, *message.Properties.To)
assert.Equal(t, *traceParent, message.ApplicationProperties["cloudEvents:traceparent"])
assert.Equal(t, *traceState, message.ApplicationProperties["cloudEvents:tracestate"])
// Check deserialized message
var auditEvent LegacyAuditEvent

View file

@ -29,13 +29,27 @@ func NewMockAuditApi() (*AuditApi, error) {
// Log implements AuditApi.Log.
// Validates and serializes the event but doesn't send it.
func (a *MockAuditApi) Log(
_ context.Context,
ctx context.Context,
event *auditV1.AuditLogEntry,
visibility auditV1.Visibility,
routableIdentifier *RoutableIdentifier,
) error {
_, err := a.ValidateAndSerialize(event, visibility, routableIdentifier)
return a.LogWithTrace(ctx, event, visibility, routableIdentifier, nil, nil)
}
// LogWithTrace implements AuditApi.LogWithTrace.
// Validates and serializes the event but doesn't send it.
func (a *MockAuditApi) LogWithTrace(
_ context.Context,
event *auditV1.AuditLogEntry,
visibility auditV1.Visibility,
routableIdentifier *RoutableIdentifier,
traceParent *string,
traceState *string,
) error {
_, err := a.ValidateAndSerializeWithTrace(event, visibility, routableIdentifier, traceParent, traceState)
return err
}
@ -46,6 +60,18 @@ func (a *MockAuditApi) ValidateAndSerialize(
routableIdentifier *RoutableIdentifier,
) (*CloudEvent, error) {
return a.ValidateAndSerializeWithTrace(event, visibility, routableIdentifier, nil, nil)
}
// ValidateAndSerializeWithTrace implements AuditApi.ValidateAndSerializeWithTrace
func (a *MockAuditApi) ValidateAndSerializeWithTrace(
event *auditV1.AuditLogEntry,
visibility auditV1.Visibility,
routableIdentifier *RoutableIdentifier,
traceParent *string,
traceState *string,
) (*CloudEvent, error) {
routableEvent, err := validateAndSerializePartially(a.validator, event, visibility, routableIdentifier)
if err != nil {
return nil, err
@ -67,8 +93,8 @@ func (a *MockAuditApi) ValidateAndSerialize(
// TODO check if this is correct
subject: event.ProtoPayload.ResourceName,
data: routableEventBytes,
traceParent: nil,
traceState: nil,
traceParent: traceParent,
traceState: traceState,
}
return &message, nil

View file

@ -26,8 +26,8 @@ func TestMockAuditApi_Log(t *testing.T) {
t.Run("ValidateAndSerialize", func(t *testing.T) {
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
cloudEvent, err := (*auditApi).ValidateAndSerialize(
event, visibility, routableObjectIdentifier)
cloudEvent, err := (*auditApi).ValidateAndSerializeWithTrace(
event, visibility, routableObjectIdentifier, nil, nil)
assert.NoError(t, err)

View file

@ -109,7 +109,20 @@ func (a *routableAuditApi) Log(
routableIdentifier *RoutableIdentifier,
) error {
cloudEvent, err := a.ValidateAndSerialize(event, visibility, routableIdentifier)
return a.LogWithTrace(ctx, event, visibility, routableIdentifier, nil, nil)
}
// LogWithTrace implements AuditApi.LogWithTrace
func (a *routableAuditApi) LogWithTrace(
ctx context.Context,
event *auditV1.AuditLogEntry,
visibility auditV1.Visibility,
routableIdentifier *RoutableIdentifier,
traceParent *string,
traceState *string,
) error {
cloudEvent, err := a.ValidateAndSerializeWithTrace(event, visibility, routableIdentifier, traceParent, traceState)
if err != nil {
return err
}
@ -124,6 +137,18 @@ func (a *routableAuditApi) ValidateAndSerialize(
routableIdentifier *RoutableIdentifier,
) (*CloudEvent, error) {
return a.ValidateAndSerializeWithTrace(event, visibility, routableIdentifier, nil, nil)
}
// ValidateAndSerializeWithTrace implements AuditApi.ValidateAndSerializeWithTrace
func (a *routableAuditApi) ValidateAndSerializeWithTrace(
event *auditV1.AuditLogEntry,
visibility auditV1.Visibility,
routableIdentifier *RoutableIdentifier,
traceParent *string,
traceState *string,
) (*CloudEvent, error) {
routableEvent, err := validateAndSerializePartially(
a.validator,
event,
@ -148,11 +173,10 @@ func (a *routableAuditApi) ValidateAndSerialize(
dataContentType: ContentTypeCloudEventsProtobuf,
dataType: fmt.Sprintf("%v", routableEvent.ProtoReflect().Descriptor().FullName()),
// TODO check if this is correct
subject: event.ProtoPayload.ResourceName,
data: routableEventBytes,
// TODO set trace fields
traceParent: nil,
traceState: nil,
subject: event.ProtoPayload.ResourceName,
data: routableEventBytes,
traceParent: traceParent,
traceState: traceState,
}
return &message, nil

View file

@ -37,11 +37,15 @@ func TestRoutableAuditApi(t *testing.T) {
validator, err := protovalidate.New()
assert.NoError(t, err)
traceParent := "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
traceState := "rojo=00f067aa0ba902b7,congo=t61rcWkgMzE"
// Instantiate the audit api
organizationTopicPrefix := "org"
projectTopicPrefix := "project"
folderTopicPrefix := "folder"
systemTopicName := "topic://system/admin-events"
auditApi, err := newRoutableAuditApi(
messagingApi,
topicNameConfig{
@ -67,11 +71,13 @@ func TestRoutableAuditApi(t *testing.T) {
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
assert.NoError(t, (*auditApi).Log(
assert.NoError(t, (*auditApi).LogWithTrace(
ctx,
event,
visibility,
NewRoutableIdentifier(objectIdentifier),
&traceParent,
&traceState,
))
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
@ -84,7 +90,9 @@ func TestRoutableAuditApi(t *testing.T) {
objectIdentifier,
event,
"stackit.resourcemanager.v2.organization.created",
visibility)
visibility,
&traceParent,
&traceState)
})
t.Run("Log private organization event", func(t *testing.T) {
@ -104,11 +112,13 @@ func TestRoutableAuditApi(t *testing.T) {
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
assert.NoError(t,
(*auditApi).Log(
(*auditApi).LogWithTrace(
ctx,
event,
visibility,
NewRoutableIdentifier(objectIdentifier),
&traceParent,
&traceState,
))
// Receive the event from solace
@ -122,7 +132,9 @@ func TestRoutableAuditApi(t *testing.T) {
objectIdentifier,
event,
"stackit.resourcemanager.v2.organization.created",
visibility)
visibility,
&traceParent,
&traceState)
})
// Check logging of folder events
@ -139,11 +151,13 @@ func TestRoutableAuditApi(t *testing.T) {
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
assert.NoError(t, (*auditApi).Log(
assert.NoError(t, (*auditApi).LogWithTrace(
ctx,
event,
visibility,
NewRoutableIdentifier(objectIdentifier),
&traceParent,
&traceState,
))
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
@ -156,7 +170,9 @@ func TestRoutableAuditApi(t *testing.T) {
objectIdentifier,
event,
"stackit.resourcemanager.v2.folder.created",
visibility)
visibility,
&traceParent,
&traceState)
})
t.Run("Log private folder event", func(t *testing.T) {
@ -174,11 +190,13 @@ func TestRoutableAuditApi(t *testing.T) {
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
assert.NoError(t,
(*auditApi).Log(
(*auditApi).LogWithTrace(
ctx,
event,
visibility,
NewRoutableIdentifier(objectIdentifier),
&traceParent,
&traceState,
))
// Receive the event from solace
@ -192,7 +210,9 @@ func TestRoutableAuditApi(t *testing.T) {
objectIdentifier,
event,
"stackit.resourcemanager.v2.folder.created",
visibility)
visibility,
&traceParent,
&traceState)
})
// Check logging of project events
@ -209,11 +229,13 @@ func TestRoutableAuditApi(t *testing.T) {
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
assert.NoError(t,
(*auditApi).Log(
(*auditApi).LogWithTrace(
ctx,
event,
visibility,
NewRoutableIdentifier(objectIdentifier),
&traceParent,
&traceState,
))
// Receive the event from solace
@ -227,7 +249,9 @@ func TestRoutableAuditApi(t *testing.T) {
objectIdentifier,
event,
"stackit.resourcemanager.v2.project.created",
visibility)
visibility,
&traceParent,
&traceState)
})
t.Run("Log private project event", func(t *testing.T) {
@ -243,11 +267,13 @@ func TestRoutableAuditApi(t *testing.T) {
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
assert.NoError(t,
(*auditApi).Log(
(*auditApi).LogWithTrace(
ctx,
event,
visibility,
NewRoutableIdentifier(objectIdentifier),
&traceParent,
&traceState,
))
// Receive the event from solace
@ -261,7 +287,9 @@ func TestRoutableAuditApi(t *testing.T) {
objectIdentifier,
event,
"stackit.resourcemanager.v2.project.created",
visibility)
visibility,
&traceParent,
&traceState)
})
// Check logging of system events
@ -278,11 +306,13 @@ func TestRoutableAuditApi(t *testing.T) {
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
assert.NoError(t,
(*auditApi).Log(
(*auditApi).LogWithTrace(
ctx,
event,
visibility,
RoutableSystemIdentifier,
nil,
nil,
))
// Receive the event from solace
@ -301,6 +331,8 @@ func TestRoutableAuditApi(t *testing.T) {
assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().UnixMilli(), applicationProperties["cloudEvents:time"])
assert.Equal(t, "application/cloudevents+protobuf", applicationProperties["cloudEvents:datacontenttype"])
assert.Equal(t, "audit.v1.RoutableAuditEvent", applicationProperties["cloudEvents:type"])
assert.Nil(t, applicationProperties["cloudEvents:traceparent"])
assert.Nil(t, applicationProperties["cloudEvents:tracestate"])
// Check deserialized message
validateRoutableEventPayload(
@ -326,11 +358,13 @@ func TestRoutableAuditApi(t *testing.T) {
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
assert.NoError(t, (*auditApi).Log(
assert.NoError(t, (*auditApi).LogWithTrace(
ctx,
event,
visibility,
NewRoutableIdentifier(objectIdentifier),
&traceParent,
&traceState,
))
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
@ -343,7 +377,9 @@ func TestRoutableAuditApi(t *testing.T) {
objectIdentifier,
event,
"stackit.resourcemanager.v2.organization.created",
visibility)
visibility,
&traceParent,
&traceState)
})
}
@ -355,6 +391,8 @@ func validateSentEvent(
event *auditV1.AuditLogEntry,
eventName string,
visibility auditV1.Visibility,
traceParent *string,
traceState *string,
) {
// Check topic name
@ -369,8 +407,10 @@ func validateSentEvent(
_, isUuid := uuid.Parse(fmt.Sprintf("%s", applicationProperties["cloudEvents:id"]))
assert.True(t, true, isUuid)
assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().UnixMilli(), applicationProperties["cloudEvents:time"])
assert.Equal(t, "application/cloudevents+protobuf", applicationProperties["cloudEvents:datacontenttype"])
assert.Equal(t, ContentTypeCloudEventsProtobuf, applicationProperties["cloudEvents:datacontenttype"])
assert.Equal(t, "audit.v1.RoutableAuditEvent", applicationProperties["cloudEvents:type"])
assert.Equal(t, *traceParent, applicationProperties["cloudEvents:traceparent"])
assert.Equal(t, *traceState, applicationProperties["cloudEvents:tracestate"])
// Check deserialized message
validateRoutableEventPayload(
@ -443,7 +483,7 @@ func TestRoutableAuditApi_Log_ValidationFailed(t *testing.T) {
auditApi := routableAuditApi{validator: &protobufValidator}
event := NewSystemAuditEvent(nil)
err := auditApi.Log(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier)
err := auditApi.LogWithTrace(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier, nil, nil)
assert.ErrorIs(t, err, expectedError)
}