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