Skip to content

Commit

Permalink
State transitions for presence event engine (#203)
Browse files Browse the repository at this point in the history
Co-authored-by: Mohit Tejani <[email protected]>
Co-authored-by: Mohit Tejani <[email protected]>
  • Loading branch information
3 people authored Feb 19, 2024
1 parent 5d12d95 commit 8cb735c
Show file tree
Hide file tree
Showing 22 changed files with 801 additions and 7 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: |
2.1.519
dotnet-version: |
5.0.x
6.0.x
- name: Build packages
Expand Down
36 changes: 36 additions & 0 deletions src/Api/PubnubApi/EventEngine/Presence/Common/PresenceInput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Collections.Generic;
using System.Linq;

namespace PubnubApi.EventEngine.Presence.Common
{
public class PresenceInput
{
public IEnumerable<string> Channels { get; set; }
public IEnumerable<string> ChannelGroups { get; set; }

public static PresenceInput operator +(PresenceInput a, PresenceInput b)
{
return new PresenceInput
{
Channels = a.Channels?.Union(b.Channels ?? new string[0]),
ChannelGroups = a.ChannelGroups?.Union(b.ChannelGroups ?? new string[0]),
};
}

public static PresenceInput operator -(PresenceInput a, PresenceInput b)
{
return new PresenceInput
{
Channels = a.Channels?.Except(b.Channels ?? new string[0]),
ChannelGroups = a.ChannelGroups?.Except(b.ChannelGroups ?? new string[0]),
};
}

public bool IsEmpty()
{
return (Channels == null && ChannelGroups == null)
|| ((Channels != null && Channels.Count() == 0)
&& (ChannelGroups != null && ChannelGroups.Count() == 0));
}
}
}
35 changes: 35 additions & 0 deletions src/Api/PubnubApi/EventEngine/Presence/Events/PresenceEvents.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.Collections.Generic;
using PubnubApi;
using PubnubApi.EventEngine.Presence.Common;

namespace PubnubApi.EventEngine.Presence.Events {
public class JoinedEvent : Core.IEvent
{
public PresenceInput Input { get; set; }
}

public class LeftEvent : Core.IEvent
{
public PresenceInput Input { get; set; }
}

public class LeftAllEvent : Core.IEvent {}

public class HeartbeatSuccessEvent : Core.IEvent {}

public class HeartbeatFailureEvent : Core.IEvent
{
public PNStatus Status { get; set; }
}

public class HeartbeatGiveUpEvent : Core.IEvent
{
public PNStatus Status { get; set; }
}

public class ReconnectEvent : Core.IEvent {}

public class DisconnectEvent : Core.IEvent {}

public class TimesUpEvent : Core.IEvent {}
}
4 changes: 4 additions & 0 deletions src/Api/PubnubApi/EventEngine/Presence/Invocations/Dummy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// TODO: Dummy Invocation until we have real ones
namespace PubnubApi.EventEngine.Presence.Invocations {
public class DummyInvocation : Core.IEffectInvocation {}
}
27 changes: 27 additions & 0 deletions src/Api/PubnubApi/EventEngine/Presence/States/APresenceState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using PubnubApi.EventEngine.Core;
using PubnubApi.EventEngine.Presence.Common;

namespace PubnubApi.EventEngine.Presence.States
{
public abstract class APresenceState : Core.State
{
public PresenceInput Input { get; set; }

public bool IsEmpty()
{
return Input.IsEmpty();
}

protected TransitionResult HandleLeftEvent(Events.LeftEvent e)
{
var newInput = this.Input - e.Input;

return newInput.IsEmpty()
? (TransitionResult)new InactiveState()
: (TransitionResult)new HeartbeatingState()
{
Input = newInput,
};
}
}
}
38 changes: 38 additions & 0 deletions src/Api/PubnubApi/EventEngine/Presence/States/CooldownState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using PubnubApi;
using PubnubApi.EventEngine.Presence.Invocations;
using PubnubApi.EventEngine.Core;
using System.Collections.Generic;
using System;

namespace PubnubApi.EventEngine.Presence.States
{
public class CooldownState : APresenceState
{
// TODO: Dummy Invocation until we have real ones
public override IEnumerable<IEffectInvocation> OnEntry => new DummyInvocation().AsArray();
public override IEnumerable<IEffectInvocation> OnExit => new DummyInvocation().AsArray();

// TODO: transitions
public override TransitionResult Transition(IEvent ev)
{
return ev switch
{
Events.JoinedEvent e => new HeartbeatingState()
{
Input = e.Input != this.Input ? this.Input + e.Input : this.Input,
},
Events.LeftEvent e => HandleLeftEvent(e),
Events.LeftAllEvent e => new InactiveState(),
Events.DisconnectEvent e => new StoppedState()
{
Input = this.Input,
},
Events.TimesUpEvent e => new HeartbeatingState()
{
Input = this.Input,
},
_ => null,
};
}
}
}
40 changes: 40 additions & 0 deletions src/Api/PubnubApi/EventEngine/Presence/States/FailedState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using PubnubApi.EventEngine.Presence.Invocations;
using PubnubApi.EventEngine.Core;
using System.Collections.Generic;
using System;

namespace PubnubApi.EventEngine.Presence.States
{
public class FailedState : APresenceState
{
public PNStatus Reason { get; set; }

// TODO: Dummy Invocation until we have real ones
public override IEnumerable<IEffectInvocation> OnEntry => new DummyInvocation().AsArray();
public override IEnumerable<IEffectInvocation> OnExit => new DummyInvocation().AsArray();

// TODO: transitions
public override TransitionResult Transition(IEvent ev)
{
return ev switch
{
Events.JoinedEvent e => new HeartbeatingState()
{
Input = e.Input != this.Input ? this.Input + e.Input : this.Input,
},
Events.LeftEvent e => HandleLeftEvent(e),
Events.LeftAllEvent e => new InactiveState(),
Events.ReconnectEvent e => new HeartbeatingState()
{
Input = this.Input,
},
Events.DisconnectEvent e => new StoppedState()
{
Input = this.Input,
},
_ => null,
};
}
}

}
51 changes: 51 additions & 0 deletions src/Api/PubnubApi/EventEngine/Presence/States/HeartBeatingState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using PubnubApi.EventEngine.Presence.Invocations;
using PubnubApi.EventEngine.Core;
using System.Collections.Generic;
using System;

namespace PubnubApi.EventEngine.Presence.States
{
public class HeartbeatingState : APresenceState
{
// TODO: Dummy Invocation until we have real ones
public override IEnumerable<IEffectInvocation> OnEntry => new DummyInvocation().AsArray();
public override IEnumerable<IEffectInvocation> OnExit => new DummyInvocation().AsArray();

// TODO: transitions
public override TransitionResult Transition(IEvent ev)
{
return ev switch
{
Events.JoinedEvent e => new HeartbeatingState()
{
Input = e.Input != this.Input ? this.Input + e.Input : this.Input,
},
Events.LeftEvent e => HandleLeftEvent(e),
Events.LeftAllEvent e => new InactiveState(),
Events.HeartbeatSuccessEvent e => new CooldownState()
{
Input = this.Input,
},
Events.HeartbeatFailureEvent e => HandleHeartbeatFailureEvent(e),
Events.DisconnectEvent e => new StoppedState()
{
Input = this.Input,
},
_ => null,
};
}

private TransitionResult HandleHeartbeatFailureEvent(Events.HeartbeatFailureEvent e)
{
return e.Status.Category == PNStatusCategory.PNCancelledCategory
? (TransitionResult)null
: (TransitionResult)new ReconnectingState()
{
Input = this.Input,
RetryCount = 1,
Reason = e.Status,
};
}
}

}
33 changes: 33 additions & 0 deletions src/Api/PubnubApi/EventEngine/Presence/States/InactiveState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using PubnubApi.EventEngine.Presence.Invocations;
using PubnubApi.EventEngine.Core;
using System.Collections.Generic;
using System;

namespace PubnubApi.EventEngine.Presence.States
{
public class InactiveState : APresenceState
{
public InactiveState()
{
Input = null;
}

// TODO: Dummy Invocation until we have real ones
public override IEnumerable<IEffectInvocation> OnEntry => new DummyInvocation().AsArray();
public override IEnumerable<IEffectInvocation> OnExit => new DummyInvocation().AsArray();

// TODO: transitions
public override TransitionResult Transition(IEvent ev)
{
return ev switch
{
Events.JoinedEvent e => new HeartbeatingState()
{
Input = e.Input,
},
_ => null,
};
}
}

}
59 changes: 59 additions & 0 deletions src/Api/PubnubApi/EventEngine/Presence/States/ReconnectingState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using PubnubApi.EventEngine.Presence.Invocations;
using PubnubApi.EventEngine.Core;
using System.Collections.Generic;
using System;

namespace PubnubApi.EventEngine.Presence.States
{
public class ReconnectingState : APresenceState
{
public int RetryCount { get; set; }
public PNStatus Reason { get; set; }

// TODO: Dummy Invocation until we have real ones
public override IEnumerable<IEffectInvocation> OnEntry => new DummyInvocation().AsArray();
public override IEnumerable<IEffectInvocation> OnExit => new DummyInvocation().AsArray();

// TODO: transitions
public override TransitionResult Transition(IEvent ev)
{
return ev switch
{
Events.JoinedEvent e => new HeartbeatingState()
{
Input = e.Input != this.Input ? this.Input + e.Input : this.Input,
},
Events.LeftEvent e => HandleLeftEvent(e),
Events.LeftAllEvent e => new InactiveState(),
Events.HeartbeatSuccessEvent e => new CooldownState()
{
Input = this.Input,
},
Events.HeartbeatFailureEvent e => HandleHeartbeatFailureEvent(e),
Events.HeartbeatGiveUpEvent e => new FailedState()
{
Input = this.Input,
Reason = this.Reason,
},
Events.DisconnectEvent e => new StoppedState()
{
Input = this.Input,
},
_ => null,
};
}

private TransitionResult HandleHeartbeatFailureEvent(Events.HeartbeatFailureEvent e)
{
return e.Status.Category == PNStatusCategory.PNCancelledCategory
? (TransitionResult)null
: (TransitionResult)new ReconnectingState()
{
Input = this.Input,
RetryCount = this.RetryCount + 1,
Reason = e.Status,
};
}
}

}
46 changes: 46 additions & 0 deletions src/Api/PubnubApi/EventEngine/Presence/States/StoppedState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using PubnubApi.EventEngine.Presence.Invocations;
using PubnubApi.EventEngine.Core;
using System.Collections.Generic;
using System;

namespace PubnubApi.EventEngine.Presence.States
{
public class StoppedState : APresenceState
{
// TODO: Dummy Invocation until we have real ones
public override IEnumerable<IEffectInvocation> OnEntry => new DummyInvocation().AsArray();
public override IEnumerable<IEffectInvocation> OnExit => new DummyInvocation().AsArray();

// TODO: transitions
public override TransitionResult Transition(IEvent ev)
{
return ev switch
{
Events.JoinedEvent e => new StoppedState()
{
Input = e.Input != this.Input ? this.Input + e.Input : this.Input,
},
Events.LeftEvent e => HandleLeftEvent(e),
Events.LeftAllEvent e => new InactiveState(),
Events.ReconnectEvent e => new HeartbeatingState()
{
Input = this.Input,
},
_ => null,
};
}

protected TransitionResult HandleLeftEvent(Events.LeftEvent e)
{
var newInput = this.Input - e.Input;

return newInput.IsEmpty()
? (TransitionResult)new InactiveState()
: (TransitionResult)new StoppedState()
{
Input = newInput,
};
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ public override async Task Run(EmitStatusInvocation invocation)
this.statusEmitterFunction(this.pubnubInstance, invocation.Status);
}
}
}
}
Loading

0 comments on commit 8cb735c

Please sign in to comment.