...

Source file src/github.com/sigstore/rekor/pkg/events/events.go

Documentation: github.com/sigstore/rekor/pkg/events

     1  // Copyright 2023 The Sigstore Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package events
    16  package events
    17  
    18  import (
    19  	"encoding/json"
    20  	"errors"
    21  	"fmt"
    22  	"time"
    23  
    24  	"google.golang.org/protobuf/encoding/protojson"
    25  	"google.golang.org/protobuf/proto"
    26  	"google.golang.org/protobuf/reflect/protoreflect"
    27  	"google.golang.org/protobuf/types/known/anypb"
    28  	"google.golang.org/protobuf/types/known/timestamppb"
    29  
    30  	pb "github.com/sigstore/protobuf-specs/gen/pb-go/events/v1"
    31  )
    32  
    33  // The content type of an event.
    34  type EventContentType string
    35  
    36  const (
    37  	ContentTypeProtobuf EventContentType = "application/protobuf"
    38  	ContentTypeJSON     EventContentType = "application/json"
    39  )
    40  
    41  // Keys that cannot be used in the optional event attributes map due to
    42  // collisions with mandatory fields in the CloudEvents spec.
    43  var reservedFields = map[string]struct{}{
    44  	"datacontenttype": {},
    45  	"data":            {},
    46  	"id":              {},
    47  	"source":          {},
    48  	"specversion":     {},
    49  	"type":            {},
    50  }
    51  
    52  // Event is an instance of a certain event type.
    53  type Event struct {
    54  	ty    *EventType
    55  	id    string
    56  	msg   protoreflect.ProtoMessage
    57  	attrs map[string]any
    58  }
    59  
    60  // Type returns the underlying event type.
    61  func (e Event) Type() *EventType {
    62  	return e.ty
    63  }
    64  
    65  // ID returns the identifier for the event.
    66  func (e Event) ID() string {
    67  	return e.id
    68  }
    69  
    70  // Attributes returns the attributes attached to the event. These attributes
    71  // are optional and this function may return an empty map.
    72  func (e Event) Attributes() map[string]any {
    73  	return e.attrs
    74  }
    75  
    76  // Message returns the underlying message (aka data) of the event.
    77  func (e Event) Message() protoreflect.ProtoMessage {
    78  	return e.msg
    79  }
    80  
    81  // New creates a new event of a given type.
    82  //
    83  // The "id" and "msg" parameters are required. The "attributes" parameter
    84  // supports the following value types:
    85  //   - string
    86  //   - int
    87  //   - bool
    88  //   - []byte
    89  //   - time.Time
    90  func (t *EventType) New(id string, msg protoreflect.ProtoMessage, attributes map[string]any) (*Event, error) {
    91  	if id == "" {
    92  		return nil, errors.New("id must be set")
    93  	}
    94  	if msg == nil {
    95  		return nil, errors.New("msg must be set")
    96  	}
    97  	ty := msg.ProtoReflect().Descriptor().FullName()
    98  	if tty := t.Descriptor().FullName(); ty != tty {
    99  		return nil, fmt.Errorf("msg type %q does not match expected type %q", ty, tty)
   100  	}
   101  
   102  	for name, value := range attributes {
   103  		if _, ok := reservedFields[name]; ok {
   104  			return nil, fmt.Errorf("attribute name %q is one of the reserved CloudEvents names %v", name, reservedFields)
   105  		}
   106  		switch v := value.(type) {
   107  		case string, bool, int, []byte, time.Time:
   108  			// Allowed types are a no-op.
   109  		default:
   110  			return nil, fmt.Errorf("unsupported attribute type for %q: %T", name, v)
   111  		}
   112  	}
   113  
   114  	return &Event{ty: t, id: id, msg: msg, attrs: attributes}, nil
   115  }
   116  
   117  // MarshalJSON serializes the event to JSON, following the CloudEvents
   118  // specification.
   119  func (e *Event) MarshalJSON() ([]byte, error) {
   120  	data, err := protojson.Marshal(e.msg)
   121  	if err != nil {
   122  		return nil, fmt.Errorf("marshal data to JSON: %w", err)
   123  	}
   124  
   125  	event := map[string]any{
   126  		"specversion":     CloudEventsSpecVersion,
   127  		"id":              e.ID(),
   128  		"source":          e.Type().Source(),
   129  		"type":            e.Type().Name(),
   130  		"datacontenttype": ContentTypeJSON,
   131  		"data":            string(data),
   132  	}
   133  	for k, v := range e.attrs {
   134  		event[k] = v
   135  	}
   136  
   137  	return json.Marshal(event)
   138  }
   139  
   140  // MarshalProto serializes the event to the CloudEvents Protobuf wire format.
   141  func (e *Event) MarshalProto() ([]byte, error) {
   142  	msg, err := e.Proto()
   143  	if err != nil {
   144  		return nil, fmt.Errorf("build proto: %w", err)
   145  	}
   146  	return proto.Marshal(msg)
   147  }
   148  
   149  // Proto returns the CloudEvents protobuf for the event.
   150  func (e *Event) Proto() (*pb.CloudEvent, error) {
   151  	data, err := proto.Marshal(e.msg)
   152  	if err != nil {
   153  		return nil, fmt.Errorf("marshal data: %w", err)
   154  	}
   155  
   156  	attrs := make(map[string]*pb.CloudEvent_CloudEventAttributeValue)
   157  	for name, value := range e.attrs {
   158  		switch v := value.(type) {
   159  		case string:
   160  			attrs[name] = &pb.CloudEvent_CloudEventAttributeValue{
   161  				Attr: &pb.CloudEvent_CloudEventAttributeValue_CeString{CeString: v},
   162  			}
   163  		case bool:
   164  			attrs[name] = &pb.CloudEvent_CloudEventAttributeValue{
   165  				Attr: &pb.CloudEvent_CloudEventAttributeValue_CeBoolean{CeBoolean: v},
   166  			}
   167  		case int:
   168  			attrs[name] = &pb.CloudEvent_CloudEventAttributeValue{
   169  				Attr: &pb.CloudEvent_CloudEventAttributeValue_CeInteger{CeInteger: int32(v)},
   170  			}
   171  		case time.Time:
   172  			attrs[name] = &pb.CloudEvent_CloudEventAttributeValue{
   173  				Attr: &pb.CloudEvent_CloudEventAttributeValue_CeTimestamp{
   174  					CeTimestamp: timestamppb.New(v),
   175  				},
   176  			}
   177  		case []byte:
   178  			attrs[name] = &pb.CloudEvent_CloudEventAttributeValue{
   179  				Attr: &pb.CloudEvent_CloudEventAttributeValue_CeBytes{CeBytes: v},
   180  			}
   181  		}
   182  	}
   183  
   184  	event := &pb.CloudEvent{
   185  		SpecVersion: CloudEventsSpecVersion,
   186  		Id:          e.ID(),
   187  		Source:      e.Type().Source(),
   188  		Type:        e.Type().Name(),
   189  		Attributes:  attrs,
   190  		Data: &pb.CloudEvent_ProtoData{
   191  			ProtoData: &anypb.Any{
   192  				Value:   data,
   193  				TypeUrl: string(e.Type().Descriptor().FullName()),
   194  			},
   195  		},
   196  	}
   197  
   198  	return event, nil
   199  }
   200  

View as plain text