Exemple #1
0
 public override Result QueryFeature(FeatureQuery query)
 {
     if (query.GetLong(0, out var accountId))
     {
         var account = CurrentChain.GetFeatureAccount(accountId);
         if (account != null)
         {
             var container = account.GetFeatureContainer <FanContainer>(FeatureId);
             if (container != null)
             {
                 var action = query.Action;
                 if (action == FansAction)
                 {
                     return(new PackableResult(container.GetFans()));
                 }
                 else if (action == FanOfAction)
                 {
                     return(new PackableResult(container.GetFanOf()));
                 }
                 else if (action == FansLastTransactionInfoAction)
                 {
                     return(new PackableResult(container.LastFansTransactionInfo));
                 }
                 else if (action == FanOfLastTransactionInfoAction)
                 {
                     return(new PackableResult(container.LastFanOfTransactionInfo));
                 }
             }
             return(Result.DataNotFound);
         }
         return(Result.AccountNotFound);
     }
     return(Result.InvalidQuery);
 }
        public void the_resource_hash_is_deterministic_by_pattern()
        {
            var hash1 = new CurrentChain(theChain, theRouteData).ResourceHash();
            var hash2 = new CurrentChain(theSecondChain, theRouteData).ResourceHash();

            hash1.ShouldNotEqual(hash2);
        }
Exemple #3
0
        public override void ConsumeFeatureRequest(CommitItems commitItems, Commit commit, FeatureRequest featureRequest, Transaction transaction)
        {
            var fanRequest   = featureRequest as FanRequest;
            var fanMode      = fanRequest.FanMode;
            var receiverData = transaction.GetFeature <Receiver>(Receiver.FeatureId);

            var accountId = transaction.AccountId;
            var fanId     = receiverData.Receivers[0];

            var accountContainer = CurrentChain.GetFeatureAccount(accountId).GetOrAddFeatureContainer <FanContainer>(FeatureId);
            var fanContainer     = CurrentChain.GetFeatureAccount(fanId).GetOrAddFeatureContainer <FanContainer>(FeatureId);

            if (fanMode == FanRequestMode.AddFanOf)
            {
                accountContainer.AddFanOf(fanId, transaction);
                fanContainer.AddFan(accountId, transaction);
            }
            else if (fanMode == FanRequestMode.RemoveFanOf)
            {
                accountContainer.RemoveFanOf(fanId, transaction);
                fanContainer.RemoveFan(accountId, transaction);
            }

            commitItems.DirtyAccounts.Add(accountId);
            commitItems.DirtyAccounts.Add(fanId);
        }
