Merge "8/23: merge casablanca to master"
[so.git] / bpmn / MSOCommonBPMN / src / main / groovy / org / onap / so / bpmn / common / scripts / OofUtils.groovy
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2018 Intel Corp. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.so.bpmn.common.scripts
22
23 import org.camunda.bpm.engine.delegate.DelegateExecution
24 import org.onap.so.bpmn.common.scripts.AbstractServiceTaskProcessor
25 import org.onap.so.bpmn.common.scripts.ExceptionUtil
26 import org.onap.so.bpmn.core.UrnPropertiesReader
27 import org.onap.so.bpmn.core.domain.HomingSolution
28 import org.onap.so.bpmn.core.domain.ModelInfo
29 import org.onap.so.bpmn.core.domain.Resource
30 import org.onap.so.bpmn.core.domain.AllottedResource
31 import org.onap.so.bpmn.core.domain.ServiceDecomposition
32 import org.onap.so.bpmn.core.domain.ServiceInstance
33 import org.onap.so.bpmn.core.domain.Subscriber
34 import org.onap.so.bpmn.core.domain.VnfResource
35 import org.onap.so.bpmn.core.json.JsonUtils
36 import org.onap.so.db.catalog.beans.CloudSite
37 import org.onap.so.rest.APIResponse
38 import org.onap.so.rest.RESTClient
39 import org.onap.so.rest.RESTConfig
40 import org.springframework.http.HttpEntity
41 import org.springframework.http.HttpHeaders
42 import org.springframework.http.HttpMethod
43 import org.springframework.http.ResponseEntity
44 import org.springframework.http.client.BufferingClientHttpRequestFactory
45 import org.springframework.http.client.HttpComponentsClientHttpRequestFactory
46 import org.springframework.web.client.RestTemplate
47 import org.springframework.web.util.UriComponentsBuilder
48
49 import javax.ws.rs.core.MediaType
50 import javax.ws.rs.core.Response
51 import javax.xml.ws.http.HTTPException
52
53 import static org.onap.so.bpmn.common.scripts.GenericUtils.*
54
55 class OofUtils {
56     ExceptionUtil exceptionUtil = new ExceptionUtil()
57     JsonUtils jsonUtil = new JsonUtils()
58
59     private AbstractServiceTaskProcessor utils
60
61     OofUtils(AbstractServiceTaskProcessor taskProcessor) {
62         this.utils = taskProcessor
63     }
64
65     /**
66      * This method builds the service-agnostic
67      * OOF json request to get a homing solution
68      * and license solution
69      *
70      * @param execution
71      * @param requestId
72      * @param decomposition - ServiceDecomposition object
73      * @param customerLocation -
74      * @param existingCandidates -
75      * @param excludedCandidates -
76      * @param requiredCandidates -
77      *
78      * @return request - OOF v1 payload - https://wiki.onap.org/pages/viewpage.action?pageId=25435066
79      */
80     String buildRequest(DelegateExecution execution,
81                         String requestId,
82                         ServiceDecomposition decomposition,
83                         Subscriber subscriber = null,
84                         Map customerLocation,
85                         ArrayList existingCandidates = null,
86                         ArrayList excludedCandidates = null,
87                         ArrayList requiredCandidates = null) {
88         def isDebugEnabled = execution.getVariable("isDebugLogEnabled")
89         utils.log("DEBUG", "Started Building OOF Request", isDebugEnabled)
90         String callbackEndpoint = UrnPropertiesReader.getVariable("mso.oof.callbackEndpoint", execution)
91         utils.log("DEBUG", "mso.oof.callbackEndpoint is: " + callbackEndpoint, isDebugEnabled)
92         try {
93             def callbackUrl = utils.createHomingCallbackURL(callbackEndpoint, "oofResponse", requestId)
94             utils.log("DEBUG", "callbackUrl is: " + callbackUrl, isDebugEnabled)
95
96
97             def transactionId = requestId
98             utils.log("DEBUG", "transactionId is: " + transactionId, isDebugEnabled)
99             //ServiceInstance Info
100             ServiceInstance serviceInstance = decomposition.getServiceInstance()
101             def serviceInstanceId = ""
102             def serviceName = ""
103
104             serviceInstanceId = execution.getVariable("serviceInstanceId")
105             utils.log("DEBUG", "serviceInstanceId is: " + serviceInstanceId, isDebugEnabled)
106             serviceName = execution.getVariable("subscriptionServiceType")
107             utils.log("DEBUG", "serviceName is: " + serviceName, isDebugEnabled)
108
109             if (serviceInstanceId == null || serviceInstanceId == "null") {
110                 utils.log("DEBUG", "Unable to obtain Service Instance Id", isDebugEnabled)
111                 exceptionUtil.buildAndThrowWorkflowException(execution, 400, "Internal Error - Unable to " +
112                         "obtain Service Instance Id, execution.getVariable(\"serviceInstanceId\") is null")
113             }
114             if (serviceName == null || serviceName == "null") {
115                 utils.log("DEBUG", "Unable to obtain Service Name", isDebugEnabled)
116                 exceptionUtil.buildAndThrowWorkflowException(execution, 400, "Internal Error - Unable to " +
117                         "obtain Service Name, execution.getVariable(\"subscriptionServiceType\") is null")
118             }
119             //Model Info
120             ModelInfo model = decomposition.getModelInfo()
121             utils.log("DEBUG", "ModelInfo: " + model.toString(), isDebugEnabled)
122             String modelType = model.getModelType()
123             String modelInvariantId = model.getModelInvariantUuid()
124             String modelVersionId = model.getModelUuid()
125             String modelName = model.getModelName()
126             String modelVersion = model.getModelVersion()
127             //Subscriber Info
128             String subscriberId = ""
129             String subscriberName = ""
130             String commonSiteId = ""
131             if (subscriber != null) {
132                 subscriberId = subscriber.getGlobalId()
133                 subscriberName = subscriber.getName()
134                 commonSiteId = subscriber.getCommonSiteId()
135             }
136
137             //Determine RequestType
138             //TODO Figure out better way to determine this
139             String requestType = "create"
140             List<Resource> resources = decomposition.getServiceResources()
141             for (Resource r : resources) {
142                 HomingSolution currentSolution = (HomingSolution) r.getCurrentHomingSolution()
143                 if (currentSolution != null) {
144                     requestType = "speed changed"
145                 }
146             }
147
148             //Demands
149             String placementDemands = ""
150             StringBuilder sb = new StringBuilder()
151             List<AllottedResource> allottedResourceList = decomposition.getAllottedResources()
152             List<VnfResource> vnfResourceList = decomposition.getVnfResources()
153
154             if (allottedResourceList == null || allottedResourceList.isEmpty()) {
155                 utils.log("DEBUG", "Allotted Resources List is empty - will try to get service VNFs instead.",
156                         isDebugEnabled)
157             } else {
158                 for (AllottedResource resource : allottedResourceList) {
159                     utils.log("DEBUG", "Allotted Resource: " + resource.toString(),
160                             isDebugEnabled)
161                     def serviceResourceId = resource.getResourceId()
162                     def resourceModelInvariantId = resource.getModelInfo().getModelInvariantUuid()
163                     def resourceModelVersionId = resource.getModelInfo().getModelUuid()
164                     def resourceModelName = resource.getModelInfo().getModelName()
165                     def resourceModelVersion = resource.getModelInfo().getModelVersion()
166                     def resourceModelType = resource.getModelInfo().getModelType()
167                     def tenantId = execution.getVariable("tenantId")
168                     def requiredCandidatesJson = ""
169
170                     requiredCandidatesJson = createCandidateJson(
171                             existingCandidates,
172                             excludedCandidates,
173                             requiredCandidates)
174
175                     String demand =
176                             "      {\n" +
177                                     "      \"resourceModuleName\": \"${resourceModelName}\",\n" +
178                                     "      \"serviceResourceId\": \"${serviceResourceId}\",\n" +
179                                     "      \"tenantId\": \"${tenantId}\",\n" +
180                                     "      \"resourceModelInfo\": {\n" +
181                                     "        \"modelInvariantId\": \"${resourceModelInvariantId}\",\n" +
182                                     "        \"modelVersionId\": \"${resourceModelVersionId}\",\n" +
183                                     "        \"modelName\": \"${resourceModelName}\",\n" +
184                                     "        \"modelType\": \"${resourceModelType}\",\n" +
185                                     "        \"modelVersion\": \"${resourceModelVersion}\",\n" +
186                                     "        \"modelCustomizationName\": \"\"\n" +
187                                     "        }" + requiredCandidatesJson + "\n" +
188                                     "      },"
189
190                     placementDemands = sb.append(demand)
191                 }
192             }
193
194             if (vnfResourceList == null || vnfResourceList.isEmpty()) {
195                 utils.log("DEBUG", "VNF Resources List is empty",
196                         isDebugEnabled)
197             } else {
198
199                 for (VnfResource vnfResource : vnfResourceList) {
200                     utils.log("DEBUG", "VNF Resource: " + vnfResource.toString(),
201                             isDebugEnabled)
202                     ModelInfo vnfResourceModelInfo = vnfResource.getModelInfo()
203                     def serviceResourceId = vnfResource.getResourceId()
204                     def resourceModelInvariantId = vnfResourceModelInfo.getModelInvariantUuid()
205                     def resourceModelName = vnfResourceModelInfo.getModelName()
206                     def resourceModelVersion = vnfResourceModelInfo.getModelVersion()
207                     def resourceModelVersionId = vnfResourceModelInfo.getModelUuid()
208                     def resourceModelType = vnfResourceModelInfo.getModelType()
209                     def tenantId = execution.getVariable("tenantId")
210                     def requiredCandidatesJson = ""
211
212
213                     String placementDemand =
214                             "      {\n" +
215                                     "      \"resourceModuleName\": \"${resourceModelName}\",\n" +
216                                     "      \"serviceResourceId\": \"${serviceResourceId}\",\n" +
217                                     "      \"tenantId\": \"${tenantId}\",\n" +
218                                     "      \"resourceModelInfo\": {\n" +
219                                     "        \"modelInvariantId\": \"${resourceModelInvariantId}\",\n" +
220                                     "        \"modelVersionId\": \"${resourceModelVersionId}\",\n" +
221                                     "        \"modelName\": \"${resourceModelName}\",\n" +
222                                     "        \"modelType\": \"${resourceModelType}\",\n" +
223                                     "        \"modelVersion\": \"${resourceModelVersion}\",\n" +
224                                     "        \"modelCustomizationName\": \"\"\n" +
225                                     "        }" + requiredCandidatesJson + "\n" +
226                                     "      },"
227
228                     placementDemands = sb.append(placementDemand)
229                 }
230                 placementDemands = placementDemands.substring(0, placementDemands.length() - 1)
231             }
232
233             /* Commenting Out Licensing as OOF doesn't support for Beijing
234         String licenseDemands = ""
235         sb = new StringBuilder()
236         if (vnfResourceList.isEmpty() || vnfResourceList == null) {
237             utils.log("DEBUG", "Vnf Resources List is Empty", isDebugEnabled)
238         } else {
239             for (VnfResource vnfResource : vnfResourceList) {
240                 ModelInfo vnfResourceModelInfo = vnfResource.getModelInfo()
241                 def resourceInstanceType = vnfResource.getResourceType()
242                 def serviceResourceId = vnfResource.getResourceId()
243                 def resourceModuleName = vnfResource.getResourceType()
244                 def resouceModelInvariantId = vnfResourceModelInfo.getModelInvariantUuid()
245                 def resouceModelName = vnfResourceModelInfo.getModelName()
246                 def resouceModelVersion = vnfResourceModelInfo.getModelVersion()
247                 def resouceModelVersionId = vnfResourceModelInfo.getModelUuid()
248                 def resouceModelType = vnfResourceModelInfo.getModelType()
249
250                 // TODO Add Existing Licenses to demand
251                 //"existingLicenses": {
252                 //"entitlementPoolUUID": ["87257b49-9602-4ca1-9817-094e52bc873b",
253                 // "43257b49-9602-4fe5-9337-094e52bc9435"],
254                 //"licenseKeyGroupUUID": ["87257b49-9602-4ca1-9817-094e52bc873b",
255                 // "43257b49-9602-4fe5-9337-094e52bc9435"]
256                 //}
257
258                     String licenseDemand =
259                         "{\n" +
260                         "\"resourceModuleName\": \"${resourceModuleName}\",\n" +
261                         "\"serviceResourceId\": \"${serviceResourceId}\",\n" +
262                         "\"resourceInstanceType\": \"${resourceInstanceType}\",\n" +
263                         "\"resourceModelInfo\": {\n" +
264                         "  \"modelInvariantId\": \"${resouceModelInvariantId}\",\n" +
265                         "  \"modelVersionId\": \"${resouceModelVersionId}\",\n" +
266                         "  \"modelName\": \"${resouceModelName}\",\n" +
267                         "  \"modelType\": \"${resouceModelType}\",\n" +
268                         "  \"modelVersion\": \"${resouceModelVersion}\",\n" +
269                         "  \"modelCustomizationName\": \"\"\n" +
270                         "  }\n"
271                         "},"
272
273                 licenseDemands = sb.append(licenseDemand)
274             }
275             licenseDemands = licenseDemands.substring(0, licenseDemands.length() - 1)
276         }*/
277
278             String request =
279                     "{\n" +
280                             "  \"requestInfo\": {\n" +
281                             "    \"transactionId\": \"${transactionId}\",\n" +
282                             "    \"requestId\": \"${requestId}\",\n" +
283                             "    \"callbackUrl\": \"${callbackUrl}\",\n" +
284                             "    \"sourceId\": \"so\",\n" +
285                             "    \"requestType\": \"${requestType}\"," +
286                             "    \"numSolutions\": 1,\n" +
287                             "    \"optimizers\": [\"placement\"],\n" +
288                             "    \"timeout\": 600\n" +
289                             "    },\n" +
290                             "  \"placementInfo\": {\n" +
291                             "    \"requestParameters\": {\n" +
292                             "      \"customerLatitude\": \"${customerLocation.customerLatitude}\",\n" +
293                             "      \"customerLongitude\": \"${customerLocation.customerLongitude}\",\n" +
294                             "      \"customerName\": \"${customerLocation.customerName}\"\n" +
295                             "    }," +
296                             "    \"subscriberInfo\": { \n" +
297                             "      \"globalSubscriberId\": \"${subscriberId}\",\n" +
298                             "      \"subscriberName\": \"${subscriberName}\",\n" +
299                             "      \"subscriberCommonSiteId\": \"${commonSiteId}\"\n" +
300                             "    },\n" +
301                             "    \"placementDemands\": [\n" +
302                             "      ${placementDemands}\n" +
303                             "      ]\n" +
304                             "    },\n" +
305                             "  \"serviceInfo\": {\n" +
306                             "    \"serviceInstanceId\": \"${serviceInstanceId}\",\n" +
307                             "    \"serviceName\": \"${serviceName}\",\n" +
308                             "    \"modelInfo\": {\n" +
309                             "      \"modelType\": \"${modelType}\",\n" +
310                             "      \"modelInvariantId\": \"${modelInvariantId}\",\n" +
311                             "      \"modelVersionId\": \"${modelVersionId}\",\n" +
312                             "      \"modelName\": \"${modelName}\",\n" +
313                             "      \"modelVersion\": \"${modelVersion}\",\n" +
314                             "      \"modelCustomizationName\": \"\"\n" +
315                             "    }\n" +
316                             "  }\n" +
317                             "}"
318
319
320             utils.log("DEBUG", "Completed Building OOF Request", isDebugEnabled)
321             return request
322         } catch (Exception ex) {
323              utils.log("DEBUG", "buildRequest Exception: " + ex, isDebugEnabled)
324         }
325     }
326
327     /**
328      * This method validates the callback response
329      * from OOF. If the response contains an
330      * exception the method will build and throw
331      * a workflow exception.
332      *
333      * @param execution
334      * @param response - the async callback response from oof
335      */
336     Void validateCallbackResponse(DelegateExecution execution, String response) {
337         def isDebugEnabled = execution.getVariable("isDebugLogEnabled")
338         String placements = ""
339         if (isBlank(response)) {
340             exceptionUtil.buildAndThrowWorkflowException(execution, 5000, "OOF Async Callback Response is Empty")
341         } else {
342             if (JsonUtils.jsonElementExist(response, "solutions.placementSolutions")) {
343                 placements = jsonUtil.getJsonValue(response, "solutions.placementSolutions")
344                 if (isBlank(placements) || placements.equalsIgnoreCase("[]")) {
345                     String statusMessage = jsonUtil.getJsonValue(response, "statusMessage")
346                     if (isBlank(statusMessage)) {
347                         utils.log("DEBUG", "Error Occurred in Homing: OOF Async Callback Response does " +
348                                 "not contain placement solution.", isDebugEnabled)
349                         exceptionUtil.buildAndThrowWorkflowException(execution, 400,
350                                 "OOF Async Callback Response does not contain placement solution.")
351                     } else {
352                         utils.log("DEBUG", "Error Occurred in Homing: " + statusMessage, isDebugEnabled)
353                         exceptionUtil.buildAndThrowWorkflowException(execution, 400, statusMessage)
354                     }
355                 } else {
356                     return
357                 }
358             } else if (response.contains("error") || response.contains("Error") ) {
359                 String errorMessage = ""
360                 if (response.contains("policyException")) {
361                     String text = jsonUtil.getJsonValue(response, "requestError.policyException.text")
362                     errorMessage = "OOF Async Callback Response contains a Request Error Policy Exception: " + text
363                 } else if (response.contains("Unable to find any candidate for demand")) {
364                     errorMessage = "OOF Async Callback Response contains error: Unable to find any candidate for " +
365                             "demand *** Response: " + response.toString()
366                 } else if (response.contains("serviceException")) {
367                     String text = jsonUtil.getJsonValue(response, "requestError.serviceException.text")
368                     errorMessage = "OOF Async Callback Response contains a Request Error Service Exception: " + text
369                 } else {
370                     errorMessage = "OOF Async Callback Response contains a Request Error. Unable to determine the Request Error Exception."
371                 }
372                 utils.log("DEBUG", "Error Occurred in Homing: " + errorMessage, isDebugEnabled)
373                 exceptionUtil.buildAndThrowWorkflowException(execution, 400, errorMessage)
374
375             } else {
376                 utils.log("DEBUG", "Error Occurred in Homing: Received an Unknown Async Callback Response from OOF.", isDebugEnabled)
377                 exceptionUtil.buildAndThrowWorkflowException(execution, 2500, "Received an Unknown Async Callback Response from OOF.")
378             }
379         }
380
381     }
382
383     /**
384      * This method creates candidates json for placement Demands.
385      *
386      * @param execution
387      * @param existingCandidates -
388      * @param excludedCandidates -
389      * @param requiredCandidates -
390      *
391      * @return candidatesJson - a JSON string with candidates
392      */
393     String createCandidateJson(ArrayList existingCandidates = null,
394                                ArrayList excludedCandidates = null,
395                                ArrayList requiredCandidates = null) {
396         def candidatesJson = ""
397         def type = ""
398         if (existingCandidates != null && existingCandidates != {}) {
399             sb = new StringBuilder()
400             sb.append(",\n" +
401                     "  \"existingCandidates\": [\n")
402             def existingCandidateJson = ""
403             existingCandidates.each { existingCandidate ->
404                 type = existingCandidate.get('identifierType')
405                 if (type == 'vimId') {
406                     def cloudOwner = existingCandidate.get('cloudOwner')
407                     def cloudRegionId = existingCandidate.get('identifiers')
408                     existingCandidateJson = "{\n" +
409                             "    \"identifierType\": \"vimId\",\n" +
410                             "    \"cloudOwner\": \"${cloudOwner}\",\n" +
411                             "    \"identifiers\": [\"${cloudRegionId}\"]\n" +
412                             "    },"
413                     sb.append(existingCandidateJson)
414                 }
415                 if (type == 'serviceInstanceId') {
416                     def serviceInstanceId = existingCandidate.get('identifiers')
417                     existingCandidateJson += "{\n" +
418                             "    \"identifierType\": \"serviceInstanceId\",\n" +
419                             "    \"identifiers\": [\"${serviceInstanceId}\"]\n" +
420                             "    },"
421                     sb.append(existingCandidateJson)
422                 }
423             }
424             if (existingCandidateJson != "") {
425                 sb.setLength(sb.length() - 1)
426                 candidatesJson = sb.append(",\n],")
427             }
428         }
429         if (excludedCandidates != null && excludedCandidates != {}) {
430             sb = new StringBuilder()
431             sb.append(",\n" +
432                     "  \"excludedCandidates\": [\n")
433             def excludedCandidateJson = ""
434             excludedCandidates.each { excludedCandidate ->
435                 type = excludedCandidate.get('identifierType')
436                 if (type == 'vimId') {
437                     def cloudOwner = excludedCandidate.get('cloudOwner')
438                     def cloudRegionId = excludedCandidate.get('identifiers')
439                     excludedCandidateJson = "{\n" +
440                             "    \"identifierType\": \"vimId\",\n" +
441                             "    \"cloudOwner\": \"${cloudOwner}\",\n" +
442                             "    \"identifiers\": [\"${cloudRegionId}\"]\n" +
443                             "    },"
444                     sb.append(excludedCandidateJson)
445                 }
446                 if (type == 'serviceInstanceId') {
447                     def serviceInstanceId = excludedCandidate.get('identifiers')
448                     excludedCandidateJson += "{\n" +
449                             "    \"identifierType\": \"serviceInstanceId\",\n" +
450                             "    \"identifiers\": [\"${serviceInstanceId}\"]\n" +
451                             "    },"
452                     sb.append(excludedCandidateJson)
453                 }
454             }
455             if (excludedCandidateJson != "") {
456                 sb.setLength(sb.length() - 1)
457                 candidatesJson = sb.append(",\n],")
458             }
459         }
460         if (requiredCandidates != null && requiredCandidates != {}) {
461             sb = new StringBuilder()
462             sb.append(",\n" +
463                     "  \"requiredCandidates\": [\n")
464             def requiredCandidatesJson = ""
465             requiredCandidates.each { requiredCandidate ->
466                 type = requiredCandidate.get('identifierType')
467                 if (type == 'vimId') {
468                     def cloudOwner = requiredCandidate.get('cloudOwner')
469                     def cloudRegionId = requiredCandidate.get('identifiers')
470                     requiredCandidatesJson = "{\n" +
471                             "    \"identifierType\": \"vimId\",\n" +
472                             "    \"cloudOwner\": \"${cloudOwner}\",\n" +
473                             "    \"identifiers\": [\"${cloudRegionId}\"]\n" +
474                             "    },"
475                     sb.append(requiredCandidatesJson)
476                 }
477                 if (type == 'serviceInstanceId') {
478                     def serviceInstanceId = requiredCandidate.get('identifiers')
479                     requiredCandidatesJson += "{\n" +
480                             "    \"identifierType\": \"serviceInstanceId\",\n" +
481                             "    \"identifiers\": [\"${serviceInstanceId}\"]\n" +
482                             "    },"
483                     sb.append(requiredCandidatesJson)
484                 }
485             }
486             if (requiredCandidatesJson != "") {
487                 sb.setLength(sb.length() - 1)
488                 candidatesJson = sb.append(",\n],")
489             }
490         }
491         if (candidatesJson != "") {candidatesJson = candidatesJson.substring(0, candidatesJson.length() - 1)}
492         return candidatesJson
493     }
494     /**
495      * This method creates a cloudsite in catalog database.
496      *
497      * @param CloudSite cloudSite
498      *
499      * @return void
500      */
501     Void createCloudSiteCatalogDb(CloudSite cloudSite, DelegateExecution execution) {
502
503         String endpoint = UrnPropertiesReader.getVariable("mso.catalog.db.spring.endpoint", execution)
504         String auth = UrnPropertiesReader.getVariable("mso.db.auth", execution)
505         String uri = "/cloudSite"
506
507         HttpHeaders headers = new HttpHeaders()
508
509         headers.set(HttpHeaders.AUTHORIZATION, auth)
510         headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)
511         headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
512
513         UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(endpoint + uri)
514         HttpEntity<CloudSite> request = new HttpEntity<CloudSite>(cloudSite, headers)
515         RESTConfig config = new RESTConfig(endpoint + uri)
516         RESTClient client = new RESTClient(config).addAuthorizationHeader(auth).
517                 addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON).addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
518         APIResponse response = client.httpPost(request.getBody().toString())
519
520         int responseCode = response.getStatusCode()
521         logDebug("CatalogDB response code is: " + responseCode, isDebugEnabled)
522         String syncResponse = response.getResponseBodyAsString()
523         logDebug("CatalogDB response is: " + syncResponse, isDebugEnabled)
524
525         if(responseCode != 202){
526             exceptionUtil.buildAndThrowWorkflowException(execution, responseCode, "Received a Bad Sync Response from CatalogDB.")
527         }
528     }
529 }