Example #1
0
        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)
                }
            }
        }
Example #3
0
        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
                    }
                                           );
                }
            }
        }