Exemple #4
0
        public InvocationContext(Envelope envelope, HandlerChain chain)
        {
            if (envelope == null)
            {
                throw new ArgumentNullException("envelope");
            }

            if (envelope.Log != null)
            {
                Set(typeof(IChainExecutionLog), envelope.Log);
            }

            var currentChain = new CurrentChain(chain, _emptyDictionary);

            Set(typeof(ICurrentChain), currentChain);

            _envelope = envelope;
            var inputType = envelope.Message.GetType();
            var request   = new InMemoryFubuRequest();

            request.Set(inputType, _envelope.Message);

            Set(typeof(IFubuRequest), request);
            Set(typeof(IInvocationContext), this);
            Set(typeof(Envelope), envelope);
        }
        public override Result QueryFeature(FeatureQuery query)
        {
            if (query.Action == LastTransactionInfoAction)
            {
                return(GetAccountData <PreviousAccountTransactionContainer>(query, 0, (container) =>
                {
                    return new PackableResult(container?.LastTransactionInfo ?? LastTransactionCountInfo.Empty);
                }));
            }
            else if (query.Action == LastTransactionInfoBatchAction)
            {
                return(GetBatchData <long>(query, 0, (u, l) => u.Unpack(l), (accountIds) =>
                {
                    var batchResult = new LastTransactionCountInfoBatch();

                    foreach (var accountId in accountIds)
                    {
                        var account = CurrentChain.GetFeatureAccount(accountId);
                        var info = account?.GetFeatureContainer <PreviousAccountTransactionContainer>(FeatureId)?.LastTransactionInfo ?? LastTransactionCountInfo.Empty;

                        batchResult.Add(account != null, accountId, info);
                    }

                    return new PackableResult(batchResult);
                }));
            }

            return(Result.InvalidQuery);
        }
        public void the_resource_hash_is_deterministic_by_route_parameters()
        {
            var hash1 = new CurrentChain(theChain, theRouteData).ResourceHash();
            var hash2 = new CurrentChain(theChain, theRouteData).ResourceHash();

            hash1.ShouldEqual(hash2);

            var hash3 = new CurrentChain(theChain, someDifferentRouteData).ResourceHash();

            hash1.ShouldNotEqual(hash3);
        }
        public void is_in_partial_negative_after_popping_the_last_child()
        {
            var currentChain = new CurrentChain(theChain, null);

            currentChain.Push(theSecondChain);
            currentChain.Push(theThirdChain);

            currentChain.Pop();
            currentChain.Pop();

            currentChain.IsInPartial().ShouldBeFalse();
        }
        public void is_in_partial_positive()
        {
            var currentChain = new CurrentChain(theChain, null);

            currentChain.Push(theSecondChain);
            currentChain.IsInPartial().ShouldBeTrue();

            currentChain.Push(theThirdChain);
            currentChain.IsInPartial().ShouldBeTrue();

            currentChain.Pop();
            currentChain.IsInPartial().ShouldBeTrue();
        }
Exemple #9
0
 public void Clear()
 {
     Fields[0].Clear();
     Fields[1].Clear();
     LastChainPlayer = -1;
     MainPhaseEnd    = false;
     CurrentChain.Clear();
     ChainTargets.Clear();
     ChainTargetOnly.Clear();
     LastSummonPlayer = -1;
     SummoningCards.Clear();
     LastSummonedCards.Clear();
 }
        public void the_top_chain_is_always_the_originating_chain()
        {
            var currentChain = new CurrentChain(theChain, theRouteData);

            currentChain.OriginatingChain.ShouldBeTheSameAs(theChain);

            currentChain.Push(theSecondChain);
            currentChain.OriginatingChain.ShouldBeTheSameAs(theChain);

            currentChain.Pop();

            currentChain.OriginatingChain.ShouldBeTheSameAs(theChain);
        }
Exemple #11
0
        public void hash_values_when_the_chain_has_a_route_but_not_real_values()
        {
            var chain = new RoutedChain(new RouteDefinition("some/pattern/url"));


            var currentChain = new CurrentChain(chain, new Dictionary <string, object>());

            var varyBy = new VaryByResource(currentChain);

            var values = varyBy.Values();

            values.Select(x => "{0}={1}".ToFormat(x.Key, x.Value)).ShouldHaveTheSameElementsAs("chain=" + chain.GetRoutePattern());
        }
Exemple #12
0
        public void SetUp()
        {
            var theHttpRequest = new CurrentChain(new BehaviorChain(), new Dictionary <string, object>());

            theExpectedHash = ResourceHash.For(theHttpRequest);

            FubuApplication.SetupNamingStrategyForHttpHeaders();

            theEtagRequest = BindingScenario <ETaggedRequest> .Build(x =>
            {
                x.Service <ICurrentChain>(theHttpRequest);
                x.Data("If-None-Match", "12345");
            });
        }
Exemple #13
0
        public void hash_values_with_a_route_That_has_substitutions()
        {
            var chain = new RoutedChain(RouteBuilder.Build <Query>("some/pattern/url/{from}/{to}"));

            var currentChain = new CurrentChain(chain, new Dictionary <string, object> {
                { "from", 1 }, { "to", 2 }
            });
            var varyBy = new VaryByResource(currentChain);

            var values = varyBy.Values();

            values.Select(x => "{0}={1}".ToFormat(x.Key, x.Value))
            .ShouldHaveTheSameElementsAs("chain=" + "some/pattern/url/{from}/{to}", "from=1", "to=2");
        }
Exemple #14
0
        public void Invoke(ServiceArguments arguments, IDictionary <string, object> routeValues)
        {
            var currentChain = new CurrentChain(_chain, routeValues);

            arguments.Set(typeof(ICurrentChain), currentChain);

            if (_chain.Filters.Any(filter => filter.Filter(arguments) == DoNext.Stop))
            {
                return;
            }

            var behavior = _factory.BuildBehavior(arguments, _chain.UniqueId);

            behavior.Invoke();
        }
Exemple #15
0
        public void hash_values_with_a_chain_that_is_a_partial()
        {
            var chain = new BehaviorChain()
            {
            };

            var currentChain = new CurrentChain(chain, new Dictionary <string, object> {
                { "from", 1 }, { "to", 2 }
            });
            var varyBy = new VaryByResource(currentChain);

            var values = varyBy.Values();

            values.Select(x => "{0}={1}".ToFormat(x.Key, x.Value))
            .ShouldHaveTheSameElementsAs("chain=" + chain.ToString());
        }
Exemple #16
0
        public override (bool, int) Validate(Transaction transaction, FeatureData featureData)
        {
            var error = ReceiverError.None;

            if (!(featureData is Receiver receiverData) || !receiverData.Valid)
            {
                error = ReceiverError.InvalidReceiverData;
                goto end;
            }

            var receivers = receiverData.Receivers;

            if (receivers.Count > MaxReceivers)
            {
                error = ReceiverError.TooManyReceivers;
                goto end;
            }

            if (receivers.Count == 0)
            {
                error = ReceiverError.InvalidReceiver;
                goto end;
            }

            var receiversList = new HashSet <long>();

            foreach (var receiverAccountId in receivers)
            {
                if (!CurrentChain.FeatureAccountExists(receiverAccountId))
                {
                    error = ReceiverError.InvalidReceiver;
                    goto end;
                }

                if (receiversList.Contains(receiverAccountId))
                {
                    error = ReceiverError.InvalidReceiver;
                    goto end;
                }

                receiversList.Add(receiverAccountId);
            }

end:

            return(error == ReceiverError.None, (int)error);
        }
        public void push_and_pop_track_the_current_chain()
        {
            var currentChain = new CurrentChain(theChain, theRouteData);

            currentChain.Push(theSecondChain);

            currentChain.Current.ShouldBeTheSameAs(theSecondChain);

            currentChain.Push(theThirdChain);
            currentChain.Current.ShouldBeTheSameAs(theThirdChain);

            currentChain.Pop();
            currentChain.Current.ShouldBeTheSameAs(theSecondChain);

            currentChain.Pop();
            currentChain.Current.ShouldBeTheSameAs(theChain);
        }
