diff --git a/sdk/monitor/Azure.Monitor.Ingestion/api/Azure.Monitor.Ingestion.net461.cs b/sdk/monitor/Azure.Monitor.Ingestion/api/Azure.Monitor.Ingestion.net461.cs index 00b8cd4568de..1eeb6ced3469 100644 --- a/sdk/monitor/Azure.Monitor.Ingestion/api/Azure.Monitor.Ingestion.net461.cs +++ b/sdk/monitor/Azure.Monitor.Ingestion/api/Azure.Monitor.Ingestion.net461.cs @@ -26,7 +26,9 @@ public LogsIngestionClient(System.Uri endpoint, Azure.Core.TokenCredential crede public LogsIngestionClient(System.Uri endpoint, Azure.Core.TokenCredential credential, Azure.Monitor.Ingestion.LogsIngestionClientOptions options) { } public virtual Azure.Core.Pipeline.HttpPipeline Pipeline { get { throw null; } } public virtual Azure.Response Upload(string ruleId, string streamName, Azure.Core.RequestContent content, string contentEncoding = null, Azure.RequestContext context = null) { throw null; } + public virtual Azure.Response Upload(string ruleId, string streamName, System.Collections.Generic.IEnumerable> body, string contentEncoding = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task UploadAsync(string ruleId, string streamName, Azure.Core.RequestContent content, string contentEncoding = null, Azure.RequestContext context = null) { throw null; } + public virtual System.Threading.Tasks.Task UploadAsync(string ruleId, string streamName, System.Collections.Generic.IEnumerable> body, string contentEncoding = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task UploadAsync(string ruleId, string streamName, System.Collections.Generic.IEnumerable logs, Azure.Monitor.Ingestion.LogsUploadOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response Upload(string ruleId, string streamName, System.Collections.Generic.IEnumerable logs, Azure.Monitor.Ingestion.LogsUploadOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } diff --git a/sdk/monitor/Azure.Monitor.Ingestion/api/Azure.Monitor.Ingestion.netstandard2.0.cs b/sdk/monitor/Azure.Monitor.Ingestion/api/Azure.Monitor.Ingestion.netstandard2.0.cs index 00b8cd4568de..1eeb6ced3469 100644 --- a/sdk/monitor/Azure.Monitor.Ingestion/api/Azure.Monitor.Ingestion.netstandard2.0.cs +++ b/sdk/monitor/Azure.Monitor.Ingestion/api/Azure.Monitor.Ingestion.netstandard2.0.cs @@ -26,7 +26,9 @@ public LogsIngestionClient(System.Uri endpoint, Azure.Core.TokenCredential crede public LogsIngestionClient(System.Uri endpoint, Azure.Core.TokenCredential credential, Azure.Monitor.Ingestion.LogsIngestionClientOptions options) { } public virtual Azure.Core.Pipeline.HttpPipeline Pipeline { get { throw null; } } public virtual Azure.Response Upload(string ruleId, string streamName, Azure.Core.RequestContent content, string contentEncoding = null, Azure.RequestContext context = null) { throw null; } + public virtual Azure.Response Upload(string ruleId, string streamName, System.Collections.Generic.IEnumerable> body, string contentEncoding = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task UploadAsync(string ruleId, string streamName, Azure.Core.RequestContent content, string contentEncoding = null, Azure.RequestContext context = null) { throw null; } + public virtual System.Threading.Tasks.Task UploadAsync(string ruleId, string streamName, System.Collections.Generic.IEnumerable> body, string contentEncoding = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task UploadAsync(string ruleId, string streamName, System.Collections.Generic.IEnumerable logs, Azure.Monitor.Ingestion.LogsUploadOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response Upload(string ruleId, string streamName, System.Collections.Generic.IEnumerable logs, Azure.Monitor.Ingestion.LogsUploadOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } diff --git a/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/Docs/LogsIngestionClient.xml b/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/Docs/LogsIngestionClient.xml index 7d2530c2b7e1..2560fc6c5fd9 100644 --- a/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/Docs/LogsIngestionClient.xml +++ b/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/Docs/LogsIngestionClient.xml @@ -1,7 +1,7 @@ - + This sample shows how to call UploadAsync. "); TokenCredential credential = new DefaultAzureCredential(); LogsIngestionClient client = new LogsIngestionClient(endpoint, credential); -using RequestContent content = RequestContent.Create(new object[] +Response response = await client.UploadAsync("c512b59c-4005-40e2-b13d-ac29cfb46af4", "Custom-TestStream", new IDictionary[] { - new object() + new Dictionary + { + ["Time"] = BinaryData.FromObjectAsJson("2021-12-08T23:51:14.1104269Z"), + ["Computer"] = BinaryData.FromObjectAsJson("Computer1"), + ["AdditionalContext"] = BinaryData.FromObjectAsJson(new Dictionary + { + ["$id"] = "55", + ["InstanceName"] = "user1", + ["TimeZone"] = "Pacific Time", + ["Level"] = 4, + ["CounterName"] = "AppMetric1", + ["CounterValue"] = 15.3F + }) + }, + new Dictionary + { + ["Time"] = BinaryData.FromObjectAsJson("2021-12-08T23:51:14.1104269Z"), + ["Computer"] = BinaryData.FromObjectAsJson("Computer2"), + ["AdditionalContext"] = BinaryData.FromObjectAsJson(new Dictionary + { + ["$id"] = "61", + ["InstanceName"] = "user2", + ["TimeZone"] = "Central Time", + ["Level"] = 3, + ["CounterName"] = "AppMetric1", + ["CounterValue"] = 23.5F + }) + } }); -Response response = await client.UploadAsync("", "", content); - -Console.WriteLine(response.Status); -]]> -This sample shows how to call UploadAsync with all parameters and request content. +]]> + + + +This sample shows how to call Upload. "); TokenCredential credential = new DefaultAzureCredential(); LogsIngestionClient client = new LogsIngestionClient(endpoint, credential); -using RequestContent content = RequestContent.Create(new object[] +Response response = client.Upload("c512b59c-4005-40e2-b13d-ac29cfb46af4", "Custom-TestStream", new IDictionary[] { - new object() + new Dictionary + { + ["Time"] = BinaryData.FromObjectAsJson("2021-12-08T23:51:14.1104269Z"), + ["Computer"] = BinaryData.FromObjectAsJson("Computer1"), + ["AdditionalContext"] = BinaryData.FromObjectAsJson(new Dictionary + { + ["$id"] = "55", + ["InstanceName"] = "user1", + ["TimeZone"] = "Pacific Time", + ["Level"] = 4, + ["CounterName"] = "AppMetric1", + ["CounterValue"] = 15.3F + }) + }, + new Dictionary + { + ["Time"] = BinaryData.FromObjectAsJson("2021-12-08T23:51:14.1104269Z"), + ["Computer"] = BinaryData.FromObjectAsJson("Computer2"), + ["AdditionalContext"] = BinaryData.FromObjectAsJson(new Dictionary + { + ["$id"] = "61", + ["InstanceName"] = "user2", + ["TimeZone"] = "Central Time", + ["Level"] = 3, + ["CounterName"] = "AppMetric1", + ["CounterValue"] = 23.5F + }) + } }); -Response response = await client.UploadAsync("", "", content, contentEncoding: ""); - -Console.WriteLine(response.Status); ]]> - + -This sample shows how to call Upload. +This sample shows how to call UploadAsync. "); TokenCredential credential = new DefaultAzureCredential(); @@ -42,13 +93,43 @@ LogsIngestionClient client = new LogsIngestionClient(endpoint, credential); using RequestContent content = RequestContent.Create(new object[] { - new object() + new + { + Time = "2021-12-08T23:51:14.1104269Z", + Computer = "Computer1", + AdditionalContext = new Dictionary + { + ["$id"] = "55", + ["InstanceName"] = "user1", + ["TimeZone"] = "Pacific Time", + ["Level"] = 4, + ["CounterName"] = "AppMetric1", + ["CounterValue"] = 15.3F + }, + }, + new + { + Time = "2021-12-08T23:51:14.1104269Z", + Computer = "Computer2", + AdditionalContext = new Dictionary + { + ["$id"] = "61", + ["InstanceName"] = "user2", + ["TimeZone"] = "Central Time", + ["Level"] = 3, + ["CounterName"] = "AppMetric1", + ["CounterValue"] = 23.5F + }, + } }); -Response response = client.Upload("", "", content); +Response response = await client.UploadAsync("c512b59c-4005-40e2-b13d-ac29cfb46af4", "Custom-TestStream", content); Console.WriteLine(response.Status); -]]> -This sample shows how to call Upload with all parameters and request content. +]]> + + + +This sample shows how to call Upload. "); TokenCredential credential = new DefaultAzureCredential(); @@ -56,9 +137,36 @@ LogsIngestionClient client = new LogsIngestionClient(endpoint, credential); using RequestContent content = RequestContent.Create(new object[] { - new object() + new + { + Time = "2021-12-08T23:51:14.1104269Z", + Computer = "Computer1", + AdditionalContext = new Dictionary + { + ["$id"] = "55", + ["InstanceName"] = "user1", + ["TimeZone"] = "Pacific Time", + ["Level"] = 4, + ["CounterName"] = "AppMetric1", + ["CounterValue"] = 15.3F + }, + }, + new + { + Time = "2021-12-08T23:51:14.1104269Z", + Computer = "Computer2", + AdditionalContext = new Dictionary + { + ["$id"] = "61", + ["InstanceName"] = "user2", + ["TimeZone"] = "Central Time", + ["Level"] = 3, + ["CounterName"] = "AppMetric1", + ["CounterValue"] = 23.5F + }, + } }); -Response response = client.Upload("", "", content, contentEncoding: ""); +Response response = client.Upload("c512b59c-4005-40e2-b13d-ac29cfb46af4", "Custom-TestStream", content); Console.WriteLine(response.Status); ]]> diff --git a/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/IngestionClientBuilderExtensions.cs b/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/IngestionClientBuilderExtensions.cs index 652d6e4ae38d..34bfd6be0284 100644 --- a/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/IngestionClientBuilderExtensions.cs +++ b/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/IngestionClientBuilderExtensions.cs @@ -16,7 +16,7 @@ public static partial class IngestionClientBuilderExtensions { /// Registers a instance. /// The builder to register with. - /// The Data Collection Endpoint for the Data Collection Rule, for example https://dce-name.eastus-2.ingest.monitor.azure.com. + /// The to use. public static IAzureClientBuilder AddLogsIngestionClient(this TBuilder builder, Uri endpoint) where TBuilder : IAzureClientFactoryBuilderWithCredential { diff --git a/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/Internal/ModelSerializationExtensions.cs b/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/Internal/ModelSerializationExtensions.cs new file mode 100644 index 000000000000..86aea19f2339 --- /dev/null +++ b/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/Internal/ModelSerializationExtensions.cs @@ -0,0 +1,398 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Text.Json; +using System.Xml; +using Azure.Core; + +namespace Azure.Monitor.Ingestion +{ + internal static class ModelSerializationExtensions + { + internal static readonly ModelReaderWriterOptions WireOptions = new ModelReaderWriterOptions("W"); + + public static object GetObject(this JsonElement element) + { + switch (element.ValueKind) + { + case JsonValueKind.String: + return element.GetString(); + case JsonValueKind.Number: + if (element.TryGetInt32(out int intValue)) + { + return intValue; + } + if (element.TryGetInt64(out long longValue)) + { + return longValue; + } + return element.GetDouble(); + case JsonValueKind.True: + return true; + case JsonValueKind.False: + return false; + case JsonValueKind.Undefined: + case JsonValueKind.Null: + return null; + case JsonValueKind.Object: + var dictionary = new Dictionary(); + foreach (var jsonProperty in element.EnumerateObject()) + { + dictionary.Add(jsonProperty.Name, jsonProperty.Value.GetObject()); + } + return dictionary; + case JsonValueKind.Array: + var list = new List(); + foreach (var item in element.EnumerateArray()) + { + list.Add(item.GetObject()); + } + return list.ToArray(); + default: + throw new NotSupportedException($"Not supported value kind {element.ValueKind}"); + } + } + + public static byte[] GetBytesFromBase64(this JsonElement element, string format) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + + return format switch + { + "U" => TypeFormatters.FromBase64UrlString(element.GetRequiredString()), + "D" => element.GetBytesFromBase64(), + _ => throw new ArgumentException($"Format is not supported: '{format}'", nameof(format)) + }; + } + + public static DateTimeOffset GetDateTimeOffset(this JsonElement element, string format) => format switch + { + "U" when element.ValueKind == JsonValueKind.Number => DateTimeOffset.FromUnixTimeSeconds(element.GetInt64()), + _ => TypeFormatters.ParseDateTimeOffset(element.GetString(), format) + }; + + public static TimeSpan GetTimeSpan(this JsonElement element, string format) => TypeFormatters.ParseTimeSpan(element.GetString(), format); + + public static char GetChar(this JsonElement element) + { + if (element.ValueKind == JsonValueKind.String) + { + var text = element.GetString(); + if (text == null || text.Length != 1) + { + throw new NotSupportedException($"Cannot convert \"{text}\" to a char"); + } + return text[0]; + } + else + { + throw new NotSupportedException($"Cannot convert {element.ValueKind} to a char"); + } + } + + [Conditional("DEBUG")] + public static void ThrowNonNullablePropertyIsNull(this JsonProperty property) + { + throw new JsonException($"A property '{property.Name}' defined as non-nullable but received as null from the service. This exception only happens in DEBUG builds of the library and would be ignored in the release build"); + } + + public static string GetRequiredString(this JsonElement element) + { + var value = element.GetString(); + if (value == null) + { + throw new InvalidOperationException($"The requested operation requires an element of type 'String', but the target element has type '{element.ValueKind}'."); + } + return value; + } + + public static void WriteStringValue(this Utf8JsonWriter writer, DateTimeOffset value, string format) + { + writer.WriteStringValue(TypeFormatters.ToString(value, format)); + } + + public static void WriteStringValue(this Utf8JsonWriter writer, DateTime value, string format) + { + writer.WriteStringValue(TypeFormatters.ToString(value, format)); + } + + public static void WriteStringValue(this Utf8JsonWriter writer, TimeSpan value, string format) + { + writer.WriteStringValue(TypeFormatters.ToString(value, format)); + } + + public static void WriteStringValue(this Utf8JsonWriter writer, char value) + { + writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture)); + } + + public static void WriteBase64StringValue(this Utf8JsonWriter writer, byte[] value, string format) + { + if (value == null) + { + writer.WriteNullValue(); + return; + } + switch (format) + { + case "U": + writer.WriteStringValue(TypeFormatters.ToBase64UrlString(value)); + break; + case "D": + writer.WriteBase64StringValue(value); + break; + default: + throw new ArgumentException($"Format is not supported: '{format}'", nameof(format)); + } + } + + public static void WriteNumberValue(this Utf8JsonWriter writer, DateTimeOffset value, string format) + { + if (format != "U") + { + throw new ArgumentOutOfRangeException(nameof(format), "Only 'U' format is supported when writing a DateTimeOffset as a Number."); + } + writer.WriteNumberValue(value.ToUnixTimeSeconds()); + } + + public static void WriteObjectValue(this Utf8JsonWriter writer, T value, ModelReaderWriterOptions options = null) + { + switch (value) + { + case null: + writer.WriteNullValue(); + break; + case IJsonModel jsonModel: + jsonModel.Write(writer, options ?? WireOptions); + break; + case IUtf8JsonSerializable serializable: + serializable.Write(writer); + break; + case byte[] bytes: + writer.WriteBase64StringValue(bytes); + break; + case BinaryData bytes0: + writer.WriteBase64StringValue(bytes0); + break; + case JsonElement json: + json.WriteTo(writer); + break; + case int i: + writer.WriteNumberValue(i); + break; + case decimal d: + writer.WriteNumberValue(d); + break; + case double d0: + if (double.IsNaN(d0)) + { + writer.WriteStringValue("NaN"); + } + else + { + writer.WriteNumberValue(d0); + } + break; + case float f: + writer.WriteNumberValue(f); + break; + case long l: + writer.WriteNumberValue(l); + break; + case string s: + writer.WriteStringValue(s); + break; + case bool b: + writer.WriteBooleanValue(b); + break; + case Guid g: + writer.WriteStringValue(g); + break; + case DateTimeOffset dateTimeOffset: + writer.WriteStringValue(dateTimeOffset, "O"); + break; + case DateTime dateTime: + writer.WriteStringValue(dateTime, "O"); + break; + case IEnumerable> enumerable: + writer.WriteStartObject(); + foreach (var pair in enumerable) + { + writer.WritePropertyName(pair.Key); + writer.WriteObjectValue(pair.Value, options); + } + writer.WriteEndObject(); + break; + case IEnumerable objectEnumerable: + writer.WriteStartArray(); + foreach (var item in objectEnumerable) + { + writer.WriteObjectValue(item, options); + } + writer.WriteEndArray(); + break; + case TimeSpan timeSpan: + writer.WriteStringValue(timeSpan, "P"); + break; + default: + throw new NotSupportedException($"Not supported type {value.GetType()}"); + } + } + + public static void WriteObjectValue(this Utf8JsonWriter writer, object value, ModelReaderWriterOptions options = null) + { + writer.WriteObjectValue(value, options); + } + + internal static class TypeFormatters + { + private const string RoundtripZFormat = "yyyy-MM-ddTHH:mm:ss.fffffffZ"; + public const string DefaultNumberFormat = "G"; + + public static string ToString(bool value) => value ? "true" : "false"; + + public static string ToString(DateTime value, string format) => value.Kind switch + { + DateTimeKind.Utc => ToString((DateTimeOffset)value, format), + _ => throw new NotSupportedException($"DateTime {value} has a Kind of {value.Kind}. Azure SDK requires it to be UTC. You can call DateTime.SpecifyKind to change Kind property value to DateTimeKind.Utc.") + }; + + public static string ToString(DateTimeOffset value, string format) => format switch + { + "D" => value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture), + "U" => value.ToUnixTimeSeconds().ToString(CultureInfo.InvariantCulture), + "O" => value.ToUniversalTime().ToString(RoundtripZFormat, CultureInfo.InvariantCulture), + "o" => value.ToUniversalTime().ToString(RoundtripZFormat, CultureInfo.InvariantCulture), + "R" => value.ToString("r", CultureInfo.InvariantCulture), + _ => value.ToString(format, CultureInfo.InvariantCulture) + }; + + public static string ToString(TimeSpan value, string format) => format switch + { + "P" => XmlConvert.ToString(value), + _ => value.ToString(format, CultureInfo.InvariantCulture) + }; + + public static string ToString(byte[] value, string format) => format switch + { + "U" => ToBase64UrlString(value), + "D" => Convert.ToBase64String(value), + _ => throw new ArgumentException($"Format is not supported: '{format}'", nameof(format)) + }; + + public static string ToBase64UrlString(byte[] value) + { + int numWholeOrPartialInputBlocks = checked(value.Length + 2) / 3; + int size = checked(numWholeOrPartialInputBlocks * 4); + char[] output = new char[size]; + + int numBase64Chars = Convert.ToBase64CharArray(value, 0, value.Length, output, 0); + + int i = 0; + for (; i < numBase64Chars; i++) + { + char ch = output[i]; + if (ch == '+') + { + output[i] = '-'; + } + else + { + if (ch == '/') + { + output[i] = '_'; + } + else + { + if (ch == '=') + { + break; + } + } + } + } + + return new string(output, 0, i); + } + + public static byte[] FromBase64UrlString(string value) + { + int paddingCharsToAdd = (value.Length % 4) switch + { + 0 => 0, + 2 => 2, + 3 => 1, + _ => throw new InvalidOperationException("Malformed input") + }; + char[] output = new char[(value.Length + paddingCharsToAdd)]; + int i = 0; + for (; i < value.Length; i++) + { + char ch = value[i]; + if (ch == '-') + { + output[i] = '+'; + } + else + { + if (ch == '_') + { + output[i] = '/'; + } + else + { + output[i] = ch; + } + } + } + + for (; i < output.Length; i++) + { + output[i] = '='; + } + + return Convert.FromBase64CharArray(output, 0, output.Length); + } + + public static DateTimeOffset ParseDateTimeOffset(string value, string format) => format switch + { + "U" => DateTimeOffset.FromUnixTimeSeconds(long.Parse(value, CultureInfo.InvariantCulture)), + _ => DateTimeOffset.Parse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal) + }; + + public static TimeSpan ParseTimeSpan(string value, string format) => format switch + { + "P" => XmlConvert.ToTimeSpan(value), + _ => TimeSpan.ParseExact(value, format, CultureInfo.InvariantCulture) + }; + + public static string ConvertToString(object value, string format = null) => value switch + { + null => "null", + string s => s, + bool b => ToString(b), + int or float or double or long or decimal => ((IFormattable)value).ToString(DefaultNumberFormat, CultureInfo.InvariantCulture), + byte[] b0 when format != null => ToString(b0, format), + IEnumerable s0 => string.Join(",", s0), + DateTimeOffset dateTime when format != null => ToString(dateTime, format), + TimeSpan timeSpan when format != null => ToString(timeSpan, format), + TimeSpan timeSpan0 => XmlConvert.ToString(timeSpan0), + Guid guid => guid.ToString(), + BinaryData binaryData => ConvertToString(binaryData.ToArray(), format), + _ => value.ToString() + }; + } + } +} diff --git a/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/Internal/RequestContentHelper.cs b/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/Internal/RequestContentHelper.cs new file mode 100644 index 000000000000..6aca1914f758 --- /dev/null +++ b/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/Internal/RequestContentHelper.cs @@ -0,0 +1,136 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.Text.Json; +using Azure.Core; + +namespace Azure.Monitor.Ingestion +{ + internal static class RequestContentHelper + { + public static RequestContent FromEnumerable(IEnumerable enumerable) + where T : notnull + { + Utf8JsonRequestContent content = new Utf8JsonRequestContent(); + content.JsonWriter.WriteStartArray(); + foreach (var item in enumerable) + { + content.JsonWriter.WriteObjectValue(item, ModelSerializationExtensions.WireOptions); + } + content.JsonWriter.WriteEndArray(); + + return content; + } + + public static RequestContent FromEnumerable(IEnumerable enumerable) + { + Utf8JsonRequestContent content = new Utf8JsonRequestContent(); + content.JsonWriter.WriteStartArray(); + foreach (var item in enumerable) + { + if (item == null) + { + content.JsonWriter.WriteNullValue(); + } + else + { +#if NET6_0_OR_GREATER + content.JsonWriter.WriteRawValue(item); +#else + using (JsonDocument document = JsonDocument.Parse(item)) + { + JsonSerializer.Serialize(content.JsonWriter, document.RootElement); + } +#endif + } + } + content.JsonWriter.WriteEndArray(); + + return content; + } + + public static RequestContent FromEnumerable(ReadOnlySpan span) + where T : notnull + { + Utf8JsonRequestContent content = new Utf8JsonRequestContent(); + content.JsonWriter.WriteStartArray(); + for (int i = 0; i < span.Length; i++) + { + content.JsonWriter.WriteObjectValue(span[i], ModelSerializationExtensions.WireOptions); + } + content.JsonWriter.WriteEndArray(); + + return content; + } + + public static RequestContent FromDictionary(IDictionary dictionary) + where TValue : notnull + { + Utf8JsonRequestContent content = new Utf8JsonRequestContent(); + content.JsonWriter.WriteStartObject(); + foreach (var item in dictionary) + { + content.JsonWriter.WritePropertyName(item.Key); + content.JsonWriter.WriteObjectValue(item.Value, ModelSerializationExtensions.WireOptions); + } + content.JsonWriter.WriteEndObject(); + + return content; + } + + public static RequestContent FromDictionary(IDictionary dictionary) + { + Utf8JsonRequestContent content = new Utf8JsonRequestContent(); + content.JsonWriter.WriteStartObject(); + foreach (var item in dictionary) + { + content.JsonWriter.WritePropertyName(item.Key); + if (item.Value == null) + { + content.JsonWriter.WriteNullValue(); + } + else + { +#if NET6_0_OR_GREATER + content.JsonWriter.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(content.JsonWriter, document.RootElement); + } +#endif + } + } + content.JsonWriter.WriteEndObject(); + + return content; + } + + public static RequestContent FromObject(object value) + { + Utf8JsonRequestContent content = new Utf8JsonRequestContent(); + content.JsonWriter.WriteObjectValue(value, ModelSerializationExtensions.WireOptions); + return content; + } + + public static RequestContent FromObject(BinaryData value) + { + Utf8JsonRequestContent content = new Utf8JsonRequestContent(); +#if NET6_0_OR_GREATER + content.JsonWriter.WriteRawValue(value); +#else + using (JsonDocument document = JsonDocument.Parse(value)) + { + JsonSerializer.Serialize(content.JsonWriter, document.RootElement); + } +#endif + return content; + } + } +} diff --git a/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/Internal/Utf8JsonRequestContent.cs b/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/Internal/Utf8JsonRequestContent.cs new file mode 100644 index 000000000000..713b6120236a --- /dev/null +++ b/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/Internal/Utf8JsonRequestContent.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.IO; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Azure.Core; + +namespace Azure.Monitor.Ingestion +{ + internal class Utf8JsonRequestContent : RequestContent + { + private readonly MemoryStream _stream; + private readonly RequestContent _content; + + public Utf8JsonRequestContent() + { + _stream = new MemoryStream(); + _content = Create(_stream); + JsonWriter = new Utf8JsonWriter(_stream); + } + + public Utf8JsonWriter JsonWriter { get; } + + public override async Task WriteToAsync(Stream stream, CancellationToken cancellationToken = default) + { + await JsonWriter.FlushAsync().ConfigureAwait(false); + await _content.WriteToAsync(stream, cancellationToken).ConfigureAwait(false); + } + + public override void WriteTo(Stream stream, CancellationToken cancellationToken = default) + { + JsonWriter.Flush(); + _content.WriteTo(stream, cancellationToken); + } + + public override bool TryComputeLength(out long length) + { + length = JsonWriter.BytesCommitted + JsonWriter.BytesPending; + return true; + } + + public override void Dispose() + { + JsonWriter.Dispose(); + _content.Dispose(); + _stream.Dispose(); + } + } +} diff --git a/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/LogsIngestionClient.cs b/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/LogsIngestionClient.cs index 15f20ed11e21..754230806d16 100644 --- a/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/LogsIngestionClient.cs +++ b/sdk/monitor/Azure.Monitor.Ingestion/src/Generated/LogsIngestionClient.cs @@ -6,6 +6,8 @@ #nullable disable using System; +using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Azure.Core; using Azure.Core.Pipeline; @@ -13,10 +15,10 @@ namespace Azure.Monitor.Ingestion { // Data plane generated client. - /// The LogsIngestion service client. + /// Azure Monitor data collection client. public partial class LogsIngestionClient { - private static readonly string[] AuthorizationScopes = new string[] { "https://monitor.azure.com//.default" }; + private static readonly string[] AuthorizationScopes = new string[] { "https://monitor.azure.com/.default" }; private readonly TokenCredential _tokenCredential; private readonly HttpPipeline _pipeline; private readonly Uri _endpoint; @@ -29,27 +31,74 @@ public partial class LogsIngestionClient public virtual HttpPipeline Pipeline => _pipeline; /// Initializes a new instance of LogsIngestionClient. - /// The Data Collection Endpoint for the Data Collection Rule, for example https://dce-name.eastus-2.ingest.monitor.azure.com. + /// The to use. /// A credential used to authenticate to an Azure Service. /// or is null. public LogsIngestionClient(Uri endpoint, TokenCredential credential) : this(endpoint, credential, new LogsIngestionClientOptions()) { } + /// Ingestion API used to directly ingest data using Data Collection Rules. + /// The immutable ID of the Data Collection Rule resource. + /// The streamDeclaration name as defined in the Data Collection Rule. + /// The array of objects matching the schema defined by the provided stream. + /// The content encoding of the request body which is always 'gzip'. + /// The cancellation token to use. + /// , or is null. + /// or is an empty string, and was expected to be non-empty. + /// + public virtual async Task UploadAsync(string ruleId, string streamName, IEnumerable> body, string contentEncoding = null, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(ruleId, nameof(ruleId)); + Argument.AssertNotNullOrEmpty(streamName, nameof(streamName)); + Argument.AssertNotNull(body, nameof(body)); + + using RequestContent content = RequestContentHelper.FromEnumerable(body); + RequestContext context = FromCancellationToken(cancellationToken); + Response response = await UploadAsync(ruleId, streamName, content, contentEncoding, context).ConfigureAwait(false); + return response; + } + + /// Ingestion API used to directly ingest data using Data Collection Rules. + /// The immutable ID of the Data Collection Rule resource. + /// The streamDeclaration name as defined in the Data Collection Rule. + /// The array of objects matching the schema defined by the provided stream. + /// The content encoding of the request body which is always 'gzip'. + /// The cancellation token to use. + /// , or is null. + /// or is an empty string, and was expected to be non-empty. + /// + public virtual Response Upload(string ruleId, string streamName, IEnumerable> body, string contentEncoding = null, CancellationToken cancellationToken = default) + { + Argument.AssertNotNullOrEmpty(ruleId, nameof(ruleId)); + Argument.AssertNotNullOrEmpty(streamName, nameof(streamName)); + Argument.AssertNotNull(body, nameof(body)); + + using RequestContent content = RequestContentHelper.FromEnumerable(body); + RequestContext context = FromCancellationToken(cancellationToken); + Response response = Upload(ruleId, streamName, content, contentEncoding, context); + return response; + } + /// - /// [Protocol Method] Ingestion API used to directly ingest data using Data Collection Rules + /// [Protocol Method] Ingestion API used to directly ingest data using Data Collection Rules. /// /// /// /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. /// /// + /// + /// + /// Please try the simpler convenience overload with strongly typed models first. + /// + /// /// /// - /// The immutable Id of the Data Collection Rule resource. + /// The immutable ID of the Data Collection Rule resource. /// The streamDeclaration name as defined in the Data Collection Rule. /// The content to send as the body of the request. - /// If content is already gzipped, put "gzip". Default behavior is to gzip all input. + /// The content encoding of the request body which is always 'gzip'. /// The request context, which can override default behaviors of the client pipeline on a per-call basis. /// , or is null. /// or is an empty string, and was expected to be non-empty. @@ -77,19 +126,24 @@ public virtual async Task UploadAsync(string ruleId, string streamName } /// - /// [Protocol Method] Ingestion API used to directly ingest data using Data Collection Rules + /// [Protocol Method] Ingestion API used to directly ingest data using Data Collection Rules. /// /// /// /// This protocol method allows explicit creation of the request and processing of the response for advanced scenarios. /// /// + /// + /// + /// Please try the simpler convenience overload with strongly typed models first. + /// + /// /// /// - /// The immutable Id of the Data Collection Rule resource. + /// The immutable ID of the Data Collection Rule resource. /// The streamDeclaration name as defined in the Data Collection Rule. /// The content to send as the body of the request. - /// If content is already gzipped, put "gzip". Default behavior is to gzip all input. + /// The content encoding of the request body which is always 'gzip'. /// The request context, which can override default behaviors of the client pipeline on a per-call basis. /// , or is null. /// or is an empty string, and was expected to be non-empty. @@ -116,6 +170,17 @@ public virtual Response Upload(string ruleId, string streamName, RequestContent } } + private static RequestContext DefaultRequestContext = new RequestContext(); + internal static RequestContext FromCancellationToken(CancellationToken cancellationToken = default) + { + if (!cancellationToken.CanBeCanceled) + { + return DefaultRequestContext; + } + + return new RequestContext() { CancellationToken = cancellationToken }; + } + private static ResponseClassifier _responseClassifier204; private static ResponseClassifier ResponseClassifier204 => _responseClassifier204 ??= new StatusCodeClassifier(stackalloc ushort[] { 204 }); } diff --git a/sdk/monitor/Azure.Monitor.Ingestion/src/autorest.md b/sdk/monitor/Azure.Monitor.Ingestion/src/autorest.md deleted file mode 100644 index 5ed752539b56..000000000000 --- a/sdk/monitor/Azure.Monitor.Ingestion/src/autorest.md +++ /dev/null @@ -1,35 +0,0 @@ -# Azure SDK Code Generation for Data Plane - -Run `dotnet build /t:GenerateCode` to generate code. - -### AutoRest Configuration -> see https://aka.ms/autorest - -``` yaml -input-file: -- https://github.com/Azure/azure-rest-api-specs/blob/f07297ce913bfc911470a86436e73c9aceec0587/specification/monitor/data-plane/ingestion/stable/2023-01-01/DataCollectionRules.json -security: AADToken -security-scopes: https://monitor.azure.com//.default -``` - -### Renames parameter in Upload methods to streamName -``` yaml -directive: -- from: swagger-document - where: $.paths["/dataCollectionRules/{ruleId}/streams/{stream}"].post.parameters[1] - transform: $["x-ms-client-name"] = "streamName"; -``` -### Updates type of endpoint in LogsIngestionClient to Uri -``` yaml -directive: -- from: swagger-document - where: $.parameters.Endpoint - transform: $.format = "url"; -``` -### Updates parameter description in DPG Upload/UploadAsync methods -``` yaml -directive: -- from: swagger-document - where: $.paths["/dataCollectionRules/{ruleId}/streams/{stream}"].post.parameters[3] - transform: $["description"] = "If content is already gzipped, put \"gzip\". Default behavior is to gzip all input"; -``` diff --git a/sdk/monitor/Azure.Monitor.Ingestion/tests/Generated/Samples/Samples_LogsIngestionClient.cs b/sdk/monitor/Azure.Monitor.Ingestion/tests/Generated/Samples/Samples_LogsIngestionClient.cs index 083049e9b44a..e8e9688aa1b9 100644 --- a/sdk/monitor/Azure.Monitor.Ingestion/tests/Generated/Samples/Samples_LogsIngestionClient.cs +++ b/sdk/monitor/Azure.Monitor.Ingestion/tests/Generated/Samples/Samples_LogsIngestionClient.cs @@ -6,6 +6,7 @@ #nullable disable using System; +using System.Collections.Generic; using System.Threading.Tasks; using Azure.Core; using Azure.Identity; @@ -17,7 +18,7 @@ public partial class Samples_LogsIngestionClient { [Test] [Ignore("Only validating compilation of examples")] - public void Example_Upload_ShortVersion() + public void Example_LogsIngestionClient_Upload_IngestDataUsingADataCollectionRule() { Uri endpoint = new Uri(""); TokenCredential credential = new DefaultAzureCredential(); @@ -25,16 +26,43 @@ public void Example_Upload_ShortVersion() using RequestContent content = RequestContent.Create(new object[] { -new object() +new +{ +Time = "2021-12-08T23:51:14.1104269Z", +Computer = "Computer1", +AdditionalContext = new Dictionary +{ +["$id"] = "55", +["InstanceName"] = "user1", +["TimeZone"] = "Pacific Time", +["Level"] = 4, +["CounterName"] = "AppMetric1", +["CounterValue"] = 15.3F +}, +}, +new +{ +Time = "2021-12-08T23:51:14.1104269Z", +Computer = "Computer2", +AdditionalContext = new Dictionary +{ +["$id"] = "61", +["InstanceName"] = "user2", +["TimeZone"] = "Central Time", +["Level"] = 3, +["CounterName"] = "AppMetric1", +["CounterValue"] = 23.5F +}, +} }); - Response response = client.Upload("", "", content); + Response response = client.Upload("c512b59c-4005-40e2-b13d-ac29cfb46af4", "Custom-TestStream", content); Console.WriteLine(response.Status); } [Test] [Ignore("Only validating compilation of examples")] - public async Task Example_Upload_ShortVersion_Async() + public async Task Example_LogsIngestionClient_Upload_IngestDataUsingADataCollectionRule_Async() { Uri endpoint = new Uri(""); TokenCredential credential = new DefaultAzureCredential(); @@ -42,45 +70,120 @@ public async Task Example_Upload_ShortVersion_Async() using RequestContent content = RequestContent.Create(new object[] { -new object() +new +{ +Time = "2021-12-08T23:51:14.1104269Z", +Computer = "Computer1", +AdditionalContext = new Dictionary +{ +["$id"] = "55", +["InstanceName"] = "user1", +["TimeZone"] = "Pacific Time", +["Level"] = 4, +["CounterName"] = "AppMetric1", +["CounterValue"] = 15.3F +}, +}, +new +{ +Time = "2021-12-08T23:51:14.1104269Z", +Computer = "Computer2", +AdditionalContext = new Dictionary +{ +["$id"] = "61", +["InstanceName"] = "user2", +["TimeZone"] = "Central Time", +["Level"] = 3, +["CounterName"] = "AppMetric1", +["CounterValue"] = 23.5F +}, +} }); - Response response = await client.UploadAsync("", "", content); + Response response = await client.UploadAsync("c512b59c-4005-40e2-b13d-ac29cfb46af4", "Custom-TestStream", content); Console.WriteLine(response.Status); } [Test] [Ignore("Only validating compilation of examples")] - public void Example_Upload_AllParameters() + public void Example_LogsIngestionClient_Upload_IngestDataUsingADataCollectionRule_Convenience() { Uri endpoint = new Uri(""); TokenCredential credential = new DefaultAzureCredential(); LogsIngestionClient client = new LogsIngestionClient(endpoint, credential); - using RequestContent content = RequestContent.Create(new object[] + Response response = client.Upload("c512b59c-4005-40e2-b13d-ac29cfb46af4", "Custom-TestStream", new IDictionary[] { -new object() +new Dictionary +{ +["Time"] = BinaryData.FromObjectAsJson("2021-12-08T23:51:14.1104269Z"), +["Computer"] = BinaryData.FromObjectAsJson("Computer1"), +["AdditionalContext"] = BinaryData.FromObjectAsJson(new Dictionary +{ +["$id"] = "55", +["InstanceName"] = "user1", +["TimeZone"] = "Pacific Time", +["Level"] = 4, +["CounterName"] = "AppMetric1", +["CounterValue"] = 15.3F +}) +}, +new Dictionary +{ +["Time"] = BinaryData.FromObjectAsJson("2021-12-08T23:51:14.1104269Z"), +["Computer"] = BinaryData.FromObjectAsJson("Computer2"), +["AdditionalContext"] = BinaryData.FromObjectAsJson(new Dictionary +{ +["$id"] = "61", +["InstanceName"] = "user2", +["TimeZone"] = "Central Time", +["Level"] = 3, +["CounterName"] = "AppMetric1", +["CounterValue"] = 23.5F +}) +} }); - Response response = client.Upload("", "", content, contentEncoding: ""); - - Console.WriteLine(response.Status); } [Test] [Ignore("Only validating compilation of examples")] - public async Task Example_Upload_AllParameters_Async() + public async Task Example_LogsIngestionClient_Upload_IngestDataUsingADataCollectionRule_Convenience_Async() { Uri endpoint = new Uri(""); TokenCredential credential = new DefaultAzureCredential(); LogsIngestionClient client = new LogsIngestionClient(endpoint, credential); - using RequestContent content = RequestContent.Create(new object[] + Response response = await client.UploadAsync("c512b59c-4005-40e2-b13d-ac29cfb46af4", "Custom-TestStream", new IDictionary[] { -new object() +new Dictionary +{ +["Time"] = BinaryData.FromObjectAsJson("2021-12-08T23:51:14.1104269Z"), +["Computer"] = BinaryData.FromObjectAsJson("Computer1"), +["AdditionalContext"] = BinaryData.FromObjectAsJson(new Dictionary +{ +["$id"] = "55", +["InstanceName"] = "user1", +["TimeZone"] = "Pacific Time", +["Level"] = 4, +["CounterName"] = "AppMetric1", +["CounterValue"] = 15.3F +}) +}, +new Dictionary +{ +["Time"] = BinaryData.FromObjectAsJson("2021-12-08T23:51:14.1104269Z"), +["Computer"] = BinaryData.FromObjectAsJson("Computer2"), +["AdditionalContext"] = BinaryData.FromObjectAsJson(new Dictionary +{ +["$id"] = "61", +["InstanceName"] = "user2", +["TimeZone"] = "Central Time", +["Level"] = 3, +["CounterName"] = "AppMetric1", +["CounterValue"] = 23.5F +}) +} }); - Response response = await client.UploadAsync("", "", content, contentEncoding: ""); - - Console.WriteLine(response.Status); } } } diff --git a/sdk/monitor/Azure.Monitor.Ingestion/tsp-location.yaml b/sdk/monitor/Azure.Monitor.Ingestion/tsp-location.yaml new file mode 100644 index 000000000000..a1a74f58b8aa --- /dev/null +++ b/sdk/monitor/Azure.Monitor.Ingestion/tsp-location.yaml @@ -0,0 +1,4 @@ +directory: specification/monitor/Ingestion +commit: 20b8ba31ffb3814d9001ad9618dc48bf3def925d +repo: Azure/azure-rest-api-specs +additionalDirectories: