How to stop a Httpclient in c# - c#

In my wpf project I have this:
private async void RequestStart(HttpClient client, Task task)
{
NetworkModel nmodel = new NetworkModel();
NetworkModel1.Rootobject tryModel = await nmodel.ClientStock();
var apiClient = new APIClient();
Task<string> getResponseColourSize = apiClient.GetAsync($"https://www.try.com/{id}.json");
var ATCvalues = new Dictionary<string, string>
{
{ "style", sizeColourID.colourID.ToString() },
{ "size", sizeColourID.sizeID.ToString() },
};
var ATCcontent = new FormUrlEncodedContent(ATCvalues);
var ATCresponse = await client.PostAsync($"https://www.try.com/{id}/cart.json", ATCcontent);
var ATCresponseString = await ATCresponse.Content.ReadAsStringAsync();
System.Windows.MessageBox.Show(ATCresponseString);
}
Firstly I make a GET request and then after several other GET requests, I make a POST request
How would I be able to make it so that on a button click the user would be able to stop the request. The problem is that I cannot find something online to satisfy this. The only things I have found are for either GET or POST requests. Would I just need to stop the RequestStart altogether? Any help would be appreciated!

Related

Google calendar event list is empty

Here is my code:
using System;
using System.Net;
using Google.Apis.Calendar.v3;
using Google.Apis.Calendar.v3.Data;
using Google.Apis.Services;
using Google.Apis.Auth.OAuth2;
using System.Threading;
using System.Linq;
namespace WebsiteTextExtractor {
class Program {
async static Task Main(string[] args) {
string websiteURL = "CensoredURLThatWorks";
WebClient client = new WebClient();
string websiteText = client.DownloadString(websiteURL);
//File.WriteAllText(Environment.GetFolderPath(Environment.SpecialFolder.Desktop)+ "\\ElieCours.ics", websiteText);
// Console.WriteLine($"ElieCours.ics Updated at {Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\ElieCours.ics"}.");
string[] scopes = new string[] { CalendarService.Scope.Calendar };
var credentials = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets {
ClientId = "[redacted]",
ClientSecret = "[redacted]"
},
scopes,
"MyClient",
CancellationToken.None
).Result;
var service = new CalendarService(new BaseClientService.Initializer() {
HttpClientInitializer = credentials,
ApplicationName = "CalendarCleaner"
});
var events = service.Events.List("fc47dab8b05e7b6d0e549ff3207b9f400adfb8ff96b50ecc4ed51a9ecc75ebbe#group.calendar.google.com").Execute().Items;
//Deleting all events
ParallelOptions parallel = new() {
MaxDegreeOfParallelism = 3,
};
Console.WriteLine(events.Count);
await Parallel.ForEachAsync(events, parallel, async (ev, token) => {
await Task.Delay(300, token);
await service.Events.Delete("fc47dab8b05e7b6d0e549ff3207b9f400adfb8ff96b50ecc4ed51a9ecc75ebbe#group.calendar.google.com", ev.Id).ExecuteAsync(token);
});
Ical.Net.Calendar calendarICS = Ical.Net.Calendar.Load(websiteText);
var eventsICS = calendarICS.Events;
await Parallel.ForEachAsync(eventsICS, parallel, async (ev, token) => {
Event newEvent = new() {
Summary = ev.Summary,
Start = new EventDateTime { DateTime = ev.Start.AsSystemLocal },
End = new EventDateTime { DateTime = ev.End.AsSystemLocal },
Description = ev.Description,
};
await Task.Delay(300, token);
await service.Events.Insert(newEvent, "fc47dab8b05e7b6d0e549ff3207b9f400adfb8ff96b50ecc4ed51a9ecc75ebbe#group.calendar.google.com").ExecuteAsync(token);
});
}
}
}
the part where i'm supposed to be getting the events that are in the "events" var is troubling me: the array is empty. But the adding events part just below is working perfectly. Why?
I saw this post: Google Calendar Events Response is Empty
and i was even more confused, because my methods has nothing to do with what they have.
i tried adding a service account, freshly created, to my agenda: nothing. Making my agenda public: nothing. Whatever i do, this array is empty. Best thing is that it wasn't earlier this morning, and now it is. And i know this might be dumb to specify, but i triplechecked my agenda, and it's not empty.
Worst thing is : deleting used to work.
EDIT: saw this post: Google Calendar v3 API [Events: list] request return Empty List
An answer was really interesting... Detailed the way google doesn't really deleted the events or so...? I'm so confused.
"There is no way I know of to return all events in one call. You need to loop through the process getting a page at a time until the "NextPageToken" is no longer returned. This makes sense, because for users that have huge calendars with 1000's of appointments, it's inefficient to return everything in one request." Even more here, never heard of nextpagetoken or so

