| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | 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 |
| | } |
| | } |
| |
|