...

Source file src/github.com/launchdarkly/go-server-sdk/v6/internal/datakinds/data_kinds_impl.go

Documentation: github.com/launchdarkly/go-server-sdk/v6/internal/datakinds

     1  package datakinds
     2  
     3  import (
     4  	"github.com/launchdarkly/go-server-sdk-evaluation/v2/ldmodel"
     5  	"github.com/launchdarkly/go-server-sdk/v6/subsystems/ldstoretypes"
     6  
     7  	"github.com/launchdarkly/go-jsonstream/v3/jreader"
     8  )
     9  
    10  // This file defines the StoreDataKind implementations corresponding to our two top-level data model
    11  // types, FeatureFlag and Segment (both of which are defined in go-server-sdk-evaluation). We access
    12  // these objects directly throughout the SDK. We also export them indirectly in the package
    13  // subsystems/ldstoreimpl, because they may be needed by external code that is implementing a
    14  // custom data store.
    15  
    16  //nolint:gochecknoglobals // global used as a constant for efficiency
    17  var modelSerialization = ldmodel.NewJSONDataModelSerialization()
    18  
    19  // When we produce a JSON representation for a deleted item (tombstone), we need to ensure that it has
    20  // all the properties that the SDKs expect to see in a non-deleted item as well, since some SDKS (like
    21  // PHP) are not tolerant of missing properties when unmarshaling. That means it should have a key as
    22  // well - but the Serialize methods do not receive a key parameter, so we need to make up something.
    23  // The SDK should not actually do anything with the key property in a tombstone but just in case, we'll
    24  // make it something that cannot conflict with a real flag key (since '$' is not allowed).
    25  const deletedItemPlaceholderKey = "$deleted"
    26  
    27  // Type aliases for our two implementations of StoreDataKind
    28  type featureFlagStoreDataKind struct{}
    29  type segmentStoreDataKind struct{}
    30  
    31  // Features is the global StoreDataKind instance for feature flags.
    32  var Features DataKindInternal = featureFlagStoreDataKind{} //nolint:gochecknoglobals
    33  
    34  // Segments is the global StoreDataKind instance for segments.
    35  var Segments DataKindInternal = segmentStoreDataKind{} //nolint:gochecknoglobals
    36  
    37  // AllDataKinds returns all the supported data StoreDataKinds.
    38  func AllDataKinds() []ldstoretypes.DataKind {
    39  	return []ldstoretypes.DataKind{Features, Segments}
    40  }
    41  
    42  // GetName returns the unique namespace identifier for feature flag objects.
    43  func (fk featureFlagStoreDataKind) GetName() string {
    44  	return "features"
    45  }
    46  
    47  // Serialize is used internally by the SDK when communicating with a PersistentDataStore.
    48  func (fk featureFlagStoreDataKind) Serialize(item ldstoretypes.ItemDescriptor) []byte {
    49  	if item.Item == nil {
    50  		flag := ldmodel.FeatureFlag{Key: deletedItemPlaceholderKey, Version: item.Version, Deleted: true}
    51  		if bytes, err := modelSerialization.MarshalFeatureFlag(flag); err == nil {
    52  			return bytes
    53  		}
    54  	} else if flag, ok := item.Item.(*ldmodel.FeatureFlag); ok {
    55  		if bytes, err := modelSerialization.MarshalFeatureFlag(*flag); err == nil {
    56  			return bytes
    57  		}
    58  	}
    59  	return nil
    60  }
    61  
    62  // Deserialize is used internally by the SDK when communicating with a PersistentDataStore.
    63  func (fk featureFlagStoreDataKind) Deserialize(data []byte) (ldstoretypes.ItemDescriptor, error) {
    64  	flag, err := modelSerialization.UnmarshalFeatureFlag(data)
    65  	return maybeFlag(flag, err)
    66  }
    67  
    68  // DeserializeFromJSONReader is used internally by the SDK when parsing multiple flags at once.
    69  func (fk featureFlagStoreDataKind) DeserializeFromJSONReader(reader *jreader.Reader) (
    70  	ldstoretypes.ItemDescriptor, error) {
    71  	flag := ldmodel.UnmarshalFeatureFlagFromJSONReader(reader)
    72  	return maybeFlag(flag, reader.Error())
    73  }
    74  
    75  func maybeFlag(flag ldmodel.FeatureFlag, err error) (ldstoretypes.ItemDescriptor, error) {
    76  	if err != nil {
    77  		return ldstoretypes.ItemDescriptor{}, err
    78  	}
    79  	if flag.Deleted {
    80  		return ldstoretypes.ItemDescriptor{Version: flag.Version, Item: nil}, nil
    81  	}
    82  	return ldstoretypes.ItemDescriptor{Version: flag.Version, Item: &flag}, nil
    83  }
    84  
    85  // String returns a human-readable string identifier.
    86  func (fk featureFlagStoreDataKind) String() string {
    87  	return fk.GetName()
    88  }
    89  
    90  // GetName returns the unique namespace identifier for segment objects.
    91  func (sk segmentStoreDataKind) GetName() string {
    92  	return "segments"
    93  }
    94  
    95  // Serialize is used internally by the SDK when communicating with a PersistentDataStore.
    96  func (sk segmentStoreDataKind) Serialize(item ldstoretypes.ItemDescriptor) []byte {
    97  	if item.Item == nil {
    98  		segment := ldmodel.Segment{Key: deletedItemPlaceholderKey, Version: item.Version, Deleted: true}
    99  		if bytes, err := modelSerialization.MarshalSegment(segment); err == nil {
   100  			return bytes
   101  		}
   102  	} else if segment, ok := item.Item.(*ldmodel.Segment); ok {
   103  		if bytes, err := modelSerialization.MarshalSegment(*segment); err == nil {
   104  			return bytes
   105  		}
   106  	}
   107  	return nil
   108  }
   109  
   110  // Deserialize is used internally by the SDK when communicating with a PersistentDataStore.
   111  func (sk segmentStoreDataKind) Deserialize(data []byte) (ldstoretypes.ItemDescriptor, error) {
   112  	segment, err := modelSerialization.UnmarshalSegment(data)
   113  	return maybeSegment(segment, err)
   114  }
   115  
   116  // DeserializeFromJSONReader is used internally by the SDK when parsing multiple flags at once.
   117  func (sk segmentStoreDataKind) DeserializeFromJSONReader(reader *jreader.Reader) (ldstoretypes.ItemDescriptor, error) {
   118  	segment := ldmodel.UnmarshalSegmentFromJSONReader(reader)
   119  	return maybeSegment(segment, reader.Error())
   120  }
   121  
   122  func maybeSegment(segment ldmodel.Segment, err error) (ldstoretypes.ItemDescriptor, error) {
   123  	if err != nil {
   124  		return ldstoretypes.ItemDescriptor{}, err
   125  	}
   126  	if segment.Deleted {
   127  		return ldstoretypes.ItemDescriptor{Version: segment.Version, Item: nil}, nil
   128  	}
   129  	return ldstoretypes.ItemDescriptor{Version: segment.Version, Item: &segment}, nil
   130  }
   131  
   132  // String returns a human-readable string identifier.
   133  func (sk segmentStoreDataKind) String() string {
   134  	return sk.GetName()
   135  }
   136  

View as plain text