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 }