初始化
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// LogServiceClientBuilders.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// 身份验证凭据。
|
||||
/// </summary>
|
||||
public class Credential
|
||||
{
|
||||
public String AccessKeyId { get; }
|
||||
|
||||
public String AccessKey { get; }
|
||||
|
||||
public String StsToken { get; }
|
||||
|
||||
public Credential(String accessKeyId, String accessKey, String stsToken = null)
|
||||
{
|
||||
this.AccessKeyId = accessKeyId;
|
||||
this.AccessKey = accessKey;
|
||||
this.StsToken = stsToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// CompressType.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Protocol
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据压缩类型。
|
||||
/// </summary>
|
||||
public enum CompressType
|
||||
{
|
||||
/// <summary>
|
||||
/// 无压缩(默认)
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// 使用标准LZ4压缩方式。
|
||||
/// </summary>
|
||||
/// <seealso ref="http://wikipedia.org/wiki/LZ4_(compression_algorithm)"/>
|
||||
Lz4,
|
||||
|
||||
/// <summary>
|
||||
/// 使用Deflate(Zlib包装,RFC 1950)压缩方式。
|
||||
/// </summary>
|
||||
/// <seealso ref="http://www.ietf.org/rfc/rfc1950.txt"/>
|
||||
/// <seealso ref="http://www.ietf.org/rfc/rfc1951.txt"/>
|
||||
Deflate,
|
||||
}
|
||||
}
|
||||
55
Aliyun.Api.LogService/Infrastructure/Protocol/Error.cs
Normal file
55
Aliyun.Api.LogService/Infrastructure/Protocol/Error.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// Error.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Protocol
|
||||
{
|
||||
/// <summary>
|
||||
/// 错误信息
|
||||
/// </summary>
|
||||
public class Error
|
||||
{
|
||||
/// <summary>
|
||||
/// 错误码
|
||||
/// </summary>
|
||||
public ErrorCode ErrorCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 错误消息
|
||||
/// </summary>
|
||||
public String ErrorMessage { get; }
|
||||
|
||||
public Error(ErrorCode errorCode, String errorMessage)
|
||||
{
|
||||
this.ErrorCode = errorCode;
|
||||
this.ErrorMessage = errorMessage;
|
||||
}
|
||||
|
||||
public override String ToString()
|
||||
=> $"{this.ErrorCode}{(this.ErrorMessage == null ? String.Empty : $" ({this.ErrorMessage})")}";
|
||||
}
|
||||
}
|
||||
229
Aliyun.Api.LogService/Infrastructure/Protocol/ErrorCode.cs
Normal file
229
Aliyun.Api.LogService/Infrastructure/Protocol/ErrorCode.cs
Normal file
@@ -0,0 +1,229 @@
|
||||
//
|
||||
// ErrorCode.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Protocol
|
||||
{
|
||||
public class ErrorCode
|
||||
{
|
||||
#region Known error codes
|
||||
|
||||
/// <summary>
|
||||
/// SDK 内部错误。
|
||||
/// </summary>
|
||||
internal static ErrorCode SdkInternalError { get; } = new ErrorCode(nameof(SdkInternalError));
|
||||
|
||||
///<summary>
|
||||
///没有提供必须的 Content-Length 请求头。
|
||||
///</summary>
|
||||
public static ErrorCode MissingContentLength { get; } = new ErrorCode(nameof(MissingContentLength));
|
||||
|
||||
///<summary>
|
||||
///不支持 Content-Type 指定的类型。
|
||||
///</summary>
|
||||
public static ErrorCode InvalidContentType { get; } = new ErrorCode(nameof(InvalidContentType));
|
||||
|
||||
///<summary>
|
||||
///没有为 Body 不为空的 HTTP 请求指定 Content-Type 头。
|
||||
///</summary>
|
||||
public static ErrorCode MissingContentType { get; } = new ErrorCode(nameof(MissingContentType));
|
||||
|
||||
///<summary>
|
||||
///压缩场景下没有提供必须的 x-log-bodyrawsize 请求头。
|
||||
///</summary>
|
||||
public static ErrorCode MissingBodyRawSize { get; } = new ErrorCode(nameof(MissingBodyRawSize));
|
||||
|
||||
///<summary>
|
||||
///x-log-bodyrawsize 的值无效。
|
||||
///</summary>
|
||||
public static ErrorCode InvalidBodyRawSize { get; } = new ErrorCode(nameof(InvalidBodyRawSize));
|
||||
|
||||
///<summary>
|
||||
///x-log-compresstype 指定的压缩方式不支持。
|
||||
///</summary>
|
||||
public static ErrorCode InvalidCompressType { get; } = new ErrorCode(nameof(InvalidCompressType));
|
||||
|
||||
///<summary>
|
||||
///没有提供 HTTP 标准请求头 Host。
|
||||
///</summary>
|
||||
public static ErrorCode MissingHost { get; } = new ErrorCode(nameof(MissingHost));
|
||||
|
||||
///<summary>
|
||||
///没有提供 HTTP 标准请求头 Date。
|
||||
///</summary>
|
||||
public static ErrorCode MissingDate { get; } = new ErrorCode(nameof(MissingDate));
|
||||
|
||||
///<summary>
|
||||
///Date 请求头的值不符合 RFC822 标准。
|
||||
///</summary>
|
||||
public static ErrorCode InvalidDateFormat { get; } = new ErrorCode(nameof(InvalidDateFormat));
|
||||
|
||||
///<summary>
|
||||
///没有提供 HTTP 请求头 x-log-apiversion。
|
||||
///</summary>
|
||||
public static ErrorCode MissingAPIVersion { get; } = new ErrorCode(nameof(MissingAPIVersion));
|
||||
|
||||
///<summary>
|
||||
///HTTP 请求头 x-log-apiversion 的值不支持。
|
||||
///</summary>
|
||||
public static ErrorCode InvalidAPIVersion { get; } = new ErrorCode(nameof(InvalidAPIVersion));
|
||||
|
||||
///<summary>
|
||||
///没有在 Authorization 头部提供 AccessKeyId。
|
||||
///</summary>
|
||||
public static ErrorCode MissAccessKeyId { get; } = new ErrorCode(nameof(MissAccessKeyId));
|
||||
|
||||
///<summary>
|
||||
///提供的 AccessKeyId 值未授权。
|
||||
///</summary>
|
||||
public static ErrorCode Unauthorized { get; } = new ErrorCode(nameof(Unauthorized));
|
||||
|
||||
///<summary>
|
||||
///没有提供 HTTP 请求头 x-log-signaturemethod。
|
||||
///</summary>
|
||||
public static ErrorCode MissingSignatureMethod { get; } = new ErrorCode(nameof(MissingSignatureMethod));
|
||||
|
||||
///<summary>
|
||||
///x-log-signaturemethod 头部指定的签名方法不支持。
|
||||
///</summary>
|
||||
public static ErrorCode InvalidSignatureMethod { get; } = new ErrorCode(nameof(InvalidSignatureMethod));
|
||||
|
||||
///<summary>
|
||||
///请求的发送时间超过当前服务处理时间前后 15 分钟的范围。
|
||||
///</summary>
|
||||
public static ErrorCode RequestTimeTooSkewed { get; } = new ErrorCode(nameof(RequestTimeTooSkewed));
|
||||
|
||||
///<summary>
|
||||
///日志项目(Project)不存在。
|
||||
///</summary>
|
||||
public static ErrorCode ProjectNotExist { get; } = new ErrorCode(nameof(ProjectNotExist));
|
||||
|
||||
///<summary>
|
||||
///请求的数字签名不匹配。
|
||||
///</summary>
|
||||
public static ErrorCode SignatureNotMatch { get; } = new ErrorCode(nameof(SignatureNotMatch));
|
||||
|
||||
///<summary>
|
||||
///超过写入日志限额。
|
||||
///</summary>
|
||||
public static ErrorCode WriteQuotaExceed { get; } = new ErrorCode(nameof(WriteQuotaExceed));
|
||||
|
||||
///<summary>
|
||||
///超过读取日志限额。
|
||||
///</summary>
|
||||
public static ErrorCode ReadQuotaExceed { get; } = new ErrorCode(nameof(ReadQuotaExceed));
|
||||
|
||||
///<summary>
|
||||
///服务器内部错误。
|
||||
///</summary>
|
||||
public static ErrorCode InternalServerError { get; } = new ErrorCode(nameof(InternalServerError));
|
||||
|
||||
///<summary>
|
||||
///服务器正忙,请稍后再试。
|
||||
///</summary>
|
||||
public static ErrorCode ServerBusy { get; } = new ErrorCode(nameof(ServerBusy));
|
||||
|
||||
#endregion
|
||||
|
||||
private static readonly IDictionary<String, ErrorCode> KnownErrorCodes;
|
||||
|
||||
public String Code { get; }
|
||||
|
||||
static ErrorCode()
|
||||
{
|
||||
KnownErrorCodes = new Dictionary<String, ErrorCode>(typeof(ErrorCode)
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
|
||||
.Where(x => x.PropertyType == typeof(ErrorCode))
|
||||
.Select(x => x.GetValue(null))
|
||||
.Cast<ErrorCode>()
|
||||
.ToDictionary(x => x.Code, x => x));
|
||||
}
|
||||
|
||||
private ErrorCode(String code)
|
||||
{
|
||||
this.Code = code;
|
||||
}
|
||||
|
||||
public static Boolean IsKnownErrorCode(String code)
|
||||
{
|
||||
return KnownErrorCodes.ContainsKey(code);
|
||||
}
|
||||
|
||||
public Boolean IsKnownErrorCode()
|
||||
{
|
||||
return IsKnownErrorCode(this.Code);
|
||||
}
|
||||
|
||||
public override Boolean Equals(Object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, obj))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (obj)
|
||||
{
|
||||
case ErrorCode other:
|
||||
return String.Equals(this.Code, other.Code);
|
||||
case String other:
|
||||
return String.Equals(this.Code, other);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override Int32 GetHashCode()
|
||||
=> this.Code?.GetHashCode() ?? 0;
|
||||
|
||||
public override String ToString()
|
||||
=> this.Code;
|
||||
|
||||
public static implicit operator String(ErrorCode code)
|
||||
=> code?.Code;
|
||||
|
||||
public static implicit operator ErrorCode(String code)
|
||||
=> code == null
|
||||
? null
|
||||
: (KnownErrorCodes.TryGetValue(code, out var errorCode)
|
||||
? errorCode
|
||||
: new ErrorCode(code));
|
||||
|
||||
public static Boolean operator ==(ErrorCode lhs, ErrorCode rhs)
|
||||
=> lhs?.Code == rhs?.Code;
|
||||
|
||||
public static Boolean operator !=(ErrorCode lhs, ErrorCode rhs)
|
||||
=> lhs?.Code != rhs?.Code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,439 @@
|
||||
//
|
||||
// HttpLogServiceClient.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Aliyun.Api.LogService.Domain.Config;
|
||||
using Aliyun.Api.LogService.Domain.Log;
|
||||
using Aliyun.Api.LogService.Domain.LogStore;
|
||||
using Aliyun.Api.LogService.Domain.LogStore.Index;
|
||||
using Aliyun.Api.LogService.Domain.LogStore.Shard;
|
||||
using Aliyun.Api.LogService.Domain.LogStore.Shipper;
|
||||
using Aliyun.Api.LogService.Domain.MachineGroup;
|
||||
using Aliyun.Api.LogService.Domain.Project;
|
||||
using Aliyun.Api.LogService.Infrastructure.Authentication;
|
||||
using Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf;
|
||||
using Aliyun.Api.LogService.Utils;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Protocol.Http
|
||||
{
|
||||
public class HttpLogServiceClient : ILogServiceClient
|
||||
{
|
||||
private readonly HttpClient httpClient;
|
||||
|
||||
private readonly Func<Credential> credentialProvider;
|
||||
|
||||
public HttpLogServiceClient(HttpClient httpClient, Func<Credential> credentialProvider)
|
||||
{
|
||||
this.httpClient = httpClient;
|
||||
this.credentialProvider = credentialProvider;
|
||||
}
|
||||
|
||||
|
||||
#region Helper
|
||||
|
||||
private async Task<TResponse> SendRequestAsync<TResponse>(IRequestBuilder<HttpRequestMessage> requestBuilder,
|
||||
Func<IResponseResolver, Task<TResponse>> resposneResolver, Boolean outOfProject = false, String project = null)
|
||||
{
|
||||
var credential = this.credentialProvider();
|
||||
|
||||
var httpRequestMessage = requestBuilder
|
||||
.Authenticate(credential)
|
||||
.Sign(SignatureType.HmacSha1)
|
||||
.Build();
|
||||
|
||||
if (outOfProject)
|
||||
{
|
||||
httpRequestMessage.Headers.Host = this.httpClient.BaseAddress.Host;
|
||||
} else if (project.IsNotEmpty())
|
||||
{
|
||||
httpRequestMessage.Headers.Host = $"{project}.{this.httpClient.BaseAddress.Host}";
|
||||
}
|
||||
|
||||
var responseMessage = await this.httpClient.SendAsync(httpRequestMessage);
|
||||
return await resposneResolver(HttpResponseMessageResolver.For(responseMessage));
|
||||
}
|
||||
|
||||
private Task<IResponse> SendRequestAsync(IRequestBuilder<HttpRequestMessage> requestBuilder, Boolean outOfProject = false, String project = null)
|
||||
=> this.SendRequestAsync(requestBuilder, x => x.ResolveAsync(), outOfProject, project);
|
||||
|
||||
private Task<IResponse<TResult>> SendRequestAsync<TResult>(IRequestBuilder<HttpRequestMessage> requestBuilder, Boolean outOfProject = false, String project = null)
|
||||
where TResult : class
|
||||
=> this.SendRequestAsync(requestBuilder, x => x.With<TResult>().ResolveAsync(), outOfProject, project);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region LogStore
|
||||
|
||||
public Task<IResponse> CreateLogStoreAsync(CreateLogStoreRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Post, "/logstores")
|
||||
.Content(request)
|
||||
.Serialize(SerializeType.Json),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse> DeleteLogStoreAsync(DeleteLogStoreRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Delete, $"/logstores/{request.LogstoreName}"),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse> UpdateLogStoreAsync(UpdateLogStoreRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Put, $"/logstores/{request.LogstoreName}")
|
||||
.Content(request)
|
||||
.Serialize(SerializeType.Json),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse<GetLogStoreResult>> GetLogStoreAsync(GetLogStoreRequest request)
|
||||
=> this.SendRequestAsync<GetLogStoreResult>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, $"/logstores/{request.LogstoreName}"),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse<ListLogStoreResult>> ListLogStoreAsync(ListLogStoreRequest request)
|
||||
=> this.SendRequestAsync<ListLogStoreResult>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, "/logstores")
|
||||
.Query(request),
|
||||
project:request.ProjectName);
|
||||
|
||||
#region Shard
|
||||
|
||||
public Task<IResponse<IList<ShardInfo>>> ListShardsAsync(ListShardRequest request)
|
||||
=> this.SendRequestAsync<IList<ShardInfo>>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, $"/logstores/{request.LogstoreName}/shards"),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse<IList<ShardInfo>>> SplitShardAsync(SplitShardRequest request)
|
||||
=> this.SendRequestAsync<IList<ShardInfo>>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Post, $"/logstores/{request.LogstoreName}/shards/{request.ShardId}")
|
||||
.Query("action", "split")
|
||||
.Query("key", request.SplitKey),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse<IList<ShardInfo>>> MergeShardsAsync(MergeShardRequest request)
|
||||
=> this.SendRequestAsync<IList<ShardInfo>>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Post, $"/logstores/{request.LogstoreName}/shards/{request.ShardId}")
|
||||
.Query("action", "merge"),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse<GetCursorResult>> GetCursorAsync(GetCursorRequest request)
|
||||
=> this.SendRequestAsync<GetCursorResult>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, $"/logstores/{request.LogstoreName}/shards/{request.ShardId}")
|
||||
.Query("type", "cursor")
|
||||
.Query("from", request.From),
|
||||
project:request.ProjectName);
|
||||
|
||||
#endregion Shard
|
||||
|
||||
#region Shipper
|
||||
|
||||
public Task<IResponse<GetShipperResult>> GetShipperStatusAsync(GetShipperRequest request)
|
||||
=> this.SendRequestAsync<GetShipperResult>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, $"/logstores/{request.LogstoreName}/shipper/{request.ShipperName}/tasks")
|
||||
.Query("from", request.From.ToString())
|
||||
.Query("to", request.To.ToString())
|
||||
.Query("status", request.Status)
|
||||
.Query("offset", request.Offset.ToString())
|
||||
.Query("size", request.Size.ToString()),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse> RetryShipperTaskAsync(RetryShipperRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Put, $"/logstores/{request.LogstoreName}/shipper/{request.ShipperName}/tasks")
|
||||
.Content(request.TaskIds)
|
||||
.Serialize(SerializeType.Json),
|
||||
project:request.ProjectName);
|
||||
|
||||
#endregion Shipper
|
||||
|
||||
#region Index
|
||||
|
||||
public Task<IResponse> CreateIndexAsync(CreateIndexRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Post, $"/logstores/{request.LogstoreName}/index")
|
||||
.Content(request)
|
||||
.Serialize(SerializeType.Json),
|
||||
project:request.ProjectName);
|
||||
|
||||
#endregion Index
|
||||
|
||||
#endregion LogStore
|
||||
|
||||
|
||||
#region Logs
|
||||
|
||||
public Task<IResponse> PostLogStoreLogsAsync(PostLogsRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Post, request.HashKey.IsEmpty()
|
||||
? $"/logstores/{request.LogstoreName}/shards/lb"
|
||||
: $"/logstores/{request.LogstoreName}/shards/route?key={request.HashKey}")
|
||||
.Content(request.LogGroup.ToProtoModel())
|
||||
.Serialize(SerializeType.Protobuf)
|
||||
.Compress(CompressType.Lz4),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse<PullLogsResult>> PullLogsAsync(PullLogsRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, $"/logstores/{request.LogstoreName}/shards/{request.ShardId}")
|
||||
.Query("type", "logs")
|
||||
.Query("cursor", request.Cursor)
|
||||
.Query("count", request.Count.ToString())
|
||||
.Header("Accept", "application/x-protobuf")
|
||||
.Header("Accept-Encoding", "lz4"),
|
||||
resolver => resolver
|
||||
.Deserialize(x => LogGroupList.Parser.ParseFrom(x))
|
||||
.ResolveAsync(x => new PullLogsResult(x.ToDomainModel())),
|
||||
project:request.ProjectName);
|
||||
|
||||
public async Task<IResponse<GetLogsResult>> GetLogsAsync(GetLogsRequest request)
|
||||
=> (await this.SendRequestAsync<IList<IDictionary<String, String>>>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, $"/logstores/{request.Logstorename}")
|
||||
.Query("type", "log")
|
||||
.Query("from", request.From.ToString())
|
||||
.Query("to", request.To.ToString())
|
||||
.Query("topic", request.Topic)
|
||||
.Query("query", request.Query)
|
||||
.Query("line", request.Line.ToString())
|
||||
.Query("offset", request.Offset.ToString())
|
||||
.Query("reverse", request.Reverse.ToString()),
|
||||
project:request.ProjectName))
|
||||
.Transform((httpHeaders, data) =>
|
||||
{
|
||||
var newResult = new GetLogsResult(data)
|
||||
{
|
||||
Count = Convert.ToInt32(httpHeaders.GetValueOrDefault(LogHeaders.Count)),
|
||||
ProcessedRows = Convert.ToInt32(httpHeaders.GetValueOrDefault(LogHeaders.ProcessedRows)),
|
||||
ElapsedMillisecond = Convert.ToInt32(httpHeaders.GetValueOrDefault(LogHeaders.ElapsedMillisecond)),
|
||||
HasSql = Convert.ToBoolean(httpHeaders.GetValueOrDefault(LogHeaders.HasSql)),
|
||||
AggQuery = httpHeaders.GetValueOrDefault(LogHeaders.AggQuery),
|
||||
WhereQuery = httpHeaders.GetValueOrDefault(LogHeaders.WhereQuery)
|
||||
};
|
||||
|
||||
// Parse Progress
|
||||
if (Enum.TryParse<LogProgressState>(httpHeaders.GetValueOrDefault(LogHeaders.Progress), true, out var progress))
|
||||
{
|
||||
newResult.Progress = progress;
|
||||
}
|
||||
|
||||
// Parse QueryInfo
|
||||
if (httpHeaders.TryGetValue(LogHeaders.QueryInfo, out var queryInfoValue))
|
||||
{
|
||||
newResult.QueryInfo = JsonConvert.DeserializeObject<LogQueryInfo>(queryInfoValue);
|
||||
}
|
||||
|
||||
return newResult;
|
||||
});
|
||||
|
||||
public async Task<IResponse<GetLogsResult>> GetProjectLogsAsync(GetProjectLogsRequest request)
|
||||
=> (await this.SendRequestAsync<IList<IDictionary<String, String>>>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, "/logs")
|
||||
.Query(request),
|
||||
project:request.ProjectName))
|
||||
.Transform((httpHeaders, data) =>
|
||||
{
|
||||
var newResult = new GetLogsResult(data)
|
||||
{
|
||||
Count = Convert.ToInt32(httpHeaders.GetValueOrDefault(LogHeaders.Count)),
|
||||
ProcessedRows = Convert.ToInt32(httpHeaders.GetValueOrDefault(LogHeaders.ProcessedRows)),
|
||||
ElapsedMillisecond = Convert.ToInt32(httpHeaders.GetValueOrDefault(LogHeaders.ElapsedMillisecond)),
|
||||
HasSql = Convert.ToBoolean(httpHeaders.GetValueOrDefault(LogHeaders.HasSql)),
|
||||
AggQuery = httpHeaders.GetValueOrDefault(LogHeaders.AggQuery),
|
||||
WhereQuery = httpHeaders.GetValueOrDefault(LogHeaders.WhereQuery)
|
||||
};
|
||||
|
||||
// Parse Progress
|
||||
if (Enum.TryParse<LogProgressState>(httpHeaders.GetValueOrDefault(LogHeaders.Progress), true, out var progress))
|
||||
{
|
||||
newResult.Progress = progress;
|
||||
}
|
||||
|
||||
// Parse QueryInfo
|
||||
if (httpHeaders.TryGetValue(LogHeaders.QueryInfo, out var queryInfoValue))
|
||||
{
|
||||
newResult.QueryInfo = JsonConvert.DeserializeObject<LogQueryInfo>(queryInfoValue);
|
||||
}
|
||||
|
||||
return newResult;
|
||||
});
|
||||
|
||||
public async Task<IResponse<GetLogHistogramsResult>> GetHistogramsAsync(GetLogHistogramsRequest request)
|
||||
=> (await this.SendRequestAsync<IList<LogHistogramInfo>>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, $"/logstores/{request.Logstorename}")
|
||||
.Query("type", "histogram")
|
||||
.Query("from", request.From.ToString())
|
||||
.Query("to", request.To.ToString())
|
||||
.Query("topic", request.Topic)
|
||||
.Query("query", request.Query),
|
||||
project:request.ProjectName))
|
||||
.Transform((httpHeaders, data) =>
|
||||
{
|
||||
var newResult = new GetLogHistogramsResult(data)
|
||||
{
|
||||
Count = Convert.ToInt32(httpHeaders.GetValueOrDefault(LogHeaders.Count))
|
||||
};
|
||||
|
||||
// Parse Progress
|
||||
if (Enum.TryParse<LogProgressState>(httpHeaders.GetValueOrDefault(LogHeaders.Progress), true, out var progress))
|
||||
{
|
||||
newResult.Progress = progress;
|
||||
}
|
||||
|
||||
return newResult;
|
||||
});
|
||||
|
||||
#endregion Logs
|
||||
|
||||
|
||||
#region MachineGroup
|
||||
|
||||
public Task<IResponse> CreateMachineGroupAsync(CreateMachineGroupRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Post, "/machinegroups")
|
||||
.Content(request)
|
||||
.Serialize(SerializeType.Json),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse> DeleteMachineGroupAsync(DeleteMachineGroupRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Delete, $"/machinegroups/{request.GroupName}"),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse> UpdateMachineGroupAsync(UpdateMachineGroupRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Put, $"/machinegroups/{request.GroupName}")
|
||||
.Content(request)
|
||||
.Serialize(SerializeType.Json),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse<ListMachineGroupResult>> ListMachineGroupAsync(ListMachineGroupRequest request)
|
||||
=> this.SendRequestAsync<ListMachineGroupResult>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, "/machinegroups")
|
||||
.Query(request),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse<GetMachineGroupResult>> GetMachineGroupAsync(GetMachineGroupRequest request)
|
||||
=> this.SendRequestAsync<GetMachineGroupResult>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, $"/machinegroups/{request.GroupName}"));
|
||||
|
||||
public Task<IResponse> ApplyConfigToMachineGroupAsync(ApplyConfigToMachineGroupRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Put, $"/machinegroups/{request.GroupName}/configs/{request.ConfigName}"),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse> RemoveConfigFromMachineGroupAsync(RemoveConfigFromMachineGroupRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Delete, $"/machinegroups/{request.GroupName}/configs/{request.ConfigName}"),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse<ListMachinesResult>> ListMachinesAsync(ListMachinesRequest request)
|
||||
=> this.SendRequestAsync<ListMachinesResult>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, $"/machinegroups/{request.GroupName}/machines")
|
||||
.Query("offset", request.Offset.ToString())
|
||||
.Query("size", request.Size.ToString()),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse<GetAppliedConfigsResult>> GetAppliedConfigsAsync(GetAppliedConfigsRequest request)
|
||||
=> this.SendRequestAsync<GetAppliedConfigsResult>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, $"/machinegroups/{request.GroupName}/configs"),
|
||||
project:request.ProjectName);
|
||||
|
||||
#endregion MachineGroup
|
||||
|
||||
|
||||
#region Config
|
||||
|
||||
public Task<IResponse> CreateConfigAsync(CreateConfigRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Post, "/configs")
|
||||
.Content(request)
|
||||
.Serialize(SerializeType.Json),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse<ListConfigResult>> ListConfigAsync(ListConfigRequest request)
|
||||
=> this.SendRequestAsync<ListConfigResult>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, "/configs")
|
||||
.Query(request),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse<GetAppliedMachineGroupsResult>> GetAppliedMachineGroupsAsync(GetAppliedMachineGroupsRequest request)
|
||||
=> this.SendRequestAsync<GetAppliedMachineGroupsResult>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, $"/configs/{request.ConfigName}/machinegroups"),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse<GetConfigResult>> GetConfigAsync(GetConfigRequest request)
|
||||
=> this.SendRequestAsync<GetConfigResult>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, $"/configs/{request.ConfigName}"),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse> DeleteConfigAsync(DeleteConfigRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Delete, $"/configs/{request.ConfigName}"),
|
||||
project:request.ProjectName);
|
||||
|
||||
public Task<IResponse> UpdateConfigAsync(UpdateConfigRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Put, $"/configs/{request.ConfigName}")
|
||||
.Content(request)
|
||||
.Serialize(SerializeType.Json),
|
||||
project:request.ProjectName);
|
||||
|
||||
#endregion Config
|
||||
|
||||
|
||||
#region Project
|
||||
|
||||
public Task<IResponse> CreateProjectAsync(CreateProjectRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Post, "/")
|
||||
.Content(request)
|
||||
.Serialize(SerializeType.Json),
|
||||
outOfProject: true);
|
||||
|
||||
public Task<IResponse<ListProjectResult>> ListProjectAsync(ListProjectRequest request)
|
||||
=> this.SendRequestAsync<ListProjectResult>(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, "/")
|
||||
.Query(request),
|
||||
outOfProject: true);
|
||||
|
||||
public Task<IResponse<GetProjectResult>> GetProjectAsync(GetProjectRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Get, "/"),
|
||||
resolver => resolver
|
||||
.With<ProjectDetailInfo>()
|
||||
.ResolveAsync(x => new GetProjectResult(x)),
|
||||
project: request.ProjectName);
|
||||
|
||||
public Task<IResponse> DeleteProjectAsync(DeleteProjectRequest request)
|
||||
=> this.SendRequestAsync(
|
||||
new HttpRequestMessageBuilder(HttpMethod.Delete, "/"),
|
||||
project: request.ProjectName);
|
||||
|
||||
#endregion Project
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
//
|
||||
// HttpLogServiceClientBuilder.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using Aliyun.Api.LogService.Infrastructure.Authentication;
|
||||
using Aliyun.Api.LogService.Utils;
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Protocol.Http
|
||||
{
|
||||
public class HttpLogServiceClientBuilder
|
||||
{
|
||||
private Uri endpoint;
|
||||
|
||||
private Func<Credential> credentialProvider;
|
||||
|
||||
private Boolean? useProxy;
|
||||
|
||||
private WebProxy proxy;
|
||||
|
||||
private TimeSpan? timeout;
|
||||
|
||||
/// <summary>
|
||||
/// 设置服务入口地址以及项目名称。
|
||||
/// </summary>
|
||||
/// <param name="endpoint">服务入口地址,协议部分可以忽略(支持如:end.point 或 http://end.point 或 https://end.point)。</param>
|
||||
/// <param name="project">项目名称。</param>
|
||||
/// <returns>当前实例。</returns>
|
||||
public HttpLogServiceClientBuilder Endpoint(String endpoint, String project)
|
||||
{
|
||||
Ensure.NotEmpty(endpoint, nameof(endpoint));
|
||||
Ensure.NotEmpty(project, nameof(project));
|
||||
|
||||
this.endpoint = parseUri(endpoint, project);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private Uri parseUri(String endpoint, String project)
|
||||
{
|
||||
Uri uri;
|
||||
if (endpoint.StartsWith("http://"))
|
||||
{
|
||||
uri = new UriBuilder(endpoint.Insert(7, project + ".")).Uri;
|
||||
} else if (endpoint.StartsWith("https://"))
|
||||
{
|
||||
uri = new UriBuilder(endpoint.Insert(8, project + ".")).Uri;
|
||||
} else
|
||||
{
|
||||
uri = new UriBuilder("http://" + project + "." + endpoint).Uri;
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置服务入口地址以及项目名称。
|
||||
/// </summary>
|
||||
/// <param name="endpoint">服务入口地址。</param>
|
||||
/// <param name="project">项目名称。</param>
|
||||
/// <returns>当前实例。</returns>
|
||||
public HttpLogServiceClientBuilder Endpoint(Uri endpoint, String project)
|
||||
{
|
||||
Ensure.NotNull(endpoint, nameof(endpoint));
|
||||
Ensure.NotEmpty(project, nameof(project));
|
||||
|
||||
this.endpoint = parseUri(endpoint.Host, project);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置服务使用的 accessKeyId 和 accessKey,此方法设置的凭据只能用于 RAM 模式下的主账号/子账号的访问。
|
||||
/// 跨账号访问需要使用 STS 模式并定时刷新凭据信息,<see cref="Credential(Func{Aliyun.Api.LogService.Infrastructure.Authentication.Credential})"/>。
|
||||
/// </summary>
|
||||
/// <param name="accessKeyId">accessKeyId。</param>
|
||||
/// <param name="accessKey">accessKey。</param>
|
||||
/// <returns>当前实例。</returns>
|
||||
public HttpLogServiceClientBuilder Credential(String accessKeyId, String accessKey)
|
||||
{
|
||||
Ensure.NotEmpty(accessKeyId, nameof(accessKeyId));
|
||||
Ensure.NotEmpty(accessKey, nameof(accessKey));
|
||||
|
||||
var credential = new Credential(accessKeyId, accessKey);
|
||||
this.credentialProvider = () => credential;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置服务使用的 accessKeyId、 accessKey 及 stsToken 的动态提供者。
|
||||
/// 此方法设置的凭据支持 RAM 及 STS 模式。
|
||||
/// </summary>
|
||||
/// <param name="credentialProvider">凭据提供者。需要注意的是提供的 <paramref name="credentialProvider"/> 在每次请求中都会被调用,请注意缓存有效的服务凭据。</param>
|
||||
/// <returns>当前实例。</returns>
|
||||
public HttpLogServiceClientBuilder Credential(Func<Credential> credentialProvider)
|
||||
{
|
||||
Ensure.NotNull(credentialProvider, nameof(credentialProvider));
|
||||
|
||||
this.credentialProvider = credentialProvider;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置连接是否使用代理。
|
||||
/// 如果无法确定是否需要使用,或想使用系统默认配置,请跳过此方法的调用即可。
|
||||
/// </summary>
|
||||
/// <param name="useProxy"><c>true</c> 表示使用代理,<c>false</c> 表示不使用代理(同时会跳过系统级别的代理设置)。</param>
|
||||
/// <returns>当前实例。</returns>
|
||||
public HttpLogServiceClientBuilder UseProxy(Boolean useProxy)
|
||||
{
|
||||
this.useProxy = useProxy;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置使用代理的信息。
|
||||
/// 如果 <see cref="UseProxy"/> 设置为 <c>false</c> 则此项设置无意义。
|
||||
/// </summary>
|
||||
/// <param name="proxyHost">代理地址(必填)。</param>
|
||||
/// <param name="proxyPort">代理端口(可选),不填则使用默认端口。</param>
|
||||
/// <param name="proxyUserName">代理验证用户(可选),不填则跳过验证。</param>
|
||||
/// <param name="proxyPassword">代理验证密码(可选),不填或者 <paramref name="proxyUserName"/> 为空时跳过验证。</param>
|
||||
/// <param name="proxyDomain">代理验证域(可选)。</param>
|
||||
/// <returns>当前实例。</returns>
|
||||
public HttpLogServiceClientBuilder Proxy(String proxyHost,
|
||||
Int32? proxyPort = null, String proxyUserName = null, String proxyPassword = null, String proxyDomain = null)
|
||||
{
|
||||
Ensure.NotEmpty(proxyHost, nameof(proxyHost));
|
||||
|
||||
var webProxy = proxyPort.HasValue
|
||||
? new WebProxy(proxyHost, proxyPort.Value)
|
||||
: new WebProxy(proxyHost);
|
||||
|
||||
if (proxyUserName.IsNotEmpty())
|
||||
{
|
||||
webProxy.Credentials = proxyDomain.IsEmpty()
|
||||
? new NetworkCredential(proxyUserName, proxyPassword ?? String.Empty)
|
||||
: new NetworkCredential(proxyUserName, proxyPassword ?? String.Empty, proxyDomain);
|
||||
}
|
||||
|
||||
this.proxy = webProxy;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置请求超时时间,单位毫秒,需要使用其它单位的请使用 <see cref="RequestTimeout(TimeSpan)"/>。
|
||||
/// 如跳过此项设置,则使用底层框架的默认超时时间。
|
||||
/// </summary>
|
||||
/// <param name="timeoutMillis">请求超时时间,单位毫秒。</param>
|
||||
/// <returns>当前实例。</returns>
|
||||
public HttpLogServiceClientBuilder RequestTimeout(Int32 timeoutMillis)
|
||||
{
|
||||
return this.RequestTimeout(TimeSpan.FromMilliseconds(timeoutMillis));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置请求超时时间。
|
||||
/// 如跳过此项设置,则使用底层框架的默认超时时间。
|
||||
/// </summary>
|
||||
/// <param name="timeout">请求超时时间。</param>
|
||||
/// <returns>当前实例。</returns>
|
||||
public HttpLogServiceClientBuilder RequestTimeout(TimeSpan timeout)
|
||||
{
|
||||
this.timeout = timeout;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据上述设置构建 <see cref="HttpLogServiceClient"/>。
|
||||
/// </summary>
|
||||
/// <returns>构建好的 <see cref="HttpLogServiceClient"/>。</returns>
|
||||
public HttpLogServiceClient Build()
|
||||
{
|
||||
Ensure.NotNull(this.endpoint, nameof(this.endpoint));
|
||||
Ensure.NotNull(this.credentialProvider, nameof(this.credentialProvider));
|
||||
|
||||
HttpClient httpClient;
|
||||
if (this.useProxy != null || this.proxy != null)
|
||||
{
|
||||
var httpClientHandler = new HttpClientHandler();
|
||||
|
||||
if (this.useProxy != null)
|
||||
{
|
||||
httpClientHandler.UseProxy = this.useProxy.Value;
|
||||
}
|
||||
|
||||
if (this.proxy != null)
|
||||
{
|
||||
httpClientHandler.Proxy = this.proxy;
|
||||
}
|
||||
|
||||
httpClient = new HttpClient(httpClientHandler);
|
||||
} else
|
||||
{
|
||||
httpClient = new HttpClient();
|
||||
}
|
||||
|
||||
httpClient.BaseAddress = this.endpoint;
|
||||
|
||||
// Default use project scope api.
|
||||
httpClient.DefaultRequestHeaders.Host = this.endpoint.Host;
|
||||
|
||||
if (this.timeout != null)
|
||||
{
|
||||
httpClient.Timeout = this.timeout.Value;
|
||||
}
|
||||
|
||||
var client = new HttpLogServiceClient(httpClient, this.credentialProvider);
|
||||
|
||||
return client;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
//
|
||||
// HttpRequestMessageBuilder.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using Aliyun.Api.LogService.Infrastructure.Authentication;
|
||||
using Aliyun.Api.LogService.Utils;
|
||||
using Google.Protobuf;
|
||||
using Ionic.Zlib;
|
||||
using LZ4;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Protocol.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// Builder for constructing the <see cref="HttpRequestMessage"/>.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public class HttpRequestMessageBuilder : IRequestBuilder<HttpRequestMessage>
|
||||
{
|
||||
private static readonly Byte[] EmptyByteArray = new Byte[0];
|
||||
|
||||
private static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
};
|
||||
|
||||
private readonly HttpRequestMessage httpRequestMessage;
|
||||
|
||||
private readonly Encoding encoding;
|
||||
|
||||
private readonly String path;
|
||||
|
||||
private readonly IDictionary<String, String> query;
|
||||
|
||||
/// <summary>
|
||||
/// The authentication credential.
|
||||
/// </summary>
|
||||
private Credential credential;
|
||||
|
||||
/// <summary>
|
||||
/// The real content to transfer.
|
||||
/// </summary>
|
||||
private Object content;
|
||||
|
||||
/// <summary>
|
||||
/// The Content-MD5 header in HEX format.
|
||||
/// </summary>
|
||||
private String contentMd5Hex;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the serialized content.
|
||||
/// </summary>
|
||||
private Byte[] SerializedContent =>
|
||||
this.content == null
|
||||
? null
|
||||
: this.content as Byte[]
|
||||
?? throw new InvalidOperationException("Content must serialized before this operation.");
|
||||
|
||||
/// <summary>
|
||||
/// Proceed the actions after content prepared (i.e., all transforms (e.g., serialize, compress, encrypt, encode) of <see cref="content"/> are applied).
|
||||
/// </summary>
|
||||
private Action contentHandler;
|
||||
|
||||
/// <summary>
|
||||
/// The signature type.
|
||||
/// </summary>
|
||||
private SignatureType signatureType;
|
||||
|
||||
public HttpRequestMessageBuilder(HttpMethod method, String uri)
|
||||
{
|
||||
this.httpRequestMessage = new HttpRequestMessage(method, uri);
|
||||
this.encoding = Encoding.UTF8;
|
||||
ParseUri(uri, out this.path, out this.query);
|
||||
|
||||
this.FillDefaultHeaders();
|
||||
}
|
||||
|
||||
private static void ParseUri(String uri, out String path, out IDictionary<String, String> query)
|
||||
{
|
||||
var absUri = new Uri(new Uri("http://fa.ke"), uri);
|
||||
path = absUri.AbsolutePath;
|
||||
query = absUri.ParseQueryString()
|
||||
.ToEnumerable()
|
||||
.ToDictionary(kv => kv.Key, kv => kv.Value); // NOTE: Restricted mode, key cannot be duplicated.
|
||||
}
|
||||
|
||||
private void FillDefaultHeaders()
|
||||
{
|
||||
this.httpRequestMessage.Headers.Date = DateTimeOffset.Now;
|
||||
this.httpRequestMessage.Headers.UserAgent.Add(new ProductInfoHeaderValue("log-dotnetcore-sdk", Constants.AssemblyVersion));
|
||||
this.httpRequestMessage.Headers.Add(LogHeaders.ApiVersion, "0.6.0");
|
||||
}
|
||||
|
||||
#region Query
|
||||
|
||||
public IRequestBuilder<HttpRequestMessage> Query(String key, String value)
|
||||
{
|
||||
this.query.Add(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IRequestBuilder<HttpRequestMessage> Query(Object queryModel)
|
||||
{
|
||||
foreach (var kv in JObject.FromObject(queryModel, JsonSerializer.CreateDefault(JsonSerializerSettings)))
|
||||
{
|
||||
this.query.Add(kv.Key, kv.Value.Value<String>());
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Header
|
||||
|
||||
/// <summary>
|
||||
/// Set headers of <see cref="T:System.Net.Http.Headers.HttpRequestHeaders" />
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder<HttpRequestMessage> Header(String key, String value)
|
||||
{
|
||||
this.httpRequestMessage.Headers.Add(key, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private void ContentHeader(Action<HttpContentHeaders> option)
|
||||
{
|
||||
if (this.httpRequestMessage.Content == null)
|
||||
{
|
||||
this.contentHandler += () => option(this.httpRequestMessage.Content.Headers);
|
||||
} else
|
||||
{
|
||||
option(this.httpRequestMessage.Content.Headers);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetBodyRawSize(Int32 size)
|
||||
=> this.httpRequestMessage.Headers.Add(LogHeaders.BodyRawSize, size.ToString());
|
||||
|
||||
private void SetCompressType(String compressType)
|
||||
=> this.httpRequestMessage.Headers.Add(LogHeaders.CompressType, compressType);
|
||||
|
||||
private void SetSignatureMethod(String signatureMethod)
|
||||
=> this.httpRequestMessage.Headers.Add(LogHeaders.SignatureMethod, signatureMethod);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Content
|
||||
|
||||
public IRequestBuilder<HttpRequestMessage> Content(Byte[] content)
|
||||
=> this.Content((Object) content);
|
||||
|
||||
public IRequestBuilder<HttpRequestMessage> Content(Object content)
|
||||
{
|
||||
this.content = content;
|
||||
|
||||
if (content is Byte[] data)
|
||||
{
|
||||
this.SetBodyRawSize(data.Length);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Serialize
|
||||
|
||||
public IRequestBuilder<HttpRequestMessage> Serialize(SerializeType serializeType)
|
||||
{
|
||||
switch (this.content)
|
||||
{
|
||||
case null:
|
||||
throw new InvalidOperationException("Nothing to serialize.");
|
||||
case Byte[] _:
|
||||
throw new InvalidOperationException("Content has already been serialized.");
|
||||
}
|
||||
|
||||
switch (serializeType)
|
||||
{
|
||||
case SerializeType.Json:
|
||||
{
|
||||
this.ContentHeader(x => x.ContentType = new MediaTypeHeaderValue("application/json"));
|
||||
var json = JsonConvert.SerializeObject(this.content, JsonSerializerSettings);
|
||||
this.Content(this.encoding.GetBytes(json));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SerializeType.Protobuf:
|
||||
{
|
||||
if (!(this.content is IMessage protoMessage))
|
||||
{
|
||||
throw new ArgumentException("Serialization of ProtoBuf requires IMessage.");
|
||||
}
|
||||
|
||||
this.ContentHeader(x => x.ContentType = new MediaTypeHeaderValue("application/x-protobuf"));
|
||||
this.Content(protoMessage.ToByteArray());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(serializeType), serializeType, null);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
#endregion Serialize
|
||||
|
||||
|
||||
#region Compress
|
||||
|
||||
public IRequestBuilder<HttpRequestMessage> Compress(CompressType compressType)
|
||||
{
|
||||
if (this.SerializedContent == null)
|
||||
{
|
||||
throw new InvalidOperationException("Nothing to compress.");
|
||||
}
|
||||
|
||||
switch (compressType)
|
||||
{
|
||||
case CompressType.None:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case CompressType.Lz4:
|
||||
{
|
||||
this.SetCompressType("lz4");
|
||||
this.content = LZ4Codec.Encode(this.SerializedContent, 0, this.SerializedContent.Length);
|
||||
break;
|
||||
}
|
||||
|
||||
case CompressType.Deflate:
|
||||
{
|
||||
this.SetCompressType("deflate");
|
||||
this.content = ZlibStream.CompressBuffer(this.SerializedContent);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(compressType), compressType, null);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
#endregion Compress
|
||||
|
||||
|
||||
#region Authentication
|
||||
|
||||
public IRequestBuilder<HttpRequestMessage> Authenticate(Credential credential)
|
||||
{
|
||||
Ensure.NotNull(credential, nameof(credential));
|
||||
Ensure.NotEmpty(credential.AccessKeyId, nameof(credential.AccessKeyId));
|
||||
Ensure.NotEmpty(credential.AccessKey, nameof(credential.AccessKey));
|
||||
|
||||
this.credential = credential;
|
||||
return this;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Sign
|
||||
|
||||
public IRequestBuilder<HttpRequestMessage> Sign(SignatureType signatureType)
|
||||
{
|
||||
this.signatureType = signatureType;
|
||||
return this;
|
||||
}
|
||||
|
||||
private Byte[] ComputeSignature()
|
||||
{
|
||||
switch (this.signatureType)
|
||||
{
|
||||
case SignatureType.HmacSha1:
|
||||
{
|
||||
using (var hasher = new HMACSHA1(this.encoding.GetBytes(this.credential.AccessKey)))
|
||||
{
|
||||
this.SetSignatureMethod("hmac-sha1"); // This header must be set before generating sign source.
|
||||
var signSource = this.GenerateSignSource();
|
||||
var sign = hasher.ComputeHash(this.encoding.GetBytes(signSource));
|
||||
|
||||
return sign;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(this.signatureType), this.signatureType, "Currently only support [hmac-sha1] signature.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String GenerateSignSource()
|
||||
{
|
||||
var verb = this.httpRequestMessage.Method.Method;
|
||||
var contentMd5 = this.contentMd5Hex;
|
||||
var contentType = this.httpRequestMessage.Content?.Headers.ContentType.MediaType;
|
||||
var date = this.httpRequestMessage.Headers.Date?.ToString("r"); /* RFC 822 format */
|
||||
var logHeaders = String.Join("\n", this.httpRequestMessage.Headers
|
||||
.Concat(this.httpRequestMessage.Content?.Headers ?? Enumerable.Empty<KeyValuePair<String, IEnumerable<String>>>())
|
||||
.Where(x => x.Key.StartsWith("x-log") || x.Key.StartsWith("x-acs"))
|
||||
.Select(x => new KeyValuePair<String, String>(x.Key.ToLower(), x.Value.SingleOrDefault() /* Fault tolerance */))
|
||||
.Where(x => x.Value.IsNotEmpty()) // Remove empty header
|
||||
.OrderBy(x => x.Key)
|
||||
.Select(x => $"{x.Key}:{x.Value}"));
|
||||
|
||||
var resource = this.httpRequestMessage.RequestUri.OriginalString;
|
||||
|
||||
String signSource;
|
||||
if (this.query.IsEmpty())
|
||||
{
|
||||
signSource = String.Join("\n", verb, contentMd5 ?? String.Empty, contentType ?? String.Empty, date, logHeaders, resource);
|
||||
} else
|
||||
{
|
||||
signSource = String.Join("\n", verb, contentMd5 ?? String.Empty, contentType ?? String.Empty, date, logHeaders, resource) + "?" +
|
||||
String.Join("&", this.query
|
||||
.OrderBy(x => x.Key)
|
||||
.Select(x => $"{x.Key}={x.Value}"));
|
||||
}
|
||||
return signSource;
|
||||
}
|
||||
|
||||
private Byte[] CalculateContentMd5()
|
||||
{
|
||||
using (var hasher = MD5.Create())
|
||||
{
|
||||
return hasher.ComputeHash(this.SerializedContent);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Signature
|
||||
|
||||
public HttpRequestMessage Build()
|
||||
{
|
||||
// Validate
|
||||
Ensure.NotNull(this.credential, nameof(this.credential));
|
||||
Ensure.NotEmpty(this.credential.AccessKeyId, nameof(this.credential.AccessKeyId));
|
||||
Ensure.NotEmpty(this.credential.AccessKey, nameof(this.credential.AccessKey));
|
||||
|
||||
// Process sts-token.
|
||||
var hasSecurityToken = this.httpRequestMessage.Headers.TryGetValues(LogHeaders.SecurityToken, out var securityTokens)
|
||||
&& securityTokens.FirstOrDefault().IsNotEmpty();
|
||||
|
||||
if (!hasSecurityToken && this.credential.StsToken.IsNotEmpty())
|
||||
{
|
||||
this.httpRequestMessage.Headers.Add(LogHeaders.SecurityToken, this.credential.StsToken);
|
||||
}
|
||||
|
||||
// NOTE: If x-log-bodyrawsize is empty, fill it with "0". Otherwise, some method call will be corrupted.
|
||||
if (!this.httpRequestMessage.Headers.Contains(LogHeaders.BodyRawSize))
|
||||
{
|
||||
this.SetBodyRawSize(0);
|
||||
}
|
||||
|
||||
// Build content if necessary
|
||||
if (this.SerializedContent.IsNotEmpty())
|
||||
{
|
||||
this.httpRequestMessage.Content = new ByteArrayContent(this.SerializedContent);
|
||||
this.contentHandler?.Invoke();
|
||||
|
||||
// Prepare header
|
||||
this.ContentHeader(x =>
|
||||
{
|
||||
// Compute actual length
|
||||
x.ContentLength = this.SerializedContent.Length;
|
||||
// Compute actual MD5
|
||||
this.contentMd5Hex = BitConverter.ToString(this.CalculateContentMd5()).Replace("-", String.Empty);
|
||||
|
||||
x.Add("Content-MD5", this.contentMd5Hex); // Non-standard header
|
||||
});
|
||||
} else if (this.httpRequestMessage.Method == HttpMethod.Post || this.httpRequestMessage.Method == HttpMethod.Put)
|
||||
{
|
||||
// When content is empty as well as method is `POST` or `PUT`, generate an empty content and corresponding headers.
|
||||
|
||||
this.httpRequestMessage.Content = new ByteArrayContent(EmptyByteArray);
|
||||
// Don't invoke `contentHandler` here!
|
||||
|
||||
/*
|
||||
* NOTE:
|
||||
* Here is a annoying hack, the log service service cannot accept empty `Content-Type`
|
||||
* header when POST or PUT methods. So, we have to force set some header value.
|
||||
*/
|
||||
this.ContentHeader(x =>
|
||||
{
|
||||
x.ContentType = new MediaTypeHeaderValue("application/json");
|
||||
// For some reason, I think it is better to set `Content-Type` to `0` to prevent
|
||||
// some unexpected behavior on server side.
|
||||
x.ContentLength = 0;
|
||||
});
|
||||
}
|
||||
|
||||
// Do signature
|
||||
var signature = Convert.ToBase64String(this.ComputeSignature());
|
||||
this.httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("LOG", $"{this.credential.AccessKeyId}:{signature}");
|
||||
|
||||
// Rebuild the RequestUri
|
||||
var queryString = String.Join("&", this.query
|
||||
.OrderBy(x => x.Key)
|
||||
.Select(x => $"{encodeUrl(x.Key)}={encodeUrl(x.Value)}"));
|
||||
var pathAndQuery = queryString.IsNotEmpty() ? $"{this.path}?{queryString}" : this.path;
|
||||
this.httpRequestMessage.RequestUri = new Uri(pathAndQuery, UriKind.Relative);
|
||||
|
||||
return this.httpRequestMessage;
|
||||
}
|
||||
|
||||
private String encodeUrl(String value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
string encoded = HttpUtility.UrlEncode(value, this.encoding);
|
||||
return encoded.Replace("+", "%20").Replace("*", "%2A").Replace("~", "%7E").Replace("/", "%2F");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
//
|
||||
// HttpResponse.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading.Tasks;
|
||||
using Aliyun.Api.LogService.Domain;
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Protocol.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// 服务响应包装对象,包含未反序列化的原始数据,可通过 <c>ReadXxxAsync()</c> 方法读取原始报文。
|
||||
/// </summary>
|
||||
public class HttpResponse : IResponse
|
||||
{
|
||||
internal HttpResponseMessage ResponseMessage { get; }
|
||||
|
||||
public Boolean IsSuccess { get; }
|
||||
|
||||
public HttpStatusCode StatusCode { get; }
|
||||
|
||||
public String RequestId { get; }
|
||||
|
||||
public IDictionary<String, String> Headers { get; }
|
||||
|
||||
public Error Error { get; }
|
||||
|
||||
public HttpResponse(HttpResponseMessage responseMessage, Boolean isSuccess, HttpStatusCode statusCode, String requestId, IDictionary<String, String> headers, Error error)
|
||||
{
|
||||
this.ResponseMessage = responseMessage;
|
||||
this.IsSuccess = isSuccess;
|
||||
this.StatusCode = statusCode;
|
||||
this.RequestId = requestId;
|
||||
this.Headers = headers;
|
||||
this.Error = error;
|
||||
}
|
||||
|
||||
public IResponse EnsureSuccess()
|
||||
{
|
||||
if (!this.IsSuccess)
|
||||
{
|
||||
throw this.Error == null
|
||||
? new LogServiceException(this.RequestId, ErrorCode.SdkInternalError, "The error detail result is missing.")
|
||||
: new LogServiceException(this.RequestId, this.Error.ErrorCode, this.Error.ErrorMessage);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Task<TResult> ReadAsAsync<TResult>()
|
||||
{
|
||||
return this.ResponseMessage.Content.ReadAsAsync<TResult>();
|
||||
}
|
||||
|
||||
public Task<Stream> ReadAsByteStreamAsync()
|
||||
{
|
||||
return this.ResponseMessage.Content.ReadAsStreamAsync();
|
||||
}
|
||||
|
||||
public Task<Byte[]> ReadAsByteArrayAsync()
|
||||
{
|
||||
return this.ResponseMessage.Content.ReadAsByteArrayAsync();
|
||||
}
|
||||
|
||||
public override String ToString()
|
||||
=> $"[{this.RequestId}] {this.StatusCode}{(this.IsSuccess ? String.Empty : " Error:" + this.Error)}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 服务响应包装对象,此类型包含一个已反序列化为 <typeparamref name="TResult" /> 的 <see cref="Result">结果对象</see>。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">响应包含结果的类型。</typeparam>
|
||||
public class HttpResponse<TResult> : HttpResponse, IResponse<TResult>
|
||||
where TResult : class
|
||||
{
|
||||
public TResult Result { get; }
|
||||
|
||||
public HttpResponse(HttpResponseMessage responseMessage, Boolean isSuccess, HttpStatusCode statusCode, String requestId, IDictionary<String, String> headers, Error error)
|
||||
: base(responseMessage, isSuccess, statusCode, requestId, headers, error)
|
||||
{
|
||||
this.Result = null;
|
||||
}
|
||||
|
||||
public HttpResponse(HttpResponseMessage responseMessage, Boolean isSuccess, HttpStatusCode statusCode, String requestId, IDictionary<String, String> headers, TResult result)
|
||||
: base(responseMessage, isSuccess, statusCode, requestId, headers, null)
|
||||
{
|
||||
this.Result = result;
|
||||
}
|
||||
|
||||
IResponse<TResult> IResponse<TResult>.EnsureSuccess()
|
||||
{
|
||||
this.EnsureSuccess();
|
||||
return this;
|
||||
}
|
||||
|
||||
public override String ToString()
|
||||
=> base.ToString() + $" Result:{(this.Result == null ? "<null>" : this.Result.GetType().FullName)}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// HttpResponseExtensions.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Protocol.Http
|
||||
{
|
||||
public static class HttpResponseExtensions
|
||||
{
|
||||
private static HttpResponse ToHttpResponse(this IResponse response)
|
||||
=> response is HttpResponse httpResponse
|
||||
? httpResponse
|
||||
: throw new ArgumentException($"response must be [{nameof(HttpResponse)}].", nameof(response));
|
||||
|
||||
private static HttpResponse<T> ToHttpResponse<T>(this IResponse<T> response)
|
||||
where T : class
|
||||
=> response is HttpResponse<T> httpResponse
|
||||
? httpResponse
|
||||
: throw new ArgumentException($"response must be [{nameof(HttpResponse)}].", nameof(response));
|
||||
|
||||
/// <summary>
|
||||
/// 获取此响应对象对应底层的 <see cref="HttpResponseMessage"/> 。
|
||||
/// </summary>
|
||||
/// <param name="response">响应对象。</param>
|
||||
/// <returns>底层的 <see cref="HttpResponseMessage"/> 。</returns>
|
||||
public static HttpResponseMessage GetHttpResponseMessage(this IResponse response)
|
||||
{
|
||||
return response.ToHttpResponse().ResponseMessage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取此响应对象对应的 HTTP 响应码。
|
||||
/// </summary>
|
||||
/// <param name="response">响应对象。</param>
|
||||
/// <returns>HTTP 响应码。</returns>
|
||||
public static HttpStatusCode GetHttpStatusCode(this IResponse response)
|
||||
{
|
||||
return response.ToHttpResponse().StatusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="source">带有结果对象类型的响应消息解释器。</param>
|
||||
/// <param name="transformer">结果转换器。</param>
|
||||
/// <typeparam name="TSource">转换前结果对象类型。</typeparam>
|
||||
/// <typeparam name="TResult">转换后结果对象类型。</typeparam>
|
||||
/// <returns>异步解释结果。</returns>
|
||||
public static IResponse<TResult> Transform<TSource, TResult>(this IResponse<TSource> source,
|
||||
Func<IDictionary<String, String>, TSource, TResult> transformer)
|
||||
where TSource : class
|
||||
where TResult : class
|
||||
{
|
||||
var httpResponse = source.ToHttpResponse();
|
||||
var result = transformer(source.Headers, source.Result);
|
||||
var response = new HttpResponse<TResult>(httpResponse.ResponseMessage, httpResponse.IsSuccess, httpResponse.StatusCode, httpResponse.RequestId, httpResponse.Headers, result);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
//
|
||||
// HttpResponseMessageResolver.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Aliyun.Api.LogService.Utils;
|
||||
using Ionic.Zlib;
|
||||
using LZ4;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Protocol.Http
|
||||
{
|
||||
public class HttpResponseMessageResolver : IResponseResolver
|
||||
{
|
||||
public HttpResponseMessage HttpResponseMessage { get; }
|
||||
|
||||
public String RequestId { get; private set; }
|
||||
|
||||
public Boolean IsSuccess { get; private set; }
|
||||
|
||||
public HttpStatusCode StatusCode { get; private set; }
|
||||
|
||||
public IDictionary<String, String> Headers { get; private set; }
|
||||
|
||||
private Func<Byte[], Byte[]> decompressor;
|
||||
|
||||
private Func<Byte[], Type, Object> deserializer;
|
||||
|
||||
public HttpResponseMessageResolver(HttpResponseMessage httpResponseMessage)
|
||||
{
|
||||
this.HttpResponseMessage = httpResponseMessage;
|
||||
|
||||
this.decompressor = this.AutoDecompressContent;
|
||||
this.deserializer = this.AutoDeserializeContent;
|
||||
}
|
||||
|
||||
public static IResponseResolver For(HttpResponseMessage httpResponseMessage)
|
||||
{
|
||||
return new HttpResponseMessageResolver(httpResponseMessage);
|
||||
}
|
||||
|
||||
public static IResponseResolver<TResult> For<TResult>(HttpResponseMessage httpResponseMessage)
|
||||
where TResult : class
|
||||
{
|
||||
return new HttpResponseMessageResolver(httpResponseMessage).With<TResult>();
|
||||
}
|
||||
|
||||
public IResponseResolver<TResult> With<TResult>()
|
||||
where TResult : class
|
||||
{
|
||||
return new TypedWrapper<TResult>(this);
|
||||
}
|
||||
|
||||
public IResponseResolver Decompress(Func<Byte[], Byte[]> decompressor)
|
||||
{
|
||||
this.decompressor = decompressor ?? throw new ArgumentNullException(nameof(decompressor));
|
||||
return this;
|
||||
}
|
||||
|
||||
public IResponseResolver<TResult> Deserialize<TResult>(Func<Byte[], TResult> deserializer) where TResult : class
|
||||
{
|
||||
if (deserializer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(deserializer));
|
||||
}
|
||||
|
||||
this.deserializer = (data, resultType) =>
|
||||
{
|
||||
var bindType = typeof(TResult);
|
||||
if (bindType != resultType)
|
||||
{
|
||||
throw new ArgumentException($"Type mismatch, binding type: [{bindType}], actual type: [{resultType}]", nameof(TResult));
|
||||
}
|
||||
|
||||
return deserializer(data);
|
||||
};
|
||||
|
||||
return new TypedWrapper<TResult>(this);
|
||||
}
|
||||
|
||||
|
||||
#region Decompress
|
||||
|
||||
private Byte[] AutoDecompressContent(Byte[] data)
|
||||
{
|
||||
// Try decompress data if necessary
|
||||
if (this.TryGetCompressTypeHeader(out var compressType))
|
||||
{
|
||||
var optionalBodyRawSize = this.GetOptionalBodyRawSizeHeader();
|
||||
// Replace the data
|
||||
return DecompressContent(compressType, data, optionalBodyRawSize);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private Boolean TryGetCompressTypeHeader(out CompressType compressType)
|
||||
{
|
||||
if (!this.HttpResponseMessage.Headers.TryGetValues(LogHeaders.CompressType, out var compressTypes))
|
||||
{
|
||||
// No header
|
||||
compressType = CompressType.None;
|
||||
return false;
|
||||
}
|
||||
|
||||
var compressTypeValue = compressTypes.FirstOrDefault(); // Fault tolerance (TODO: Show warns about duplicated keys)
|
||||
if (compressTypeValue.IsEmpty())
|
||||
{
|
||||
// Header is empty
|
||||
compressType = CompressType.None;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert value to enum
|
||||
return Enum.TryParse(compressTypeValue, true, out compressType)
|
||||
? true
|
||||
: throw new ArgumentException($"Compress type [{compressTypeValue}] is not supported.", LogHeaders.CompressType);
|
||||
}
|
||||
|
||||
private Int32? GetOptionalBodyRawSizeHeader()
|
||||
{
|
||||
if (!this.HttpResponseMessage.Headers.TryGetValues(LogHeaders.BodyRawSize, out var bodyRawSizes))
|
||||
{
|
||||
// No header
|
||||
return null;
|
||||
}
|
||||
|
||||
var bodyRawSizeValue = bodyRawSizes.FirstOrDefault(); // Fault tolerance (TODO: Show warns about duplicated keys)
|
||||
if (bodyRawSizeValue.IsEmpty())
|
||||
{
|
||||
// Header is empty
|
||||
return null;
|
||||
}
|
||||
|
||||
return Int32.Parse(bodyRawSizeValue); // Let exception raise when format is incorrect.
|
||||
}
|
||||
|
||||
private static Byte[] DecompressContent(CompressType compressType, Byte[] orignData, Int32? rawSize)
|
||||
{
|
||||
switch (compressType)
|
||||
{
|
||||
case CompressType.None:
|
||||
{
|
||||
return orignData;
|
||||
}
|
||||
|
||||
case CompressType.Lz4:
|
||||
{
|
||||
if (!rawSize.HasValue)
|
||||
{
|
||||
throw new ArgumentException($"{LogHeaders.BodyRawSize} is required when using [lz4] compress.");
|
||||
}
|
||||
|
||||
var rawData = LZ4Codec.Decode(orignData, 0, orignData.Length, rawSize.Value);
|
||||
return rawData;
|
||||
}
|
||||
|
||||
case CompressType.Deflate:
|
||||
{
|
||||
var rawData = ZlibStream.UncompressBuffer(orignData);
|
||||
return rawData;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(compressType), compressType, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Decompress
|
||||
|
||||
#region Deserialize
|
||||
|
||||
private Object AutoDeserializeContent(Byte[] data, Type resultType)
|
||||
{
|
||||
// Content negotiate is not supported.
|
||||
using (var stream = new MemoryStream(data, false))
|
||||
using (var textReader = new StreamReader(stream, Encoding.UTF8 /*TODO: Hard code*/))
|
||||
{
|
||||
return JsonSerializer.CreateDefault().Deserialize(textReader, resultType);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void ResolveInternal()
|
||||
{
|
||||
if (this.HttpResponseMessage.Headers.TryGetValues(LogHeaders.RequestId, out var requestIds))
|
||||
{
|
||||
this.RequestId = requestIds.FirstOrDefault(); // Fault tolerance.
|
||||
}
|
||||
this.IsSuccess = this.HttpResponseMessage.IsSuccessStatusCode;
|
||||
this.StatusCode = this.HttpResponseMessage.StatusCode;
|
||||
|
||||
this.Headers = this.HttpResponseMessage.Headers
|
||||
.Concat(this.HttpResponseMessage.Content.Headers ?? Enumerable.Empty<KeyValuePair<String, IEnumerable<String>>>())
|
||||
.ToDictionary(kv => kv.Key, kv => kv.Value.FirstOrDefault() /* Fault tolerance */);
|
||||
}
|
||||
|
||||
private async Task<TResult> ResolveResultAsync<TResult>()
|
||||
where TResult : class
|
||||
{
|
||||
var httpContent = this.HttpResponseMessage.Content;
|
||||
if (httpContent == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var data = await httpContent.ReadAsByteArrayAsync();
|
||||
if (data.IsEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
data = this.decompressor(data);
|
||||
var result = this.deserializer(data, typeof(TResult));
|
||||
|
||||
return (TResult) result; // Always safe! Expect the custom serializer does some weird operations.
|
||||
}
|
||||
|
||||
public async Task<IResponse> ResolveAsync()
|
||||
{
|
||||
this.ResolveInternal();
|
||||
|
||||
var readOnlyHeaders = new ReadOnlyDictionary<String, String>(this.Headers);
|
||||
|
||||
var error = this.IsSuccess ? null : await this.HttpResponseMessage.Content.ReadAsAsync<Error>();
|
||||
|
||||
return new HttpResponse(this.HttpResponseMessage, this.IsSuccess, this.StatusCode, this.RequestId, readOnlyHeaders, error);
|
||||
}
|
||||
|
||||
public async Task<IResponse<TResult>> ResolveAsync<TResult>() where TResult : class
|
||||
{
|
||||
this.ResolveInternal();
|
||||
|
||||
var readOnlyHeaders = new ReadOnlyDictionary<String, String>(this.Headers);
|
||||
|
||||
if (!this.IsSuccess)
|
||||
{
|
||||
var error = await this.HttpResponseMessage.Content.ReadAsAsync<Error>();
|
||||
return new HttpResponse<TResult>(this.HttpResponseMessage, this.IsSuccess, this.StatusCode, this.RequestId, readOnlyHeaders, error);
|
||||
}
|
||||
|
||||
var result = await this.ResolveResultAsync<TResult>();
|
||||
|
||||
return new HttpResponse<TResult>(this.HttpResponseMessage, this.IsSuccess, this.StatusCode, this.RequestId, readOnlyHeaders, result);
|
||||
}
|
||||
|
||||
private class TypedWrapper<TResult> : IResponseResolver<TResult>
|
||||
where TResult : class
|
||||
{
|
||||
private readonly HttpResponseMessageResolver innerResolver;
|
||||
|
||||
internal TypedWrapper(HttpResponseMessageResolver innerResolver)
|
||||
{
|
||||
this.innerResolver = innerResolver;
|
||||
}
|
||||
|
||||
public IResponseResolver<TResult> Decompress(Func<Byte[], Byte[]> decompressor)
|
||||
{
|
||||
this.innerResolver.Decompress(decompressor);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IResponseResolver<TResult> Deserialize(Func<Byte[], TResult> deserializer)
|
||||
{
|
||||
this.innerResolver.Deserialize(deserializer);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Task<IResponse<TResult>> ResolveAsync()
|
||||
{
|
||||
return this.innerResolver.ResolveAsync<TResult>();
|
||||
}
|
||||
|
||||
public async Task<IResponse<TNewResult>> ResolveAsync<TNewResult>(Func<TResult, TNewResult> transformer)
|
||||
where TNewResult : class
|
||||
{
|
||||
this.innerResolver.ResolveInternal();
|
||||
|
||||
var readOnlyHeaders = new ReadOnlyDictionary<String, String>(this.innerResolver.Headers);
|
||||
|
||||
if (!this.innerResolver.IsSuccess)
|
||||
{
|
||||
var error = await this.innerResolver.HttpResponseMessage.Content.ReadAsAsync<Error>();
|
||||
return new HttpResponse<TNewResult>(this.innerResolver.HttpResponseMessage, this.innerResolver.IsSuccess, this.innerResolver.StatusCode, this.innerResolver.RequestId, readOnlyHeaders, error);
|
||||
}
|
||||
|
||||
var result = await this.innerResolver.ResolveResultAsync<TResult>();
|
||||
var newResult = transformer(result);
|
||||
|
||||
return new HttpResponse<TNewResult>(this.innerResolver.HttpResponseMessage, this.innerResolver.IsSuccess, this.innerResolver.StatusCode, this.innerResolver.RequestId, readOnlyHeaders, newResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
//
|
||||
// LogServiceClientBuilders.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Aliyun.Api.LogService.Domain.Log;
|
||||
using Aliyun.Api.LogService.Utils;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Protocol.Http
|
||||
{
|
||||
public static class LogHeaderExtensions
|
||||
{
|
||||
private static T? ParseNullable<T>(this String source, Func<String, T> parse) where T : struct
|
||||
{
|
||||
if (source.IsEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return parse(source);
|
||||
}
|
||||
|
||||
private static T ParseNotNull<T>(this String source, Func<String, T> parse)
|
||||
{
|
||||
return parse(source);
|
||||
}
|
||||
|
||||
#region PullLogs
|
||||
|
||||
/// <summary>
|
||||
/// 获取下一条数据的cursor。
|
||||
/// </summary>
|
||||
/// <param name="response"><c>PullLogs</c> 的响应消息。</param>
|
||||
/// <returns>下一条数据的cursor。</returns>
|
||||
public static String GetLogCursor(this IResponse<PullLogsResult> response)
|
||||
=> response.Headers.GetValueOrDefault(LogHeaders.Cursor);
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前返回数量。
|
||||
/// </summary>
|
||||
/// <param name="response"><c>PullLogs</c> 的响应消息。</param>
|
||||
/// <returns>当前返回数量。</returns>
|
||||
/// <exception cref="FormatException">Header的值不是整数形式。</exception>
|
||||
public static Int64 GetLogCount(this IResponse<PullLogsResult> response)
|
||||
=> response.Headers.GetValueOrDefault(LogHeaders.Count)
|
||||
.ParseNotNull(Int64.Parse);
|
||||
|
||||
/// <summary>
|
||||
/// 获取报文压缩类型(可空)。
|
||||
/// </summary>
|
||||
/// <param name="response"><c>PullLogs</c> 的响应消息。</param>
|
||||
/// <returns>报文压缩类型(可空)。</returns>
|
||||
/// <exception cref="OverflowException">Header的值在当前SDK版本不支持。</exception>
|
||||
/// <seealso cref="CompressType"/>
|
||||
public static CompressType? GetLogCompressType(this IResponse<PullLogsResult> response)
|
||||
=> response.Headers.GetValueOrDefault(LogHeaders.CompressType)
|
||||
.ParseNullable(x => (CompressType) Enum.Parse(typeof(CompressType), x, true));
|
||||
|
||||
/// <summary>
|
||||
/// 获取响应消息中 Body 的原始大小。
|
||||
/// </summary>
|
||||
/// <param name="response"><c>PullLogs</c> 的响应消息。</param>
|
||||
/// <returns>响应消息中 Body 的原始大小。</returns>
|
||||
/// <exception cref="FormatException">当Header的值不是整数形式。</exception>
|
||||
public static Int64 GetLogBodyRawSize(this IResponse<PullLogsResult> response)
|
||||
=> response.Headers.GetValueOrDefault(LogHeaders.BodyRawSize)
|
||||
.ParseNotNull(Int64.Parse);
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetLogs
|
||||
|
||||
/// <summary>
|
||||
/// 获取查询结果的状态。
|
||||
/// </summary>
|
||||
/// <param name="response"><c>GetLogs</c> 的响应消息。</param>
|
||||
/// <returns>获取查询结果的状态。</returns>
|
||||
/// <exception cref="OverflowException">Header的值在当前SDK版本不支持。</exception>
|
||||
/// <seealso cref="LogProgressState"/>
|
||||
public static LogProgressState GetLogProgress(this IResponse<GetLogsResult> response)
|
||||
=> response.Headers.GetValueOrDefault(LogHeaders.Progress)
|
||||
.ParseNotNull(x => (LogProgressState) Enum.Parse(typeof(LogProgressState), x, true));
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前返回数量。
|
||||
/// </summary>
|
||||
/// <param name="response"><c>GetLogs</c> 的响应消息。</param>
|
||||
/// <returns>当前返回数量。</returns>
|
||||
/// <exception cref="FormatException">Header的值不是整数形式。</exception>
|
||||
public static Int64 GetLogCount(this IResponse<GetLogsResult> response)
|
||||
=> response.Headers.GetValueOrDefault(LogHeaders.Count)
|
||||
.ParseNotNull(Int64.Parse);
|
||||
|
||||
/// <summary>
|
||||
/// (TODO: 暂无文档)
|
||||
/// </summary>
|
||||
/// <param name="response"><c>GetLogs</c> 的响应消息。</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="FormatException">Header的值不是整数形式。</exception>
|
||||
public static Int64 GetLogProcessedRows(this IResponse<GetLogsResult> response)
|
||||
=> response.Headers.GetValueOrDefault(LogHeaders.ProcessedRows)
|
||||
.ParseNotNull(Int64.Parse);
|
||||
|
||||
/// <summary>
|
||||
/// (TODO: 暂无文档)
|
||||
/// </summary>
|
||||
/// <param name="response"><c>GetLogs</c> 的响应消息。</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="FormatException">Header的值不是整数形式。</exception>
|
||||
public static Int64 GetLogElapsedMillisecond(this IResponse<GetLogsResult> response)
|
||||
=> response.Headers.GetValueOrDefault(LogHeaders.ElapsedMillisecond)
|
||||
.ParseNotNull(Int64.Parse);
|
||||
|
||||
/// <summary>
|
||||
/// (TODO: 暂无文档)
|
||||
/// </summary>
|
||||
/// <param name="response"><c>GetLogs</c> 的响应消息。</param>
|
||||
/// <returns></returns>
|
||||
public static LogQueryInfo GetLogQueryInfo(this IResponse<GetLogsResult> response)
|
||||
=> response.Headers.GetValueOrDefault(LogHeaders.QueryInfo)
|
||||
.ParseNotNull(JsonConvert.DeserializeObject<LogQueryInfo>);
|
||||
|
||||
/// <summary>
|
||||
/// (TODO: 暂无文档)
|
||||
/// </summary>
|
||||
/// <param name="response"><c>GetLogs</c> 的响应消息。</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="FormatException">Header的值不是JSON对象形式。</exception>
|
||||
public static dynamic GetLogQueryInfoAsDynamic(this IResponse<GetLogsResult> response)
|
||||
=> response.Headers.GetValueOrDefault(LogHeaders.QueryInfo)
|
||||
.ParseNotNull(x =>
|
||||
{
|
||||
if (x.IsEmpty())
|
||||
{
|
||||
return new JObject();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return JObject.Parse(x);
|
||||
} catch (JsonReaderException e)
|
||||
{
|
||||
// Prevent underlying type expose explicitly.
|
||||
throw new FormatException(e.Message, e);
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// (TODO: 暂无文档)
|
||||
/// </summary>
|
||||
/// <param name="response"><c>GetLogs</c> 的响应消息。</param>
|
||||
/// <returns></returns>
|
||||
public static Boolean GetLogHasSql(this IResponse<GetLogsResult> response)
|
||||
=> response.Headers.GetValueOrDefault(LogHeaders.HasSql)
|
||||
.ParseNotNull(Boolean.Parse);
|
||||
|
||||
/// <summary>
|
||||
/// (TODO: 暂无文档)
|
||||
/// </summary>
|
||||
/// <param name="response"><c>GetLogs</c> 的响应消息。</param>
|
||||
/// <returns></returns>
|
||||
public static String GetLogAggQuery(this IResponse<GetLogsResult> response)
|
||||
=> response.Headers.GetValueOrDefault(LogHeaders.AggQuery);
|
||||
|
||||
/// <summary>
|
||||
/// (TODO: 暂无文档)
|
||||
/// </summary>
|
||||
/// <param name="response"><c>GetLogs</c> 的响应消息。</param>
|
||||
/// <returns></returns>
|
||||
public static String GetLogWhereQuery(this IResponse<GetLogsResult> response)
|
||||
=> response.Headers.GetValueOrDefault(LogHeaders.WhereQuery);
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetHistograms
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前返回数量。
|
||||
/// </summary>
|
||||
/// <param name="response"><c>GetLogHistograms</c> 的响应消息。</param>
|
||||
/// <returns>当前返回数量。</returns>
|
||||
/// <exception cref="FormatException">Header的值不是整数形式。</exception>
|
||||
public static Int64 GetLogCount(this IResponse<GetLogHistogramsResult> response)
|
||||
=> response.Headers.GetValueOrDefault(LogHeaders.Count)
|
||||
.ParseNotNull(Int64.Parse);
|
||||
|
||||
/// <summary>
|
||||
/// 获取查询结果的状态。
|
||||
/// </summary>
|
||||
/// <param name="response"><c>GetLogHistograms</c> 的响应消息。</param>
|
||||
/// <returns>获取查询结果的状态。</returns>
|
||||
/// <exception cref="OverflowException">Header的值在当前SDK版本不支持。</exception>
|
||||
/// <seealso cref="LogProgressState"/>
|
||||
public static LogProgressState GetLogProgress(this IResponse<GetLogHistogramsResult> response)
|
||||
=> response.Headers.GetValueOrDefault(LogHeaders.Progress)
|
||||
.ParseNotNull(x => (LogProgressState) Enum.Parse(typeof(LogProgressState), x, true));
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
110
Aliyun.Api.LogService/Infrastructure/Protocol/Http/LogHeaders.cs
Normal file
110
Aliyun.Api.LogService/Infrastructure/Protocol/Http/LogHeaders.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// LogHeaders.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using Aliyun.Api.LogService.Domain.Log;
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Protocol.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// 日志服务扩展Header。
|
||||
/// </summary>
|
||||
public static class LogHeaders
|
||||
{
|
||||
/// <summary>
|
||||
/// API 的版本号。
|
||||
/// </summary>
|
||||
public static readonly String ApiVersion = $"x-log-{nameof(ApiVersion).ToLower()}";
|
||||
|
||||
/// <summary>
|
||||
/// 请求的 Body 原始大小。
|
||||
/// 当无 Body 时,该字段为 0;
|
||||
/// 当 Body 是压缩数据,则为压缩前的原始数据大小。
|
||||
/// 该域取值范围为 0 ~ 3 * 1024 * 1024。
|
||||
/// 该字段为非必选字段,只在压缩时需要。
|
||||
/// </summary>
|
||||
public static readonly String BodyRawSize = $"x-log-{nameof(BodyRawSize).ToLower()}";
|
||||
|
||||
/// <summary>
|
||||
/// API 请求中 Body 部分使用的压缩方式。
|
||||
/// 如果不压缩可以不提供该请求头。
|
||||
/// </summary>
|
||||
/// <seealso cref="CompressType"/>
|
||||
public static readonly String CompressType = $"x-log-{nameof(CompressType).ToLower()}";
|
||||
|
||||
/// <summary>
|
||||
/// 当前发送时刻的时间,格式和 Date 头一致。
|
||||
/// 该请求头为可选项。如果请求中包含该公共请求头,它的值会取代 Date 标准头的值用于服务端请求验证(该字段不参与签名)。
|
||||
/// 无论是否有 x-log-date 头,HTTP 标准 Date 头都必须提供。
|
||||
/// </summary>
|
||||
public static readonly String LogDate = $"x-log-{nameof(LogDate).ToLower()}";
|
||||
|
||||
/// <summary>
|
||||
/// 签名计算方式。
|
||||
/// </summary>
|
||||
/// <seealso cref="SignatureType"/>
|
||||
public static readonly String SignatureMethod = $"x-log-{nameof(SignatureMethod).ToLower()}";
|
||||
|
||||
/// <summary>
|
||||
/// 使用 STS 临时身份发送数据。当使用 STS 临时身份时必填,其他情况不要填写。
|
||||
/// </summary>
|
||||
public static readonly String SecurityToken = "x-acs-security-token";
|
||||
|
||||
/// <summary>
|
||||
/// 服务端产生的标示该请求的唯一 ID。该响应头与具体应用无关,主要用于跟踪和调查问题。
|
||||
/// 如果用户希望调查出现问题的 API 请求,可以向 Log Service 团队提供该 ID。
|
||||
/// </summary>
|
||||
public static readonly String RequestId = $"x-log-{nameof(RequestId).ToLower()}";
|
||||
|
||||
/// <summary>
|
||||
/// 日志上传的hash key,用来判断上传日志应该落在哪个 shard 中。
|
||||
/// </summary>
|
||||
public static readonly String HashKey = $"x-log-{nameof(HashKey).ToLower()}";
|
||||
|
||||
/// <summary>
|
||||
/// 当前返回数量。
|
||||
/// </summary>
|
||||
public static readonly String Count = $"x-log-{nameof(Count).ToLower()}";
|
||||
|
||||
/// <summary>
|
||||
/// 当前读取数据下一条 cursor。
|
||||
/// </summary>
|
||||
public static readonly String Cursor = $"x-log-{nameof(Cursor).ToLower()}";
|
||||
|
||||
/// <summary>
|
||||
/// 查询结果的状态,表示本次是否完整。
|
||||
/// </summary>
|
||||
/// <seealso cref="LogProgressState"/>
|
||||
public static readonly String Progress = "x-log-progress";
|
||||
|
||||
public static readonly String AggQuery = "x-log-agg-query";
|
||||
public static readonly String ElapsedMillisecond = "x-log-elapsed-millisecond";
|
||||
public static readonly String HasSql = "x-log-has-sql";
|
||||
public static readonly String ProcessedRows = "x-log-processed-rows";
|
||||
public static readonly String QueryInfo = "x-log-query-info";
|
||||
public static readonly String WhereQuery = "x-log-where-query";
|
||||
}
|
||||
}
|
||||
115
Aliyun.Api.LogService/Infrastructure/Protocol/IRequestBuilder.cs
Normal file
115
Aliyun.Api.LogService/Infrastructure/Protocol/IRequestBuilder.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
//
|
||||
// IRequestBuilder.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using Aliyun.Api.LogService.Infrastructure.Authentication;
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Protocol
|
||||
{
|
||||
/// <summary>
|
||||
/// The builder for constructing request <typeparamref name="T"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of building request.</typeparam>
|
||||
public interface IRequestBuilder<out T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds query of request.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of query.</param>
|
||||
/// <param name="value">The value of query.</param>
|
||||
/// <returns>This builder.</returns>
|
||||
/// <exception cref="ArgumentException"><paramref name="key"/> has already been exist.</exception>
|
||||
IRequestBuilder<T> Query(String key, String value);
|
||||
|
||||
/// <summary>
|
||||
/// Add all public gettable properties of <paramref name="queryModel"/> to query.
|
||||
/// </summary>
|
||||
/// <param name="queryModel">A simple data object with no nested complex properties.</param>
|
||||
/// <returns>This builder.</returns>
|
||||
/// <exception cref="InvalidCastException">If <paramref name="queryModel"/> has nested complex properties.</exception>
|
||||
IRequestBuilder<T> Query(Object queryModel);
|
||||
|
||||
/// <summary>
|
||||
/// Adds header of request.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of header.</param>
|
||||
/// <param name="value">The value of header.</param>
|
||||
/// <returns>This builder.</returns>
|
||||
IRequestBuilder<T> Header(String key, String value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets serialized content of request.
|
||||
/// </summary>
|
||||
/// <param name="content">The serialized content.</param>
|
||||
/// <returns>This builder.</returns>
|
||||
/// <seealso cref="Content(Object)"/>
|
||||
IRequestBuilder<T> Content(Byte[] content);
|
||||
|
||||
/// <summary>
|
||||
/// Sets content of request.
|
||||
/// </summary>
|
||||
/// <param name="content">The content, can be serialized or not.</param>
|
||||
/// <returns>This builder.</returns>
|
||||
IRequestBuilder<T> Content(Object content);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize the content.
|
||||
/// </summary>
|
||||
/// <param name="serializeType">The serialze type</param>
|
||||
/// <returns>This builder.</returns>
|
||||
/// <exception cref="InvalidOperationException">If nothing to serialize, or content has already been serialized.</exception>
|
||||
IRequestBuilder<T> Serialize(SerializeType serializeType);
|
||||
|
||||
/// <summary>
|
||||
/// Compress the content.
|
||||
/// </summary>
|
||||
/// <param name="compressType">The compress type</param>
|
||||
/// <returns>This builder.</returns>
|
||||
/// <exception cref="InvalidOperationException">If nothing to compress, or content is not serialized.</exception>
|
||||
IRequestBuilder<T> Compress(CompressType compressType);
|
||||
|
||||
/// <summary>
|
||||
/// Set credential to authenticate.
|
||||
/// </summary>
|
||||
/// <param name="credential">The authenticate credential.</param>
|
||||
/// <returns></returns>
|
||||
IRequestBuilder<HttpRequestMessage> Authenticate(Credential credential);
|
||||
|
||||
/// <summary>
|
||||
/// Specify the signature type and credentials to sign.
|
||||
/// </summary>
|
||||
/// <param name="signatureType">The signature type</param>
|
||||
/// <returns>This builder.</returns>
|
||||
IRequestBuilder<T> Sign(SignatureType signatureType);
|
||||
|
||||
/// <summary>
|
||||
/// Build the request <typeparamref name="T"/>.
|
||||
/// </summary>
|
||||
/// <returns>The built request.</returns>
|
||||
T Build();
|
||||
}
|
||||
}
|
||||
110
Aliyun.Api.LogService/Infrastructure/Protocol/IResponse.cs
Normal file
110
Aliyun.Api.LogService/Infrastructure/Protocol/IResponse.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// IResponse.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Mime;
|
||||
using System.Threading.Tasks;
|
||||
using Aliyun.Api.LogService.Domain;
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Protocol
|
||||
{
|
||||
/// <summary>
|
||||
/// 服务响应包装对象,包含未反序列化的原始数据,可通过 <c>ReadXxxAsync()</c> 方法读取原始报文。
|
||||
/// </summary>
|
||||
public interface IResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前响应是否为一个成功的响应。
|
||||
/// </summary>
|
||||
Boolean IsSuccess { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 服务端产生的标示该请求的唯一 ID。
|
||||
/// 该响应头与具体应用无关,主要用于跟踪和调查问题。
|
||||
/// 如果用户希望调查出现问题的 API 请求,可以向 Log Service 团队提供该 ID。
|
||||
/// </summary>
|
||||
String RequestId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前响应的元数据。
|
||||
/// </summary>
|
||||
IDictionary<String, String> Headers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前响应包含的错误信息,在<see cref="IsSuccess"/>为<c>false</c>时存在。
|
||||
/// </summary>
|
||||
Error Error { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 确保当前响应是成功的,否则将抛出包含错误码及错误消息的 <see cref="LogServiceException"/> 。
|
||||
/// </summary>
|
||||
/// <returns>当前响应实例。</returns>
|
||||
/// <exception cref="LogServiceException">当前响应的 <see cref="IsSuccess"/> 不为<c>true</c></exception>
|
||||
IResponse EnsureSuccess();
|
||||
|
||||
/// <summary>
|
||||
/// 读取原始数据并反序列化为 <typeparamref name="TResult"/> 。
|
||||
/// 反序列化过程会根据<see cref="ContentType"/>确定原始数据类型。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">反序列化结果的类型。</typeparam>
|
||||
/// <returns>反序列化对象异步结果。</returns>
|
||||
Task<TResult> ReadAsAsync<TResult>();
|
||||
|
||||
/// <summary>
|
||||
/// 读取原始数据并且作为字节流的形式返回。
|
||||
/// </summary>
|
||||
/// <returns>异步字节数据流。</returns>
|
||||
Task<Stream> ReadAsByteStreamAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 读取原始数据并且作为字节数组的形式返回。
|
||||
/// </summary>
|
||||
/// <returns>异步字节数组。</returns>
|
||||
Task<Byte[]> ReadAsByteArrayAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 服务响应包装对象,此类型包含一个已反序列化为 <typeparamref name="TResult" /> 的 <see cref="Result">结果对象</see>。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">响应包含结果的类型。</typeparam>
|
||||
public interface IResponse<out TResult> : IResponse
|
||||
where TResult : class
|
||||
{
|
||||
/// <summary>
|
||||
/// 已反序列化的结果对象,在<see cref="IResponse.IsSuccess"/>为<c>true</c>时存在。
|
||||
/// </summary>
|
||||
TResult Result { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 确保当前响应是成功的,否则将抛出包含错误码及错误消息的 <see cref="LogServiceException"/> 。
|
||||
/// </summary>
|
||||
/// <returns>当前响应实例。</returns>
|
||||
/// <exception cref="LogServiceException">当前响应的 <see cref="IResponse.IsSuccess"/> 不为<c>true</c></exception>
|
||||
new IResponse<TResult> EnsureSuccess();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// IResponseResolver.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Protocol
|
||||
{
|
||||
/// <summary>
|
||||
/// 响应消息解释器。
|
||||
/// </summary>
|
||||
public interface IResponseResolver
|
||||
{
|
||||
/// <summary>
|
||||
/// 使用 <typeparamref name="TResult"/> 作为目标对象类型。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">目标对象类型</typeparam>
|
||||
/// <returns>带有结果对象类型的响应消息解释器。</returns>
|
||||
IResponseResolver<TResult> With<TResult>()
|
||||
where TResult : class;
|
||||
|
||||
/// <summary>
|
||||
/// 设置解压缩原始数据的处理器,此操作会使默认解压缩处理器失效。
|
||||
/// </summary>
|
||||
/// <param name="decompressor">解压缩处理器。</param>
|
||||
/// <returns>当前解释器。</returns>
|
||||
IResponseResolver Decompress(Func<Byte[], Byte[]> decompressor);
|
||||
|
||||
/// <summary>
|
||||
/// 设置反序列化原始数据的处理器,此操作会使默认反序列化处理器失效。
|
||||
/// </summary>
|
||||
/// <param name="deserializer">反序列化处理器。</param>
|
||||
/// <typeparam name="TResult">结果对象类型。</typeparam>
|
||||
/// <returns>带有结果对象类型的响应消息解释器。</returns>
|
||||
IResponseResolver<TResult> Deserialize<TResult>(Func<Byte[], TResult> deserializer)
|
||||
where TResult : class;
|
||||
|
||||
/// <summary>
|
||||
/// 解释响应消息。
|
||||
/// </summary>
|
||||
/// <returns>异步解释结果。</returns>
|
||||
Task<IResponse> ResolveAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 解释响应消息,并反序列化为 <typeparamref name="TResult"/> 。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">结果对象类型。</typeparam>
|
||||
/// <returns>异步解释结果。</returns>
|
||||
Task<IResponse<TResult>> ResolveAsync<TResult>()
|
||||
where TResult : class;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 带有结果对象类型的响应消息解释器。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">结果对象类型。</typeparam>
|
||||
public interface IResponseResolver<TResult>
|
||||
where TResult : class
|
||||
{
|
||||
/// <summary>
|
||||
/// 设置解压缩原始数据的处理器,此操作会使默认解压缩处理器失效。
|
||||
/// </summary>
|
||||
/// <param name="decompressor">解压缩处理器。</param>
|
||||
/// <returns>当前解释器。</returns>
|
||||
IResponseResolver<TResult> Decompress(Func<Byte[], Byte[]> decompressor);
|
||||
|
||||
/// <summary>
|
||||
/// 设置反序列化原始数据的处理器,此操作会使默认反序列化处理器失效。
|
||||
/// </summary>
|
||||
/// <param name="deserializer">反序列化处理器。</param>
|
||||
/// <returns>当前解释器。</returns>
|
||||
IResponseResolver<TResult> Deserialize(Func<Byte[], TResult> deserializer);
|
||||
|
||||
/// <summary>
|
||||
/// 解释响应消息,并反序列化为 <typeparamref name="TResult"/> 。
|
||||
/// </summary>
|
||||
/// <returns>异步解释结果。</returns>
|
||||
Task<IResponse<TResult>> ResolveAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 解释响应消息,反序列化为 <typeparamref name="TResult"/> 后通过 <paramref name="transformer"/> 转换为 <typeparamref name="TNewResult"/> 。
|
||||
/// </summary>
|
||||
/// <param name="transformer">结果转换器。</param>
|
||||
/// <typeparam name="TNewResult">转换后结果对象类型。</typeparam>
|
||||
/// <returns>异步解释结果。</returns>
|
||||
Task<IResponse<TNewResult>> ResolveAsync<TNewResult>(Func<TResult, TNewResult> transformer)
|
||||
where TNewResult : class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// SerializeType.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Protocol
|
||||
{
|
||||
/// <summary>
|
||||
/// 序列化类型。
|
||||
/// </summary>
|
||||
public enum SerializeType
|
||||
{
|
||||
/// <summary>
|
||||
/// Json(默认)。
|
||||
/// </summary>
|
||||
/// <seealso ref="http://www.json.org/"/>
|
||||
Json,
|
||||
|
||||
/// <summary>
|
||||
/// Google Protocol Buffers
|
||||
/// </summary>
|
||||
/// <seealso ref="http://developers.google.com/protocol-buffers/"/>
|
||||
Protobuf,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// SignatureType.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Protocol
|
||||
{
|
||||
/// <summary>
|
||||
/// 签名类型。
|
||||
/// </summary>
|
||||
public enum SignatureType
|
||||
{
|
||||
/// <summary>
|
||||
/// HMAC-SHA1签名。
|
||||
/// </summary>
|
||||
HmacSha1,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,921 @@
|
||||
//
|
||||
// Log.Generated.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
// *****************************************************************************
|
||||
// * NOTE: *
|
||||
// * The following code has been modified by the compatibility of empty string *
|
||||
// * handling between proto2 and proto3. *
|
||||
// * *
|
||||
// * !!! DO NOT FORGET TO MODIFY CODES AFTER REGENERATION !!! *
|
||||
// * *
|
||||
// * Diff: *
|
||||
// * $ git diff 983301d 5d4f432 -- Log.Generated.cs *
|
||||
// * *
|
||||
// * For more detail: *
|
||||
// * https://github.com/aliyun/aliyun-log-dotnetcore-sdk/issues/4 *
|
||||
// *****************************************************************************
|
||||
|
||||
// <auto-generated>
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: Log.proto
|
||||
// </auto-generated>
|
||||
#pragma warning disable 1591, 0612, 3021
|
||||
#region Designer generated code
|
||||
|
||||
using pb = global::Google.Protobuf;
|
||||
using pbc = global::Google.Protobuf.Collections;
|
||||
using pbr = global::Google.Protobuf.Reflection;
|
||||
using scg = global::System.Collections.Generic;
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf {
|
||||
|
||||
/// <summary>Holder for reflection information generated from Log.proto</summary>
|
||||
public static partial class LogReflection {
|
||||
|
||||
#region Descriptor
|
||||
/// <summary>File descriptor for Log.proto</summary>
|
||||
public static pbr::FileDescriptor Descriptor {
|
||||
get { return descriptor; }
|
||||
}
|
||||
private static pbr::FileDescriptor descriptor;
|
||||
|
||||
static LogReflection() {
|
||||
byte[] descriptorData = global::System.Convert.FromBase64String(
|
||||
string.Concat(
|
||||
"CglMb2cucHJvdG8SNGFsaXl1bi5hcGkubG9nLmluZnJhc3RydWN0dXJlLnNl",
|
||||
"cmlhbGl6YXRpb24ucHJvdG9idWYijwEKA0xvZxIMCgRUaW1lGAEgASgNElMK",
|
||||
"CENvbnRlbnRzGAIgAygLMkEuYWxpeXVuLmFwaS5sb2cuaW5mcmFzdHJ1Y3R1",
|
||||
"cmUuc2VyaWFsaXphdGlvbi5wcm90b2J1Zi5Mb2cuQ29udGVudBolCgdDb250",
|
||||
"ZW50EgsKA0tleRgBIAEoCRINCgVWYWx1ZRgCIAEoCSIkCgZMb2dUYWcSCwoD",
|
||||
"S2V5GAEgASgJEg0KBVZhbHVlGAIgASgJItMBCghMb2dHcm91cBJHCgRMb2dz",
|
||||
"GAEgAygLMjkuYWxpeXVuLmFwaS5sb2cuaW5mcmFzdHJ1Y3R1cmUuc2VyaWFs",
|
||||
"aXphdGlvbi5wcm90b2J1Zi5Mb2cSEAoIUmVzZXJ2ZWQYAiABKAkSDQoFVG9w",
|
||||
"aWMYAyABKAkSDgoGU291cmNlGAQgASgJEk0KB0xvZ1RhZ3MYBiADKAsyPC5h",
|
||||
"bGl5dW4uYXBpLmxvZy5pbmZyYXN0cnVjdHVyZS5zZXJpYWxpemF0aW9uLnBy",
|
||||
"b3RvYnVmLkxvZ1RhZyJkCgxMb2dHcm91cExpc3QSVAoMbG9nR3JvdXBMaXN0",
|
||||
"GAEgAygLMj4uYWxpeXVuLmFwaS5sb2cuaW5mcmFzdHJ1Y3R1cmUuc2VyaWFs",
|
||||
"aXphdGlvbi5wcm90b2J1Zi5Mb2dHcm91cEI+qgI7QWxpeXVuLkFwaS5Mb2dT",
|
||||
"ZXJ2aWNlLkluZnJhc3RydWN0dXJlLlNlcmlhbGl6YXRpb24uUHJvdG9idWZi",
|
||||
"BnByb3RvMw=="));
|
||||
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
|
||||
new pbr::FileDescriptor[] { },
|
||||
new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
|
||||
new pbr::GeneratedClrTypeInfo(typeof(global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.Log), global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.Log.Parser, new[]{ "Time", "Contents" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.Log.Types.Content), global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.Log.Types.Content.Parser, new[]{ "Key", "Value" }, null, null, null)}),
|
||||
new pbr::GeneratedClrTypeInfo(typeof(global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogTag), global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogTag.Parser, new[]{ "Key", "Value" }, null, null, null),
|
||||
new pbr::GeneratedClrTypeInfo(typeof(global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogGroup), global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogGroup.Parser, new[]{ "Logs", "Reserved", "Topic", "Source", "LogTags" }, null, null, null),
|
||||
new pbr::GeneratedClrTypeInfo(typeof(global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogGroupList), global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogGroupList.Parser, new[]{ "LogGroupList_" }, null, null, null)
|
||||
}));
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
#region Messages
|
||||
public sealed partial class Log : pb::IMessage<Log> {
|
||||
private static readonly pb::MessageParser<Log> _parser = new pb::MessageParser<Log>(() => new Log());
|
||||
private pb::UnknownFieldSet _unknownFields;
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public static pb::MessageParser<Log> Parser { get { return _parser; } }
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public static pbr::MessageDescriptor Descriptor {
|
||||
get { return global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogReflection.Descriptor.MessageTypes[0]; }
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
pbr::MessageDescriptor pb::IMessage.Descriptor {
|
||||
get { return Descriptor; }
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public Log() {
|
||||
OnConstruction();
|
||||
}
|
||||
|
||||
partial void OnConstruction();
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public Log(Log other) : this() {
|
||||
time_ = other.time_;
|
||||
contents_ = other.contents_.Clone();
|
||||
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public Log Clone() {
|
||||
return new Log(this);
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "Time" field.</summary>
|
||||
public const int TimeFieldNumber = 1;
|
||||
private uint time_;
|
||||
/// <summary>
|
||||
/// UNIX Time Format
|
||||
/// </summary>
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public uint Time {
|
||||
get { return time_; }
|
||||
set {
|
||||
time_ = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "Contents" field.</summary>
|
||||
public const int ContentsFieldNumber = 2;
|
||||
private static readonly pb::FieldCodec<global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.Log.Types.Content> _repeated_contents_codec
|
||||
= pb::FieldCodec.ForMessage(18, global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.Log.Types.Content.Parser);
|
||||
private readonly pbc::RepeatedField<global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.Log.Types.Content> contents_ = new pbc::RepeatedField<global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.Log.Types.Content>();
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public pbc::RepeatedField<global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.Log.Types.Content> Contents {
|
||||
get { return contents_; }
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public override bool Equals(object other) {
|
||||
return Equals(other as Log);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public bool Equals(Log other) {
|
||||
if (ReferenceEquals(other, null)) {
|
||||
return false;
|
||||
}
|
||||
if (ReferenceEquals(other, this)) {
|
||||
return true;
|
||||
}
|
||||
if (Time != other.Time) return false;
|
||||
if(!contents_.Equals(other.contents_)) return false;
|
||||
return Equals(_unknownFields, other._unknownFields);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public override int GetHashCode() {
|
||||
int hash = 1;
|
||||
if (Time != 0) hash ^= Time.GetHashCode();
|
||||
hash ^= contents_.GetHashCode();
|
||||
if (_unknownFields != null) {
|
||||
hash ^= _unknownFields.GetHashCode();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public override string ToString() {
|
||||
return pb::JsonFormatter.ToDiagnosticString(this);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public void WriteTo(pb::CodedOutputStream output) {
|
||||
if (Time != 0) {
|
||||
output.WriteRawTag(8);
|
||||
output.WriteUInt32(Time);
|
||||
}
|
||||
contents_.WriteTo(output, _repeated_contents_codec);
|
||||
if (_unknownFields != null) {
|
||||
_unknownFields.WriteTo(output);
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public int CalculateSize() {
|
||||
int size = 0;
|
||||
if (Time != 0) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Time);
|
||||
}
|
||||
size += contents_.CalculateSize(_repeated_contents_codec);
|
||||
if (_unknownFields != null) {
|
||||
size += _unknownFields.CalculateSize();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public void MergeFrom(Log other) {
|
||||
if (other == null) {
|
||||
return;
|
||||
}
|
||||
if (other.Time != 0) {
|
||||
Time = other.Time;
|
||||
}
|
||||
contents_.Add(other.contents_);
|
||||
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public void MergeFrom(pb::CodedInputStream input) {
|
||||
uint tag;
|
||||
while ((tag = input.ReadTag()) != 0) {
|
||||
switch(tag) {
|
||||
default:
|
||||
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
|
||||
break;
|
||||
case 8: {
|
||||
Time = input.ReadUInt32();
|
||||
break;
|
||||
}
|
||||
case 18: {
|
||||
contents_.AddEntriesFrom(input, _repeated_contents_codec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Nested types
|
||||
/// <summary>Container for nested types declared in the Log message type.</summary>
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public static partial class Types {
|
||||
public sealed partial class Content : pb::IMessage<Content> {
|
||||
private static readonly pb::MessageParser<Content> _parser = new pb::MessageParser<Content>(() => new Content());
|
||||
private pb::UnknownFieldSet _unknownFields;
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public static pb::MessageParser<Content> Parser { get { return _parser; } }
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public static pbr::MessageDescriptor Descriptor {
|
||||
get { return global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.Log.Descriptor.NestedTypes[0]; }
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
pbr::MessageDescriptor pb::IMessage.Descriptor {
|
||||
get { return Descriptor; }
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public Content() {
|
||||
OnConstruction();
|
||||
}
|
||||
|
||||
partial void OnConstruction();
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public Content(Content other) : this() {
|
||||
key_ = other.key_;
|
||||
value_ = other.value_;
|
||||
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public Content Clone() {
|
||||
return new Content(this);
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "Key" field.</summary>
|
||||
public const int KeyFieldNumber = 1;
|
||||
private string key_ = "";
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public string Key {
|
||||
get { return key_; }
|
||||
set {
|
||||
key_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "Value" field.</summary>
|
||||
public const int ValueFieldNumber = 2;
|
||||
private string value_ = "";
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public string Value {
|
||||
get { return value_; }
|
||||
set {
|
||||
value_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public override bool Equals(object other) {
|
||||
return Equals(other as Content);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public bool Equals(Content other) {
|
||||
if (ReferenceEquals(other, null)) {
|
||||
return false;
|
||||
}
|
||||
if (ReferenceEquals(other, this)) {
|
||||
return true;
|
||||
}
|
||||
if (Key != other.Key) return false;
|
||||
if (Value != other.Value) return false;
|
||||
return Equals(_unknownFields, other._unknownFields);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public override int GetHashCode() {
|
||||
int hash = 1;
|
||||
if (Key.Length != 0) hash ^= Key.GetHashCode();
|
||||
if (Value.Length != 0) hash ^= Value.GetHashCode();
|
||||
if (_unknownFields != null) {
|
||||
hash ^= _unknownFields.GetHashCode();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public override string ToString() {
|
||||
return pb::JsonFormatter.ToDiagnosticString(this);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public void WriteTo(pb::CodedOutputStream output) {
|
||||
if (Key.Length != 0) {
|
||||
output.WriteRawTag(10);
|
||||
output.WriteString(Key);
|
||||
}
|
||||
if (value_ != null) {
|
||||
output.WriteRawTag(18);
|
||||
output.WriteString(Value);
|
||||
}
|
||||
if (_unknownFields != null) {
|
||||
_unknownFields.WriteTo(output);
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public int CalculateSize() {
|
||||
int size = 0;
|
||||
if (Key.Length != 0) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeStringSize(Key);
|
||||
}
|
||||
if (value_ != null) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeStringSize(Value);
|
||||
}
|
||||
if (_unknownFields != null) {
|
||||
size += _unknownFields.CalculateSize();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public void MergeFrom(Content other) {
|
||||
if (other == null) {
|
||||
return;
|
||||
}
|
||||
if (other.Key.Length != 0) {
|
||||
Key = other.Key;
|
||||
}
|
||||
if (other.value_ != null) {
|
||||
Value = other.Value;
|
||||
}
|
||||
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public void MergeFrom(pb::CodedInputStream input) {
|
||||
uint tag;
|
||||
while ((tag = input.ReadTag()) != 0) {
|
||||
switch(tag) {
|
||||
default:
|
||||
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
|
||||
break;
|
||||
case 10: {
|
||||
Key = input.ReadString();
|
||||
break;
|
||||
}
|
||||
case 18: {
|
||||
Value = input.ReadString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
public sealed partial class LogTag : pb::IMessage<LogTag> {
|
||||
private static readonly pb::MessageParser<LogTag> _parser = new pb::MessageParser<LogTag>(() => new LogTag());
|
||||
private pb::UnknownFieldSet _unknownFields;
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public static pb::MessageParser<LogTag> Parser { get { return _parser; } }
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public static pbr::MessageDescriptor Descriptor {
|
||||
get { return global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogReflection.Descriptor.MessageTypes[1]; }
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
pbr::MessageDescriptor pb::IMessage.Descriptor {
|
||||
get { return Descriptor; }
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public LogTag() {
|
||||
OnConstruction();
|
||||
}
|
||||
|
||||
partial void OnConstruction();
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public LogTag(LogTag other) : this() {
|
||||
key_ = other.key_;
|
||||
value_ = other.value_;
|
||||
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public LogTag Clone() {
|
||||
return new LogTag(this);
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "Key" field.</summary>
|
||||
public const int KeyFieldNumber = 1;
|
||||
private string key_ = "";
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public string Key {
|
||||
get { return key_; }
|
||||
set {
|
||||
key_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "Value" field.</summary>
|
||||
public const int ValueFieldNumber = 2;
|
||||
private string value_ = "";
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public string Value {
|
||||
get { return value_; }
|
||||
set {
|
||||
value_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public override bool Equals(object other) {
|
||||
return Equals(other as LogTag);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public bool Equals(LogTag other) {
|
||||
if (ReferenceEquals(other, null)) {
|
||||
return false;
|
||||
}
|
||||
if (ReferenceEquals(other, this)) {
|
||||
return true;
|
||||
}
|
||||
if (Key != other.Key) return false;
|
||||
if (Value != other.Value) return false;
|
||||
return Equals(_unknownFields, other._unknownFields);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public override int GetHashCode() {
|
||||
int hash = 1;
|
||||
if (Key.Length != 0) hash ^= Key.GetHashCode();
|
||||
if (Value.Length != 0) hash ^= Value.GetHashCode();
|
||||
if (_unknownFields != null) {
|
||||
hash ^= _unknownFields.GetHashCode();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public override string ToString() {
|
||||
return pb::JsonFormatter.ToDiagnosticString(this);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public void WriteTo(pb::CodedOutputStream output) {
|
||||
if (Key.Length != 0) {
|
||||
output.WriteRawTag(10);
|
||||
output.WriteString(Key);
|
||||
}
|
||||
if (value_ != null) {
|
||||
output.WriteRawTag(18);
|
||||
output.WriteString(Value);
|
||||
}
|
||||
if (_unknownFields != null) {
|
||||
_unknownFields.WriteTo(output);
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public int CalculateSize() {
|
||||
int size = 0;
|
||||
if (Key.Length != 0) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeStringSize(Key);
|
||||
}
|
||||
if (value_ != null) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeStringSize(Value);
|
||||
}
|
||||
if (_unknownFields != null) {
|
||||
size += _unknownFields.CalculateSize();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public void MergeFrom(LogTag other) {
|
||||
if (other == null) {
|
||||
return;
|
||||
}
|
||||
if (other.Key.Length != 0) {
|
||||
Key = other.Key;
|
||||
}
|
||||
if (other.value_ != null) {
|
||||
Value = other.Value;
|
||||
}
|
||||
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public void MergeFrom(pb::CodedInputStream input) {
|
||||
uint tag;
|
||||
while ((tag = input.ReadTag()) != 0) {
|
||||
switch(tag) {
|
||||
default:
|
||||
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
|
||||
break;
|
||||
case 10: {
|
||||
Key = input.ReadString();
|
||||
break;
|
||||
}
|
||||
case 18: {
|
||||
Value = input.ReadString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public sealed partial class LogGroup : pb::IMessage<LogGroup> {
|
||||
private static readonly pb::MessageParser<LogGroup> _parser = new pb::MessageParser<LogGroup>(() => new LogGroup());
|
||||
private pb::UnknownFieldSet _unknownFields;
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public static pb::MessageParser<LogGroup> Parser { get { return _parser; } }
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public static pbr::MessageDescriptor Descriptor {
|
||||
get { return global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogReflection.Descriptor.MessageTypes[2]; }
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
pbr::MessageDescriptor pb::IMessage.Descriptor {
|
||||
get { return Descriptor; }
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public LogGroup() {
|
||||
OnConstruction();
|
||||
}
|
||||
|
||||
partial void OnConstruction();
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public LogGroup(LogGroup other) : this() {
|
||||
logs_ = other.logs_.Clone();
|
||||
reserved_ = other.reserved_;
|
||||
topic_ = other.topic_;
|
||||
source_ = other.source_;
|
||||
logTags_ = other.logTags_.Clone();
|
||||
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public LogGroup Clone() {
|
||||
return new LogGroup(this);
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "Logs" field.</summary>
|
||||
public const int LogsFieldNumber = 1;
|
||||
private static readonly pb::FieldCodec<global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.Log> _repeated_logs_codec
|
||||
= pb::FieldCodec.ForMessage(10, global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.Log.Parser);
|
||||
private readonly pbc::RepeatedField<global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.Log> logs_ = new pbc::RepeatedField<global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.Log>();
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public pbc::RepeatedField<global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.Log> Logs {
|
||||
get { return logs_; }
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "Reserved" field.</summary>
|
||||
public const int ReservedFieldNumber = 2;
|
||||
private string reserved_ = "";
|
||||
/// <summary>
|
||||
/// reserved fields
|
||||
/// </summary>
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public string Reserved {
|
||||
get { return reserved_; }
|
||||
set {
|
||||
reserved_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "Topic" field.</summary>
|
||||
public const int TopicFieldNumber = 3;
|
||||
private string topic_ = "";
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public string Topic {
|
||||
get { return topic_; }
|
||||
set {
|
||||
topic_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "Source" field.</summary>
|
||||
public const int SourceFieldNumber = 4;
|
||||
private string source_ = "";
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public string Source {
|
||||
get { return source_; }
|
||||
set {
|
||||
source_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "LogTags" field.</summary>
|
||||
public const int LogTagsFieldNumber = 6;
|
||||
private static readonly pb::FieldCodec<global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogTag> _repeated_logTags_codec
|
||||
= pb::FieldCodec.ForMessage(50, global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogTag.Parser);
|
||||
private readonly pbc::RepeatedField<global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogTag> logTags_ = new pbc::RepeatedField<global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogTag>();
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public pbc::RepeatedField<global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogTag> LogTags {
|
||||
get { return logTags_; }
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public override bool Equals(object other) {
|
||||
return Equals(other as LogGroup);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public bool Equals(LogGroup other) {
|
||||
if (ReferenceEquals(other, null)) {
|
||||
return false;
|
||||
}
|
||||
if (ReferenceEquals(other, this)) {
|
||||
return true;
|
||||
}
|
||||
if(!logs_.Equals(other.logs_)) return false;
|
||||
if (Reserved != other.Reserved) return false;
|
||||
if (Topic != other.Topic) return false;
|
||||
if (Source != other.Source) return false;
|
||||
if(!logTags_.Equals(other.logTags_)) return false;
|
||||
return Equals(_unknownFields, other._unknownFields);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public override int GetHashCode() {
|
||||
int hash = 1;
|
||||
hash ^= logs_.GetHashCode();
|
||||
if (Reserved.Length != 0) hash ^= Reserved.GetHashCode();
|
||||
if (Topic.Length != 0) hash ^= Topic.GetHashCode();
|
||||
if (Source.Length != 0) hash ^= Source.GetHashCode();
|
||||
hash ^= logTags_.GetHashCode();
|
||||
if (_unknownFields != null) {
|
||||
hash ^= _unknownFields.GetHashCode();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public override string ToString() {
|
||||
return pb::JsonFormatter.ToDiagnosticString(this);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public void WriteTo(pb::CodedOutputStream output) {
|
||||
logs_.WriteTo(output, _repeated_logs_codec);
|
||||
if (Reserved.Length != 0) {
|
||||
output.WriteRawTag(18);
|
||||
output.WriteString(Reserved);
|
||||
}
|
||||
if (Topic.Length != 0) {
|
||||
output.WriteRawTag(26);
|
||||
output.WriteString(Topic);
|
||||
}
|
||||
if (Source.Length != 0) {
|
||||
output.WriteRawTag(34);
|
||||
output.WriteString(Source);
|
||||
}
|
||||
logTags_.WriteTo(output, _repeated_logTags_codec);
|
||||
if (_unknownFields != null) {
|
||||
_unknownFields.WriteTo(output);
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public int CalculateSize() {
|
||||
int size = 0;
|
||||
size += logs_.CalculateSize(_repeated_logs_codec);
|
||||
if (Reserved.Length != 0) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeStringSize(Reserved);
|
||||
}
|
||||
if (Topic.Length != 0) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeStringSize(Topic);
|
||||
}
|
||||
if (Source.Length != 0) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeStringSize(Source);
|
||||
}
|
||||
size += logTags_.CalculateSize(_repeated_logTags_codec);
|
||||
if (_unknownFields != null) {
|
||||
size += _unknownFields.CalculateSize();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public void MergeFrom(LogGroup other) {
|
||||
if (other == null) {
|
||||
return;
|
||||
}
|
||||
logs_.Add(other.logs_);
|
||||
if (other.Reserved.Length != 0) {
|
||||
Reserved = other.Reserved;
|
||||
}
|
||||
if (other.Topic.Length != 0) {
|
||||
Topic = other.Topic;
|
||||
}
|
||||
if (other.Source.Length != 0) {
|
||||
Source = other.Source;
|
||||
}
|
||||
logTags_.Add(other.logTags_);
|
||||
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public void MergeFrom(pb::CodedInputStream input) {
|
||||
uint tag;
|
||||
while ((tag = input.ReadTag()) != 0) {
|
||||
switch(tag) {
|
||||
default:
|
||||
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
|
||||
break;
|
||||
case 10: {
|
||||
logs_.AddEntriesFrom(input, _repeated_logs_codec);
|
||||
break;
|
||||
}
|
||||
case 18: {
|
||||
Reserved = input.ReadString();
|
||||
break;
|
||||
}
|
||||
case 26: {
|
||||
Topic = input.ReadString();
|
||||
break;
|
||||
}
|
||||
case 34: {
|
||||
Source = input.ReadString();
|
||||
break;
|
||||
}
|
||||
case 50: {
|
||||
logTags_.AddEntriesFrom(input, _repeated_logTags_codec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public sealed partial class LogGroupList : pb::IMessage<LogGroupList> {
|
||||
private static readonly pb::MessageParser<LogGroupList> _parser = new pb::MessageParser<LogGroupList>(() => new LogGroupList());
|
||||
private pb::UnknownFieldSet _unknownFields;
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public static pb::MessageParser<LogGroupList> Parser { get { return _parser; } }
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public static pbr::MessageDescriptor Descriptor {
|
||||
get { return global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogReflection.Descriptor.MessageTypes[3]; }
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
pbr::MessageDescriptor pb::IMessage.Descriptor {
|
||||
get { return Descriptor; }
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public LogGroupList() {
|
||||
OnConstruction();
|
||||
}
|
||||
|
||||
partial void OnConstruction();
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public LogGroupList(LogGroupList other) : this() {
|
||||
logGroupList_ = other.logGroupList_.Clone();
|
||||
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public LogGroupList Clone() {
|
||||
return new LogGroupList(this);
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "logGroupList" field.</summary>
|
||||
public const int LogGroupList_FieldNumber = 1;
|
||||
private static readonly pb::FieldCodec<global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogGroup> _repeated_logGroupList_codec
|
||||
= pb::FieldCodec.ForMessage(10, global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogGroup.Parser);
|
||||
private readonly pbc::RepeatedField<global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogGroup> logGroupList_ = new pbc::RepeatedField<global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogGroup>();
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public pbc::RepeatedField<global::Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf.LogGroup> LogGroupList_ {
|
||||
get { return logGroupList_; }
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public override bool Equals(object other) {
|
||||
return Equals(other as LogGroupList);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public bool Equals(LogGroupList other) {
|
||||
if (ReferenceEquals(other, null)) {
|
||||
return false;
|
||||
}
|
||||
if (ReferenceEquals(other, this)) {
|
||||
return true;
|
||||
}
|
||||
if(!logGroupList_.Equals(other.logGroupList_)) return false;
|
||||
return Equals(_unknownFields, other._unknownFields);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public override int GetHashCode() {
|
||||
int hash = 1;
|
||||
hash ^= logGroupList_.GetHashCode();
|
||||
if (_unknownFields != null) {
|
||||
hash ^= _unknownFields.GetHashCode();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public override string ToString() {
|
||||
return pb::JsonFormatter.ToDiagnosticString(this);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public void WriteTo(pb::CodedOutputStream output) {
|
||||
logGroupList_.WriteTo(output, _repeated_logGroupList_codec);
|
||||
if (_unknownFields != null) {
|
||||
_unknownFields.WriteTo(output);
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public int CalculateSize() {
|
||||
int size = 0;
|
||||
size += logGroupList_.CalculateSize(_repeated_logGroupList_codec);
|
||||
if (_unknownFields != null) {
|
||||
size += _unknownFields.CalculateSize();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public void MergeFrom(LogGroupList other) {
|
||||
if (other == null) {
|
||||
return;
|
||||
}
|
||||
logGroupList_.Add(other.logGroupList_);
|
||||
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
public void MergeFrom(pb::CodedInputStream input) {
|
||||
uint tag;
|
||||
while ((tag = input.ReadTag()) != 0) {
|
||||
switch(tag) {
|
||||
default:
|
||||
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
|
||||
break;
|
||||
case 10: {
|
||||
logGroupList_.AddEntriesFrom(input, _repeated_logGroupList_codec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
#endregion Designer generated code
|
||||
@@ -0,0 +1,39 @@
|
||||
/******************************************************************************
|
||||
* Compile command:
|
||||
* $ protoc --csharp_out=. --csharp_opt=file_extension=.Generated.cs Log.proto
|
||||
******************************************************************************/
|
||||
|
||||
syntax = "proto3";
|
||||
option csharp_namespace = "Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf";
|
||||
|
||||
package aliyun.api.log.infrastructure.serialization.protobuf;
|
||||
|
||||
message Log
|
||||
{
|
||||
uint32 Time = 1;// UNIX Time Format
|
||||
message Content
|
||||
{
|
||||
string Key = 1;
|
||||
string Value = 2;
|
||||
}
|
||||
repeated Content Contents= 2;
|
||||
|
||||
}
|
||||
message LogTag
|
||||
{
|
||||
string Key = 1;
|
||||
string Value = 2;
|
||||
}
|
||||
message LogGroup
|
||||
{
|
||||
repeated Log Logs= 1;
|
||||
string Reserved = 2; // reserved fields
|
||||
string Topic = 3;
|
||||
string Source = 4;
|
||||
repeated LogTag LogTags = 6;
|
||||
}
|
||||
|
||||
message LogGroupList
|
||||
{
|
||||
repeated LogGroup logGroupList = 1;
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
//
|
||||
// LogExtensions.cs
|
||||
//
|
||||
// Author:
|
||||
// MiNG <developer@ming.gz.cn>
|
||||
//
|
||||
// Copyright (c) 2018 Alibaba Cloud
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Aliyun.Api.LogService.Domain.Log;
|
||||
|
||||
namespace Aliyun.Api.LogService.Infrastructure.Serialization.Protobuf
|
||||
{
|
||||
public static class LogExtensions
|
||||
{
|
||||
public static LogInfo ToDomainModel(this Log proto)
|
||||
=> proto == null
|
||||
? null
|
||||
: new LogInfo
|
||||
{
|
||||
Time = DateTimeOffset.FromUnixTimeSeconds(proto.Time),
|
||||
Contents = proto.Contents?
|
||||
.ToDictionary(x => x.Key, x => x.Value) // NOTE: potential `ArgumentException` when key duplicated.
|
||||
};
|
||||
|
||||
public static LogGroupInfo ToDomainModel(this LogGroup proto)
|
||||
=> proto == null
|
||||
? null
|
||||
: new LogGroupInfo
|
||||
{
|
||||
Topic = proto.Topic,
|
||||
Source = proto.Source,
|
||||
LogTags = proto.LogTags?
|
||||
.ToDictionary(x => x.Key, x => x.Value), // NOTE: potential `ArgumentException` when key duplicated.
|
||||
Logs = proto.Logs?
|
||||
.Select(x => x.ToDomainModel())
|
||||
.ToList()
|
||||
};
|
||||
|
||||
public static IList<LogGroupInfo> ToDomainModel(this LogGroupList proto)
|
||||
=> proto?.LogGroupList_?
|
||||
.Select(x => x.ToDomainModel())
|
||||
.ToList();
|
||||
|
||||
|
||||
public static Log ToProtoModel(this LogInfo domain)
|
||||
=> domain == null
|
||||
? null
|
||||
: new Log
|
||||
{
|
||||
Time = (UInt32) domain.Time.ToUnixTimeSeconds(),
|
||||
Contents =
|
||||
{
|
||||
domain.Contents?
|
||||
.Select(kv => new Log.Types.Content
|
||||
{
|
||||
Key = kv.Key,
|
||||
Value = kv.Value ?? String.Empty // Empty is allowed, but not null.
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
public static LogGroup ToProtoModel(this LogGroupInfo domain)
|
||||
=> domain == null
|
||||
? null
|
||||
: new LogGroup
|
||||
{
|
||||
// https://github.com/aliyun/aliyun-log-dotnetcore-sdk/issues/14
|
||||
Topic = domain.Topic ?? String.Empty, // Empty is allowed, but not null.
|
||||
Source = domain.Source ?? String.Empty, // Empty is allowed, but not null.
|
||||
LogTags =
|
||||
{
|
||||
domain.LogTags?
|
||||
.Select(x => new LogTag
|
||||
{
|
||||
Key = x.Key,
|
||||
Value = x.Value ?? String.Empty // Empty is allowed, but not null.
|
||||
})
|
||||
?? Enumerable.Empty<LogTag>()
|
||||
},
|
||||
Logs =
|
||||
{
|
||||
domain.Logs?
|
||||
.Select(x => x.ToProtoModel())
|
||||
?? Enumerable.Empty<Log>()
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user