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  }