Add base64 url encoded support (#372)
This commit is contained in:
Родитель
3cd5465b41
Коммит
ed95c33eca
|
@ -1,15 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
namespace AutoRest.CSharp.V3.ClientModels
|
||||
{
|
||||
internal class BinaryTypeReference : ClientTypeReference
|
||||
{
|
||||
public BinaryTypeReference(bool isNullable)
|
||||
{
|
||||
IsNullable = isNullable;
|
||||
}
|
||||
|
||||
public override bool IsNullable { get; }
|
||||
}
|
||||
}
|
|
@ -23,12 +23,15 @@ namespace AutoRest.CSharp.V3.ClientModels
|
|||
return;
|
||||
}
|
||||
|
||||
var expectedType = type switch
|
||||
Type expectedType;
|
||||
if (type is FrameworkTypeReference frameworkType)
|
||||
{
|
||||
BinaryTypeReference _ => typeof(byte[]),
|
||||
FrameworkTypeReference frameworkType => frameworkType.Type,
|
||||
_ => throw new InvalidOperationException("Unexpected type kind")
|
||||
};
|
||||
expectedType = frameworkType.Type;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Unexpected type kind");
|
||||
}
|
||||
|
||||
if (value.GetType() != expectedType)
|
||||
{
|
||||
|
|
|
@ -15,10 +15,8 @@ namespace AutoRest.CSharp.V3.ClientModels
|
|||
public static ClientTypeReference CreateType(Schema schema, bool isNullable) => schema switch
|
||||
{
|
||||
ConstantSchema constantSchema => CreateType(constantSchema.ValueType, isNullable),
|
||||
BinarySchema _ => (ClientTypeReference)new BinaryTypeReference(isNullable),
|
||||
ByteArraySchema _ => new BinaryTypeReference(isNullable),
|
||||
//https://devblogs.microsoft.com/dotnet/do-more-with-patterns-in-c-8-0/
|
||||
{ Type: AllSchemaTypes.Binary } => new BinaryTypeReference(false),
|
||||
BinarySchema _ => new FrameworkTypeReference(typeof(byte[]), isNullable),
|
||||
ByteArraySchema _ => new FrameworkTypeReference(typeof(byte[]), isNullable),
|
||||
ArraySchema array => new CollectionTypeReference(CreateType(array.ElementType, false), isNullable),
|
||||
DictionarySchema dictionary => new DictionaryTypeReference(new FrameworkTypeReference(typeof(string)), CreateType(dictionary.ElementType, false), isNullable),
|
||||
NumberSchema number => new FrameworkTypeReference(ToFrameworkNumberType(number), isNullable),
|
||||
|
@ -30,10 +28,12 @@ namespace AutoRest.CSharp.V3.ClientModels
|
|||
{
|
||||
var normalizedValue = type switch
|
||||
{
|
||||
BinaryTypeReference _ when value is string base64String => Convert.FromBase64String(base64String),
|
||||
FrameworkTypeReference frameworkType when
|
||||
frameworkType.Type == typeof(DateTimeOffset) &&
|
||||
value is string dateTimeString => DateTimeOffset.Parse(dateTimeString, styles: DateTimeStyles.AssumeUniversal),
|
||||
frameworkType.Type == typeof(byte[]) &&
|
||||
value is string base64String => Convert.FromBase64String(base64String),
|
||||
FrameworkTypeReference frameworkType when
|
||||
frameworkType.Type == typeof(DateTimeOffset) &&
|
||||
value is string dateTimeString => DateTimeOffset.Parse(dateTimeString, styles: DateTimeStyles.AssumeUniversal),
|
||||
FrameworkTypeReference frameworkType => Convert.ChangeType(value, frameworkType.Type),
|
||||
_ => null
|
||||
};
|
||||
|
@ -42,6 +42,7 @@ namespace AutoRest.CSharp.V3.ClientModels
|
|||
|
||||
public static SerializationFormat GetSerializationFormat(Schema schema) => schema switch
|
||||
{
|
||||
ByteArraySchema byteArraySchema when byteArraySchema.Format == ByteArraySchemaFormat.Base64url => SerializationFormat.Bytes_Base64Url,
|
||||
UnixTimeSchema _ => SerializationFormat.DateTime_Unix,
|
||||
DateTimeSchema dateTimeSchema when dateTimeSchema.Format == DateTimeSchemaFormat.DateTime => SerializationFormat.DateTime_ISO8601,
|
||||
DateTimeSchema dateTimeSchema when dateTimeSchema.Format == DateTimeSchemaFormat.DateTimeRfc1123 => SerializationFormat.DateTime_RFC1123,
|
||||
|
@ -65,6 +66,7 @@ namespace AutoRest.CSharp.V3.ClientModels
|
|||
AllSchemaTypes.Uri => typeof(Uri),
|
||||
AllSchemaTypes.Uuid => typeof(string),
|
||||
AllSchemaTypes.Any => typeof(object),
|
||||
AllSchemaTypes.Binary => typeof(byte[]),
|
||||
_ => null
|
||||
};
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace AutoRest.CSharp.V3.ClientModels
|
|||
DateTime_ISO8601,
|
||||
DateTime_Unix,
|
||||
Date_ISO8601,
|
||||
Duration_ISO8601
|
||||
Duration_ISO8601,
|
||||
Bytes_Base64Url
|
||||
}
|
||||
}
|
||||
|
|
|
@ -186,10 +186,7 @@ namespace AutoRest.CSharp.V3.CodeGen
|
|||
d = d.ToUniversalTime();
|
||||
writer.Append($"new {typeof(DateTimeOffset)}({d.Year:L}, {d.Month:L}, {d.Day:L} ,{d.Hour:L}, {d.Minute:L}, {d.Second:L}, {d.Millisecond:L}, {typeof(TimeSpan)}.{nameof(TimeSpan.Zero)})");
|
||||
break;
|
||||
case FrameworkTypeReference _:
|
||||
writer.Literal(constant.Value);
|
||||
break;
|
||||
case BinaryTypeReference _:
|
||||
case FrameworkTypeReference frameworkType when frameworkType.Type == typeof(byte[]):
|
||||
var value = (byte[])constant.Value;
|
||||
writer.Append($"new byte[] {{");
|
||||
foreach (byte b in value)
|
||||
|
@ -198,6 +195,9 @@ namespace AutoRest.CSharp.V3.CodeGen
|
|||
}
|
||||
writer.Append($"}}");
|
||||
break;
|
||||
case FrameworkTypeReference _:
|
||||
writer.Literal(constant.Value);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException("Unknown constant type");
|
||||
}
|
||||
|
@ -238,6 +238,12 @@ namespace AutoRest.CSharp.V3.CodeGen
|
|||
|
||||
private void WriteSerializationFormat(CodeWriter writer, SerializationFormat format)
|
||||
{
|
||||
if (format == SerializationFormat.Bytes_Base64Url)
|
||||
{
|
||||
// base64url is the only options for paths ns queries
|
||||
return;
|
||||
}
|
||||
|
||||
var formatSpecifier = format.ToFormatSpecifier();
|
||||
if (formatSpecifier != null)
|
||||
{
|
||||
|
|
|
@ -87,10 +87,6 @@ namespace AutoRest.CSharp.V3.CodeGen
|
|||
}
|
||||
return;
|
||||
|
||||
case BinaryTypeReference _:
|
||||
writer.Line($"{writerName}.WriteBase64StringValue({name});");
|
||||
return;
|
||||
|
||||
case FrameworkTypeReference frameworkTypeReference:
|
||||
var frameworkType = frameworkTypeReference.Type;
|
||||
bool writeFormat = false;
|
||||
|
@ -118,6 +114,11 @@ namespace AutoRest.CSharp.V3.CodeGen
|
|||
{
|
||||
writer.AppendRaw("WriteBooleanValue");
|
||||
}
|
||||
else if (frameworkType == typeof(byte[]))
|
||||
{
|
||||
writer.AppendRaw("WriteBase64StringValue");
|
||||
writeFormat = true;
|
||||
}
|
||||
else if (frameworkType == typeof(DateTimeOffset) ||
|
||||
frameworkType == typeof(DateTime) ||
|
||||
frameworkType == typeof(TimeSpan))
|
||||
|
@ -289,10 +290,6 @@ namespace AutoRest.CSharp.V3.CodeGen
|
|||
writer.ToDeserializeCall(schemaTypeReference, typeFactory, element);
|
||||
return;
|
||||
|
||||
case BinaryTypeReference _:
|
||||
writer.Append($"{element}.GetBytesFromBase64()");
|
||||
return;
|
||||
|
||||
case FrameworkTypeReference frameworkTypeReference:
|
||||
bool includeFormat = false;
|
||||
var frameworkType = frameworkTypeReference.Type;
|
||||
|
@ -318,6 +315,12 @@ namespace AutoRest.CSharp.V3.CodeGen
|
|||
if (frameworkType == typeof(string))
|
||||
writer.AppendRaw("GetString");
|
||||
|
||||
if (frameworkType == typeof(byte[]))
|
||||
{
|
||||
writer.AppendRaw("GetBytesFromBase64");
|
||||
includeFormat = true;
|
||||
}
|
||||
|
||||
if (frameworkType == typeof(DateTimeOffset))
|
||||
{
|
||||
writer.AppendRaw("GetDateTimeOffset");
|
||||
|
@ -368,6 +371,7 @@ namespace AutoRest.CSharp.V3.CodeGen
|
|||
SerializationFormat.DateTime_ISO8601 => "S",
|
||||
SerializationFormat.Date_ISO8601 => "D",
|
||||
SerializationFormat.DateTime_Unix => "U",
|
||||
SerializationFormat.Bytes_Base64Url => "U",
|
||||
SerializationFormat.Duration_ISO8601 => "P",
|
||||
_ => null
|
||||
};
|
||||
|
|
|
@ -32,7 +32,6 @@ namespace AutoRest.CSharp.V3.CodeGen
|
|||
CollectionTypeReference arraySchema => ArrayTypeInfo(arraySchema, useConcrete, useInput),
|
||||
DictionaryTypeReference dictionarySchema => DictionaryTypeInfo(dictionarySchema, useConcrete),
|
||||
SchemaTypeReference schemaTypeReference => DefaultTypeInfo(schemaTypeReference),
|
||||
BinaryTypeReference binary => new CSharpType(typeof(byte[]), isNullable: binary.IsNullable),
|
||||
FrameworkTypeReference frameworkTypeReference => new CSharpType(frameworkTypeReference.Type, isNullable: frameworkTypeReference.IsNullable),
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
|
|
|
@ -2618,4 +2618,4 @@ namespace AutoRest.CSharp.V3.Pipeline.Generated
|
|||
[System.Runtime.Serialization.EnumMember(Value = @"default")]
|
||||
Default = 62,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,12 @@ namespace Azure.Core
|
|||
}
|
||||
}
|
||||
|
||||
public static byte[] GetBytesFromBase64(in this JsonElement element, string format) => format switch
|
||||
{
|
||||
"U" => TypeFormatters.FromBase64UrlString(element.GetString()),
|
||||
_ => throw new ArgumentException("Format is not supported", nameof(format))
|
||||
};
|
||||
|
||||
public static DateTimeOffset GetDateTimeOffset(in this JsonElement element, string format) => format switch
|
||||
{
|
||||
"D" => element.GetDateTimeOffset(),
|
||||
|
|
|
@ -59,5 +59,53 @@ namespace Azure.Core
|
|||
|
||||
return new string(output, 0, i);
|
||||
}
|
||||
|
||||
public static byte[] FromBase64UrlString(string value)
|
||||
{
|
||||
var paddingCharsToAdd = GetNumBase64PaddingCharsToAddForDecode(value.Length);
|
||||
|
||||
var output = new char[value.Length + paddingCharsToAdd];
|
||||
|
||||
int i;
|
||||
for (i = 0; i < value.Length; i++)
|
||||
{
|
||||
var 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);
|
||||
}
|
||||
|
||||
|
||||
private static int GetNumBase64PaddingCharsToAddForDecode(int inputLength)
|
||||
{
|
||||
switch (inputLength % 4)
|
||||
{
|
||||
case 0:
|
||||
return 0;
|
||||
case 2:
|
||||
return 2;
|
||||
case 3:
|
||||
return 1;
|
||||
default:
|
||||
throw new InvalidOperationException("Malformed input");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,19 @@ namespace Azure.Core
|
|||
{
|
||||
value.Write(writer);
|
||||
}
|
||||
|
||||
public static void WriteBase64StringValue(this Utf8JsonWriter writer, byte[] value, string format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case "U":
|
||||
writer.WriteStringValue(TypeFormatters.ToBase64UrlString(value));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Format is not supported", nameof(format));
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteObjectValue(this Utf8JsonWriter writer, object value)
|
||||
{
|
||||
switch (value)
|
||||
|
|
|
@ -60,12 +60,15 @@ namespace AutoRest.TestServer.Tests
|
|||
});
|
||||
|
||||
[Test]
|
||||
[Ignore("https://github.com/Azure/autorest.csharp/issues/367")]
|
||||
public Task GetArrayBase64Url() => Test(async (host, pipeline) =>
|
||||
{
|
||||
var result = await ArrayOperations.GetBase64UrlAsync(ClientDiagnostics, pipeline, host);
|
||||
|
||||
CollectionAssert.AreEqual(new[] { new object[] { "1", "2", "3" }, new object[] { "4", "5", "6" }, new object[] { "7", "8", "9" } }, result.Value);
|
||||
var values = result.Value.ToArray();
|
||||
|
||||
CollectionAssert.AreEqual(new byte[] { 97, 32, 115, 116, 114, 105, 110, 103, 32, 116, 104, 97, 116, 32, 103, 101, 116, 115, 32, 101, 110, 99, 111, 100, 101, 100, 32, 119, 105, 116, 104, 32, 98, 97, 115, 101, 54, 52, 117, 114, 108 }, values[0]);
|
||||
CollectionAssert.AreEqual(new byte[] { 116, 101, 115, 116, 32, 115, 116, 114, 105, 110, 103 }, values[1]);
|
||||
CollectionAssert.AreEqual(new byte[] { 76, 111, 114, 101, 109, 32, 105, 112, 115, 117, 109 }, values[2]);
|
||||
});
|
||||
|
||||
[Test]
|
||||
|
|
|
@ -29,33 +29,30 @@ namespace AutoRest.TestServer.Tests
|
|||
});
|
||||
|
||||
[Test]
|
||||
[Ignore("base64 not implemented")]
|
||||
[Ignore("https://github.com/Azure/autorest.csharp/issues/289")]
|
||||
public Task GetStringNullBase64UrlEncoding() => Test(async (host, pipeline) =>
|
||||
{
|
||||
var result = await StringOperations.GetNullBase64UrlEncodedAsync(ClientDiagnostics, pipeline, host);
|
||||
Assert.AreEqual(null, result.Value);
|
||||
Assert.AreEqual(new byte[] { }, result.Value);
|
||||
});
|
||||
|
||||
[Test]
|
||||
[Ignore("base64 not implemented")]
|
||||
public Task GetStringBase64Encoded() => Test(async (host, pipeline) =>
|
||||
{
|
||||
var result = await StringOperations.GetBase64EncodedAsync(ClientDiagnostics, pipeline, host);
|
||||
Assert.AreEqual("a string that gets encoded with base64", result.Value);
|
||||
Assert.AreEqual(new byte[] { 97, 32, 115, 116, 114, 105, 110, 103, 32, 116, 104, 97, 116, 32, 103, 101, 116, 115, 32, 101, 110, 99, 111, 100, 101, 100, 32, 119, 105, 116, 104, 32, 98, 97, 115, 101, 54, 52}, result.Value);
|
||||
});
|
||||
|
||||
[Test]
|
||||
[Ignore("base64 not implemented")]
|
||||
public Task GetStringBase64UrlEncoded() => Test(async (host, pipeline) =>
|
||||
{
|
||||
var result = await StringOperations.GetBase64UrlEncodedAsync(ClientDiagnostics, pipeline, host);
|
||||
Assert.AreEqual("a string that gets encoded with base64", result.Value);
|
||||
Assert.AreEqual(new byte[] { 97, 32, 115, 116, 114, 105, 110, 103, 32, 116, 104, 97, 116, 32, 103, 101, 116, 115, 32, 101, 110, 99, 111, 100, 101, 100, 32, 119, 105, 116, 104, 32, 98, 97, 115, 101, 54, 52, 117, 114, 108 }, result.Value);
|
||||
});
|
||||
|
||||
[Test]
|
||||
[Ignore("base64 not implemented")]
|
||||
public Task PutStringBase64UrlEncoded() => TestStatus(async (host, pipeline) =>
|
||||
await StringOperations.PutBase64UrlEncodedAsync(ClientDiagnostics, pipeline, new byte[0], host));
|
||||
await StringOperations.PutBase64UrlEncodedAsync(ClientDiagnostics, pipeline, new byte[] { 97, 32, 115, 116, 114, 105, 110, 103, 32, 116, 104, 97, 116, 32, 103, 101, 116, 115, 32, 101, 110, 99, 111, 100, 101, 100, 32, 119, 105, 116, 104, 32, 98, 97, 115, 101, 54, 52, 117, 114, 108 }, host));
|
||||
|
||||
[Test]
|
||||
public Task PutStringMultiByteCharacters() => Test(async (host, pipeline) =>
|
||||
|
|
|
@ -2014,7 +2014,7 @@ namespace body_array
|
|||
ICollection<Byte[]> value = new List<Byte[]>();
|
||||
foreach (var item in document.RootElement.EnumerateArray())
|
||||
{
|
||||
value.Add(item.GetBytesFromBase64());
|
||||
value.Add(item.GetBytesFromBase64("U"));
|
||||
}
|
||||
return Response.FromValue(value, response);
|
||||
}
|
||||
|
|
|
@ -1859,7 +1859,7 @@ namespace body_dictionary
|
|||
IDictionary<string, Byte[]> value = new Dictionary<string, Byte[]>();
|
||||
foreach (var property in document.RootElement.EnumerateObject())
|
||||
{
|
||||
value.Add(property.Name, property.Value.GetBytesFromBase64());
|
||||
value.Add(property.Name, property.Value.GetBytesFromBase64("U"));
|
||||
}
|
||||
return Response.FromValue(value, response);
|
||||
}
|
||||
|
|
|
@ -340,7 +340,7 @@ namespace body_string
|
|||
case 200:
|
||||
{
|
||||
using var document = await JsonDocument.ParseAsync(response.ContentStream, default, cancellationToken).ConfigureAwait(false);
|
||||
var value = document.RootElement.GetBytesFromBase64();
|
||||
var value = document.RootElement.GetBytesFromBase64("U");
|
||||
return Response.FromValue(value, response);
|
||||
}
|
||||
default:
|
||||
|
@ -374,7 +374,7 @@ namespace body_string
|
|||
case 200:
|
||||
{
|
||||
using var document = await JsonDocument.ParseAsync(response.ContentStream, default, cancellationToken).ConfigureAwait(false);
|
||||
var value = document.RootElement.GetBytesFromBase64();
|
||||
var value = document.RootElement.GetBytesFromBase64("U");
|
||||
return Response.FromValue(value, response);
|
||||
}
|
||||
default:
|
||||
|
@ -408,7 +408,7 @@ namespace body_string
|
|||
request.Uri.AppendPath("/string/base64UrlEncoding", false);
|
||||
request.Headers.Add("Content-Type", "application/json");
|
||||
using var content = new Utf8JsonRequestContent();
|
||||
content.JsonWriter.WriteBase64StringValue(stringBody);
|
||||
content.JsonWriter.WriteBase64StringValue(stringBody, "U");
|
||||
request.Content = content;
|
||||
var response = await pipeline.SendRequestAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
switch (response.Status)
|
||||
|
@ -446,7 +446,7 @@ namespace body_string
|
|||
case 200:
|
||||
{
|
||||
using var document = await JsonDocument.ParseAsync(response.ContentStream, default, cancellationToken).ConfigureAwait(false);
|
||||
var value = document.RootElement.GetBytesFromBase64();
|
||||
var value = document.RootElement.GetBytesFromBase64("U");
|
||||
return Response.FromValue(value, response);
|
||||
}
|
||||
default:
|
||||
|
|
Загрузка…
Ссылка в новой задаче