| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| using Google.Apis.Discovery; |
| using Google.Apis.Http; |
| using Google.Apis.Logging; |
| using Google.Apis.Requests.Parameters; |
| using Google.Apis.Services; |
| using Google.Apis.Testing; |
| using Google.Apis.Util; |
| using System; |
| using System.Collections.Generic; |
| using System.IO; |
| using System.Net.Http; |
| using System.Runtime.ExceptionServices; |
| using System.Threading; |
| using System.Threading.Tasks; |
|
|
| namespace Google.Apis.Requests |
| { |
| |
| |
| |
| public abstract class ClientServiceRequest |
| { |
| |
| protected List<IHttpUnsuccessfulResponseHandler> _unsuccessfulResponseHandlers; |
| |
| protected List<IHttpExceptionHandler> _exceptionHandlers; |
| |
| protected List<IHttpExecuteInterceptor> _executeInterceptors; |
|
|
| |
| |
| |
| |
| |
| public IHttpExecuteInterceptor Credential { get; set; } |
|
|
| |
| |
| |
| |
| public void AddUnsuccessfulResponseHandler(IHttpUnsuccessfulResponseHandler handler) |
| { |
| handler.ThrowIfNull(nameof(handler)); |
| if (_unsuccessfulResponseHandlers == null) |
| { |
| _unsuccessfulResponseHandlers = new List<IHttpUnsuccessfulResponseHandler>(); |
| } |
| _unsuccessfulResponseHandlers.Add(handler); |
| } |
|
|
| |
| |
| |
| |
| public void AddExceptionHandler(IHttpExceptionHandler handler) |
| { |
| handler.ThrowIfNull(nameof(handler)); |
| if (_exceptionHandlers == null) |
| { |
| _exceptionHandlers = new List<IHttpExceptionHandler>(); |
| } |
| _exceptionHandlers.Add(handler); |
| } |
|
|
| |
| |
| |
| |
| |
| public void AddExecuteInterceptor(IHttpExecuteInterceptor handler) |
| { |
| handler.ThrowIfNull(nameof(handler)); |
| if (_executeInterceptors == null) |
| { |
| _executeInterceptors = new List<IHttpExecuteInterceptor>(); |
| } |
| _executeInterceptors.Add(handler); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| public abstract class ClientServiceRequest<TResponse> : ClientServiceRequest, IClientServiceRequest<TResponse> |
| { |
| internal const string ApiVersionHeaderName = "x-goog-api-version"; |
|
|
| |
| private static readonly ILogger Logger = ApplicationContext.Logger.ForType<ClientServiceRequest<TResponse>>(); |
|
|
| |
| private readonly IClientService service; |
|
|
| |
| public ETagAction ETagAction { get; set; } |
|
|
| |
| |
| |
| public Action<HttpRequestMessage> ModifyRequest { get; set; } |
|
|
| |
| |
| |
| |
| |
| |
| public bool? ValidateParameters { get; set; } |
|
|
| |
| |
| |
| |
| |
| public virtual string ApiVersion => null; |
|
|
| #region IClientServiceRequest Properties |
|
|
| |
| public abstract string MethodName { get; } |
|
|
| |
| public abstract string RestPath { get; } |
|
|
| |
| public abstract string HttpMethod { get; } |
|
|
| |
| public IDictionary<string, IParameter> RequestParameters { get; private set; } |
|
|
| |
| public IClientService Service |
| { |
| get { return service; } |
| } |
|
|
| #endregion |
|
|
| |
| protected ClientServiceRequest(IClientService service) |
| { |
| this.service = service; |
| } |
|
|
| |
| |
| |
| |
| protected virtual void InitParameters() |
| { |
| RequestParameters = new Dictionary<string, IParameter>(); |
| } |
|
|
| #region Execution |
|
|
| |
| public TResponse Execute() |
| { |
| try |
| { |
| using (var response = ExecuteUnparsedAsync(CancellationToken.None).Result) |
| { |
| return ParseResponse(response).Result; |
| } |
| } |
| catch (AggregateException aex) |
| { |
| |
| ExceptionDispatchInfo.Capture(aex.InnerException ?? aex).Throw(); |
| |
| throw; |
| } |
| } |
|
|
| |
| public Stream ExecuteAsStream() |
| { |
| |
| try |
| { |
| |
| var response = ExecuteUnparsedAsync(CancellationToken.None).Result; |
| return response.Content.ReadAsStreamAsync().Result; |
| } |
| catch (AggregateException aex) |
| { |
| |
| throw aex.InnerException; |
| } |
| |
| } |
|
|
| |
| public async Task<TResponse> ExecuteAsync() |
| { |
| return await ExecuteAsync(CancellationToken.None).ConfigureAwait(false); |
| } |
|
|
| |
| public async Task<TResponse> ExecuteAsync(CancellationToken cancellationToken) |
| { |
| using (var response = await ExecuteUnparsedAsync(cancellationToken).ConfigureAwait(false)) |
| { |
| cancellationToken.ThrowIfCancellationRequested(); |
| return await ParseResponse(response).ConfigureAwait(false); |
| } |
| } |
|
|
| |
| public async Task<Stream> ExecuteAsStreamAsync() |
| { |
| return await ExecuteAsStreamAsync(CancellationToken.None).ConfigureAwait(false); |
| } |
|
|
| |
| public async Task<Stream> ExecuteAsStreamAsync(CancellationToken cancellationToken) |
| { |
| |
| var response = await ExecuteUnparsedAsync(cancellationToken).ConfigureAwait(false); |
|
|
| cancellationToken.ThrowIfCancellationRequested(); |
| return await response.Content.ReadAsStreamAsync().ConfigureAwait(false); |
| } |
|
|
| #region Helpers |
|
|
| |
| private async Task<HttpResponseMessage> ExecuteUnparsedAsync(CancellationToken cancellationToken) |
| { |
| using (var request = CreateRequest()) |
| { |
| return await service.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); |
| } |
| } |
|
|
| |
| private async Task<TResponse> ParseResponse(HttpResponseMessage response) |
| { |
| if (response.IsSuccessStatusCode) |
| { |
| return await service.DeserializeResponse<TResponse>(response).ConfigureAwait(false); |
| } |
| var error = await service.DeserializeError(response).ConfigureAwait(false); |
| throw new GoogleApiException(service.Name) |
| { |
| Error = error, |
| HttpStatusCode = response.StatusCode |
| }; |
| } |
|
|
| #endregion |
|
|
| #endregion |
|
|
| |
| public HttpRequestMessage CreateRequest(bool? overrideGZipEnabled = null) |
| { |
| var builder = CreateBuilder(); |
| var request = builder.CreateRequest(); |
| object body = GetBody(); |
| request.SetRequestSerailizedContent(service, body, overrideGZipEnabled.HasValue |
| ? overrideGZipEnabled.Value : service.GZipEnabled); |
| AddETag(request); |
|
|
| if (_unsuccessfulResponseHandlers != null) |
| { |
| request.SetOption(ConfigurableMessageHandler.UnsuccessfulResponseHandlerKey, _unsuccessfulResponseHandlers); |
| } |
| if (_exceptionHandlers != null) |
| { |
| request.SetOption(ConfigurableMessageHandler.ExceptionHandlerKey, _exceptionHandlers); |
| } |
| if (_executeInterceptors != null) |
| { |
| request.SetOption(ConfigurableMessageHandler.ExecuteInterceptorKey, _executeInterceptors); |
| } |
| if (Credential != null) |
| { |
| request.SetOption(ConfigurableMessageHandler.CredentialKey, Credential); |
| } |
| if (ApiVersion is string apiVersion) |
| { |
| request.Headers.Add(ApiVersionHeaderName, apiVersion); |
| } |
|
|
| ModifyRequest?.Invoke(request); |
| return request; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| private RequestBuilder CreateBuilder() |
| { |
| var builder = new RequestBuilder() |
| { |
| BaseUri = new Uri(Service.BaseUri), |
| Path = RestPath, |
| Method = HttpMethod, |
| }; |
|
|
| |
| if (service.ApiKey != null) |
| { |
| builder.AddParameter(RequestParameterType.Query, "key", service.ApiKey); |
| } |
| var parameters = ParameterUtils.CreateParameterDictionary(this); |
| AddParameters(builder, ParameterCollection.FromDictionary(parameters)); |
| return builder; |
| } |
|
|
| |
| protected string GenerateRequestUri() => CreateBuilder().BuildUri().AbsoluteUri; |
|
|
| |
| |
| protected virtual object GetBody() => null; |
|
|
| #region ETag |
|
|
| |
| |
| |
| private void AddETag(HttpRequestMessage request) |
| { |
| IDirectResponseSchema body = GetBody() as IDirectResponseSchema; |
| if (body != null && !string.IsNullOrEmpty(body.ETag)) |
| { |
| var etag = body.ETag; |
| ETagAction action = ETagAction == ETagAction.Default ? GetDefaultETagAction(HttpMethod) : ETagAction; |
| |
| |
| |
| switch (action) |
| { |
| case ETagAction.IfMatch: |
| |
| request.Headers.TryAddWithoutValidation("If-Match", etag); |
| break; |
| case ETagAction.IfNoneMatch: |
| |
| request.Headers.TryAddWithoutValidation("If-None-Match", etag); |
| break; |
| } |
| } |
| } |
|
|
| |
| [VisibleForTestOnly] |
| public static ETagAction GetDefaultETagAction(string httpMethod) |
| { |
| switch (httpMethod) |
| { |
| |
| case HttpConsts.Get: |
| return ETagAction.IfNoneMatch; |
|
|
| |
| case HttpConsts.Put: |
| case HttpConsts.Post: |
| case HttpConsts.Patch: |
| case HttpConsts.Delete: |
| return ETagAction.IfMatch; |
|
|
| default: |
| return ETagAction.Ignore; |
| } |
| } |
|
|
| #endregion |
|
|
| #region Parameters |
|
|
| |
| private void AddParameters(RequestBuilder requestBuilder, ParameterCollection inputParameters) |
| { |
| bool validateParameters = ValidateParameters ?? (Service as BaseClientService)?.ValidateParameters ?? true; |
|
|
| foreach (var parameter in inputParameters) |
| { |
| if (!RequestParameters.TryGetValue(parameter.Key, out IParameter parameterDefinition)) |
| { |
| throw new GoogleApiException(Service.Name, |
| $"Invalid parameter \"{parameter.Key}\" was specified"); |
| } |
|
|
| string value = parameter.Value; |
| if (validateParameters && |
| !ParameterValidator.ValidateParameter(parameterDefinition, value, out string error)) |
| { |
| throw new GoogleApiException(Service.Name, |
| $"Parameter validation failed for \"{parameterDefinition.Name}\" : {error}"); |
| } |
|
|
| if (value == null) |
| { |
| value = parameterDefinition.DefaultValue; |
| } |
|
|
| switch (parameterDefinition.ParameterType) |
| { |
| case "path": |
| requestBuilder.AddParameter(RequestParameterType.Path, parameter.Key, value); |
| break; |
| case "query": |
| |
| if (!Object.Equals(value, parameterDefinition.DefaultValue) || parameterDefinition.IsRequired) |
| { |
| requestBuilder.AddParameter(RequestParameterType.Query, parameter.Key, value); |
| } |
| break; |
| default: |
| throw new GoogleApiException(service.Name, |
| $"Unsupported parameter type \"{parameterDefinition.ParameterType}\" for \"{parameterDefinition.Name}\""); |
| } |
| } |
|
|
| |
| foreach (var parameter in RequestParameters.Values) |
| { |
| if (parameter.IsRequired && !inputParameters.ContainsKey(parameter.Name)) |
| { |
| throw new GoogleApiException(service.Name, |
| $"Parameter \"{parameter.Name}\" is missing"); |
| } |
| } |
| } |
|
|
| #endregion |
| } |
| } |
|
|