# xPushService
it is a Part of xDashboard Projects on SaherElm IT Center which provides all required models and actions in related to handling SignalR Push Platforms.
since this module used as a base infrastructure provider, you have to create a Helper Module based on it, for creating your projects requirements. we call it commonly **xPushHelper**.
## xPushHelper
it is a simple **xDashboard** based module. which depends on these modules:
- xCommons
- xPushService
this is so important, if you protect your data using **xIdentityServer**. you need to add an additional refrence to your **xIdentityHelper** module.
```c#
```
after adding dependencies. you have to extends your PushHelper module.
by following **SignalR** rules, for each topics you have to add an specific **HUB** and register it in your **DI**.
since all important actions for handling communications between connected peers must be handled in Hubs. at the first step you have to create a Base Class for all of your Hubs.
in this sample since we need to have Authorized HUB Actions, we add IdentityService as dependency to Helper Module, now next step is implement a BaseHub class which supports Authorized Actions based on our User Roles.
## XPushBaseHub
```c#
public abstract class XPushBaseHub : XBaseHub, IXAuthorizedPushActions, IXUserPushActions, IXGroupsPushActions {
//
#region Constructor ...
protected XPushBaseHub (
IXPushGroupStore groupStore,
IXPushConnectionStore connectionStore
) : base (groupStore, connectionStore) { }
#endregion
//
#region Authorized Push Actions ...
///
/// an authenticated user can push a message to others ...
///
///
///
[Authorize (Policy = XPolicies.User)]
public Task UserMessageAsync (XPushMessage message) {
//
if (message.IsNull ()) {
message = GetPushMessage ();
} else {
message.TimeStamp = DateTime.UtcNow;
}
//
return Clients.Others.SendAsync (XBasePushAction.UserMessage.GetStringValue (), message);
}
///
/// an admin user can push a message to others ...
///
///
///
[Authorize (Policy = XPolicies.Admin)]
public Task AdminMessageAsync (XPushMessage message) {
//
if (message.IsNull ()) {
message = GetPushMessage ();
} else {
message.TimeStamp = DateTime.UtcNow;
}
//
return Clients.Others.SendAsync (XBasePushAction.AdminMessage.GetStringValue (), message);
}
#endregion
//
#region User Push Actions ...
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task AddUserToGroup (string userName, string groupName) {
//
// Empty result ...
var result = Task.FromResult (false);
//
var group = GetGroup (groupName)
.RunTask ();
//
var connections = connectionStore
.GetByUserName (userName)
.RunTask ();
var mustAddConnections = connections
.Where (c => !group.Connections
.Any (gc => gc == c.Id));
if (!mustAddConnections.HasChild ()) {
return result;
}
//
// Add all new Connections to Group ...
var allUpdate = false;
mustAddConnections.ToList ().ForEach (c => {
//
try {
//
Groups
.AddToGroupAsync (c.Id, groupName)
.RunTask ();
//
group.Connections.Add (c.Id);
//
allUpdate = allUpdate || true;
} catch {
allUpdate = allUpdate || false;
}
});
if (!allUpdate) {
return result;
}
//
result = AddOrUpdateGroup (group);
//
return result;
}
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task AddUserDeviceToGroup (string userName, string groupName, XDeviceDto device) {
//
// Temp result ...
var result = Task.FromResult (false);
//
// Validate Args ...
if (
device.IsNull () ||
userName.IsNullOrEmpty () ||
groupName.IsNullOrEmpty ()
) {
return result;
}
//
// Find User Connection ...
var userConnection = connectionStore
.GetByUserDevice (
device: device,
userName: userName
)
.RunTask ();
if (userConnection.IsNull ()) {
return result;
}
//
// Get Group ...
var group = GetGroup (groupName)
.RunTask ();
//
// check if connection exists in group ...
if (group.Connections.Any (c => c == userConnection.Id)) {
return result;
}
//
try {
//
Groups
.AddToGroupAsync (
userConnection.Id,
groupName
)
.RunTask ();
//
group.Connections.Add (userConnection.Id);
} catch {
return result;
}
//
result = AddOrUpdateGroup (group);
//
return result;
}
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task RemoveUserFromGroup (string userName, string groupName) {
//
// Temp result ...
var result = Task.FromResult (false);
//
// Validate Args ...
if (
userName.IsNullOrEmpty () ||
groupName.IsNullOrEmpty ()
) {
return result;
}
//
// Check Group Exists or not ...
var isGroupExists = groupStore
.IsExistsByKey (groupName)
.RunTask ();
if (!isGroupExists) {
return result;
}
//
// Retrieve User Connections ...
var userConnections = connectionStore
.GetByUserName (userName)
.RunTask ();
if (!userConnections.HasChild ()) {
return result;
}
//
var group = GetGroup (groupName)
.RunTask ();
var mustRemoveConnections = userConnections
.Where (c => group.Connections
.Any (gcId => c.Id == gcId));
if (!mustRemoveConnections.HasChild ()) {
return result;
}
//
// try to remove connections from group ...
try {
//
mustRemoveConnections
.ToList ()
.ForEach (c => {
//
var isRemoved = connectionStore
.RemoveByConnectionId (c.Id)
.RunTask ();
if (isRemoved) {
Groups.RemoveFromGroupAsync (
c.Id,
groupName
)
.RunTask ();
}
});
//
result = Task.FromResult (true);
} catch { }
//
return result;
}
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task RemoveUserDeviceFromGroup (string userName, string groupName, XDeviceDto device) {
//
// Temp result ...
var result = Task.FromResult (false);
//
// Validate Arg ...
if (
device.IsNull () ||
userName.IsNullOrEmpty () ||
groupName.IsNullOrEmpty ()
) {
return result;
}
//
// Check group exists ...
var isGroupExists = groupStore
.IsExistsByKey (groupName)
.RunTask ();
if (!isGroupExists) {
return result;
}
//
var group = GetGroup (groupName)
.RunTask ();
if (!group.Connections.HasChild ()) {
return result;
}
//
// retrieve user connection ...
var userDeviceConnection = connectionStore
.GetByUserDevice (
device: device,
userName: userName
)
.RunTask ();
if (
userDeviceConnection.IsNull () ||
!group.Connections.Any (gc => gc != userDeviceConnection.Id)
) {
return result;
}
//
try {
//
Groups.RemoveFromGroupAsync (
groupName: groupName,
connectionId: userDeviceConnection.Id
);
} catch {
return result;
}
group.Connections.Remove (userDeviceConnection.Id);
//
result = AddOrUpdateGroup (group);
//
return result;
}
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledUser)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task SendMessageToUser (string userName, XPushMessage message) {
//
// Temp result ...
var result = Task.FromResult (false);
//
// Validate Args ...
if (userName.IsNullOrEmpty ()) {
return result;
}
//
// Normalize Message ...
if (message.IsNull ()) {
message = GetPushMessage ();
} else {
message.TimeStamp = DateTime.UtcNow;
}
//
// Check user exists ...
var userConnections = connectionStore
.GetByUserName (userName)
.RunTask ();
if (!userConnections.HasChild ()) {
return result;
}
//
try {
//
Clients
.User (userName)
.SendAsync (
XBasePushAction.PushMessage.GetStringValue (),
message
)
.RunTask();
//
result = Task.FromResult (true);
} catch { }
//
return result;
}
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledUser)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task SendMessageToUserDevice (string userName, XPushMessage message, XDeviceDto device) {
//
// Temp result ...
var result = Task.FromResult (false);
//
// Validate Args ...
if (
device.IsNull () ||
userName.IsNullOrEmpty ()
) {
return result;
}
//
// Normalize Message ...
if (message.IsNull ()) {
message = GetPushMessage ();
} else {
message.TimeStamp = DateTime.UtcNow;
}
//
// Check user exists ...
var userDeviceConnection = connectionStore
.GetByUserDevice (
device: device,
userName: userName
)
.RunTask ();
if (userDeviceConnection.IsNull ()) {
return result;
}
//
try {
//
Clients
.Client (userDeviceConnection.Id)
.SendAsync (
XBasePushAction.PushMessage.GetStringValue (),
message
)
.RunTask();
//
result = Task.FromResult (true);
} catch { }
//
return result;
}
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledUser)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task SendMessageToUsers (IEnumerable userNames, XPushMessage message) {
//
// Temp result ...
var result = Task.FromResult (false);
//
// Validate Args ...
if (!userNames.HasChild ()) {
return result;
}
//
// Normalize Message ...
if (message.IsNull ()) {
message = GetPushMessage ();
} else {
message.TimeStamp = DateTime.UtcNow;
}
//
// Check user exists ...
var usersConnections = connectionStore
.FindMany (c => userNames.Contains (c.User))
.RunTask ();
if (!usersConnections.HasChild ()) {
return result;
}
//
try {
//
Clients
.Users (
userNames
.ToList ()
.AsReadOnly ()
)
.SendAsync (
XBasePushAction.PushMessage.GetStringValue (),
message
)
.RunTask();
//
result = Task.FromResult (true);
} catch { }
//
return result;
}
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledUser)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task> GetUserGroups (string userName) {
//
// Temp Result ...
var resultList = new List ();
//
// Validate Args ...
if (userName.IsNullOrEmpty ()) {
return Task.FromResult (
resultList
.AsEnumerable ()
);
}
//
// Retrieve User Connections ...
var userConnections = connectionStore
.GetByUserName (userName)
.RunTask ();
if (!userConnections.HasChild ()) {
return Task.FromResult (
resultList
.AsEnumerable ()
);
}
//
// Retrieve user Groups ...
var userGroups = groupStore
.FindMany (pg => pg.Connections
.Any (pgCId => userConnections
.Any (uc => uc.Id == pgCId)))
.RunTask ();
if (!userGroups.HasChild ()) {
return Task.FromResult (
resultList
.AsEnumerable ()
);
}
//
// prepare result ...
resultList = userGroups
.Select (ug => ug.Id)
.ToList ();
return Task.FromResult (
resultList
.AsEnumerable ()
);
}
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task> GetUserConnections (string userName) {
//
var resultList = new List ();
//
// Validate Args ...
if (userName.IsNullOrEmpty ()) {
return Task.FromResult (
resultList
.AsEnumerable ()
);
}
//
// check current user is Admin or not ...
var connectedUser = GetUserName ();
var connectedUserRole = GetUserRole ();
var isAdminOrAgent = connectedUserRole.ToNormalString () == "admin" ||
connectedUserRole.ToNormalString () == "agent";
var isUserSame = connectedUser.ToNormalString () == userName.ToNormalString ();
//
// check current user ...
if (!isAdminOrAgent && !isUserSame) {
return Task.FromResult (
resultList
.AsEnumerable ()
);
}
//
return connectionStore
.GetByUserName (userName);
}
#endregion
//
#region Group Push Actions ...
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task SendMessageToGroup (string groupName, XPushMessage message) {
//
// Empty result ...
var result = Task.FromResult (false);
//
// Validate Args ...
if (groupName.IsNullOrEmpty ()) {
return result;
}
//
// Normalize Message ...
if (message.IsNull ()) {
message = GetPushMessage ();
} else {
message.TimeStamp = DateTime.UtcNow;
}
//
// Check Group Exists ...
var isGroupExists = groupStore
.IsExistsByKey (groupName)
.RunTask ();
if (!isGroupExists) {
return result;
}
//
try {
//
Clients
.Group (groupName)
.SendAsync (
XBasePushAction.PushMessage.GetStringValue (),
message
)
.RunTask();
//
result = Task.FromResult (true);
} catch { }
//
return result;
}
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task SendMessageToGroups (IEnumerable groupNames, XPushMessage message) {
//
// Empty result ...
var result = Task.FromResult (false);
//
// Validate Args ...
if (!groupNames.HasChild ()) {
return result;
}
//
// Normalize Message ...
if (message.IsNull ()) {
message = GetPushMessage ();
} else {
message.TimeStamp = DateTime.UtcNow;
}
//
// Check Group Exists ...
var isGroupsExists = Task.WhenAll (groupNames
.Select (gName => groupStore
.IsExistsByKey (gName)))
.RunTask ();
if (!isGroupsExists.All (isExists => !!isExists)) {
return result;
}
//
try {
//
Clients
.Groups (
groupNames
.ToList ()
.AsReadOnly ()
)
.SendAsync (
XBasePushAction.PushMessage.GetStringValue (),
message
)
.RunTask();
//
result = Task.FromResult (true);
} catch { }
//
return result;
}
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task AddConnectionToGroup (string connectionId, string groupName) {
//
// Empty result ...
var result = Task.FromResult (false);
//
// Validate Args ...
if (
groupName.IsNullOrEmpty () ||
connectionId.IsNullOrEmpty ()
) {
return result;
}
//
// Check Connection Exists ...
var isConnectionExists = connectionStore
.IsExistsConnectionId (connectionId)
.RunTask ();
if (!isConnectionExists) {
return result;
}
//
// Get Normalized Group ...
var group = GetGroup (groupName)
.RunTask ();
var isConnectionInGroup = group.Connections.Any (cId => cId == connectionId);
if (isConnectionInGroup) {
return result;
}
//
// add connection to group ...
group.Connections.Add (connectionId);
//
result = AddOrUpdateGroup (group);
//
return result;
}
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task RemoveConnectionFromGroup (string connectionId, string groupName) {
//
// Empty result ...
var result = Task.FromResult (false);
//
// Validate Args ...
if (
groupName.IsNullOrEmpty () ||
connectionId.IsNullOrEmpty ()
) {
return result;
}
//
// Check Connection Exists ...
var isConnectionExists = connectionStore
.IsExistsConnectionId (connectionId)
.RunTask ();
if (!isConnectionExists) {
return result;
}
//
// Check Group Exists ...
var isGroupExists = groupStore
.IsExistsByKey (groupName)
.RunTask ();
if (!isGroupExists) {
return result;
}
//
// Get Normalized Group ...
var group = GetGroup (groupName)
.RunTask ();
var isConnectionInGroup = group.Connections.Any (cId => cId == connectionId);
if (!isConnectionInGroup) {
return result;
}
//
// add connection to group ...
group.Connections.Remove (connectionId);
//
result = AddOrUpdateGroup (group);
//
return result;
}
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task> GetGroupNames () {
//
var groups = groupStore
.GetAll ()
.RunTask ();
//
var result = groups.Select (g => g.Id);
return Task.FromResult (result);
}
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task> GetGroups () {
return groupStore.GetAll ();
}
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task RemoveGroupConnections (string groupName) {
//
// Empty result ...
var result = Task.FromResult (false);
//
// Validate Args ...
if (groupName.IsNullOrEmpty ()) {
return result;
}
//
// retrieve normalized group ...
var group = GetGroup (groupName)
.RunTask ();
if (!group.Connections.HasChild ()) {
return result;
}
//
var allResult = false;
group.Connections
.ToList ()
.ForEach (cIde => {
//
var isRemoved = connectionStore
.RemoveByConnectionId (cIde)
.RunTask ();
//
if (isRemoved) {
group.Connections.Remove (cIde);
}
//
allResult = allResult || isRemoved;
});
if (!allResult) {
return result;
}
//
result = Task.FromResult (true);
return result;
}
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task> GetConnectionGroups (string connectionId) {
//
// Validate Args ...
if (connectionId.IsNullOrEmpty ()) {
return Task.FromResult (
new List ()
.AsEnumerable ()
);
}
//
var connectionGroups = groupStore
.FindMany (group => group.Connections
.Contains (connectionId))
.RunTask ();
//
var resultList = connectionGroups
.Select (group => group.Id);
//
return Task.FromResult (resultList);
}
[Authorize (Policy = XPolicies.Admin)]
[Authorize (Policy = XPolicies.EnabledAgent)]
public Task> GetGroupConnections (string groupName) {
//
// Validate Args ...
if (groupName.IsNullOrEmpty ()) {
return Task.FromResult (
new List ()
.AsEnumerable ()
);
}
//
var group = GetGroup (groupName)
.RunTask ();
//
var resultList = connectionStore
.FindMany (c => group.Connections.Contains (c.Id))
.RunTask ();
//
return Task.FromResult (resultList);
}
#endregion
//
#region Private ...
private Task GetGroup (string groupName) {
//
XPushGroupDto result = null;
//
// Validate Args ...
if (groupName.IsNullOrEmpty ()) {
return Task.FromResult (result);
}
//
var isExistsGroup = groupStore
.IsExistsByKey (groupName)
.RunTask ();
//
if (!isExistsGroup) {
result = new XPushGroupDto {
Id = groupName,
Connections = new List ()
};
} else {
result = groupStore
.Get (groupName)
.RunTask ();
}
//
return Task.FromResult (result);
}
private Task AddOrUpdateGroup (XPushGroupDto group) {
//
var result = false;
//
// Validate Args ...
if (group.IsNull ()) {
return Task.FromResult (result);
}
//
var isExistsGroup = groupStore
.IsExistsByKey (group.Id)
.RunTask ();
if (!isExistsGroup) {
result = groupStore
.Add (group)
.RunTask ();
} else {
//
var updatedGroup = groupStore
.Update (group)
.RunTask ();
result = !updatedGroup.IsNull ();
}
//
return Task.FromResult (result);
}
#endregion
}
```
one of most important things which you had to do in **xPushHelper** is the implementation of your **HUB**(s).
for this, you had to use **XBaseHub** class and extends your Hubs from it. here for example we create a ViewHub as follow:
## XViewHub
```c#
public class XViewHub : XBaseHub {
//
#region Props ...
public static int Count = 0;
#endregion
//
#region Constructor ...
public XViewHub (
IXPushGroupStore groupStore,
IXPushConnectionStore connectionStore
) : base (groupStore, connectionStore) { }
#endregion
//
#region Actions ...
[Authorize]
public Task NotifyCount () {
return Clients.All.SendAsync ("NotifyCount", Count);
}
public Task PublicNotifyCount () {
return Clients.All.SendAsync ("NotifyCount", Count);
}
public int IncreaseCount () {
//
Count += 1;
return Count;
}
#endregion
}
```
## Handling WebRTC connections
there is a Base Class **XBaseWebRTCHub** for enable WebRTC on your projects. all thing you had to do is to extends a class from it.
### XWebRTCHub
as you can see below, there is not anything to add to this class, all things handled by Base Class by default. but if there is some custom actions you need, add them here ...
```c#
public class XWebRTCHub : XBaseWebRTCHub {
//
#region Constructor ...
public XWebRTCHub (
IXPushGroupStore groupStore,
IXPushConnectionStore connectionStore,
IXWebRTCConnectionStore webRTCConnectionStore
) : base (groupStore, connectionStore, webRTCConnectionStore) { }
#endregion
}
```
after complete all requirements inside **xPushHelper** module, next step is to register **xPushService** in your application **DI**, which happens on **Startup.cs** file.
**NOTE:** if you want to use Authorization and Authentication inside your Hubs, you had to register xPushService after enabling Authorization.
before moving forward, please configure **xPushService** as follow, in your appSettings.json file:
```json
...
"PushServiceConfiguration": {
"BaseRoute": "hubs",
"AddSupportMessageProtocol": false
},
...
```
this is a sample configuration. for more info you can review [xPushService Documentation](http://x-dashboard.saherelm.ir/modules/xPushService/index.html#File:Configurations/XPushServiceConfiguration.cs)
then register **xPushService** in **Startup.cs**:
```c#
public class Startup {
...
public void ConfigureServices (IServiceCollection services) {
...
//
// Register XPushService ...
services.AddXPushService (Configuration);
...
}
...
}
```
next step is use **xPushService** middleware for declare and usage of your custom created **HUB**(s) in **xPushHelper** module. this must be done by an instance of **XPushServiceHelper** class which you have to use it for registering your hubs. then pass this to **xPushService** Middleware for configure them for working.
```c#
public class Startup {
...
public void Configure (IApplicationBuilder app, IWebHostEnvironment env) {
...
//
#region XPushService ...
//
var helper = new XPushServiceHelper ();
//
helper.AddHub ("view");
helper.AddHub ("webrtc");
//
app.UseXPushService (helper);
#endregion
...
}
...
}
```
for the moment **xPushService** configured successfully and activated in your application.
for client side, you can follow it's related documentation.
## Maintainer
Hadi Khazaee asl
[https://www.saherelm.ir](https://www.saherelm.ir)
[hadi_khazaee_asl@yahoo.com](mailto:hadi_khazaee_asl@yahoo.com)