Exemple #18
0
        public void hash_values_with_a_chain_that_is_a_partial()
        {
            var chain = new BehaviorChain()
            {
                Route         = RouteBuilder.Build <Query>("some/pattern/url/{from}/{to}"),
                IsPartialOnly = true
            };

            var currentChain = new CurrentChain(chain, new Dictionary <string, object> {
                { "from", 1 }, { "to", 2 }
            });
            var varyBy = new VaryByResource(currentChain);

            var values = varyBy.Values();

            values.Select(x => "{0}={1}".ToFormat(x.Key, x.Value))
            .ShouldHaveTheSameElementsAs("chain=" + chain.UniqueId.ToString());
        }
        public override void ConsumeFeatureRequest(CommitItems commitItems, Commit commit, FeatureRequest featureRequest, Transaction transaction)
        {
            var accountId = transaction.AccountId;

            if (featureRequest.RequestId == GroupRegistrationRequest.GroupRegistrationRequestId)
            {
                var feature      = transaction.GetFeature <GroupAdministration>(FeatureId);
                var container    = CurrentChain.GetFeatureAccount(accountId).GetOrAddFeatureContainer <GroupAdministrationContainer>(FeatureId);
                var registration = featureRequest as GroupRegistrationRequest;

                var groupid = feature.NewGroupId;
                var group   = new GroupAdministrationInfo(groupid, accountId, transaction.TransactionId, transaction.Timestamp, registration.GroupFlags);

                lock (this)
                    _groups[groupid] = group;
                _groupStorage.Storage.AddEntry(groupid, group.ToByteArray());

                container.AddGroup(groupid);
                commitItems.DirtyAccounts.Add(accountId);
            }
            else if (featureRequest.RequestId == GroupAdministrationRequest.GroupAdministrationRequestId)
            {
                var request = featureRequest as GroupAdministrationRequest;
                var groupId = request.GroupId;
                var group   = GetGroupInfo(groupId);

                group.ConsumeGroupAdministrationRequest(transaction, request, out var dirtyGroupAccounts);
                commit.DirtyIds.Add(groupId);

                foreach (var added in dirtyGroupAccounts.AddedAccounts)
                {
                    var container = CurrentChain.GetFeatureAccount(added).GetOrAddFeatureContainer <GroupAdministrationContainer>(FeatureId);
                    container.AddGroup(groupId);
                    commitItems.DirtyAccounts.Add(added);
                }

                foreach (var removed in dirtyGroupAccounts.RemovedAccounts)
                {
                    var container = CurrentChain.GetFeatureAccount(removed).GetOrAddFeatureContainer <GroupAdministrationContainer>(FeatureId);
                    container.RemoveGroup(groupId);
                    commitItems.DirtyAccounts.Add(removed);
                }
            }
        }
Exemple #20
0
        public void Invoke(ServiceArguments arguments, IDictionary <string, object> routeValues, IRequestCompletion requestCompletion)
        {
            var currentChain = new CurrentChain(_chain, routeValues);

            arguments.Set(typeof(ICurrentChain), currentChain);
            arguments.Set(typeof(IRequestCompletion), requestCompletion);

            if (arguments.Has(typeof(IChainExecutionLog)))
            {
                arguments.Get <IChainExecutionLog>().RootChain = _chain;
            }

            if (_chain.Filters.Any(filter => filter.Filter(arguments) == DoNext.Stop))
            {
                return;
            }

            IActionBehavior behavior = null;

            if (arguments.Has(typeof(IChainExecutionLog)))
            {
                arguments.Get <IChainExecutionLog>().Trace("Building the Behaviors", () =>
                {
                    behavior = _factory.BuildBehavior(arguments, _chain.UniqueId);
                });
            }
            else
            {
                behavior = _factory.BuildBehavior(arguments, _chain.UniqueId);
            }


            requestCompletion.WhenCompleteDo(x =>
            {
                var disposable = behavior as IDisposable;
                if (disposable != null)
                {
                    disposable.Dispose();
                }
            });

            behavior.Invoke();
        }
Exemple #21
0
        public async Task Invoke(TypeArguments arguments, IDictionary <string, object> routeValues)
        {
            var currentChain = new CurrentChain(_chain, routeValues);

            arguments.Set(typeof(ICurrentChain), currentChain);

            if (arguments.Has(typeof(IChainExecutionLog)))
            {
                arguments.Get <IChainExecutionLog>().RootChain = _chain;
            }

            if (_chain.Filters.Any(filter => filter.Filter(arguments) == DoNext.Stop))
            {
                return;
            }

            IActionBehavior behavior = null;

            if (arguments.Has(typeof(IChainExecutionLog)))
            {
                arguments.Get <IChainExecutionLog>().Trace("Building the Behaviors", () =>
                {
                    behavior = _factory.BuildBehavior(arguments, _chain.UniqueId);
                });
            }
            else
            {
                behavior = _factory.BuildBehavior(arguments, _chain.UniqueId);
            }

            try
            {
                await behavior.Invoke().ConfigureAwait(false);
            }
            finally
            {
                var disposable = behavior as IDisposable;
                disposable?.Dispose();
            }
        }
