Add base64 url encoded support (#372)

This commit is contained in:
Pavel Krymets 2019-12-23 10:19:17 -08:00 коммит произвёл GitHub
Родитель 3cd5465b41
Коммит ed95c33eca
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
16 изменённых файлов: 125 добавлений и 58 удалений

Просмотреть файл

@ -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: