go.temporal.io/server@v1.23.0/common/codec/jsonpb.go (about)

     1  // The MIT License
     2  //
     3  // Copyright (c) 2020 Temporal Technologies Inc.  All rights reserved.
     4  //
     5  // Copyright (c) 2020 Uber Technologies, Inc.
     6  //
     7  // Permission is hereby granted, free of charge, to any person obtaining a copy
     8  // of this software and associated documentation files (the "Software"), to deal
     9  // in the Software without restriction, including without limitation the rights
    10  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    11  // copies of the Software, and to permit persons to whom the Software is
    12  // furnished to do so, subject to the following conditions:
    13  //
    14  // The above copyright notice and this permission notice shall be included in
    15  // all copies or substantial portions of the Software.
    16  //
    17  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    18  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    19  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    20  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    21  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    22  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    23  // THE SOFTWARE.
    24  
    25  package codec
    26  
    27  import (
    28  	"bytes"
    29  	"encoding/json"
    30  	"fmt"
    31  
    32  	historypb "go.temporal.io/api/history/v1"
    33  	"go.temporal.io/api/temporalproto"
    34  	"google.golang.org/protobuf/encoding/protojson"
    35  	"google.golang.org/protobuf/proto"
    36  )
    37  
    38  type (
    39  	// JSONPBEncoder is JSON encoder/decoder for protobuf structs and slices of protobuf structs.
    40  	// This is an wrapper on top of jsonpb.Marshaler which supports not only single object serialization
    41  	// but also slices of concrete objects.
    42  	JSONPBEncoder struct {
    43  		marshaler   protojson.MarshalOptions
    44  		unmarshaler temporalproto.CustomJSONUnmarshalOptions
    45  	}
    46  )
    47  
    48  // NewJSONPBEncoder creates a new JSONPBEncoder.
    49  func NewJSONPBEncoder() JSONPBEncoder {
    50  	return JSONPBEncoder{}
    51  }
    52  
    53  // NewJSONPBIndentEncoder creates a new JSONPBEncoder with indent.
    54  func NewJSONPBIndentEncoder(indent string) JSONPBEncoder {
    55  	return JSONPBEncoder{
    56  		marshaler: protojson.MarshalOptions{
    57  			Indent: indent,
    58  		},
    59  	}
    60  }
    61  
    62  // Encode protobuf struct to bytes.
    63  func (e JSONPBEncoder) Encode(pb proto.Message) ([]byte, error) {
    64  	return e.marshaler.Marshal(pb)
    65  }
    66  
    67  // Decode bytes to protobuf struct.
    68  func (e JSONPBEncoder) Decode(data []byte, pb proto.Message) error {
    69  	return e.unmarshaler.Unmarshal(data, pb)
    70  }
    71  
    72  // Encode HistoryEvent slice to bytes.
    73  func (e *JSONPBEncoder) EncodeHistoryEvents(historyEvents []*historypb.HistoryEvent) ([]byte, error) {
    74  	return e.encodeSlice(
    75  		len(historyEvents),
    76  		func(i int) proto.Message { return historyEvents[i] })
    77  }
    78  
    79  // Encode History slice to bytes.
    80  func (e *JSONPBEncoder) EncodeHistories(histories []*historypb.History) ([]byte, error) {
    81  	return e.encodeSlice(
    82  		len(histories),
    83  		func(i int) proto.Message { return histories[i] })
    84  }
    85  
    86  // Decode HistoryEvent slice from bytes.
    87  func (e *JSONPBEncoder) DecodeHistoryEvents(data []byte) ([]*historypb.HistoryEvent, error) {
    88  	var historyEvents []*historypb.HistoryEvent
    89  	err := e.DecodeSlice(
    90  		data,
    91  		func() proto.Message {
    92  			historyEvent := &historypb.HistoryEvent{}
    93  			historyEvents = append(historyEvents, historyEvent)
    94  			return historyEvent
    95  		})
    96  	return historyEvents, err
    97  }
    98  
    99  // Decode History slice from bytes.
   100  func (e *JSONPBEncoder) DecodeHistories(data []byte) ([]*historypb.History, error) {
   101  	var histories []*historypb.History
   102  	err := e.DecodeSlice(
   103  		data,
   104  		func() proto.Message {
   105  			history := &historypb.History{}
   106  			histories = append(histories, history)
   107  			return history
   108  		})
   109  
   110  	return histories, err
   111  }
   112  
   113  // Due to the lack of generics in go
   114  // this function accepts callback which should return particular item by it index.
   115  func (e *JSONPBEncoder) encodeSlice(
   116  	len int,
   117  	item func(i int) proto.Message,
   118  ) ([]byte, error) {
   119  	var buf bytes.Buffer
   120  	buf.WriteString("[")
   121  	for i := 0; i < len; i++ {
   122  		pb := item(i)
   123  		bs, err := e.marshaler.Marshal(pb)
   124  		if err != nil {
   125  			return nil, err
   126  		}
   127  		buf.Write(bs)
   128  
   129  		if i == len-1 {
   130  			buf.WriteString("]")
   131  		} else {
   132  			buf.WriteString(",")
   133  		}
   134  	}
   135  	return buf.Bytes(), nil
   136  }
   137  
   138  // constructor callback must create empty object, add it to result slice, and return it.
   139  func (e *JSONPBEncoder) DecodeSlice(
   140  	data []byte,
   141  	constructor func() proto.Message) error {
   142  
   143  	dec := json.NewDecoder(bytes.NewReader(data))
   144  
   145  	tok, err := dec.Token()
   146  	if err != nil {
   147  		return err
   148  	}
   149  	if delim, ok := tok.(json.Delim); !ok || delim != '[' {
   150  		return fmt.Errorf("invalid json: expected [ but found %v", tok)
   151  	}
   152  
   153  	// We need DiscardUnknown here as the history json may have been written by a
   154  	// different proto revision
   155  	unmarshaller := temporalproto.CustomJSONUnmarshalOptions{
   156  		DiscardUnknown: true,
   157  	}
   158  
   159  	var buf json.RawMessage
   160  	for dec.More() {
   161  		if err := dec.Decode(&buf); err != nil {
   162  			return err
   163  		}
   164  		pb := constructor()
   165  		if err := unmarshaller.Unmarshal([]byte(buf), pb); err != nil {
   166  			return err
   167  		}
   168  		buf = buf[:0]
   169  	}
   170  
   171  	return nil
   172  }