github.com/Axway/agent-sdk@v1.1.101/pkg/apic/apiservicerevision.go (about)

     1  package apic
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"net/http"
    10  	"regexp"
    11  	"strconv"
    12  	"strings"
    13  	"text/template"
    14  	"time"
    15  
    16  	"github.com/Axway/agent-sdk/pkg/util"
    17  
    18  	coreapi "github.com/Axway/agent-sdk/pkg/api"
    19  	utilerrors "github.com/Axway/agent-sdk/pkg/util/errors"
    20  
    21  	management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1alpha1"
    22  	"github.com/Axway/agent-sdk/pkg/util/log"
    23  )
    24  
    25  // TODO
    26  /*
    27  	1. Search for comment "DEPRECATED to be removed on major release"
    28  	2. Remove deprecated code left from APIGOV-19751
    29  */
    30  
    31  const (
    32  	apiSvcRevTemplate = "{{.APIServiceName}}{{if ne .Stage \"\"}} ({{.StageLabel}}: {{.Stage}}){{end}} - {{.Date:YYYY/MM/DD}} - r {{.Revision}}"
    33  	defaultDateFormat = "2006/01/02"
    34  	specHashes        = "specHashes"
    35  )
    36  
    37  // APIServiceRevisionTitle - apiservicerevision template for title
    38  type APIServiceRevisionTitle struct {
    39  	APIServiceName string
    40  	Date           string
    41  	Revision       string
    42  	StageLabel     string
    43  	Stage          string
    44  }
    45  
    46  // apiSvcRevTitleDateMap - map of date formats for apiservicerevision title
    47  var apiSvcRevTitleDateMap = map[string]string{
    48  	"MM-DD-YYYY": "01-02-2006",
    49  	"MM/DD/YYYY": "01/02/2006",
    50  	"YYYY-MM-DD": "2006-01-02",
    51  	"YYYY/MM/DD": defaultDateFormat,
    52  }
    53  
    54  func (c *ServiceClient) buildAPIServiceRevision(serviceBody *ServiceBody) *management.APIServiceRevision {
    55  	newRev := management.NewAPIServiceRevision("", c.cfg.GetEnvironmentName())
    56  	newRev.Title = c.updateAPIServiceRevisionTitle(serviceBody)
    57  	newRev.Attributes = util.CheckEmptyMapStringString(serviceBody.RevisionAttributes)
    58  	newRev.Tags = mapToTagsArray(serviceBody.Tags, c.cfg.GetTagsToPublish())
    59  	newRev.Spec = buildAPIServiceRevisionSpec(serviceBody)
    60  	newRev.Owner, _ = c.getOwnerObject(serviceBody, false)
    61  
    62  	revDetails := util.MergeMapStringInterface(serviceBody.ServiceAgentDetails, serviceBody.RevisionAgentDetails)
    63  	agentDetails := buildAgentDetailsSubResource(serviceBody, false, revDetails)
    64  	util.SetAgentDetails(newRev, agentDetails)
    65  
    66  	return newRev
    67  }
    68  
    69  // processRevision -
    70  func (c *ServiceClient) processRevision(serviceBody *ServiceBody) error {
    71  	if serviceBody.serviceContext.serviceAction == updateAPI {
    72  		// get the count of revisions
    73  		serviceBody.serviceContext.revisionCount = c.getRevisionCount("metadata.references.id==" + serviceBody.serviceContext.serviceID)
    74  	}
    75  
    76  	// check if a revision with the same hash was already published
    77  	if revName, found := serviceBody.specHashes[serviceBody.specHash]; found {
    78  		name := revName.(string)
    79  
    80  		// check that the revision still exists
    81  		if c.getRevisionCount("name=="+name) == 1 {
    82  			serviceBody.serviceContext.revisionName = name
    83  			return nil
    84  		}
    85  	}
    86  
    87  	log.Infof("Creating API Service revision for %v-%v in environment %v", serviceBody.APIName, serviceBody.Version, c.cfg.GetEnvironmentName())
    88  	rev, err := c.CreateOrUpdateResource(c.buildAPIServiceRevision(serviceBody))
    89  	if err != nil {
    90  		if serviceBody.serviceContext.serviceAction == addAPI {
    91  			_, rollbackErr := c.rollbackAPIService(serviceBody.serviceContext.serviceName)
    92  			if rollbackErr != nil {
    93  				return errors.New(err.Error() + rollbackErr.Error())
    94  			}
    95  		}
    96  		return err
    97  	}
    98  
    99  	serviceBody.serviceContext.revisionName = rev.Name
   100  
   101  	return nil
   102  }
   103  
   104  func (c *ServiceClient) getRevisionCount(queryString string) int {
   105  	queryParams := map[string]string{
   106  		"query":    queryString,
   107  		"fields":   "id",
   108  		"page":     "1",
   109  		"pageSize": "1",
   110  	}
   111  	res, err := c.executeAPI(coreapi.GET, c.cfg.GetRevisionsURL(), queryParams, nil, nil)
   112  	if err != nil {
   113  		return 0
   114  	}
   115  	if _, found := res.Headers["X-Axway-Total-Count"]; !found {
   116  		return 0
   117  	}
   118  	count, err := strconv.Atoi(res.Headers["X-Axway-Total-Count"][0])
   119  	if err != nil {
   120  		return 0
   121  	}
   122  	return count
   123  }
   124  
   125  // GetAPIRevisions - Returns the list of API revisions for the specified filter
   126  // NOTE : this function can go away.  You can call GetAPIServiceRevisions directly from your function to get []*management.APIServiceRevision
   127  func (c *ServiceClient) GetAPIRevisions(query map[string]string, stage string) ([]*management.APIServiceRevision, error) {
   128  	revisions, err := c.GetAPIServiceRevisions(query, c.cfg.GetRevisionsURL(), stage)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  
   133  	return revisions, nil
   134  }
   135  
   136  // DEPRECATED to be removed on major release - else function for dateRegEx.MatchString(apiSvcRevPattern) will no longer be needed after "${tag} is invalid"
   137  // updateAPIServiceRevisionTitle - update title after creating or updating APIService Revision according to the APIServiceRevision Pattern
   138  func (c *ServiceClient) updateAPIServiceRevisionTitle(serviceBody *ServiceBody) string {
   139  	apiSvcRevPattern := c.cfg.GetAPIServiceRevisionPattern()
   140  	if apiSvcRevPattern == "" {
   141  		apiSvcRevPattern = apiSvcRevTemplate
   142  	}
   143  	dateRegEx := regexp.MustCompile(`\{\{.Date:.*?\}\}`)
   144  
   145  	var dateFormat = ""
   146  
   147  	if dateRegEx.MatchString(apiSvcRevPattern) {
   148  		datePattern := dateRegEx.FindString(apiSvcRevPattern)                              // {{.Date:YYYY/MM/DD}} or one of the validate formats from apiSvcRevTitleDateMap
   149  		index := strings.Index(datePattern, ":")                                           // get index of ":" (colon)
   150  		date := datePattern[index+1 : index+11]                                            // sub out "{{.Date:" and "}}" to get the format of the date only
   151  		dateFormat = apiSvcRevTitleDateMap[date]                                           // make sure dateFormat is a valid date format
   152  		apiSvcRevPattern = strings.Replace(apiSvcRevPattern, datePattern, "{{.Date}}", -1) // Once we have the date format, set to {{.Date}} only
   153  		if dateFormat == "" {
   154  			// Customer is entered an incorrect date format.  Set template and pattern to defaults.
   155  			log.Warnf("CENTRAL_APISERVICEREVISIONPATTERN is returning an invalid {{.Date:*}} format. Setting format to YYYY-MM-DD")
   156  			apiSvcRevPattern = apiSvcRevTemplate
   157  			dateFormat = defaultDateFormat
   158  		}
   159  	} else {
   160  		// Customer is still using deprecated date format.  Set template and pattern to defaults.
   161  		log.DeprecationWarningDoc("{{date:*}} format for CENTRAL_APISERVICEREVISIONPATTERN", "valid {{.Date:*}} formats")
   162  		apiSvcRevPattern = apiSvcRevTemplate
   163  		dateFormat = defaultDateFormat
   164  	}
   165  
   166  	// Build default apiSvcRevTitle.  To be used in case of error processing
   167  	defaultAPISvcRevTitle := fmt.Sprintf(
   168  		"%s - %s - r %s",
   169  		serviceBody.APIName,
   170  		time.Now().Format(dateFormat),
   171  		strconv.Itoa(serviceBody.serviceContext.revisionCount),
   172  	)
   173  
   174  	stage := serviceBody.Stage
   175  	if serviceBody.StageDisplayName != "" {
   176  		stage = serviceBody.StageDisplayName
   177  	}
   178  
   179  	// create apiservicerevision template
   180  	apiSvcRevTitleTemplate := APIServiceRevisionTitle{
   181  		APIServiceName: serviceBody.APIName,
   182  		Date:           time.Now().Format(dateFormat),
   183  		Revision:       strconv.Itoa(serviceBody.serviceContext.revisionCount),
   184  		StageLabel:     serviceBody.StageDescriptor,
   185  		Stage:          stage,
   186  	}
   187  
   188  	title, err := template.New("apiSvcRevTitle").Parse(apiSvcRevPattern)
   189  	if err != nil {
   190  		log.Warnf("Could not render CENTRAL_APISERVICEREVISIONPATTERN. Returning %s", defaultAPISvcRevTitle)
   191  		return defaultAPISvcRevTitle
   192  	}
   193  
   194  	var apiSvcRevTitle bytes.Buffer
   195  
   196  	err = title.Execute(&apiSvcRevTitle, apiSvcRevTitleTemplate)
   197  	if err != nil {
   198  		log.Warnf("Could not render CENTRAL_APISERVICEREVISIONPATTERN. Please refer to axway.docs regarding valid CENTRAL_APISERVICEREVISIONPATTERN. Returning %s", defaultAPISvcRevTitle)
   199  		return defaultAPISvcRevTitle
   200  	}
   201  
   202  	log.Tracef("Returning apiservicerevision title : %s", apiSvcRevTitle.String())
   203  	return apiSvcRevTitle.String()
   204  }
   205  
   206  // GetAPIRevisionByName - Returns the API revision based on its revision name
   207  func (c *ServiceClient) GetAPIRevisionByName(name string) (*management.APIServiceRevision, error) {
   208  	headers, err := c.createHeader()
   209  	if err != nil {
   210  		return nil, err
   211  	}
   212  
   213  	request := coreapi.Request{
   214  		Method:  coreapi.GET,
   215  		URL:     c.cfg.GetRevisionsURL() + "/" + name,
   216  		Headers: headers,
   217  	}
   218  
   219  	response, err := c.apiClient.Send(request)
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  	if response.Code != http.StatusOK {
   224  		if response.Code != http.StatusNotFound {
   225  			responseErr := readResponseErrors(response.Code, response.Body)
   226  			return nil, utilerrors.Wrap(ErrRequestQuery, responseErr)
   227  		}
   228  		return nil, nil
   229  	}
   230  	apiRevision := new(management.APIServiceRevision)
   231  	err = json.Unmarshal(response.Body, apiRevision)
   232  	return apiRevision, err
   233  }
   234  
   235  func buildAPIServiceRevisionSpec(serviceBody *ServiceBody) management.ApiServiceRevisionSpec {
   236  	return management.ApiServiceRevisionSpec{
   237  		ApiService: serviceBody.serviceContext.serviceName,
   238  		Definition: management.ApiServiceRevisionSpecDefinition{
   239  			Type:        getRevisionDefinitionType(*serviceBody),
   240  			Value:       base64.StdEncoding.EncodeToString(serviceBody.SpecDefinition),
   241  			ContentType: serviceBody.ResourceContentType,
   242  			Version:     serviceBody.GetSpecVersion(),
   243  		},
   244  	}
   245  }
   246  
   247  func getRevisionPrefix(serviceBody *ServiceBody) string {
   248  	if serviceBody.Stage != "" {
   249  		return sanitizeAPIName(fmt.Sprintf("%s-%s", serviceBody.serviceContext.serviceName, serviceBody.Stage))
   250  	}
   251  	return sanitizeAPIName(serviceBody.serviceContext.serviceName)
   252  }
   253  
   254  // getRevisionDefinitionType -
   255  func getRevisionDefinitionType(serviceBody ServiceBody) string {
   256  	if serviceBody.ResourceType == "" {
   257  		return Unstructured
   258  	}
   259  	return serviceBody.ResourceType
   260  }