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(¶ms.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(¶ms.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 }