func (c *clg) calculate(ctx spec.Context) (string, error) { behaviourID, ok := ctx.GetBehaviourID() if !ok { return "", maskAnyf(invalidBehaviourIDError, "must not be empty") } behaviourIDKey := key.NewNetworkKey("behaviour-id:%s:separator", behaviourID) separator, err := c.Storage().General().Get(behaviourIDKey) if storage.IsNotFound(err) { randomKey, err := c.Storage().Feature().GetRandom() if err != nil { return "", maskAny(err) } // Make sure the fetched feature is valid based on its key structure. The // key itself already contains the feature. Knowing the key structure makes // it possible to simply parse the feature from the key fetched from the // feature storage. These features are stored by the split-features CLG. // Changes in the key structure there must be aligned with implementation // details here. The current key structure looks as follows. // // feature:%s:positions // if len(randomKey) != 22 { return "", maskAnyf(invalidFeatureKeyError, "key '%s' must have length '22'", randomKey) } if !strings.HasPrefix(randomKey, "feature:") { return "", maskAnyf(invalidFeatureKeyError, "key '%s' prefix must be 'feature:'", randomKey) } if !strings.HasSuffix(randomKey, ":positions") { return "", maskAnyf(invalidFeatureKeyError, "key '%s' suffix must be ':positions'", randomKey) } // Create a new separator from the fetched random feature. Note that a // feature is considered 4 characters long and the random factory takes a // max parameter, which is exlusive. feature := randomKey[8:12] featureIndex, err := c.Service().Random().CreateMax(5) if err != nil { return "", maskAny(err) } separator = string(feature[featureIndex]) // Store the newly created separator using the CLGs own behaviour ID. In case // this CLG is asked again to return its separator, it will lookup its // separator in the general storage. err = c.Storage().General().Set(behaviourIDKey, separator) if err != nil { return "", maskAny(err) } } else if err != nil { return "", maskAny(err) } return separator, nil }
func (c *clg) forwardNetworkPayload(ctx spec.Context, informationSequence string) error { // Find the original information sequence using the information ID from the // context. informationID, ok := ctx.GetInformationID() if !ok { return maskAnyf(invalidInformationIDError, "must not be empty") } informationSequenceKey := key.NewNetworkKey("information-id:%s:information-sequence", informationID) informationSequence, err := c.Storage().General().Get(informationSequenceKey) if err != nil { return maskAny(err) } // Find the first behaviour ID using the CLG tree ID from the context. The // behaviour ID we are looking up here is the ID of the initial input CLG. clgTreeID, ok := ctx.GetCLGTreeID() if !ok { return maskAnyf(invalidCLGTreeIDError, "must not be empty") } firstBehaviourIDKey := key.NewNetworkKey("clg-tree-id:%s:first-behaviour-id", clgTreeID) inputBehaviourID, err := c.Storage().General().Get(firstBehaviourIDKey) if err != nil { return maskAny(err) } // Lookup the behaviour ID of the current output CLG. Below we are using this // to set the source of the new network payload accordingly. outputBehaviourID, ok := ctx.GetBehaviourID() if !ok { return maskAnyf(invalidBehaviourIDError, "must not be empty") } // Create a new contect using the given context and adapt the new context with // the information of the current scope. newCtx := ctx.Clone() newCtx.SetBehaviourID(inputBehaviourID) newCtx.SetCLGName("input") newCtx.SetCLGTreeID(clgTreeID) // We do not need to set the expectation because it never changes. // We do not need to set the session ID because it never changes. // Create a new network payload. newNetworkPayloadConfig := networkpayload.DefaultConfig() newNetworkPayloadConfig.Args = []reflect.Value{reflect.ValueOf(informationSequence)} newNetworkPayloadConfig.Context = newCtx newNetworkPayloadConfig.Destination = string(inputBehaviourID) newNetworkPayloadConfig.Sources = []string{string(outputBehaviourID)} newNetworkPayload, err := networkpayload.New(newNetworkPayloadConfig) if err != nil { return maskAny(err) } // Write the transformed network payload to the queue. networkPayloadKey := key.NewNetworkKey("events:network-payload") b, err := json.Marshal(newNetworkPayload) if err != nil { return maskAny(err) } err = c.Storage().General().PushToList(networkPayloadKey, string(b)) if err != nil { return maskAny(err) } return nil }