github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/pkg/operation/operation.go (about)

     1  /*
     2   * Copyright 2020 The Compass Authors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package operation
    18  
    19  import (
    20  	"context"
    21  	"time"
    22  
    23  	"github.com/kyma-incubator/compass/components/director/internal/model"
    24  
    25  	validation "github.com/go-ozzo/ozzo-validation/v4"
    26  	"github.com/go-ozzo/ozzo-validation/v4/is"
    27  	"github.com/kyma-incubator/compass/components/director/pkg/resource"
    28  
    29  	"github.com/kyma-incubator/compass/components/director/pkg/graphql"
    30  )
    31  
    32  type contextKey string
    33  
    34  const (
    35  	// OpCtxKey missing godoc
    36  	OpCtxKey contextKey = "OperationCtx"
    37  	// OpModeKey missing godoc
    38  	OpModeKey contextKey = "OperationModeCtx"
    39  )
    40  
    41  // OperationStatus denotes the different statuses that an Operation can be in
    42  type OperationStatus string
    43  
    44  const (
    45  	// OperationStatusSucceeded missing godoc
    46  	OperationStatusSucceeded OperationStatus = "SUCCEEDED"
    47  	// OperationStatusFailed missing godoc
    48  	OperationStatusFailed OperationStatus = "FAILED"
    49  	// OperationStatusInProgress missing godoc
    50  	OperationStatusInProgress OperationStatus = "IN_PROGRESS"
    51  )
    52  
    53  // OperationType missing godoc
    54  type OperationType string
    55  
    56  const (
    57  	// OperationTypeCreate missing godoc
    58  	OperationTypeCreate OperationType = "Create"
    59  	// OperationTypeUpdate Operation type used for Update and Unpair flows
    60  	OperationTypeUpdate OperationType = "Update"
    61  	// OperationTypeDelete missing godoc
    62  	OperationTypeDelete OperationType = "Delete"
    63  )
    64  
    65  // OperationResponse defines the expected response format for the Operations API
    66  type OperationResponse struct {
    67  	*Operation
    68  	Status OperationStatus `json:"status,omitempty"`
    69  	Error  *string         `json:"error,omitempty"`
    70  }
    71  
    72  // Operation represents a GraphQL mutation which has associated HTTP requests (Webhooks) that need to be executed
    73  // for the request to be completed fully. Objects of type Operation are meant to be constructed, enriched throughout
    74  // the flow of the original mutation with information such as ResourceID and ResourceType and finally scheduled through
    75  // a dedicated Scheduler implementation.
    76  type Operation struct {
    77  	OperationID       string        `json:"operation_id,omitempty"`
    78  	OperationType     OperationType `json:"operation_type,omitempty"`
    79  	OperationCategory string        `json:"operation_category,omitempty"`
    80  	ResourceID        string        `json:"resource_id,omitempty"`
    81  	ResourceType      resource.Type `json:"resource_type,omitempty"`
    82  	CreationTime      time.Time     `json:"creation_time,omitempty"`
    83  	CorrelationID     string        `json:"correlation_id,omitempty"`
    84  	WebhookIDs        []string      `json:"webhook_ids,omitempty"`
    85  	RequestObject     string        `json:"request_object,omitempty"`
    86  }
    87  
    88  // Validate ensures that the constructed Operation has valid properties
    89  func (op *Operation) Validate() error {
    90  	return validation.ValidateStruct(op,
    91  		validation.Field(&op.ResourceID, is.UUID),
    92  		validation.Field(&op.ResourceType, validation.Required, validation.In(resource.Application)))
    93  }
    94  
    95  // SaveToContext saves Operation to the context
    96  func SaveToContext(ctx context.Context, operations *[]*Operation) context.Context {
    97  	if operations == nil {
    98  		return ctx
    99  	}
   100  
   101  	operationsFromCtx, exists := FromCtx(ctx)
   102  	if exists {
   103  		*operationsFromCtx = append(*operationsFromCtx, *operations...)
   104  		return ctx
   105  	}
   106  
   107  	return context.WithValue(ctx, OpCtxKey, operations)
   108  }
   109  
   110  // FromCtx extracts Operation from context
   111  func FromCtx(ctx context.Context) (*[]*Operation, bool) {
   112  	opCtx := ctx.Value(OpCtxKey)
   113  
   114  	if operations, ok := opCtx.(*[]*Operation); ok {
   115  		return operations, true
   116  	}
   117  
   118  	return nil, false
   119  }
   120  
   121  // SaveModeToContext saves operation mode to the context
   122  func SaveModeToContext(ctx context.Context, opMode graphql.OperationMode) context.Context {
   123  	return context.WithValue(ctx, OpModeKey, opMode)
   124  }
   125  
   126  // ModeFromCtx extracts operation mode from context
   127  func ModeFromCtx(ctx context.Context) graphql.OperationMode {
   128  	opCtx := ctx.Value(OpModeKey)
   129  
   130  	if opMode, ok := opCtx.(graphql.OperationMode); ok {
   131  		return opMode
   132  	}
   133  
   134  	return graphql.OperationModeSync
   135  }
   136  
   137  func (opResponse *OperationResponse) initializeOperationType(resource model.Entity) {
   138  	if !resource.GetDeletedAt().IsZero() {
   139  		opResponse.OperationType = OperationTypeDelete
   140  	} else if !resource.GetUpdatedAt().IsZero() {
   141  		opResponse.OperationType = OperationTypeUpdate
   142  	} else {
   143  		opResponse.OperationType = OperationTypeCreate
   144  	}
   145  }
   146  
   147  func (opResponse *OperationResponse) initializeOperationStatus(resource model.Entity) {
   148  	if !resource.GetReady() {
   149  		opResponse.Status = OperationStatusInProgress
   150  	} else if resource.GetError() == nil {
   151  		opResponse.Status = OperationStatusSucceeded
   152  	} else {
   153  		opResponse.Status = OperationStatusFailed
   154  	}
   155  }
   156  
   157  func (opResponse *OperationResponse) initializeCreationTime(resource model.Entity) {
   158  	createdAt, updatedAt, deletedAt := resource.GetCreatedAt(), resource.GetUpdatedAt(), resource.GetDeletedAt()
   159  
   160  	if deletedAt.After(createdAt) && deletedAt.After(updatedAt) {
   161  		opResponse.CreationTime = deletedAt
   162  	} else if updatedAt.After(createdAt) {
   163  		opResponse.CreationTime = updatedAt
   164  	} else {
   165  		opResponse.CreationTime = createdAt
   166  	}
   167  }