github.com/newrelic/go-agent@v3.26.0+incompatible/internal/cat/txndata.go (about)

     1  // Copyright 2020 New Relic Corporation. All rights reserved.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package cat
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"errors"
    10  
    11  	"github.com/newrelic/go-agent/internal/jsonx"
    12  )
    13  
    14  // TxnDataHeader represents a decoded TxnData header.
    15  type TxnDataHeader struct {
    16  	GUID     string
    17  	TripID   string
    18  	PathHash string
    19  }
    20  
    21  var (
    22  	errInvalidTxnDataJSON     = errors.New("invalid transaction data JSON")
    23  	errInvalidTxnDataGUID     = errors.New("GUID is not a string")
    24  	errInvalidTxnDataTripID   = errors.New("trip ID is not a string or null")
    25  	errInvalidTxnDataPathHash = errors.New("path hash is not a string or null")
    26  )
    27  
    28  // MarshalJSON marshalls a TxnDataHeader as raw JSON.
    29  func (txnData *TxnDataHeader) MarshalJSON() ([]byte, error) {
    30  	// Note that, although there are two and four element versions of this header
    31  	// in the wild, we will only ever generate the four element version.
    32  
    33  	buf := bytes.NewBufferString("[")
    34  
    35  	jsonx.AppendString(buf, txnData.GUID)
    36  
    37  	// Write the unused second field.
    38  	buf.WriteString(",false,")
    39  	jsonx.AppendString(buf, txnData.TripID)
    40  
    41  	buf.WriteString(",")
    42  	jsonx.AppendString(buf, txnData.PathHash)
    43  
    44  	buf.WriteString("]")
    45  
    46  	return buf.Bytes(), nil
    47  }
    48  
    49  // UnmarshalJSON unmarshalls a TxnDataHeader from raw JSON.
    50  func (txnData *TxnDataHeader) UnmarshalJSON(data []byte) error {
    51  	var ok bool
    52  	var v interface{}
    53  
    54  	if err := json.Unmarshal(data, &v); err != nil {
    55  		return err
    56  	}
    57  
    58  	arr, ok := v.([]interface{})
    59  	if !ok {
    60  		return errInvalidTxnDataJSON
    61  	}
    62  	if len(arr) < 2 {
    63  		return errUnexpectedArraySize{
    64  			label:    "unexpected number of transaction data elements",
    65  			expected: 2,
    66  			actual:   len(arr),
    67  		}
    68  	}
    69  
    70  	if txnData.GUID, ok = arr[0].(string); !ok {
    71  		return errInvalidTxnDataGUID
    72  	}
    73  
    74  	// Ignore the unused second field.
    75  
    76  	// Set up defaults for the optional values.
    77  	txnData.TripID = ""
    78  	txnData.PathHash = ""
    79  
    80  	if len(arr) >= 3 {
    81  		// Per the cross agent tests, an explicit null is valid here.
    82  		if nil != arr[2] {
    83  			if txnData.TripID, ok = arr[2].(string); !ok {
    84  				return errInvalidTxnDataTripID
    85  			}
    86  		}
    87  
    88  		if len(arr) >= 4 {
    89  			// Per the cross agent tests, an explicit null is also valid here.
    90  			if nil != arr[3] {
    91  				if txnData.PathHash, ok = arr[3].(string); !ok {
    92  					return errInvalidTxnDataPathHash
    93  				}
    94  			}
    95  		}
    96  	}
    97  
    98  	return nil
    99  }