Messages doesn't start to show unless sending one message in signalR - asp.net

I am using signalR in my ASP.NET application, I opened the chat view in two different pages noting that I am using groups method. If I start the conversation from one of those two users, the messages doesn't showing up in the other user's page unless he sent one message.
The messages should be prevent once both users have been opened the view, please any way to solve this??
Java Script file:
var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();
//Disable the send button until connection is established.
document.getElementById("sendButton").disabled = true;
connection.on("ReceiveMessage", function (user, message) {
var li = document.createElement("li");
document.getElementById("messagesList").appendChild(li);
// We can assign user-supplied strings to an element's textContent because it
// is not interpreted as markup. If you're assigning in any other way, you
// should be aware of possible script injection concerns.
li.textContent = `${user} says ${message}`;
});
connection.start().then(function () {
document.getElementById("sendButton").disabled = false;
}).catch(function (err) {
return console.error(err.toString());
});
document.getElementById("sendButton").addEventListener("click", function (event) {
var user = document.getElementById("userInput").value;
var message = document.getElementById("messageInput").value;
var roomId = document.getElementById("RoomId").value;
connection.invoke("JoinRoom", roomId).catch(function (err) {
return console.error(err.toString());
});
connection.invoke("SendMessage", user, message, roomId).catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
});
Chat Hub:
public async Task SendMessage(string user, string message, string RoomId)
{
//await Clients.All.SendAsync("ReceiveMessage", user, message);
await Clients.Group(RoomId).SendAsync("ReceiveMessage", user, message);
}
public async Task JoinRoom(string RoomId)
{
await Groups.AddToGroupAsync(Context.ConnectionId, RoomId);
}