Exemple #22
0
        public override (bool, int) Validate(Transaction transaction, FeatureData featureData)
        {
            var friendData = featureData as Friend;

            if ((friendData.Flags & FriendFlags.ReceiversMustBeFriend) != 0)
            {
                var valid = true;

                var receivers = transaction.GetFeature <Receiver>(Receiver.FeatureId)?.Receivers;
                if (receivers != null)
                {
                    var accountId = transaction.AccountId;
                    foreach (var receiverId in receivers)
                    {
                        var receiverAccount = CurrentChain.GetFeatureAccount(receiverId)?.GetFeatureContainer <FriendContainer>(FeatureId);
                        if (receiverAccount != null)
                        {
                            if (receiverAccount.HasFriend(accountId))
                            {
                                continue;
                            }
                        }

                        valid = false;
                        break;
                    }
                }
                else
                {
                    valid = false;
                }

                return(valid, (int)(valid ? FriendError.None : FriendError.ReceiversMustBeFriends));
            }
            else
            {
                return(true, 0);
            }
        }
Exemple #23
0
        public void Invoke(ServiceArguments arguments, IDictionary <string, object> routeValues, IRequestCompletion requestCompletion)
        {
            var currentChain = new CurrentChain(_chain, routeValues);

            arguments.Set(typeof(ICurrentChain), currentChain);
            arguments.Set(typeof(IRequestCompletion), requestCompletion);

            if (_chain.Filters.Any(filter => filter.Filter(arguments) == DoNext.Stop))
            {
                return;
            }
            var behavior = _factory.BuildBehavior(arguments, _chain.UniqueId);

            requestCompletion.WhenCompleteDo(x =>
            {
                var disposable = behavior as IDisposable;
                if (disposable != null)
                {
                    disposable.Dispose();
                }
            });
            behavior.Invoke();
        }
Exemple #24
0
        public override void ConsumeFeatureRequest(CommitItems commitItems, Commit commit, FeatureRequest featureRequest, Transaction transaction)
        {
            var friendRequest = featureRequest as FriendRequest;
            var friendMode    = friendRequest.FriendMode;
            var receiverData  = transaction.GetFeature <Receiver>(Receiver.FeatureId);

            var accountId = transaction.AccountId;
            var friendId  = receiverData.Receivers[0];

            var accountContainer = CurrentChain.GetFeatureAccount(accountId).GetOrAddFeatureContainer <FriendContainer>(FeatureId);
            var friendContainer  = CurrentChain.GetFeatureAccount(friendId).GetOrAddFeatureContainer <FriendContainer>(FeatureId);

            if (friendMode == FriendRequestMode.SendInvitation)
            {
                accountContainer.AddFriendInvitation(new FriendInvitation(accountId, friendId, accountId), transaction);
                friendContainer.AddFriendInvitation(new FriendInvitation(friendId, accountId, accountId), transaction);
            }
            else if (friendMode == FriendRequestMode.AcceptInvitation)
            {
                accountContainer.AcceptFriendInvitation(friendId, transaction);
                friendContainer.AcceptFriendInvitation(accountId, transaction);
            }
            else if (friendMode == FriendRequestMode.RejectInvitation)
            {
                accountContainer.RejectFriendInvitation(friendId, transaction);
                friendContainer.RejectFriendInvitation(accountId, transaction);
            }
            else if (friendMode == FriendRequestMode.Remove)
            {
                accountContainer.RemoveFriend(friendId, transaction);
                friendContainer.RemoveFriend(accountId, transaction);
            }

            commitItems.DirtyAccounts.Add(accountId);
            commitItems.DirtyAccounts.Add(friendId);
        }
        public override Result QueryFeature(FeatureQuery query)
        {
            if (query.Action == LastTransactionInfoAction)
            {
                if (query.GetString(1, out var indexHex))
                {
                    var index = new Chain.Index(indexHex);
                    return(GetAccountData <AccountIndexContainerBase>(query, 0, (container) =>
                    {
                        return new PackableResult(container?.GetLastTransactionInfo(index) ?? LastTransactionCountInfo.Empty);
                    }));
                }
            }
            else if (query.Action == LastTransactionInfoBatchAction)
            {
                if (query.GetString(1, out var indexHex))
                {
                    var index = new Chain.Index(indexHex);

                    return(GetBatchData <long>(query, 0, (u, l) => u.Unpack(l), (accountIds) =>
                    {
                        var batchResult = new LastTransactionCountInfoBatch();

                        foreach (var accountId in accountIds)
                        {
                            var account = CurrentChain.GetFeatureAccount(accountId);
                            var info = account?.GetFeatureContainer <AccountIndexContainerBase>(FeatureId)?.GetLastTransactionInfo(index) ?? LastTransactionCountInfo.Empty;

                            batchResult.Add(info != null, accountId, info);
                        }

                        return new PackableResult(batchResult);
                    }));
                }
            }
            else if (query.Action == LastTransactionInfoIndicesBatchAction)
            {
                if (query.GetLong(0, out var accountId) && query.GetString(1, out var indicesHex))
                {
                    return(GetBatchData <Chain.Index>(query, 1, (unpacker, list) => unpacker.Unpack(list, (u) => new Chain.Index(u)), (indices) =>
                    {
                        var account = CurrentChain.GetFeatureAccount(accountId);
                        var container = account?.GetFeatureContainer <AccountIndexContainerBase>(FeatureId);

                        if (account != null)
                        {
                            var batchResult = new LastTransactionCountInfoBatch();

                            foreach (var index in indices)
                            {
                                var info = container?.GetLastTransactionInfo(index);
                                batchResult.Add(info != null, accountId, info);
                            }

                            return new PackableResult(batchResult);
                        }

                        return Result.AccountNotFound;
                    }));
                }
            }

            return(Result.InvalidQuery);
        }
