public static async Task Run( [QueueTrigger(Settings.eventQueueName, Connection = "")] CalendarSyncRequest inputQueueItem, [Queue(Settings.eventQueueName, Connection = "")] ICollector <CalendarSyncRequest> pagingQueueItems, [Table(Settings.eventTableName, Connection = "")] ICollector <EventEntity> eventEntities, ILogger log) { log.LogInformation($"C# Queue trigger function processed: {inputQueueItem}"); //If app doesn't have access token yet, fetch it from Azure AD if (string.IsNullOrEmpty(accessToken)) { accessToken = await AccessTokenHelper.FetchAccessToken(); } /* * Create Http Request * In usual we use DefaultRequestHeaders to add http request header. Because many Azure Functions instance will use same httpClient, it will make conflict to handle it. * For resolving this, we create HttpRequestMessage for each request. * https://stackoverflow.com/questions/23521626/modify-request-headers-per-request-c-sharp-httpclient-pcl * */ string requestQuery = string.IsNullOrEmpty(inputQueueItem.Url) ? CreateRequestQuery(inputQueueItem) : inputQueueItem.Url; var request = new HttpRequestMessage() { RequestUri = new Uri(requestQuery), Method = HttpMethod.Get }; request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); HttpResponseMessage response = await graphHttpClient.SendAsync(request); if (response.StatusCode == HttpStatusCode.Unauthorized) { //When token expire after 60 min, we need to get new token accessToken = await AccessTokenHelper.FetchAccessToken(); //App will re-try with same queue message pagingQueueItems.Add(inputQueueItem); } else if (response.StatusCode == HttpStatusCode.TooManyRequests) { System.Threading.Thread.Sleep(sleepInterval); pagingQueueItems.Add(inputQueueItem); } else if (response.IsSuccessStatusCode) { var responseData = await response.Content.ReadAsAsync <CalendarViewResponse>(); //Pass @odata.nextlink to storage queue for requesting MS graph with multiple Azure Functions node if (!string.IsNullOrEmpty(responseData.odatanextLink)) { pagingQueueItems.Add(new CalendarSyncRequest() { UserId = inputQueueItem.UserId, Url = responseData.odatanextLink, Start = DateTime.Now.AddDays(-1), End = DateTime.Now }); } foreach (Event eventData in responseData.value) { eventEntities.Add(new EventEntity(eventData, inputQueueItem.UserId)); } } }
public static async Task Run( [QueueTrigger(Settings.activityReportQueueName, Connection = "")] ActivityReportRequest inputQueueMessage, [Queue(Settings.activityReportQueueName, Connection = "")] ICollector <ActivityReportRequest> retryQueueMessages, ILogger log) { log.LogInformation($"C# Queue trigger function processed: {inputQueueMessage}"); //We need YYYY-MM-DD foramt string todayStringUtc = DateTime.Today.ToString("yyyy-MM-dd"); if (string.IsNullOrEmpty(accessToken)) { accessToken = await AccessTokenHelper.FetchAccessToken(); } //Generate HTTP Request string requestQuery = GenerateReportUrl(inputQueueMessage.Type, todayStringUtc); var request = new HttpRequestMessage() { RequestUri = new Uri(requestQuery), Method = HttpMethod.Get }; request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); //Send Http Request and get report url HttpResponseMessage response = await graphHttpClient.SendAsync(request); if (response.IsSuccessStatusCode) { string downloadUrl = response.RequestMessage.RequestUri.ToString(); //Console.WriteLine(downloadUrl); //Download report(csv file) and save it to blob storage var downloadResponse = await downloadClient.GetAsync(downloadUrl); if (downloadResponse.IsSuccessStatusCode) { await UploadReport(inputQueueMessage.TypeString, downloadResponse, todayStringUtc); } //If we got 427 status (TooManyRequests), we need to handle Throttling. https://docs.microsoft.com/en-us/graph/throttling else if (downloadResponse.StatusCode == HttpStatusCode.TooManyRequests) { TimeSpan sleepTime; if (downloadResponse.Headers.RetryAfter.Delta.HasValue) { //Need to handle Nullable type https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/index sleepTime = downloadResponse.Headers.RetryAfter.Delta.Value; } else { //Sleep 10 sec at 1st time, then speel 10 + 10n after 2nd time sleepTime = new TimeSpan(0, 0, 10 + inputQueueMessage.RetryCount * 10); } Thread.Sleep(sleepTime); retryQueueMessages.Add(new ActivityReportRequest(inputQueueMessage.Type, inputQueueMessage.RetryCount++)); } else { Console.WriteLine($"Response Status: {downloadResponse.StatusCode}"); Console.WriteLine($"Error Reason: {downloadResponse.ReasonPhrase}"); throw new Exception();//Throw exception and re-try with queue (max.5) } } }
public static async Task Run( [QueueTrigger(Settings.userQueueName, Connection = "")] UserSyncRequest inputQueueItem, [Queue(Settings.userQueueName, Connection = "")] ICollector <UserSyncRequest> pagingQueueItems, [Table(Settings.userTableName, Connection = "")] ICollector <UserEntity> userEntities, [Queue(Settings.eventQueueName, Connection = "")] ICollector <CalendarSyncRequest> calendarQueueItems, ILogger log) { log.LogInformation("Fetch user info started"); //If app doesn't have access token yet, fetch it from Azure AD if (string.IsNullOrEmpty(accessToken)) { accessToken = await AccessTokenHelper.FetchAccessToken(); } //If app get queue message without url, app will use default query. If not, app will use url (nextlink) string graphRequestUrl = string.IsNullOrEmpty(inputQueueItem.Url) ? defaultUserRequestUrl : inputQueueItem.Url; //Create http request message with access token var request = new HttpRequestMessage() { RequestUri = new Uri(graphRequestUrl), Method = HttpMethod.Get }; request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); HttpResponseMessage response = await graphClient.SendAsync(request); log.LogInformation($"Reponse code is: {response.StatusCode}"); if (response.StatusCode == HttpStatusCode.Unauthorized) { //When token expire after 60 min, we need to get new token accessToken = await AccessTokenHelper.FetchAccessToken(); //App will re-try with same queue message pagingQueueItems.Add(inputQueueItem); } else if (response.StatusCode == HttpStatusCode.TooManyRequests) { System.Threading.Thread.Sleep(sleepInterval); pagingQueueItems.Add(inputQueueItem); } else if (response.IsSuccessStatusCode) { var responseData = await response.Content.ReadAsAsync <UserResponse>(); //Pass @odata.nextlink to storage queue for requesting MS graph with multiple Azure Functions node if (!string.IsNullOrEmpty(responseData.NextLink)) { pagingQueueItems.Add(new UserSyncRequest() { Url = responseData.NextLink }); } foreach (User userData in responseData.value) { //Save user data to Storage Table userEntities.Add(new UserEntity(userData)); //Send queue message for fetching calendar items calendarQueueItems.Add(new CalendarSyncRequest() { UserId = userData.Id, Start = DateTime.Now.AddDays(-1), End = DateTime.Now } ); } } }