Getting different HTTP responses in API integration tests based on where I place my breakpoint

I've been getting non-deterministic test results and decided to delve deeper into the issue. I've ended up on two tests behaving differently based on whether I Debug them, or just use Run. I've managed to magically fix one by deleting and renaming it.
I'm testing my API endpoint for updating usernames via WebApplicationFactory(I'm using Mediator and a RavenDb that I recreate between every test if that makes a difference). The problem test case is User_Can_Not_Update_Username_If_It_Is_Already_Taken(). I'm expecting a Conflict response, but most of the time I'm getting OK. The weirdest thing is that the response varies on where I put my breakpoint:
OK
Conflict.
I've tried clearing Rider cache and looking into my FakeApp not disposing correctly. Trying to Debug before the UpdateUsernameCommandHandlerresults in the Conflict response status code, so I'm really at loss right now.
Here is the code of the test case:
[Fact]
public async Task User_Can_Not_Update_Username_If_It_Is_Already_Taken()
{
// Arrange
using var app = new FakeApp(DatabaseFixture.TestDbName);
var registerUserCommand = new RegisterUserCommand
{
Email = "oleksandr.torianyk#gmail.com"
};
var registerUserStringPayload = JsonConvert.SerializeObject(registerUserCommand);
var registerUserHttpContent = new StringContent(registerUserStringPayload, Encoding.UTF8, "application/json");
var registerUserResponse = await app.Client.PostAsync("/user", registerUserHttpContent);
var initialUpdateUsernameCommand = new UpdateUsernameCommand
{
Id = new Guid(await registerUserResponse.Content.ReadAsStringAsync()),
Username = "All-ToR"
};
var initialUpdateUsernameStringPayload = JsonConvert.SerializeObject(initialUpdateUsernameCommand);
var initialUpdateUsernameHttpContent = new StringContent(initialUpdateUsernameStringPayload, Encoding.UTF8, "application/json");
await app.Client.PutAsync("/user/username", initialUpdateUsernameHttpContent);
var updateUsernameCommand = new UpdateUsernameCommand
{
Id = new Guid(await registerUserResponse.Content.ReadAsStringAsync()),
Username = "All-ToR"
};
var updateUsernameStringPayload = JsonConvert.SerializeObject(updateUsernameCommand);
var updateUsernameHttpContent = new StringContent(updateUsernameStringPayload, Encoding.UTF8, "application/json");
// Act
var response = await app.Client.PutAsync("/user/username", updateUsernameHttpContent);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.Conflict);
}
FakeApp
public class FakeApp : IDisposable
{
private readonly WebApplicationFactory<Startup> _appFactory;
public HttpClient Client { get; }
public FakeApp(string ravenDbName = default)
{
_appFactory = new WebApplicationFactory<Startup>().WithWebHostBuilder(webHostBuilder =>
webHostBuilder.ConfigureServices(services =>
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.Development.json", true)
.AddEnvironmentVariables()
.Build();
services.AddRavenDb(configuration, ravenDbName);
services.AddDependencies();
services.AddMediatR(Assembly.GetExecutingAssembly());
}));
Client = _appFactory.CreateClient();
}
public void Dispose()
{
Client.Dispose();
_appFactory.Dispose();
}
}

Sending message to a group chat using bot framework from teams

