How do I properly use puppeteer-sharp with server-side Blazor / SignalR? - signalr

I can get puppeteer-sharp (with headless both true and false) to obtain server-side-rendered Blazor pages, but it looks like I'm only getting the server pre-render and NOT any client-side initiated events (e.g., for instance the second OnInitialized, or any OnAfterRender, OnAfterRenderAsync).
My best guess is that Signal R is not working properly using puppeteer-sharp.
I do this:
using var browserFetcher = new BrowserFetcher();
await browserFetcher.DownloadAsync();
await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions {
Headless = true
});
await using var page = await browser.NewPageAsync();
await page.SetJavaScriptEnabledAsync(true);
await page.GoToAsync("<<mypage>>,
new NavigationOptions {
WaitUntil = new WaitUntilNavigation[] {
WaitUntilNavigation.Load
}
}
);
string sContent = await page.GetContentAsync();
Is there any way to get ALL of the Blazor events to fire properly, or any good discussion of this on the web somewhere?
Thanks in advance!

The answer here was that puppeteer-sharp is actually working just fine with SignalR in all cases.
I really had a separate, more general problem with my authorization settings where the fallback was improperly set. In that case, pages that supposedly did not require authorization were really only providing a single server-prerender (and no later SignalR-enabled onParameterSet, etc.). This incorrect behavior was the same in both a manual browser windows and puppeteer.

Related

Is it a good / working practice to use Firebase's documentReference.get(GetOptions(source: cache)) in Flutter?

