github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.0/pkg/datastream/stream.go (about)

     1  package datastream
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  	"net/url"
     9  	"strconv"
    10  
    11  	validation "github.com/go-ozzo/ozzo-validation/v4"
    12  )
    13  
    14  type (
    15  	// Stream is a ds stream operations API interface
    16  	Stream interface {
    17  		// CreateStream creates a stream
    18  		//
    19  		// See: https://techdocs.akamai.com/datastream2/v2/reference/post-stream
    20  		CreateStream(context.Context, CreateStreamRequest) (*DetailedStreamVersion, error)
    21  
    22  		// GetStream gets stream details
    23  		//
    24  		// See: https://techdocs.akamai.com/datastream2/v2/reference/get-stream
    25  		GetStream(context.Context, GetStreamRequest) (*DetailedStreamVersion, error)
    26  
    27  		// UpdateStream updates a stream
    28  		//
    29  		// See: https://techdocs.akamai.com/datastream2/v2/reference/put-stream
    30  		UpdateStream(context.Context, UpdateStreamRequest) (*DetailedStreamVersion, error)
    31  
    32  		// DeleteStream deletes a stream
    33  		//
    34  		// See: https://techdocs.akamai.com/datastream2/v2/reference/delete-stream
    35  		DeleteStream(context.Context, DeleteStreamRequest) error
    36  
    37  		// ListStreams retrieves list of streams
    38  		//
    39  		// See: https://techdocs.akamai.com/datastream2/v2/reference/get-streams
    40  		ListStreams(context.Context, ListStreamsRequest) ([]StreamDetails, error)
    41  	}
    42  
    43  	// DetailedStreamVersion is returned from GetStream
    44  	DetailedStreamVersion struct {
    45  		ContractID            string                `json:"contractId"`
    46  		CreatedBy             string                `json:"createdBy"`
    47  		CreatedDate           string                `json:"createdDate"`
    48  		CollectMidgress       bool                  `json:"collectMidgress,omitempty"`
    49  		DatasetFields         []DataSetField        `json:"datasetFields"`
    50  		DeliveryConfiguration DeliveryConfiguration `json:"deliveryConfiguration"`
    51  		Destination           Destination           `json:"destination"`
    52  		GroupID               int                   `json:"groupId,omitempty"`
    53  		LatestVersion         int                   `json:"latestVersion"`
    54  		ModifiedBy            string                `json:"modifiedBy"`
    55  		ModifiedDate          string                `json:"modifiedDate"`
    56  		NotificationEmails    []string              `json:"notificationEmails"`
    57  		ProductID             string                `json:"productId"`
    58  		Properties            []Property            `json:"properties"`
    59  		StreamID              int64                 `json:"streamId"`
    60  		StreamName            string                `json:"streamName"`
    61  		StreamVersion         int64                 `json:"streamVersion"`
    62  		StreamStatus          StreamStatus          `json:"streamStatus"`
    63  	}
    64  
    65  	// Destination provides detailed information about the destination’s configuration in the stream
    66  	Destination struct {
    67  		AuthenticationType AuthenticationType `json:"authenticationType"`
    68  		CompressLogs       bool               `json:"compressLogs"`
    69  		DestinationType    DestinationType    `json:"destinationType"`
    70  		DisplayName        string             `json:"displayName"`
    71  		Path               string             `json:"path"`
    72  		Endpoint           string             `json:"endpoint"`
    73  		IndexName          string             `json:"indexName"`
    74  		ServiceAccountName string             `json:"serviceAccountName"`
    75  		ProjectID          string             `json:"projectId"`
    76  		Service            string             `json:"service"`
    77  		Bucket             string             `json:"bucket"`
    78  		Tags               string             `json:"tags"`
    79  		Region             string             `json:"region"`
    80  		AccountName        string             `json:"accountName"`
    81  		Namespace          string             `json:"namespace"`
    82  		ContainerName      string             `json:"containerName"`
    83  		Source             string             `json:"source"`
    84  		ContentType        string             `json:"contentType"`
    85  		CustomHeaderName   string             `json:"customHeaderName"`
    86  		CustomHeaderValue  string             `json:"customHeaderValue"`
    87  		TLSHostname        string             `json:"tlsHostname"`
    88  		MTLS               string             `json:"mTLS"`
    89  	}
    90  
    91  	// StreamConfiguration is used in CreateStream as a request body
    92  	StreamConfiguration struct {
    93  		ContractID            string                `json:"contractId"`
    94  		CollectMidgress       bool                  `json:"collectMidgress,omitempty"`
    95  		DatasetFields         []DatasetFieldID      `json:"datasetFields"`
    96  		Destination           AbstractConnector     `json:"destination"`
    97  		DeliveryConfiguration DeliveryConfiguration `json:"deliveryConfiguration"`
    98  		GroupID               int                   `json:"groupId,omitempty"`
    99  		NotificationEmails    []string              `json:"notificationEmails,omitempty"`
   100  		Properties            []PropertyID          `json:"properties"`
   101  		StreamName            string                `json:"streamName"`
   102  	}
   103  
   104  	// DeliveryConfiguration of the configuration of log lines, names of the files sent to a destination, and delivery frequency for these files
   105  	DeliveryConfiguration struct {
   106  		Delimiter        *DelimiterType `json:"fieldDelimiter,omitempty"`
   107  		Format           FormatType     `json:"format"`
   108  		Frequency        Frequency      `json:"frequency"`
   109  		UploadFilePrefix string         `json:"uploadFilePrefix,omitempty"`
   110  		UploadFileSuffix string         `json:"uploadFileSuffix,omitempty"`
   111  	}
   112  
   113  	// The Frequency of collecting logs from each uploader and sending these logs to a destination.
   114  	Frequency struct {
   115  		IntervalInSeconds IntervalInSeconds `json:"intervalInSeconds"`
   116  	}
   117  
   118  	// DataSets is a list of fields selected from the associated template that the stream monitors in logs
   119  	DataSets struct {
   120  		DataSetFields []DataSetField `json:"datasetFields"`
   121  	}
   122  
   123  	// DataSetField is a data set field selected from the associated template that the stream monitors in logs
   124  	DataSetField struct {
   125  		DatasetFieldID          int    `json:"datasetFieldId"`
   126  		DatasetFieldDescription string `json:"datasetFieldDescription"`
   127  		DatasetFieldJsonKey     string `json:"datasetFieldJsonKey"`
   128  		DatasetFieldName        string `json:"datasetFieldName"`
   129  		DatasetFieldGroup       string `json:"datasetFieldGroup"`
   130  	}
   131  
   132  	// DatasetFieldID is a dataset field value used in create stream request
   133  	DatasetFieldID struct {
   134  		DatasetFieldID int `json:"datasetFieldId"`
   135  	}
   136  
   137  	// Property identifies brief info about the properties monitored in the stream.
   138  	Property struct {
   139  		PropertyID   int    `json:"propertyId"`
   140  		PropertyName string `json:"propertyName"`
   141  	}
   142  
   143  	// PropertyID identifies property details required in the create stream request.
   144  	PropertyID struct {
   145  		PropertyID int `json:"propertyId"`
   146  	}
   147  
   148  	// StreamStatus is used to create an enum of possible StreamStatus values
   149  	StreamStatus string
   150  
   151  	// AbstractConnector is an interface for all Connector types
   152  	AbstractConnector interface {
   153  		SetDestinationType()
   154  		Validate() error
   155  	}
   156  
   157  	// DelimiterType enum
   158  	DelimiterType string
   159  
   160  	// FormatType enum
   161  	FormatType string
   162  
   163  	// IntervalInSeconds enum
   164  	IntervalInSeconds int
   165  
   166  	// CreateStreamRequest is passed to CreateStream
   167  	CreateStreamRequest struct {
   168  		StreamConfiguration StreamConfiguration
   169  		Activate            bool
   170  	}
   171  
   172  	// GetStreamRequest is passed to GetStream
   173  	GetStreamRequest struct {
   174  		StreamID int64
   175  		Version  *int64
   176  	}
   177  
   178  	// UpdateStreamRequest is passed to UpdateStream
   179  	UpdateStreamRequest struct {
   180  		StreamID            int64
   181  		StreamConfiguration StreamConfiguration
   182  		Activate            bool
   183  	}
   184  
   185  	// StreamUpdate contains information about stream ID and version
   186  	StreamUpdate struct {
   187  		StreamID      int64 `json:"streamId"`
   188  		StreamVersion int64 `json:"streamVersion"`
   189  	}
   190  
   191  	// DeleteStreamRequest is passed to DeleteStream
   192  	DeleteStreamRequest struct {
   193  		StreamID int64
   194  	}
   195  
   196  	// ListStreamsRequest is passed to ListStreams
   197  	ListStreamsRequest struct {
   198  		GroupID *int
   199  	}
   200  
   201  	// StreamDetails contains information about stream
   202  	StreamDetails struct {
   203  		ContractID    string       `json:"contractId"`
   204  		CreatedBy     string       `json:"createdBy"`
   205  		CreatedDate   string       `json:"createdDate"`
   206  		GroupID       int          `json:"groupId"`
   207  		LatestVersion int64        `json:"latestVersion"`
   208  		ModifiedBy    string       `json:"modifiedBy"`
   209  		ModifiedDate  string       `json:"modifiedDate"`
   210  		Properties    []Property   `json:"properties"`
   211  		ProductID     string       `json:"productId"`
   212  		StreamID      int64        `json:"streamId"`
   213  		StreamName    string       `json:"streamName"`
   214  		StreamStatus  StreamStatus `json:"streamStatus"`
   215  		StreamVersion int64        `json:"streamVersion"`
   216  	}
   217  )
   218  
   219  const (
   220  	// StreamStatusActivated const
   221  	StreamStatusActivated StreamStatus = "ACTIVATED"
   222  	// StreamStatusDeactivated const
   223  	StreamStatusDeactivated StreamStatus = "DEACTIVATED"
   224  	// StreamStatusActivating const
   225  	StreamStatusActivating StreamStatus = "ACTIVATING"
   226  	// StreamStatusDeactivating const state
   227  	StreamStatusDeactivating StreamStatus = "DEACTIVATING"
   228  	// StreamStatusInactive const
   229  	StreamStatusInactive StreamStatus = "INACTIVE"
   230  
   231  	// DelimiterTypeSpace const
   232  	DelimiterTypeSpace DelimiterType = "SPACE"
   233  
   234  	// FormatTypeStructured const
   235  	FormatTypeStructured FormatType = "STRUCTURED"
   236  	// FormatTypeJson const
   237  	FormatTypeJson FormatType = "JSON"
   238  
   239  	// IntervalInSeconds30 const
   240  	IntervalInSeconds30 IntervalInSeconds = 30
   241  	// IntervalInSeconds60 const
   242  	IntervalInSeconds60 IntervalInSeconds = 60
   243  )
   244  
   245  // Validate validates CreateStreamRequest
   246  func (r CreateStreamRequest) Validate() error {
   247  	return validation.Errors{
   248  		"StreamConfiguration.DeliveryConfiguration":                             validation.Validate(r.StreamConfiguration.DeliveryConfiguration, validation.Required),
   249  		"StreamConfiguration.DeliveryConfiguration.Delimiter":                   validation.Validate(r.StreamConfiguration.DeliveryConfiguration.Delimiter, validation.When(r.StreamConfiguration.DeliveryConfiguration.Format == FormatTypeStructured, validation.Required, validation.In(DelimiterTypeSpace)), validation.When(r.StreamConfiguration.DeliveryConfiguration.Format == FormatTypeJson, validation.Nil)),
   250  		"StreamConfiguration.DeliveryConfiguration.Format":                      validation.Validate(r.StreamConfiguration.DeliveryConfiguration.Format, validation.Required, validation.In(FormatTypeStructured, FormatTypeJson), validation.When(r.StreamConfiguration.DeliveryConfiguration.Delimiter != nil, validation.Required, validation.In(FormatTypeStructured))),
   251  		"StreamConfiguration.DeliveryConfiguration.Frequency":                   validation.Validate(r.StreamConfiguration.DeliveryConfiguration.Frequency, validation.Required),
   252  		"StreamConfiguration.DeliveryConfiguration.Frequency.IntervalInSeconds": validation.Validate(r.StreamConfiguration.DeliveryConfiguration.Frequency.IntervalInSeconds, validation.Required, validation.In(IntervalInSeconds30, IntervalInSeconds60)),
   253  		"StreamConfiguration.Destination":                                       validation.Validate(r.StreamConfiguration.Destination, validation.Required),
   254  		"StreamConfiguration.ContractId":                                        validation.Validate(r.StreamConfiguration.ContractID, validation.Required),
   255  		"StreamConfiguration.DatasetFields":                                     validation.Validate(r.StreamConfiguration.DatasetFields, validation.Required),
   256  		"StreamConfiguration.GroupID":                                           validation.Validate(r.StreamConfiguration.GroupID, validation.Required, validation.Min(1)),
   257  		"StreamConfiguration.Properties":                                        validation.Validate(r.StreamConfiguration.Properties, validation.Required),
   258  		"StreamConfiguration.StreamName":                                        validation.Validate(r.StreamConfiguration.StreamName, validation.Required),
   259  	}.Filter()
   260  }
   261  
   262  // Validate validates GetStreamRequest
   263  func (r GetStreamRequest) Validate() error {
   264  	return validation.Errors{
   265  		"streamId": validation.Validate(r.StreamID, validation.Required),
   266  	}.Filter()
   267  }
   268  
   269  // Validate validates UpdateStreamRequest
   270  func (r UpdateStreamRequest) Validate() error {
   271  	return validation.Errors{
   272  		"StreamConfiguration.DeliveryConfiguration":                             validation.Validate(r.StreamConfiguration.DeliveryConfiguration, validation.Required),
   273  		"StreamConfiguration.DeliveryConfiguration.Delimiter":                   validation.Validate(r.StreamConfiguration.DeliveryConfiguration.Delimiter, validation.When(r.StreamConfiguration.DeliveryConfiguration.Format == FormatTypeStructured, validation.Required, validation.In(DelimiterTypeSpace)), validation.When(r.StreamConfiguration.DeliveryConfiguration.Format == FormatTypeJson, validation.Nil)),
   274  		"StreamConfiguration.DeliveryConfiguration.Format":                      validation.Validate(r.StreamConfiguration.DeliveryConfiguration.Format, validation.In(FormatTypeStructured, FormatTypeJson)),
   275  		"StreamConfiguration.DeliveryConfiguration.Frequency":                   validation.Validate(r.StreamConfiguration.DeliveryConfiguration.Frequency, validation.Required),
   276  		"StreamConfiguration.DeliveryConfiguration.Frequency.IntervalInSeconds": validation.Validate(r.StreamConfiguration.DeliveryConfiguration.Frequency.IntervalInSeconds, validation.Required, validation.In(IntervalInSeconds30, IntervalInSeconds60)),
   277  		"StreamConfiguration.Destination":                                       validation.Validate(r.StreamConfiguration.Destination, validation.Required),
   278  		"StreamConfiguration.ContractId":                                        validation.Validate(r.StreamConfiguration.ContractID, validation.Required),
   279  		"StreamConfiguration.DatasetFields":                                     validation.Validate(r.StreamConfiguration.DatasetFields, validation.Required),
   280  		"StreamConfiguration.GroupID":                                           validation.Validate(r.StreamConfiguration.GroupID, validation.In(0)),
   281  		"StreamConfiguration.Properties":                                        validation.Validate(r.StreamConfiguration.Properties, validation.Required),
   282  		"StreamConfiguration.StreamName":                                        validation.Validate(r.StreamConfiguration.StreamName, validation.Required),
   283  	}.Filter()
   284  }
   285  
   286  // Validate validates DeleteStreamRequest
   287  func (r DeleteStreamRequest) Validate() error {
   288  	return validation.Errors{
   289  		"streamId": validation.Validate(r.StreamID, validation.Required),
   290  	}.Filter()
   291  }
   292  
   293  var (
   294  	// ErrCreateStream represents error when creating stream fails
   295  	ErrCreateStream = errors.New("creating stream")
   296  	// ErrGetStream represents error when fetching stream fails
   297  	ErrGetStream = errors.New("fetching stream information")
   298  	// ErrUpdateStream represents error when updating stream fails
   299  	ErrUpdateStream = errors.New("updating stream")
   300  	// ErrDeleteStream represents error when deleting stream fails
   301  	ErrDeleteStream = errors.New("deleting stream")
   302  	// ErrListStreams represents error when listing streams fails
   303  	ErrListStreams = errors.New("listing streams")
   304  )
   305  
   306  func (d *ds) CreateStream(ctx context.Context, params CreateStreamRequest) (*DetailedStreamVersion, error) {
   307  	logger := d.Log(ctx)
   308  	logger.Debug("CreateStream")
   309  
   310  	setDestinationType(&params.StreamConfiguration)
   311  	if err := params.Validate(); err != nil {
   312  		return nil, fmt.Errorf("%s: %w: %s", ErrCreateStream, ErrStructValidation, err)
   313  	}
   314  
   315  	uri, err := url.Parse("/datastream-config-api/v2/log/streams")
   316  	if err != nil {
   317  		return nil, fmt.Errorf("%w: parsing URL: %s", ErrCreateStream, err)
   318  	}
   319  
   320  	q := uri.Query()
   321  	q.Add("activate", fmt.Sprintf("%t", params.Activate))
   322  	uri.RawQuery = q.Encode()
   323  
   324  	req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri.String(), nil)
   325  	if err != nil {
   326  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrCreateStream, err)
   327  	}
   328  
   329  	var rval DetailedStreamVersion
   330  	resp, err := d.Exec(req, &rval, params.StreamConfiguration)
   331  	if err != nil {
   332  		return nil, fmt.Errorf("%w: request failed: %s", ErrCreateStream, err)
   333  	}
   334  
   335  	if resp.StatusCode != http.StatusCreated {
   336  		return nil, fmt.Errorf("%s: %w", ErrCreateStream, d.Error(resp))
   337  	}
   338  
   339  	return &rval, nil
   340  }
   341  
   342  func (d *ds) GetStream(ctx context.Context, params GetStreamRequest) (*DetailedStreamVersion, error) {
   343  	logger := d.Log(ctx)
   344  	logger.Debug("GetStream")
   345  
   346  	if err := params.Validate(); err != nil {
   347  		return nil, fmt.Errorf("%s: %w: %s", ErrGetStream, ErrStructValidation, err)
   348  	}
   349  
   350  	uri, err := url.Parse(fmt.Sprintf(
   351  		"/datastream-config-api/v2/log/streams/%d",
   352  		params.StreamID))
   353  	if err != nil {
   354  		return nil, fmt.Errorf("%w: failed to parse url: %s", ErrGetStream, err)
   355  	}
   356  
   357  	if params.Version != nil {
   358  		query := uri.Query()
   359  		query.Add("version", strconv.FormatInt(*params.Version, 10))
   360  		uri.RawQuery = query.Encode()
   361  	}
   362  
   363  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri.String(), nil)
   364  	if err != nil {
   365  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetStream, err)
   366  	}
   367  
   368  	var rval DetailedStreamVersion
   369  	resp, err := d.Exec(req, &rval)
   370  	if err != nil {
   371  		return nil, fmt.Errorf("%w: request failed: %s", ErrGetStream, err)
   372  	}
   373  
   374  	if resp.StatusCode != http.StatusOK {
   375  		return nil, fmt.Errorf("%s: %w", ErrGetStream, d.Error(resp))
   376  	}
   377  
   378  	return &rval, nil
   379  }
   380  
   381  func (d *ds) UpdateStream(ctx context.Context, params UpdateStreamRequest) (*DetailedStreamVersion, error) {
   382  	logger := d.Log(ctx)
   383  	logger.Debug("UpdateStream")
   384  
   385  	setDestinationType(&params.StreamConfiguration)
   386  	if err := params.Validate(); err != nil {
   387  		return nil, fmt.Errorf("%s: %w: %s", ErrUpdateStream, ErrStructValidation, err)
   388  	}
   389  
   390  	uri, err := url.Parse(fmt.Sprintf("/datastream-config-api/v2/log/streams/%d", params.StreamID))
   391  
   392  	if err != nil {
   393  		return nil, fmt.Errorf("%w: failed to parse url: %s", ErrUpdateStream, err)
   394  	}
   395  
   396  	q := uri.Query()
   397  	q.Add("activate", fmt.Sprintf("%t", params.Activate))
   398  	uri.RawQuery = q.Encode()
   399  
   400  	req, err := http.NewRequestWithContext(ctx, http.MethodPut, uri.String(), nil)
   401  	if err != nil {
   402  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateStream, err)
   403  	}
   404  
   405  	var rval DetailedStreamVersion
   406  	resp, err := d.Exec(req, &rval, params.StreamConfiguration)
   407  	if err != nil {
   408  		return nil, fmt.Errorf("%w: request failed: %s", ErrUpdateStream, err)
   409  	}
   410  
   411  	if resp.StatusCode != http.StatusOK {
   412  		return nil, fmt.Errorf("%s: %w", ErrUpdateStream, d.Error(resp))
   413  	}
   414  
   415  	return &rval, nil
   416  }
   417  
   418  func (d *ds) DeleteStream(ctx context.Context, params DeleteStreamRequest) error {
   419  	logger := d.Log(ctx)
   420  	logger.Debug("DeleteStream")
   421  
   422  	if err := params.Validate(); err != nil {
   423  		return fmt.Errorf("%s: %w: %s", ErrDeleteStream, ErrStructValidation, err)
   424  	}
   425  
   426  	uri, err := url.Parse(fmt.Sprintf(
   427  		"/datastream-config-api/v2/log/streams/%d",
   428  		params.StreamID),
   429  	)
   430  	if err != nil {
   431  		return fmt.Errorf("%w: failed to parse url: %s", ErrDeleteStream, err)
   432  	}
   433  
   434  	req, err := http.NewRequestWithContext(ctx, http.MethodDelete, uri.String(), nil)
   435  	if err != nil {
   436  		return fmt.Errorf("%w: failed to create request: %s", ErrDeleteStream, err)
   437  	}
   438  
   439  	resp, err := d.Exec(req, nil)
   440  	if err != nil {
   441  		return fmt.Errorf("%w: request failed: %s", ErrDeleteStream, err)
   442  	}
   443  
   444  	if resp.StatusCode != http.StatusNoContent {
   445  		return fmt.Errorf("%s: %w", ErrDeleteStream, d.Error(resp))
   446  	}
   447  
   448  	return nil
   449  }
   450  
   451  func (d *ds) ListStreams(ctx context.Context, params ListStreamsRequest) ([]StreamDetails, error) {
   452  	logger := d.Log(ctx)
   453  	logger.Debug("ListStreams")
   454  
   455  	uri, err := url.Parse("/datastream-config-api/v2/log/streams")
   456  	if err != nil {
   457  		return nil, fmt.Errorf("%w: failed to parse url: %s", ErrListStreams, err)
   458  	}
   459  
   460  	q := uri.Query()
   461  	if params.GroupID != nil {
   462  		q.Add("groupId", fmt.Sprintf("%d", *params.GroupID))
   463  	}
   464  
   465  	uri.RawQuery = q.Encode()
   466  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri.String(), nil)
   467  	if err != nil {
   468  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrListStreams, err)
   469  	}
   470  
   471  	var result []StreamDetails
   472  	resp, err := d.Exec(req, &result)
   473  	if err != nil {
   474  		return nil, fmt.Errorf("%w: request failed: %s", ErrListStreams, err)
   475  	}
   476  
   477  	if resp.StatusCode != http.StatusOK {
   478  		return nil, fmt.Errorf("%s: %w", ErrListStreams, d.Error(resp))
   479  	}
   480  
   481  	return result, nil
   482  }
   483  
   484  func setDestinationType(configuration *StreamConfiguration) {
   485  	configuration.Destination.SetDestinationType()
   486  }