Exemple #26
0
 public GroupValidator(Feature feature, IFeatureChain currentChain) : base(feature, currentChain)
 {
     _chainHandler = CurrentChain.GetFeatureChainHandler <GroupChainHandler>(FeatureId).GroupAdministrationChainHandler;
 }
Exemple #27
0
        public override (bool, int) ValidateFeatureRequest(FeatureRequest featureRequest, Transaction transaction)
        {
            var error        = FriendError.None;
            var receiverData = transaction.GetFeature <Receiver>(Receiver.FeatureId);

            if (receiverData == null)
            {
                error = FriendError.ReceiverFeatureRequired;
                goto end;
            }

            if (receiverData.Receivers.Count != 1)
            {
                error = FriendError.InvalidFeatureRequest;
                goto end;
            }

            if (!(featureRequest is FriendRequest friendRequest))
            {
                error = FriendError.InvalidFeatureRequest;
                goto end;
            }

            var accountId = transaction.AccountId;
            var fanId     = receiverData.Receivers[0];

            var friendId = transaction.GetFeature <Receiver>(Receiver.FeatureId).Receivers[0];

            var friendTransaction = friendRequest;
            var dataAccount       = CurrentChain.GetFeatureAccount(accountId).GetFeatureContainer <FriendContainer>(FeatureId);
            var friendAccount     = CurrentChain.GetFeatureAccount(fanId).GetFeatureContainer <FriendContainer>(FeatureId);

            var friendMode = friendTransaction.FriendMode;

            if (friendMode == FriendRequestMode.SendInvitation)
            {
                if (friendId == accountId)
                {
                    error = FriendError.InvalidFriend;
                    goto end;
                }

                var hasError = false;
                if (dataAccount != null)
                {
                    if (dataAccount.HasFriend(friendId))
                    {
                        error = FriendError.AlreadyFriends;
                        goto end;
                    }

                    if (dataAccount.GetFriendInvitation(friendId) != null)
                    {
                        hasError = true;
                    }
                }
                else
                {
                    if (friendAccount != null && friendAccount.GetFriendInvitation(accountId) != null)
                    {
                        hasError = true;
                    }
                }

                if (hasError)
                {
                    error = FriendError.AlreadyInvited;
                    goto end;
                }
            }
            else if (friendMode == FriendRequestMode.AcceptInvitation || friendMode == FriendRequestMode.RejectInvitation)
            {
                if (dataAccount == null || friendAccount == null)
                {
                    error = FriendError.Unknown;
                    goto end;
                }

                var invitation       = dataAccount.GetFriendInvitation(friendId);
                var friendInvitation = friendAccount.GetFriendInvitation(accountId);

                if (invitation == null || !invitation.HasFriendAccountApproval || friendInvitation == null || !friendInvitation.HasAccountApproval)
                {
                    error = FriendError.InvalidFriend;
                    goto end;
                }
            }
            else if (friendMode == FriendRequestMode.Remove)
            {
                if (dataAccount == null || friendAccount == null)
                {
                    error = FriendError.Unknown;
                    goto end;
                }

                var isFriend      = dataAccount.HasFriend(friendId);
                var hasInvitation = dataAccount.GetFriendInvitation(friendId) != null;

                if (!(isFriend || hasInvitation))
                {
                    error = FriendError.InvalidFriend;
                    goto end;
                }
            }
            else
            {
                error = FriendError.Unknown;
                goto end;
            }

end:

            return(error == FriendError.None, (int)error);
        }
        public void is_in_partial_negative()
        {
            var currentChain = new CurrentChain(theChain, null);

            currentChain.IsInPartial().ShouldBeFalse();
        }
        public void keeps_the_route_data()
        {
            var currentChain = new CurrentChain(theChain, theRouteData);

            currentChain.RouteData.ShouldBeTheSameAs(theRouteData);
        }