As our friend mentioned, the previous code was adding the user to the room when he sent a message so I modified the code and added this lines, which mean adding the user to the room as soon the connection has been accomplished.
connection.invoke("JoinRoom", roomId).catch(function (err) {
return console.error(err.toString());
Those lines have been added to the function connection.start()

Related

How to unit-test timeout in Flutter?

I have a facade function that reloads the current firebase user and returns it. The thing is that the user reloading part has a timeout and it needs to be tested.
Function:
Future<Option<User>> getSignedInUser() async {
// Reload currentUser if possible
// it mustn't throw [TimeoutException] for whole function,
// this is what this try/catch does
try {
await reloadCurrentUser().timeout(const Duration(seconds: 20));
} catch (e) {
log(e.toString(), name: TAG);
}
return optionOf(_auth.currentUser);
}
reloadCurrentUser() function:
Future<Either<AuthFailure, Unit>> reloadCurrentUser() async {
try {
await _auth.currentUser?.reload();
return right(unit);
} catch (e) {
log(e.toString(), name: TAG);
return left(const AuthFailure.userReloadingError());
}
}
The question is how to test reloadCurrentUser() timeout? I'm trying to throw a TimeoutException when this function is called, but then it throws an error for the whole test.
Current Test function:
test(
'Reaches timeout when reloading currentUser, '
'throws TimeoutException, but function continues '
'and returns optionOf currentUser', () async {
reset(fakeFirebaseAuth);
reset(fakeFacebookAuth);
reset(fakeGoogleSignIn);
final currentUser = FakeUser();
// It says that currentUser exists and *IS* authenticated
when(() => fakeFirebaseAuth.currentUser).thenReturn(currentUser);
when(() => firebaseAuthFacade.reloadCurrentUser())
.thenThrow(TimeoutException('timeout', const Duration(seconds: 20)));
final result = await firebaseAuthFacade.getSignedInUser();
expect(result, isA<Some<User>>());
});
Maybe it's better to remove timeout and use some connectivity package to ensure that the user has a network connection and only then reload the current user?
For testing I'm using mocktail package.
You can use the fake_async package.
Here's a simple example from their docs that you can modify for your use case:
import 'dart:async';
import 'package:fake_async/fake_async.dart';
import 'package:test/test.dart';
void main() {
test("Future.timeout() throws an error once the timeout is up", () {
// Any code run within [fakeAsync] is run within the context of the
// [FakeAsync] object passed to the callback.
fakeAsync((async) {
// All asynchronous features that rely on timing are automatically
// controlled by [fakeAsync].
expect(Completer().future.timeout(Duration(seconds: 5)),
throwsA(isA<TimeoutException>()));
// This will cause the timeout above to fire immediately, without waiting
// 5 seconds of real time.
async.elapse(Duration(seconds: 5));
});
});
}

How to complete login only after functions.auth.user().onCreate is finished

I'm using firebase functions and I have a function which add new collection when user is creating. The problem is sometimes user is logged in before function is done, so user is logged in but new collection is not created yet (and then I have error message 'Missing or insufficient permissions. because a rule cannot find that collection'). How can I handle it?
Is it possible to finish login user (for example using google provider) only when all stuff from
export const createCollection = functions.auth.user().onCreate(async user => {
try {
const addLanguages = await addFirst();
const addSecondCollection = await addSecond();
async function addFirst() {
const userRef = admin.firestore().doc(`languages/${user.uid}`);
await userRef.set(
{
language: null
},
{ merge: true }
);
return 'done';
}
async function addSecond() {
// ...
}
return await Promise.all([addLanguages, addSecondCollection]);
} catch (error) {
throw new functions.https.HttpsError('unknown', error);
}
});
is finished? So google provider window is closed and user is logged in only after that? (and don't using setTimeouts etc)
AFAIK it is not possible to directly couple the two processes implied in your application:
On one hand you have the Google sign-in flow implemented in your front-end (even if there is a call to the Auth service in the back-end), and;
On the other hand you have the Cloud Function that is executed in the back-end.
The problem you encounter comes from the fact that as soon as the Google sign-in flow is successful, your user is signed in to your app and tries to read the document to be created by the Cloud Function.
In some cases (due for example to the Cloud Function cold start) this document is not yet created when the user is signed in, resulting in an error.
One possible solution would be to set a Firestore listener in your front-end to wait for this document to be created, as follows. Note that the following code only takes into account the Firestore document created by the addFirst() function, since you don't give any details on the second document to be created through addSecond().
firebase.auth().signInWithPopup(provider)
.then(function(result) {
var token = result.credential.accessToken;
var user = result.user;
//Here we know the userId then we can set a listener to the doc languages/${user.uid}
firebase.firestore().collection("languages").doc(user.uid)
.onSnapshot(function(doc) {
if(doc.exists) {
console.log("Current data: ", doc.data());
//Do whatever you want with the user doc
} else {
console.log("Language document not yet created by the Cloud Function");
}
});
}).catch(function(error) {
var errorCode = error.code;
var errorMessage = error.message;
var email = error.email;
var credential = error.credential;
// ...
});
As said above, in the above code we only take into account the first Firestore document created by the addFirst() function. But you probably need to wait for the two docs to be created before reading them from the front-end.
So, you may modify you CF as follows:
export const createCollection = functions.auth.user().onCreate(async user => {
try {
await addFirst();
await addSecond();
return null;
async function addFirst() {
const userRef = admin.firestore().doc(`languages/${user.uid}`);
await userRef.set(
{
language: null
},
{ merge: true }
);
}
async function addSecond() {
// ...
}
} catch (error) {
console.log(error);
return null;
}
});
Note that you don't need to use Promise.all(): the following two lines already execute the two document writes to Firestore. And, since you use async/await the second document is only written after the first one is written.
const addLanguages = await addFirst();
const addSecondCollection = await addSecond();
So you just need to set the listener on the path of the second document, and you are done!
Finally, note that doing
throw new functions.https.HttpsError('unknown', error);
in your catch block is the way you should handle errors for a Callable Cloud Function. Here, you are writing a background triggered Cloud Function, and you can just use return null;

Signal-R Calling another server method after receiving previous call data

I am just reading the Microsoft tutorial on Signal-R and want to use it instead of Ajax in .Net Core, Considering the following code from Microsoft, Is there any way we can do another server call, after receiving data from the first call in the broadcastMessage function (that is defined as a javascript function)?
<script type="text/javascript">
$(function () {
// Declare a proxy to reference the hub.
var chat = $.connection.chatHub;
// Create a function that the hub can call to broadcast messages.
chat.client.broadcastMessage = function (name, message) {
// Html encode display name and message.
var encodedName = $('<div />').text(name).html();
var encodedMsg = $('<div />').text(message).html();
// Add the message to the page.
$('#discussion').append('<li><strong>' + encodedName
+ '</strong>: ' + encodedMsg + '</li>');
};
// Get the user name and store it to prepend to messages.
$('#displayname').val(prompt('Enter your name:', ''));
// Set initial focus to message input box.
$('#message').focus();
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Call the Send method on the hub.
chat.server.send($('#displayname').val(), $('#message').val());
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
});
</script>
So that we can examine data received and conditionally initiate another call.
Or if all server calls should be put inside $.connection.hub.start().done(function () {?
You can call other methods after the message is received like this:
TypeScript example:
//Start connection with message controller
public startConnectionMessage = () => {
this.hubMessageConnection = new signalR.HubConnectionBuilder()
.configureLogging(signalR.LogLevel.Debug)
.withUrl('http://localhost:20000/notifications')
.build();
this.hubMessageConnection
.start()
.then(() => {
//after connection started
console.log('Notifications Service connection started!');
// Start the Group Listener.
this.addTranferGroupMessageListener();
// Get ConnectionID.
this.GetConnectionID();
})
.catch(err => console.log('Error while starting connection: ' + err))
}
// Group channel listner.
public addTranferGroupMessageListener = () => {
this.hubMessageConnection.on("groupMessage", (data: any) => {
console.log(data);
});
}
private GetConnectionID() {
this.hubMessageConnection.invoke("GetConnectionID")
.then((connectionID: string) => {
console.log("Recived connectionID = " + connectionID);
// call the method to register AppContextData.
this.sendApplicationContextData(connectionID)
}).catch((error: Error) => {
console.log("Error: " + error);
})
}
private sendApplicationContextData(connectionID: string) {
// add the received connectionID to the payload.
this.connection.ConnectionID = connectionID;
console.log("Sending ApplicationData.");
console.log(this.connection);
//inovke server side method to pass AppContext data.
this.hubMessageConnection.invoke("RegisterAppContextData", this.connection)
.then()
.catch((error: Error) => {
console.log("Error: " + error);
});
}
You can see that after connection is established, I invok a hub method that simply returns me a connectionID and based on connectionID I invoke another method sending that parameter. The server side hub methods are:
public string GetConnectionID()
{
return this.Context.ConnectionId;
}
public async Task RegisterAppContextData(AppContextData data)
{
// Calls the groups Manager.
await this.MapClientToGroups(data);
}

Meteor showing Accounts.createUser errors

I have the following code in my Meteor app where I create new users, assign them 'basic' role. Yet I am having a trouble showing on the client side errors returned while processing Accounts.createUser, can someone please tell me how I can return errors returned by Accounts.createUser while having it on the server as my code below. Thanks
/server/users.js
Meteor.methods({
'createMemberAccount': function (data, role) {
var userId;
Meteor.call('createNewAccount', data, function(err, result) {
if (err) {
return err;
}
console.log('New account id: '+ result);
Roles.addUsersToRoles(result, role);
return userId = result;
});
return userId;
},
'createNewAccount': function (adminData) {
return Accounts.createUser({email: adminData.email, password : adminData.password, roles: adminData.roles});
}
});
/client/signup.js
Template.signupForm.events({
'submit #signup-form': function(e, t){
e.preventDefault();
var userData = {};
userData.email = $(e.target).find('[name=email]').val();
userData.password = $(e.target).find('[name=password]').val();
userData.roles = ['basic'];
Meteor.call('createMemberAccount', userData, 'basic', function(err, userId) {
if (!err) {
console.log('All OK');
} else {
console.log('Error: ' + err.message);
}
});
return false;
}
});
Since You are creating an static rol "basic", you don't need to do that pair of methods, and Meteor.calls, instead you can use
So, use the v on the client side, just like this.
Template.register.events({
'submit #register-form' : function(e, t) {
e.preventDefault();
var email = t.find('#account-email').value
, password = t.find('#account-password').value;
// Trim and validate the input
Accounts.createUser({email: email, password : password}, function(err){
if (err) {
// Inform the user that account creation failed
} else {
// Success. Account has been created and the user
// has logged in successfully.
}
});
return false;
}
});
If you see there is not any role yet incude, so now on the server.js use the onCreateUser method.
//Server.js
Accounts.onCreateUser(function(options, user) {
if (options.profile)
user.profile = options.profile;
user.role = "basic"
return user;
});
Now thats is more easy, and with less code, if you are trying to create 2 differents roles like "Admin" and "Basic", just on the client side create a profile field named "profile.roles" and do a if statement on the onCreateUser.
return Accounts.createUser({email: adminData.email, password : adminData.password, roles: adminData.roles});
This part returns the userId once it is created, it doesn't return any errors when it fails.
When it fails, the returned value will be undefined
Also, in the server, we cannot use callbacks with Accounts.createUser
If you want find the errors, you have to use Accounts.createUser in client side.
Coming to this late, but on the server side, you can assign the createUser to a variable and it will return the new user’s _id; then you can check if that exists. For example (server side only):
let email = 'foo#bar.com';
let password = 'bar';
let profile = {firstName: 'foo', lastName: 'bar'};
let newId = Accounts.createUser({
password: password,
email: email,
profile: profile
});
if (!newId) {
// New _id did not get created, reason is likely EMail Already Exists
throw new Meteor.Error(403, "Cannot create user: " + error.reason);
}
else {
// Stuff here to do after creating the user
}
The Meteor.Error line will be passed back as an error in the callback on the client side, so you can reflect that error to the browser.

Firebase profile integrations

What I am trying to do here is to implement a functionality on the start-up. I want my user's firebase authentication email variable to set a variable that represents the current user logged into my app?
With the following code the line that sets the user variable works after I click log in but not on page load! The console logs work perfectly on start-up but not the setting of user to the email...
crossfitApp.controller('globalIdCtrl', ["$scope", 'defautProfileData',
function ($scope, defautProfileData) {
var dataRef = new Firebase("https://glowing-fire-5401.firebaseIO.com");
//defautProfileData.country;
$scope.authenticated = {
currentUser: 10007,
emailAddress: "",
settings: "",
};
$scope.auth = new FirebaseSimpleLogin(dataRef, function (error, user) {
if (error) {
// an error occurred while attempting login
console.log(error);
} else if (user) {
//Not working
$scope.authenticated.currentUser = user.id;
console.log('User ID: ' + user.id + ', ProvideFr: ' + user.provider + user);
console.log(user);
} else {
console.log($scope.auth);
alert('deuces');
//!Trigger not logged in
}
});
}
]); //GlobaldCtrl
The callback to FirebaseSimpleLogin is not invoked inside the scope of Angular's HTML compiler. Normally, whenever you invoke ng-click, ng-submit, et al, Angular fires $scope.$apply(), which checks for any changes to the bound JavaScript variables and applies those to the DOM elements.
When an event outside of Angular changes a variable, you need to let Angular know by manually triggering a $apply event. The safest way to accomplish this is to use $timeout:
angular.controller('MyCtrl', function($scope, $timeout) {
$scope.auth = new FirebaseSimpleLogin(dataRef, function (error, user) {
if (error) {
// an error occurred while attempting login
console.log(error);
} else if (user) {
$timeout(function() {
$scope.currentUser = user.uid;
});
} else {
console.log('not logged in');
}
});
In general, prefer user.uid to user.id, as it is unique across providers.
A library like AngularFire can save you a lot of trouble, as it abstracts a lot of the complexities of integrating Firebase and Angular.

Resources