github.com/Ingenico-ePayments/connect-sdk-go@v0.0.0-20240318153750-1f8cd329b9c9/communicator/MetaDataProvider.go (about)

     1  package communicator
     2  
     3  import (
     4  	"encoding/base64"
     5  	"errors"
     6  	"runtime"
     7  
     8  	"github.com/Ingenico-ePayments/connect-sdk-go/communicator/communication"
     9  	"github.com/Ingenico-ePayments/connect-sdk-go/defaultimpl"
    10  	"github.com/Ingenico-ePayments/connect-sdk-go/domain/metadata"
    11  )
    12  
    13  var (
    14  	// ErrProhibitedHeader occurs when the header name is prohibited
    15  	ErrProhibitedHeader = errors.New("header is prohibited")
    16  )
    17  
    18  type serverMetaInfo struct {
    19  	PlatformIdentifier    string                          `json:"platformIdentifier,omitempty"`
    20  	SDKIdentifier         string                          `json:"sdkIdentifier,omitempty"`
    21  	SDKCreator            string                          `json:"sdkCreator,omitempty"`
    22  	Integrator            string                          `json:"integrator,omitempty"`
    23  	ShoppingCartExtension *metadata.ShoppingCartExtension `json:"shoppingCartExtension,omitempty"`
    24  }
    25  
    26  // MetaDataProvider provides meta info about the server. Thread-safe.
    27  type MetaDataProvider struct {
    28  	serverMetaInfo  serverMetaInfo
    29  	metaDataHeaders []communication.Header
    30  }
    31  
    32  // ProhibitedHeaders are the headers that can't be included in the server requests
    33  var ProhibitedHeaders = []string{serverMetaInfoHeader, "X-GCS-Idempotence-Key",
    34  	"Date", "Content-Type", "Authorization"}
    35  
    36  func getPlatformIdentifier() string {
    37  	//I believe there is no standard function thus we need to do something like
    38  	//https://github.com/matishsiao/goInfo
    39  	return runtime.GOOS + " " + runtime.Version() + "(" + runtime.GOARCH + ")"
    40  }
    41  
    42  const sdkIdentifier = "GoServerSDK/v" + sdkVersion
    43  const sdkVersion = "2.47.0"
    44  const serverMetaInfoHeader = "X-GCS-ServerMetaInfo"
    45  
    46  // NewMetaDataProviderWithBuilder creates a MetaDataProvider with the given MetaDataProviderBuilder
    47  func NewMetaDataProviderWithBuilder(builder MetaDataProviderBuilder) (*MetaDataProvider, error) {
    48  	return newMetaDataProvider(builder.integrator, builder.ShoppingCartExtension, builder.AdditionalRequestHeaders)
    49  }
    50  
    51  // NewMetaDataProviderWithIntegrator creates a MetaDataProvider with the given integrator
    52  func NewMetaDataProviderWithIntegrator(integrator string) (*MetaDataProvider, error) {
    53  	return newMetaDataProvider(integrator, nil, nil)
    54  }
    55  
    56  func newMetaDataProvider(integrator string, shoppingCartExtension *metadata.ShoppingCartExtension, additionalRequestHeaders []communication.Header) (*MetaDataProvider, error) {
    57  	err := validateAdditionalRequestHeaders(additionalRequestHeaders)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	sMetaInfo := serverMetaInfo{
    62  		getPlatformIdentifier(),
    63  		sdkIdentifier,
    64  		"Ingenico",
    65  		integrator,
    66  		shoppingCartExtension,
    67  	}
    68  	marshaller, err := defaultimpl.NewDefaultMarshaller()
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	serverMetaInfoString, err := marshaller.Marshal(&sMetaInfo)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	serverMetaInfoBase64 := base64.StdEncoding.EncodeToString([]byte(serverMetaInfoString))
    79  	header, err := communication.NewHeader(serverMetaInfoHeader, serverMetaInfoBase64)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	head := []communication.Header{*header}
    84  	head = append(head, additionalRequestHeaders...)
    85  	return &MetaDataProvider{sMetaInfo, head}, nil
    86  }
    87  
    88  func validateAdditionalRequestHeader(additionalRequestHeader communication.Header) error {
    89  	for _, a := range ProhibitedHeaders {
    90  		if a == additionalRequestHeader.Name() {
    91  			return ErrProhibitedHeader
    92  		}
    93  	}
    94  	return nil
    95  }
    96  
    97  func validateAdditionalRequestHeaders(additionalRequestHeaders []communication.Header) error {
    98  	for _, additionalRequestHeader := range additionalRequestHeaders {
    99  		err := validateAdditionalRequestHeader(additionalRequestHeader)
   100  		if err != nil {
   101  			return err
   102  		}
   103  	}
   104  	return nil
   105  }
   106  
   107  // MetaDataHeaders returns the server related headers containing the metadata to be associated with the request (if any).
   108  // This will always contain at least an automatically generated header X-GCS-ServerMetaInfo.
   109  func (m *MetaDataProvider) MetaDataHeaders() []communication.Header {
   110  	// Return a clone instead of the original slice - immutability insurance
   111  	return append([]communication.Header{}, m.metaDataHeaders...)
   112  }