Exemple #30
0
        public override (bool, int) ValidateFeatureRequest(FeatureRequest featureRequest, Transaction transaction)
        {
            var error        = FanError.None;
            var receiverData = transaction.GetFeature <Receiver>(Receiver.FeatureId);

            if (receiverData == null)
            {
                error = FanError.ReceiverFeatureRequired;
                goto end;
            }

            if (receiverData.Receivers.Count != 1)
            {
                error = FanError.InvalidFeatureRequest;
                goto end;
            }

            if (!(featureRequest is FanRequest fanRequest))
            {
                error = FanError.InvalidFeatureRequest;
                goto end;
            }

            var accountId = transaction.AccountId;
            var fanId     = receiverData.Receivers[0];

            if (fanId == accountId)
            {
                error = FanError.InvalidFan;
                goto end;
            }

            var accountContainer = CurrentChain.GetFeatureAccount(accountId).GetFeatureContainer <FanContainer>(FeatureId);
            var fanContainer     = CurrentChain.GetFeatureAccount(fanId).GetFeatureContainer <FanContainer>(FeatureId);

            if (accountContainer != null)
            {
                var fanOf = accountContainer.IsFanOf(fanId);
                if (fanRequest.FanMode == FanRequestMode.AddFanOf)
                {
                    if (fanOf)
                    {
                        error = FanError.AlreadyFan;
                        goto end;
                    }
                }
                else if (fanRequest.FanMode == FanRequestMode.RemoveFanOf)
                {
                    if (!fanOf)
                    {
                        error = FanError.InvalidFan;
                        goto end;
                    }
                }
                else
                {
                    error = FanError.Unknown;
                    goto end;
                }
            }
            else
            {
                if (fanContainer != null)
                {
                    var isFan = fanContainer.IsFan(accountId);
                    if (fanRequest.FanMode == FanRequestMode.AddFanOf)
                    {
                        if (isFan)
                        {
                            error = FanError.AlreadyFan;
                            goto end;
                        }
                    }
                    else if (fanRequest.FanMode == FanRequestMode.RemoveFanOf)
                    {
                        if (!isFan)
                        {
                            error = FanError.InvalidFan;
                            goto end;
                        }
                    }
                    else
                    {
                        error = FanError.Unknown;
                        goto end;
                    }
                }
            }
end:

            return(error == FanError.None, (int)error);
        }