github.com/Axway/agent-sdk@v1.1.101/pkg/apic/apiservice.go (about) 1 package apic 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "net/http" 8 "time" 9 10 "github.com/Axway/agent-sdk/pkg/util" 11 12 coreapi "github.com/Axway/agent-sdk/pkg/api" 13 apiv1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1" 14 management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1alpha1" 15 defs "github.com/Axway/agent-sdk/pkg/apic/definitions" 16 utilerrors "github.com/Axway/agent-sdk/pkg/util/errors" 17 "github.com/Axway/agent-sdk/pkg/util/log" 18 ) 19 20 func buildAPIServiceSpec(serviceBody *ServiceBody) management.ApiServiceSpec { 21 if serviceBody.Image != "" { 22 return management.ApiServiceSpec{ 23 Description: serviceBody.Description, 24 Icon: management.ApiServiceSpecIcon{ 25 ContentType: serviceBody.ImageContentType, 26 Data: serviceBody.Image, 27 }, 28 } 29 } 30 return management.ApiServiceSpec{ 31 Description: serviceBody.Description, 32 } 33 } 34 35 func (c *ServiceClient) buildAPIService(serviceBody *ServiceBody) *management.APIService { 36 owner, ownerErr := c.getOwnerObject(serviceBody, true) 37 svc := &management.APIService{ 38 ResourceMeta: apiv1.ResourceMeta{ 39 GroupVersionKind: management.APIServiceGVK(), 40 Title: serviceBody.NameToPush, 41 Attributes: util.CheckEmptyMapStringString(serviceBody.ServiceAttributes), 42 Tags: mapToTagsArray(serviceBody.Tags, c.cfg.GetTagsToPublish()), 43 Metadata: apiv1.Metadata{ 44 Scope: apiv1.MetadataScope{ 45 Kind: management.EnvironmentGVK().Kind, 46 Name: c.cfg.GetEnvironmentName(), 47 }, 48 }, 49 }, 50 Spec: buildAPIServiceSpec(serviceBody), 51 Owner: owner, 52 Status: buildAPIServiceStatusSubResource(ownerErr), 53 } 54 55 buildAPIServiceSourceSubResource(svc, serviceBody) 56 svcDetails := buildAgentDetailsSubResource(serviceBody, true, serviceBody.ServiceAgentDetails) 57 c.setMigrationFlags(svcDetails) 58 59 util.SetAgentDetails(svc, svcDetails) 60 61 return svc 62 } 63 64 func (c *ServiceClient) setMigrationFlags(svcDetails map[string]interface{}) { 65 svcDetails[defs.MarketplaceMigration] = defs.MigrationCompleted 66 } 67 68 func (c *ServiceClient) getOwnerObject(serviceBody *ServiceBody, warning bool) (*apiv1.Owner, error) { 69 if id, found := c.getTeamFromCache(serviceBody.TeamName); found { 70 return &apiv1.Owner{ 71 Type: apiv1.TeamOwner, 72 ID: id, 73 }, nil 74 } else if warning { 75 // warning is only true when creating service, revision and instance will not print it 76 warnMsg := fmt.Sprintf("A team named %s does not exist on Amplify, not setting an owner of the API Service for %s", serviceBody.TeamName, serviceBody.APIName) 77 log.Warnf(warnMsg) 78 return nil, errors.New(warnMsg) 79 } 80 return nil, nil 81 } 82 83 func (c *ServiceClient) updateAPIService(serviceBody *ServiceBody, svc *management.APIService) { 84 owner, ownerErr := c.getOwnerObject(serviceBody, true) 85 86 svc.GroupVersionKind = management.APIServiceGVK() 87 svc.Metadata.ResourceVersion = "" 88 svc.Title = serviceBody.NameToPush 89 svc.Tags = mapToTagsArray(serviceBody.Tags, c.cfg.GetTagsToPublish()) 90 svc.Spec.Description = serviceBody.Description 91 svc.Owner = owner 92 svc.Attributes = util.CheckEmptyMapStringString(serviceBody.ServiceAttributes) 93 svc.Status = buildAPIServiceStatusSubResource(ownerErr) 94 buildAPIServiceSourceSubResource(svc, serviceBody) 95 96 svcDetails := buildAgentDetailsSubResource(serviceBody, true, serviceBody.ServiceAgentDetails) 97 newSVCDetails := util.MergeMapStringInterface(util.GetAgentDetails(svc), svcDetails) 98 util.SetAgentDetails(svc, newSVCDetails) 99 100 // get the specHashes from the existing service 101 if revDetails, found := newSVCDetails[specHashes]; found { 102 if specHashes, ok := revDetails.(map[string]interface{}); ok { 103 serviceBody.specHashes = specHashes 104 } 105 } 106 107 if serviceBody.Image != "" { 108 svc.Spec.Icon = management.ApiServiceSpecIcon{ 109 ContentType: serviceBody.ImageContentType, 110 Data: serviceBody.Image, 111 } 112 } 113 } 114 115 func buildAPIServiceSourceSubResource(svc *management.APIService, serviceBody *ServiceBody) { 116 serviceBody.serviceContext.updateServiceSource = false 117 118 source := svc.Source 119 if source == nil { 120 svc.Source = &management.ApiServiceSource{} 121 source = svc.Source 122 } 123 124 dataplaneType := serviceBody.GetDataplaneType() 125 if dataplaneType != "" { 126 if source.DataplaneType == nil { 127 source.DataplaneType = &management.ApiServiceSourceDataplaneType{} 128 } 129 if serviceBody.IsDesignDataplane() { 130 if source.DataplaneType.Design != dataplaneType.String() { 131 source.DataplaneType.Design = dataplaneType.String() 132 serviceBody.serviceContext.updateServiceSource = true 133 } 134 } else if source.DataplaneType.Managed != dataplaneType.String() { 135 source.DataplaneType.Managed = dataplaneType.String() 136 serviceBody.serviceContext.updateServiceSource = true 137 } 138 } 139 140 referencedSvc := serviceBody.GetReferencedServiceName() 141 if referencedSvc != "" { 142 if source.References == nil { 143 source.References = &management.ApiServiceSourceReferences{} 144 } 145 if source.References.ApiService != referencedSvc { 146 source.References.ApiService = serviceBody.GetReferencedServiceName() 147 serviceBody.serviceContext.updateServiceSource = true 148 } 149 } 150 } 151 152 func buildAPIServiceStatusSubResource(ownerErr error) *apiv1.ResourceStatus { 153 // only set status if ownerErr != nil 154 if ownerErr != nil { 155 // get current time 156 activityTime := time.Now() 157 newV1Time := apiv1.Time(activityTime) 158 message := ownerErr.Error() 159 level := "Error" 160 return &apiv1.ResourceStatus{ 161 Level: level, 162 Reasons: []apiv1.ResourceStatusReason{ 163 { 164 Type: level, 165 Detail: message, 166 Timestamp: newV1Time, 167 }, 168 }, 169 } 170 } 171 return nil 172 } 173 174 // processService - 175 func (c *ServiceClient) processService(serviceBody *ServiceBody) (*management.APIService, error) { 176 // Default action to create service 177 serviceURL := c.cfg.GetServicesURL() 178 httpMethod := http.MethodPost 179 serviceBody.serviceContext.serviceAction = addAPI 180 serviceBody.specHashes = map[string]interface{}{} 181 182 // If service exists, update existing service 183 svc, err := c.getAPIServiceFromCache(serviceBody) 184 if err != nil { 185 return nil, err 186 } 187 188 if svc != nil { 189 serviceBody.serviceContext.serviceAction = updateAPI 190 httpMethod = http.MethodPut 191 serviceURL += "/" + svc.Name 192 c.updateAPIService(serviceBody, svc) 193 } else { 194 svc = c.buildAPIService(serviceBody) 195 } 196 197 buffer, err := json.Marshal(svc) 198 if err != nil { 199 return nil, err 200 } 201 202 ri, err := c.apiServiceDeployAPI(httpMethod, serviceURL, buffer) 203 if err != nil { 204 return nil, err 205 } 206 207 if ri != nil { 208 serviceBody.serviceContext.serviceName = ri.Name 209 serviceBody.serviceContext.serviceID = ri.Metadata.ID 210 } 211 212 svc.Name = serviceBody.serviceContext.serviceName 213 err = c.updateAPIServiceSubresources(svc, serviceBody.serviceContext.updateServiceSource) 214 if err != nil && serviceBody.serviceContext.serviceAction == addAPI { 215 _, e := c.rollbackAPIService(serviceBody.serviceContext.serviceName) 216 if e != nil { 217 return nil, errors.New(err.Error() + e.Error()) 218 } 219 } 220 221 ri, _ = svc.AsInstance() 222 c.caches.AddAPIService(ri) 223 return svc, err 224 } 225 226 func (c *ServiceClient) updateAPIServiceSubresources(svc *management.APIService, updateSource bool) error { 227 subResources := make(map[string]interface{}) 228 if svc.Status != nil { 229 subResources[management.ApiServiceStatusSubResourceName] = svc.Status 230 } 231 232 if len(svc.SubResources) > 0 { 233 if xAgentDetail, ok := svc.SubResources[defs.XAgentDetails]; ok { 234 subResources[defs.XAgentDetails] = xAgentDetail 235 } 236 } 237 238 if updateSource && svc.Source != nil { 239 subResources[management.ApiServiceSourceSubResourceName] = svc.Source 240 } 241 242 if len(subResources) > 0 { 243 return c.CreateSubResource(svc.ResourceMeta, subResources) 244 } 245 return nil 246 } 247 248 func (c *ServiceClient) getAPIServiceByExternalAPIID(apiID string) (*management.APIService, error) { 249 ri := c.caches.GetAPIServiceWithAPIID(apiID) 250 if ri == nil { 251 return nil, nil 252 } 253 apiSvc := &management.APIService{} 254 err := apiSvc.FromInstance(ri) 255 return apiSvc, err 256 } 257 258 func (c *ServiceClient) getAPIServiceByPrimaryKey(primaryKey string) (*management.APIService, error) { 259 ri := c.caches.GetAPIServiceWithPrimaryKey(primaryKey) 260 if ri == nil { 261 return nil, nil 262 } 263 apiSvc := &management.APIService{} 264 err := apiSvc.FromInstance(ri) 265 return apiSvc, err 266 } 267 268 func (c *ServiceClient) getAPIServiceFromCache(serviceBody *ServiceBody) (*management.APIService, error) { 269 apiService, err := c.getAPIServiceByExternalAPIID(serviceBody.RestAPIID) 270 if apiService != nil && err == nil { 271 return apiService, nil 272 } 273 274 if serviceBody.PrimaryKey != "" { 275 apiService, err = c.getAPIServiceByPrimaryKey(serviceBody.PrimaryKey) 276 } 277 return apiService, err 278 } 279 280 // rollbackAPIService - if the process to add api/revision/instance fails, delete the api that was created 281 func (c *ServiceClient) rollbackAPIService(name string) (*apiv1.ResourceInstance, error) { 282 return c.apiServiceDeployAPI(http.MethodDelete, c.cfg.DeleteServicesURL()+"/"+name, nil) 283 } 284 285 // GetAPIServiceByName - Returns the API service based on its name 286 func (c *ServiceClient) GetAPIServiceByName(name string) (*management.APIService, error) { 287 headers, err := c.createHeader() 288 if err != nil { 289 return nil, err 290 } 291 request := coreapi.Request{ 292 Method: coreapi.GET, 293 URL: c.cfg.GetServicesURL() + "/" + name, 294 Headers: headers, 295 } 296 response, err := c.apiClient.Send(request) 297 if err != nil { 298 return nil, err 299 } 300 if response.Code != http.StatusOK { 301 if response.Code != http.StatusNotFound { 302 responseErr := readResponseErrors(response.Code, response.Body) 303 return nil, utilerrors.Wrap(ErrRequestQuery, responseErr) 304 } 305 return nil, nil 306 } 307 apiService := new(management.APIService) 308 err = json.Unmarshal(response.Body, apiService) 309 return apiService, err 310 }