github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/api_json.go (about)

     1  /*
     2   * Copyright 2023 VMware, Inc.  All rights reserved.  Licensed under the Apache v2 License.
     3   */
     4  
     5  package govcd
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  	"github.com/vmware/go-vcloud-director/v2/types/v56"
    11  	"github.com/vmware/go-vcloud-director/v2/util"
    12  	"io"
    13  	"net/http"
    14  	"net/url"
    15  	"strings"
    16  )
    17  
    18  // executeJsonRequest is a wrapper around regular API call operations, similar to client.ExecuteRequest, but with JSON payback
    19  // Returns a http.Response object, which, in case of success, has its body still unread
    20  // Caller function has the responsibility for closing the response body
    21  func (client Client) executeJsonRequest(href, httpMethod string, inputStructure any, errorMessage string) (*http.Response, error) {
    22  
    23  	text, err := json.MarshalIndent(inputStructure, " ", " ")
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  	requestHref, err := url.Parse(href)
    28  	if err != nil {
    29  		return nil, err
    30  	}
    31  
    32  	var resp *http.Response
    33  	body := strings.NewReader(string(text))
    34  	apiVersion := client.APIVersion
    35  	headAccept := http.Header{}
    36  	headAccept.Set("Accept", fmt.Sprintf("application/*+json;version=%s", apiVersion))
    37  	headAccept.Set("Content-Type", "application/*+json")
    38  	request := client.newRequest(nil, nil, httpMethod, *requestHref, body, apiVersion, headAccept)
    39  	resp, err = client.Http.Do(request)
    40  	if err != nil {
    41  		return nil, fmt.Errorf(errorMessage, err)
    42  	}
    43  
    44  	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
    45  		body, _ := io.ReadAll(resp.Body)
    46  		util.ProcessResponseOutput(util.CallFuncName(), resp, string(body))
    47  		var jsonError types.OpenApiError
    48  		err = json.Unmarshal(body, &jsonError)
    49  		// By default, we return the whole response body as error message. This may also contain the stack trace
    50  		message := string(body)
    51  		// if the body contains a valid JSON representation of the error, we return a more agile message, using the
    52  		// exposed fields, and hiding the stack trace from view
    53  		if err == nil {
    54  			message = fmt.Sprintf("%s - %s", jsonError.MinorErrorCode, jsonError.Message)
    55  		}
    56  		util.ProcessResponseOutput(util.CallFuncName(), resp, string(body))
    57  		return resp, fmt.Errorf(errorMessage, message)
    58  	}
    59  
    60  	return checkRespWithErrType(types.BodyTypeJSON, resp, err, &types.Error{})
    61  }
    62  
    63  // closeBody is a wrapper function that should be used with "defer" after calling executeJsonRequest
    64  func closeBody(resp *http.Response) {
    65  	err := resp.Body.Close()
    66  	if err != nil {
    67  		util.Logger.Printf("error closing response body - Called by %s: %s\n", util.CallFuncName(), err)
    68  	}
    69  }