I want to proactively send a message to a group that the bot was added to (not a team, a simple group chat)
What I'm doing so far is saving (in memory) the conversation Id in a singleton dictionary
and then issue the notification to all conversations.
This is the API endpoint code..
public CINotificationsController(
IBotFrameworkHttpAdapter adapter,
IBot bot,
IConfiguration config,
ConcurrentDictionary<string, ConversationReference> conversationReferences,
ILogger<CINotificationsController> logger)
{
_Adapter = adapter;
_Bot = bot;
_ConversationReferences = conversationReferences;
_Logger = logger;
_AppId = config.GetSection("MicrosoftAppId").Value;
}
[HttpPost]
public async Task<IActionResult> PostAsync(CINotification notificationData)
{
_Logger.LogInformation($"Got CI notification, {JsonConvert.SerializeObject(notificationData)}");
var jobName = notificationData.JobName;
var culpritsEmails = notificationData.Commiter;
foreach (var conv in _ConversationReferences.Values)
{
await ((BotAdapter)_Adapter).ContinueConversationAsync(_AppId, conv, GetBotCallBack(notificationData, conv), default);
}
return Ok(culpritsEmails);
}
private BotCallbackHandler GetBotCallBack(CINotification notificationData, ConversationReference conv)
{
return async (ctx, cts) =>
{
_Logger.LogDebug($"conversationId:[{conv.ActivityId}], serviceUrl:[{conv.ServiceUrl}]");
var mention = new Mention();
if (!string.IsNullOrEmpty(notificationData.Commiter))
{
var membersByEmail = (await TeamsInfo.GetMembersAsync(ctx, cts)).ToDictionary(k => k.Email, v => v, StringComparer.OrdinalIgnoreCase);
_Logger.LogDebug($"members:[{string.Join(",", membersByEmail.Keys)}]");
if (membersByEmail.TryGetValue(notificationData.Commiter, out var teamMemeber))
{
mention.Mentioned = teamMemeber;
mention.Text = $"<at>{teamMemeber.Name}</at>";
}
_Logger.LogDebug($"got mentions: {mention.Text}");
}
var msgText = $"{mention.Text} {notificationData.Message}";
_Logger.LogDebug($"Sending message text: {msgText}");
var replyActivity = MessageFactory.Text(msgText);
replyActivity.Entities.Add(mention);
await ctx.SendActivityAsync(replyActivity);
};
}
Of course an in memory dictionary _ConversationReferences is a bad idea and I am looking for some way (API, something form the SDK ) to be able to list all groups the bot was added to or all conversation that were started ..
Do I have to store it in some DB or do i have a different option ?
I don't think such an API call exists. You can get from the Graph API, for instance, a list of all apps installed into a team, but there's no such thing for group chats or 1-1 chats.
However, you -have- all that information already - simply store it your side, with so many good storage options these days (cloud and otherwise). In that case, it's fully under your control anyway.
You can do it with this code
// Create or get existing chat conversation with user
var response = client.Conversations.CreateOrGetDirectConversation(activity.Recipient, activity.From, activity.GetTenantId());
// Construct the message to post to conversation
Activity newActivity = new Activity()
{
Text = "Hello",
Type = ActivityTypes.Message,
Conversation = new ConversationAccount
{
Id = response.Id
},
};
// Post the message to chat conversation with user
await client.Conversations.SendToConversationAsync(newActivity, response.Id);
Got it here
https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages?tabs=dotnet

How to batch delete events in Office365 using C# Graph SDK

I have a method that deletes multiple events. Currently the code is as following:
public async Task DeleteEvents(IEnumerable<string> eventExternalIds)
{
foreach(var eventExternalId in eventExternalIds)
{
await DeleteEvent(eventExternalId);
}
}
public async Task DeleteEvent(string eventExternalId)
{
await GraphClient
.Users[Username]
.Calendars[CalendarName]
.Events[eventExternalId]
.Request()
.DeleteAsync();
}
I would imagine it won't perform well with any significant number of id's to delete. Is there a way to delete them all in a batch(es) instead of each individually?
msgraph-sdk-dotnet v1.15.0 or above
For msgraph-sdk-dotnet version 1.15.0 or above the support for Batch request has been introduced via BatchRequestContent class
Example
//1. construct a Batch request
var batchRequestContent = new BatchRequestContent();
var step = 1;
foreach (var eventId in eventIds)
{
var requestUrl = graphClient
.Me
.Events[eventId]
.Request().RequestUrl;
var request = new HttpRequestMessage(HttpMethod.Delete, requestUrl);
var requestStep = new BatchRequestStep(step.ToString(), request, null);
batchRequestContent.AddBatchRequestStep(requestStep);
step++;
}
//2. Submit request
var batchRequest = new HttpRequestMessage(HttpMethod.Post, "https://graph.microsoft.com/v1.0/$batch");
batchRequest.Content = batchRequestContent;
await graphClient.AuthenticationProvider.AuthenticateRequestAsync(batchRequest);
var httpClient = new HttpClient();
var batchResponse = await httpClient.SendAsync(batchRequest);
//3. Process response
var batchResponseContent = new BatchResponseContent(batchResponse);
var responses = await batchResponseContent.GetResponsesAsync();
foreach (var response in responses)
{
if (response.Value.IsSuccessStatusCode)
{
//...
}
}
Issues
while targeting NetCore 2.1 or above or .NET Framework
NullReferenceException exception might occur, to address this issue
you could switch to 1.16.0-preview.1 (details)
Limitations
Note: A batch cannot hold more that 20 requests
msgraph-sdk-dotnet v1.14.0 or older
For previous versions, the following example demonstrates how to implement a support for Batch request:
var batchRequest = new BatchRequest();
foreach (var eventId in eventIds)
{
var request = graphClient.Me.Events[eventId].Request();
batchRequest.AddQuery(request,HttpMethod.Delete);
}
var responses = await graphClient.SendBatchAsync(batchRequest);
where BatchRequest is a custom class which adds support for JSON Batching

