public async Task Authenticate_Handles_OAuthExceptions()
        {
            // Arrange
            var credentials         = MockInstagramCredentials();
            var options             = Options.Create(credentials);
            var logger              = Mock.Of <ILogger <InstagramApi> >();
            var mockFactory         = MockHttpClientFactory_For_Authenticate_Exception("OAuthException.json", HttpStatusCode.BadRequest);
            var instagramHttpClient = new InstagramHttpClient(options, mockFactory.Object, logger);

            // Act
            var api = new InstagramApi(options, logger, instagramHttpClient);
            var ex  = await Assert.ThrowsAsync <InstagramOAuthException>(() => api.AuthenticateAsync("", "")).ConfigureAwait(false);

            const string expectedMessage = "Error validating access token: Session has expired on Friday, 13-Mar-20 22:00:00 PDT. The current time is Saturday, 14-Mar-20 04:25:10 PDT.";
            var          actualMessage   = ex.Message;

            const string expectedType = "OAuthException";
            var          actualType   = ex.ErrorType;

            const int expectedCode = 190;
            var       actualCode   = ex.ErrorCode;

            const string expectedTraceId = "AzyAsv5wakY_WKcdKis3N32";
            var          actualTraceId   = ex.FbTraceId;

            // Assert
            Assert.NotNull(api);
            Assert.NotNull(ex);
            Assert.Equal(expectedMessage, actualMessage);
            Assert.Equal(expectedType, actualType);
            Assert.Equal(expectedCode, actualCode);
            Assert.Equal(expectedTraceId, actualTraceId);
        }
        public async Task Authenticate_Returns_OAuthResponse()
        {
            // Arrange
            var credentials         = MockInstagramCredentials();
            var options             = Options.Create(credentials);
            var logger              = Mock.Of <ILogger <InstagramApi> >();
            var mockFactory         = MockHttpClientFactory_For_Authenticate();
            var instagramHttpClient = new InstagramHttpClient(options, mockFactory.Object, logger);

            // Act
            var api      = new InstagramApi(options, logger, instagramHttpClient);
            var response = await api.AuthenticateAsync("", "").ConfigureAwait(false);

            // Assert
            Assert.NotNull(api);
            Assert.NotNull(response);
            Assert.Equal("123", response.AccessToken);
            Assert.Equal("123", response.User.Id);
            Assert.Equal("BUSINESS", response.User.AccountType);
            Assert.Equal(116, response.User.MediaCount);
            Assert.Equal("solrevdev", response.User.Username);
        }
        public async Task Authenticate_Handles_IGExceptions()
        {
            // Arrange
            var credentials         = MockInstagramCredentials();
            var options             = Options.Create(credentials);
            var logger              = Mock.Of <ILogger <InstagramApi> >();
            var mockFactory         = MockHttpClientFactory_For_Authenticate_Exception("IGApiException.json", HttpStatusCode.BadRequest);
            var instagramHttpClient = new InstagramHttpClient(options, mockFactory.Object, logger);

            // Act
            var api = new InstagramApi(options, logger, instagramHttpClient);
            var ex  = await Assert.ThrowsAsync <InstagramApiException>(() => api.AuthenticateAsync("", "")).ConfigureAwait(false);

            const string expectedMessage = "Unsupported get request. Object with ID '3518610791' does not exist, cannot be loaded due to missing permissions, or does not support this operation";
            var          actualMessage   = ex.Message;

            const string expectedType = "IGApiException";
            var          actualType   = ex.ErrorType;

            const int expectedCode = 100;
            var       actualCode   = ex.ErrorCode;

            const int expectedSubCode = 33;
            var       actualSubCode   = ex.ErrorSubcode;

            const string expectedTraceId = "AZtb-9k2P_mHdfRi-sN4MNH";
            var          actualTraceId   = ex.FbTraceId;

            // Assert
            Assert.NotNull(api);
            Assert.NotNull(ex);
            Assert.Equal(expectedMessage, actualMessage);
            Assert.Equal(expectedType, actualType);
            Assert.Equal(expectedCode, actualCode);
            Assert.Equal(expectedSubCode, actualSubCode);
            Assert.Equal(expectedTraceId, actualTraceId);
        }
        public async Task <IActionResult> OnGetAsync(string code, string state)
        {
            _logger.LogInformation("Auth/OAuth for state [{state}] returned the code [{code}]", state, code);

            try
            {
                var response = HttpContext.Session.Get <OAuthResponse>(Strings.SessionKey) ?? await _api.AuthenticateAsync(code, state).ConfigureAwait(false);

                if (response == null)
                {
                    Message = "OAutResponse is null. Redirecting to login page";
                    return(RedirectToLoginPage());
                }

                var media = await _api.GetMediaListAsync(response).ConfigureAwait(false);

                _logger.LogInformation("Initial media response returned with [{count}] records ", media.Data.Count);
                Media.Add(media);

                //
                //  TODO: toggle the following boolean for a quick and dirty way of getting all a user's media.
                //
                if (true)
                {
                    while (!string.IsNullOrWhiteSpace(media?.Paging?.Next))
                    {
                        var next  = media?.Paging?.Next;
                        var count = media?.Data?.Count;
                        _logger.LogInformation("Getting next page [{next}]", next);

                        media = await _api.GetMediaListAsync(next).ConfigureAwait(false);

                        _logger.LogInformation("next media response returned with [{count}] records ", count);

                        Media.Add(media);
                    }
                }

                Code     = code;
                State    = state;
                UserInfo = response.User;
                HttpContext.Session.Set(Strings.SessionKey, response);

                Message = $"{UserInfo.Username} has connected to Instagram successfully.";
            }
            catch (InstagramOAuthException ex)
            {
                Message = $"InstagramOAuthException! {ex.Message} ";
                _logger.LogError(ex, "Instagram OAuth error - instagram response message : [{message}] error_type : [{type}] error_code : [{code}] fb_trace [{fbTrace}]", ex.Message, ex.ErrorType, ex.ErrorCode, ex.FbTraceId);
            }
            catch (InstagramApiException ex)
            {
                Message = $"InstagramApiException! {ex.Message} ";
                _logger.LogError(ex, "Instagram API error - instagram response message : [{message}] error_type : [{type}] error_code : [{code}] error_sub_code : [{subCode}] fb_trace [{fbTrace}]", ex.Message, ex, ex.StackTrace, ex.Message, ex.ErrorType, ex.ErrorCode, ex.ErrorSubcode, ex.FbTraceId);
            }
            catch (InstagramException ex)
            {
                Message = $"InstagramException! {ex.Message} ";
                _logger.LogError(ex, "General Instagram error - instagram response message : [{message}] error_type : [{type}] error_code : [{code}] fb_trace [{fbTrace}]", ex.Message, ex, ex.StackTrace, ex.Message, ex.ErrorType, ex.ErrorCode, ex.FbTraceId);
            }
            catch (Exception ex)
            {
                Message = $"Exception! {ex.Message} ";
                _logger.LogError(ex, "Unknown exception calling with message [{message}] and exception [{ex}] and stack [{stack}]", ex.Message, ex, ex.StackTrace);
            }

            return(Page());
        }