private Func <IConfigurableHttpClientInitializer> ValidateAndGetInitializer(GoogleDriveOptions config) { if (_config.CredentialParameters == null) { throw new ArgumentNullException(nameof(_config.CredentialParameters)); } //config should have either svc account config(ClientEmail, PrivateKey) or user account config(ClientId, ClientSecret, RefreshToken) var hasSvcAccount = !string.IsNullOrWhiteSpace(_config.CredentialParameters.ClientEmail) && !string.IsNullOrWhiteSpace(_config.CredentialParameters.PrivateKey); var hasUserAccount = !string.IsNullOrWhiteSpace(_config.CredentialParameters.ClientId) && !string.IsNullOrWhiteSpace(_config.CredentialParameters.ClientSecret) && !string.IsNullOrWhiteSpace(_config.CredentialParameters.RefreshToken); if (hasSvcAccount && hasUserAccount) { throw new ArgumentException("Ambiguous account configuration. Only Service(ClientEmail, PrivateKey) or User(ClientId, ClientSecret, RefreshToken) account data should be provided."); } if (!hasSvcAccount && !hasUserAccount) { throw new ArgumentException("No valid account configuration present."); } if (string.IsNullOrWhiteSpace(_config.ApplicationName)) { throw new ArgumentNullException(nameof(_config.ApplicationName)); } if (string.IsNullOrWhiteSpace(_config.StorageRoot)) { throw new ArgumentNullException(nameof(_config.StorageRoot)); } _config.RootFolderName = !string.IsNullOrWhiteSpace(_config.RootFolderName) ? _config.RootFolderName : _config.ApplicationName; if (hasSvcAccount) { return(ServiceAccountInitializer); } else if (hasUserAccount) { return(UserAccountInitializer); } else { throw new InvalidOperationException("Invalid configuration exception"); } }
public GoogleDriveService(IOptions <GoogleDriveOptions> config, ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger <GoogleDriveService>(); _config = config?.Value ?? throw new ArgumentNullException(nameof(config)); var httpInitializer = ValidateAndGetInitializer(_config); var svcInit = new BaseClientService.Initializer() { HttpClientInitializer = httpInitializer(), ApplicationName = _config.ApplicationName, DefaultExponentialBackOffPolicy = ExponentialBackOffPolicy.None }; _driveService = new DriveService(svcInit) { HttpClient = { Timeout = _config.TimeOut, MessageHandler = { NumTries = 20 } } }; var retryStatusCodes = new[] { HttpStatusCode.InternalServerError, HttpStatusCode.BadGateway, HttpStatusCode.ServiceUnavailable }; var backoffHandler = new BackOffHandler(new BackOffHandler.Initializer(new ExponentialBackOff(TimeSpan.FromMilliseconds(500), 20)) { MaxTimeSpan = TimeSpan.FromSeconds(65), HandleExceptionFunc = exception => { var willDoBackoff = BackOffHandler.Initializer.DefaultHandleExceptionFunc(exception); _logger.LogInformation($"'Exception' request: {exception.Message}. Try backoff: {willDoBackoff}"); return(willDoBackoff); }, HandleUnsuccessfulResponseFunc = response => { var msg = $"'Unsuccessful' request: {response.StatusCode}. "; var willDoBackoff = retryStatusCodes.Contains(response.StatusCode); if (!willDoBackoff) { try { var e = _driveService.DeserializeError(response).Result; msg += $"Reason: {(e.Errors?.Any() == true ? e.Errors[0].Reason : "unknown")}"; willDoBackoff = response.StatusCode == HttpStatusCode.Forbidden && (e.Errors[0].Reason == "rateLimitExceeded" || e.Errors[0].Reason == "userRateLimitExceeded"); } catch { } } ; _logger.LogInformation($"{msg} Backoff: {willDoBackoff}"); return(willDoBackoff); } }); _driveService.HttpClient.MessageHandler.AddUnsuccessfulResponseHandler(backoffHandler); _driveService.HttpClient.MessageHandler.AddExceptionHandler(backoffHandler); }