Task.Run sometimes returns twice

I have an azure function app that I call from a slack slash command.
Sometimes the function takes a little while to return the data requested, so I made that function return a "Calculating..." message to slack immediately, and run the actual processing on a Task.Run (the request contains a webhook that I post back to when I finally get the data) :
Task.Run(() => opsData.GenerateQuoteCheckMessage(incomingData, context.FunctionAppDirectory, log));
This works mostly fine, except every now and then when people are calling the function from slack, it will return the data twice. So it will show one "Calculating..." message and then 2 results returned from the above function.
BTW, Azure functions start with :
public static async Task
Thanks!
UPDATE : here is the code for the function:
[FunctionName("QuoteCheck")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequestMessage req, TraceWriter log, ExecutionContext context)
{
var opsHelper = new OpsHelper();
string bodyContent = await req.Content.ReadAsStringAsync();
var parsedBody = HttpUtility.ParseQueryString(bodyContent);
var commandName = parsedBody["command"];
var incomingBrandId = parsedBody["text"];
int.TryParse(incomingBrandId, out var brandId);
var responseUrl = parsedBody["response_url"];
var incomingData = new IncomingSlackRequestModel
{
UserName = parsedBody["user_name"],
ChannelName = parsedBody["channel_name"],
CommandName = commandName,
ResponseUri = new Uri(responseUrl),
BrandId = brandId
};
var opsData = OpsDataFactory.GetOpsData(context.FunctionAppDirectory, environment);
Task.Run(() => opsData.GenerateQuoteCheckMessage(incomingData, context.FunctionAppDirectory, log));
// Generate a "Calculating" response message based on the correct parameters being passed
var calculatingMessage = opsHelper.GenerateCalculatingMessage(incomingData);
// Return calculating message
return req.CreateResponse(HttpStatusCode.OK, calculatingMessage, JsonMediaTypeFormatter.DefaultMediaType);
}
}
And then the GenerateQuoteCheckMessage calculates some data and eventually posts back to slack (Using Rest Sharp) :
var client = new RestClient(responseUri);
var request = new RestRequest(Method.POST);
request.AddParameter("application/json; charset=utf-8", JsonConvert.SerializeObject(outgoingMessage), ParameterType.RequestBody);
client.Execute(request);
Using Kzrystof's suggestion, I added a service bus call in the function that posts to a queue, and added another function that reads off that queue and processes the request, responding to the webhook that slack gives me :
public void DeferProcessingToServiceBus(IncomingSlackRequestModel incomingSlackRequestModel)
{
var serializedModel = JsonConvert.SerializeObject(incomingSlackRequestModel);
var sbConnectionString = ConfigurationManager.AppSettings.Get("SERVICE_BUS_CONNECTION_STRING");
var sbQueueName = ConfigurationManager.AppSettings.Get("OpsNotificationsQueueName");
var client = QueueClient.CreateFromConnectionString(sbConnectionString, sbQueueName);
var brokeredMessage = new BrokeredMessage(serializedModel);
client.Send(brokeredMessage);
}

Categories