My issue was, that with the default GetOptions (omitting the parameter), a request like the following could load seconds if not minutes if the client is offline:
await docRef.get()...
If I check if the client is offline and in this case purposefully change the Source to Source.cache, I have performance that is at least as good, if not better, than if the client was online.
Source _source = Source.serverAndCache;
try {
final result = await InternetAddress.lookup('example.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
_source = Source.serverAndCache;
}
} on SocketException catch (_) {
_source = Source.cache;
}
and then use this variable in the following way:
docRef.get(GetOptions(source: _source))
.then(...
This code works perfectly for me now, but I am not sure, if there are any cases in which using the code like this could raise issues.
Also it seems like a lot of boilerplate code (I refactored it into a function so I can use it in any Database methods but still...)
If there are no issues with this, why wouldn't this be the Firebase default, since after trying the server for an unpredictably long time it switches to cache anyways.

How to make an HttpClient GetAsync wait for a webpage that loads data asynchronously?

I'm making a snippet that sends data to a website that analyses it then sends back results.
Is there any way to make my GetAsych wait until the website finishes its calculation before getting a "full response"?
Ps: The await will not know if the page requested contains any asynchronous processing (eg: xhr calls)- I already use await and ReadAsByteArrayAsync()/ReadAsStringAsync()
Thank you!
You will need something like Selenium to not only fetch the HTML of the website but to fully render the page and execute any dynamic scripts.
You can then hook into some events, wait for certain DOM elements to appear or just wait some time until the page is fully initialized.
Afterwards you can use the API of Selenium to access the DOM and extract the information you need.
Example code:
using (var driver = new ChromeDriver(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)))
{
driver.Navigate().GoToUrl(#"https://automatetheplanet.com/multiple-files-page-objects-item-templates/");
var link = driver.FindElement(By.PartialLinkText("TFS Test API"));
var jsToBeExecuted = $"window.scroll(0, {link.Location.Y});";
((IJavaScriptExecutor)driver).ExecuteScript(jsToBeExecuted);
var wait = new WebDriverWait(driver, TimeSpan.FromMinutes(1));
var clickableElement = wait.Until(ExpectedConditions.ElementToBeClickable(By.PartialLinkText("TFS Test API")));
clickableElement.Click();
}
Source: https://www.automatetheplanet.com/webdriver-dotnetcore2/
What you're looking for here is the await operator. According to the docs:
The await operator suspends evaluation of the enclosing async method until the asynchronous operation represented by its operand completes.
Sample use within the context of an HttpClient object:
public static async Task Main()
{
// send the HTTP GET request
var response = await httpClient.GetAsync("my-url");
// get the response string
// there are other `ReadAs...()` methods if the return type is not a string
var getResult = response.Content.ReadAsStringAsync();
}
Note that the method that encloses the await-ed code is marked as async and has a return type of Task (Task<T> would also work, depending on your needs).

MS-Graph read tasks with admin consent

I am trying to read the Planner task for the users of my tenant. So I configured admin consent for "Tasks.Read", "Tasks.Read.Shared", "Groups.Read.All", "Groups.ReadWrite.All" for the app that is doing that.
Like mentioned here: https://learn.microsoft.com/de-de/graph/api/planneruser-list-tasks?view=graph-rest-1.0&tabs=http
I desined my code to get a token like mentioned here: https://briantjackett.com/2018/12/13/introduction-to-calling-microsoft-graph-from-a-c-net-core-application/
I get a token back and it is valid. (Checked with baerer token check tool.)
I thought that I could access the tasks from the Graph API like '/v1.0/users/{userId}/planner/tasks' but I get HTTP 401 back.
Can anyone give me the missing link? Thanks a lot.
_appId = configuration.GetValue<string>("AppId");
_tenantId = configuration.GetValue<string>("TenantId");
_clientSecret = configuration.GetValue<string>("ClientSecret");
_clientApplication = ConfidentialClientApplicationBuilder
.Create(_appId)
.WithTenantId(_tenantId)
.WithClientSecret(_clientSecret)
.Build();
var graphClient = GraphClientFactory.Create(new DelegateAuthenticationProvider(Authenticate));
var result = await graphClient.GetAsync($"/v1.0/users/{userId}/planner/tasks")
public async Task<string> GetTokenAsync()
{
AuthenticationResult authResult = await _clientApplication.AcquireTokenForClient(_scopes)
.ExecuteAsync();
return authResult.AccessToken;
}
private async Task Authenticate(HttpRequestMessage request)
{
var token = await GetTokenAsync();
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}
Reading tasks for other users is currently not allowed. A user can only read their assigned tasks. As an alternative, you can read the tasks in specific Plans, and sort out the users from that data, if you want to collect assignments from a set of Plans. You can provide feedback on this behavior in Planner UserVoice with the description of what you are trying to accomplish.
Additionally, application permissions are supported now, if that works for your scenario.
/v1.0/users/{userId}/planner/tasks is for getting tasks via getting a user, and you will need User permissions (User.Read.All) to get tasks via that call.
(Also Do you really need Groups.ReadWrite.All? Are you making changes to groups? -- it's not in your original description)

Need to refresh token/add tags Firecloud messaging

I have managed to get my push notifications working on my Xamarin.Forms project. I was able to add new tags when using Google Cloud messaging (which is now deprecated) and having upgraded to FCM I am now only able to set tags upon first registration to the FCM-server.
var hub = new NotificationHub(myHubName,listenConnectionString, this);
var regId = hub.Register(token, myTags.ToArray()).RegistrationId;
How do I call FCM to refresh my instance-token?
My answer was just to "call" hub.Register with my updated tags. Register will remove all earlier tags and replace them with the new ones. Also it needs to be done in an own thread like this:
await Task.Run(() =>
{
var regId = hub.Register(token, myTags.ToArray()).RegistrationId;
});

Refreshing page with meteor iron router

Here is the problem :
I am currently programming a chatapp based on what i found on github (https://github.com/sasikanth513/chatDemo)
I am refactoring it with iron-router.
When I go to the page (clicking on the link) I get an existing chatroom (that's what I want)
When I refresh the page (F5) I get a new created chatroom ! (what i want is getting the existing chatroom ...)
Here is the code in ironrouter :
Router.route('/chatroom', {
name: 'chatroom',
data: function() {
var currentId = Session.get('currentId'); //id of the other person
var res=ChatRooms.findOne({chatIds:{$all:[currentId,Meteor.userId()]}});
console.log(res);
if(res){
Session.set("roomid",res._id);
}
else{
var newRoom= ChatRooms.insert({chatIds:[currentId, Meteor.userId()],messages:[]});
Session.set('roomid',newRoom);
}
}
});
You can find my github repo with the whole project : https://github.com/balibou/textr
Thanx a lot !
Your route data depends on Session variables which will be erased after a refresh. You have a few options but the easiest would be to put the room id directly into the route: '/chatroom/:_id'. Then you can use this.params._id to fetch the appropriate ChatRooms document. Note that you could still keep '/chatroom' for cases where the room doesn't exist, however you'd need to redirect to '/chatroom/:_id' after the insert.
In meteor, the Session object is empty when the client starts, and loading/refreshing the page via HTTP "restarts" the client. To deal with this issue, you could persist the user's correspondent id in a Meteor.user attribute, so that you could easily do:
Router.route('/chatroom', {
name: 'chatroom',
data: function() {
var currentId = Meteor.user().profile.correspondentId;
var res=ChatRooms.findOne({chatIds:{$all:[currentId,Meteor.userId()]}});
console.log(res);
if(res){
Session.set("roomid",res._id);
}
else{
var newRoom= ChatRooms.insert({chatIds:[currentId, Meteor.userId()],messages:[]});
Session.set('roomid',newRoom);
}
}
});
This would work, with the proper permissions, but I would recommend not allowing the direct update of that value on the client (I don't know if you want users to be able to override their correspondentId). So if you want to secure this process, replace all that code with a server method call, where your updates are safer.
Another (and more common case) solution was given by David Weldon, if you don't mind having ids in your URL (and therefore not a single url)

Resources