github.com/vmware/transport-go@v1.3.4/model/message.go (about)

     1  // Copyright 2019-2020 VMware, Inc.
     2  // SPDX-License-Identifier: BSD-2-Clause
     3  
     4  package model
     5  
     6  import (
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"github.com/google/uuid"
    11  	"github.com/mitchellh/mapstructure"
    12  	"reflect"
    13  )
    14  
    15  // Direction int defining which way messages are travelling on a Channel.
    16  type Direction int
    17  
    18  const (
    19  	RequestDir  Direction = 0
    20  	ResponseDir Direction = 1
    21  	ErrorDir    Direction = 2
    22  )
    23  
    24  // A Message is the encapsulation of the event sent on the bus.
    25  // It holds a Direction, errors, a Payload and more.
    26  type Message struct {
    27  	Id            *uuid.UUID      `json:"id"`            // message identifier
    28  	DestinationId *uuid.UUID      `json:"destinationId"` // destinationId (targeted recipient)
    29  	Channel       string          `json:"channel"`       // reference to channel message was sent on.
    30  	Destination   string          `json:"destination"`   // destination message was sent to (if galactic)
    31  	Payload       interface{}     `json:"payload"`
    32  	Error         error           `json:"error"`
    33  	Direction     Direction       `json:"direction"`
    34  	Headers       []MessageHeader `json:"headers"`
    35  }
    36  
    37  // A Message header can contain any meta data.
    38  type MessageHeader struct {
    39  	Label string
    40  	Value string
    41  }
    42  
    43  // CastPayloadToType converts the raw interface{} typed Payload into the
    44  // specified object passed as an argument.
    45  func (m *Message) CastPayloadToType(typ interface{}) error {
    46  	var unwrappedResponse Response
    47  
    48  	// assert pointer type
    49  	typVal := reflect.ValueOf(typ)
    50  	if typVal.Kind() != reflect.Ptr {
    51  		return fmt.Errorf("CastPayloadToType: invalid argument. argument should be the address of an object")
    52  	}
    53  
    54  	// nil-check
    55  	if typVal.IsNil() {
    56  		return fmt.Errorf("CastPayloadToType: cannot cast to nil")
    57  	}
    58  
    59  	// if message.Payload is already of *Response type, handle it here.
    60  	if resp, ok := m.Payload.(*Response); ok {
    61  		return decodeResponsePaylod(resp, typ)
    62  	}
    63  
    64  	// otherwise, unmrashal message.Payload into Response, then decode response.Payload
    65  	if err := json.Unmarshal(m.Payload.([]byte), &unwrappedResponse); err != nil {
    66  		return fmt.Errorf("CastPayloadToType: failed to unmarshal payload %v: %w", m.Payload, err)
    67  	}
    68  
    69  	return decodeResponsePaylod(&unwrappedResponse, typ)
    70  }
    71  
    72  // decodeResponsePaylod tries to unpack Response.Payload into typ.
    73  func decodeResponsePaylod(resp *Response, typ interface{}) error {
    74  	if resp.Error {
    75  		return errors.New(resp.ErrorMessage)
    76  	}
    77  	return mapstructure.Decode(resp.Payload, typ)
    78  }