github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/pkg/operation/api.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 "encoding/json" 22 "fmt" 23 "net/http" 24 25 "github.com/gorilla/mux" 26 27 "github.com/kyma-incubator/compass/components/director/internal/model" 28 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 29 30 "github.com/kyma-incubator/compass/components/director/pkg/persistence" 31 32 "github.com/kyma-incubator/compass/components/director/pkg/resource" 33 34 "github.com/kyma-incubator/compass/components/director/pkg/log" 35 ) 36 37 // ResourceIDParam missing godoc 38 const ResourceIDParam = "resource_id" 39 40 // ResourceTypeParam missing godoc 41 const ResourceTypeParam = "resource_type" 42 43 // ResourceFetcherFunc defines a function which fetches a particular resource by tenant and resource ID 44 type ResourceFetcherFunc func(ctx context.Context, tenantID, resourceID string) (model.Entity, error) 45 46 // TenantLoaderFunc defines a function which fetches the tenant for a particular request 47 type TenantLoaderFunc func(ctx context.Context) (string, error) 48 49 type handler struct { 50 transact persistence.Transactioner 51 resourceFetcherFunc ResourceFetcherFunc 52 tenantLoaderFunc TenantLoaderFunc 53 } 54 55 // NewHandler creates a new handler struct associated with the Operations API 56 func NewHandler(transact persistence.Transactioner, resourceFetcherFunc ResourceFetcherFunc, tenantLoaderFunc TenantLoaderFunc) *handler { 57 return &handler{ 58 transact: transact, 59 resourceFetcherFunc: resourceFetcherFunc, 60 tenantLoaderFunc: tenantLoaderFunc, 61 } 62 } 63 64 // ServeHTTP handles the Operations API requests 65 func (h *handler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { 66 ctx := request.Context() 67 68 tenantID, err := h.tenantLoaderFunc(ctx) 69 if err != nil { 70 log.C(ctx).WithError(err).Errorf("An error occurred while retrieving tenant from context: %s", err.Error()) 71 apperrors.WriteAppError(ctx, writer, apperrors.NewInternalError("Unable to determine tenant for request"), http.StatusInternalServerError) 72 return 73 } 74 75 routeVariables := mux.Vars(request) 76 resourceID := routeVariables[ResourceIDParam] 77 resourceType := routeVariables[ResourceTypeParam] 78 79 op := &Operation{ 80 ResourceID: resourceID, 81 ResourceType: resource.Type(resourceType), 82 } 83 84 log.C(ctx).Infof("Executing Operation API with resourceType: %s and resourceID: %s", op.ResourceType, op.ResourceID) 85 86 if err := op.Validate(); err != nil { 87 apperrors.WriteAppError(ctx, writer, apperrors.NewInvalidDataError("Unexpected resource type and/or GUID"), http.StatusBadRequest) 88 return 89 } 90 91 tx, err := h.transact.Begin() 92 if err != nil { 93 log.C(ctx).WithError(err).Errorf("An error occurred while opening db transaction: %s", err.Error()) 94 apperrors.WriteAppError(ctx, writer, apperrors.NewInternalError("Unable to establish connection with database"), http.StatusInternalServerError) 95 return 96 } 97 defer h.transact.RollbackUnlessCommitted(ctx, tx) 98 99 ctx = persistence.SaveToContext(ctx, tx) 100 101 res, err := h.resourceFetcherFunc(ctx, tenantID, op.ResourceID) 102 if err != nil { 103 log.C(ctx).WithError(err).Errorf("An error occurred while fetching resource from database: %s", err.Error()) 104 105 if apperrors.IsNotFoundError(err) { 106 apperrors.WriteAppError(ctx, writer, apperrors.NewNotFoundErrorWithMessage(resource.Application, op.ResourceID, 107 fmt.Sprintf("Operation for application with id %s not found", op.ResourceID)), http.StatusNotFound) 108 return 109 } 110 111 apperrors.WriteAppError(ctx, writer, apperrors.NewInternalError("Unable to execute database operation"), http.StatusInternalServerError) 112 return 113 } 114 115 if err := tx.Commit(); err != nil { 116 log.C(ctx).WithError(err).Errorf("An error occurred while closing database transaction: %s", err.Error()) 117 apperrors.WriteAppError(ctx, writer, apperrors.NewInternalError("Unable to finalize database operation"), http.StatusInternalServerError) 118 return 119 } 120 121 opResponse := buildLastOperation(res) 122 123 err = json.NewEncoder(writer).Encode(opResponse) 124 if err != nil { 125 log.C(ctx).WithError(err).Errorf("An error occurred while encoding operation data: %v", err) 126 } 127 } 128 129 func buildLastOperation(resource model.Entity) *OperationResponse { 130 opResponse := &OperationResponse{ 131 Operation: &Operation{ 132 ResourceID: resource.GetID(), 133 ResourceType: resource.GetType(), 134 }, 135 Error: resource.GetError(), 136 } 137 138 opResponse.initializeOperationType(resource) 139 opResponse.initializeOperationStatus(resource) 140 opResponse.initializeCreationTime(resource) 141 142 return opResponse 143 }