Ruby - Move serialization/deserialization into clientruntime from sdk (#1106)

This commit is contained in:
Vishrut Shah 2016-06-15 17:42:43 -07:00 коммит произвёл GitHub
Родитель b4a493df69
Коммит 6b71ddaf37
18 изменённых файлов: 1077 добавлений и 581 удалений

3
.gitignore поставляемый
Просмотреть файл

@ -56,6 +56,9 @@ ipch/
*.psess
*.vsp
# VS Code settings
*.vscode
# Code analysis
*.CodeAnalysisLog.xml

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

@ -82,7 +82,7 @@ namespace Microsoft.Rest.Generator.Azure.Ruby
{
var builder = new IndentedStringBuilder(" ");
string serializationLogic = type.DeserializeType(this.Scope, variableName);
string serializationLogic = GetDeserializationString(type, variableName, variableName);
return builder.AppendLine(serializationLogic).ToString();
}
@ -119,9 +119,9 @@ namespace Microsoft.Rest.Generator.Azure.Ruby
get
{
return new List<string>
{
"MsRestAzure"
};
{
"MsRestAzure"
};
}
}

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

@ -2,10 +2,10 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.Rest.Generator.Azure.Ruby.Templates;
using Microsoft.Rest.Generator.ClientModel;
using Microsoft.Rest.Generator.Ruby;
using Microsoft.Rest.Generator.Utilities;
using Microsoft.Rest.Generator.Azure.Ruby.Templates;
namespace Microsoft.Rest.Generator.Azure.Ruby
{
@ -45,36 +45,6 @@ namespace Microsoft.Rest.Generator.Azure.Ruby
return string.Empty;
}
/// <summary>
/// Generates code for model serialization.
/// </summary>
/// <param name="variableName">Variable serialize model from.</param>
/// <param name="type">The type of the model.</param>
/// <returns>The code for serialization in string format.</returns>
public override string SerializeProperty(string variableName, IType type)
{
var builder = new IndentedStringBuilder(" ");
string serializationLogic = type.AzureSerializeType(this.Scope, variableName);
builder.AppendLine(serializationLogic);
return builder.ToString();
}
/// <summary>
/// Generates code for model deserialization.
/// </summary>
/// <param name="variableName">Variable deserialize model from.</param>
/// <param name="type">The type of the model.</param>
/// <returns>The code for вуserialization in string format.</returns>
public override string DeserializeProperty(string variableName, IType type)
{
var builder = new IndentedStringBuilder(" ");
string serializationLogic = type.AzureDeserializeType(this.Scope, variableName);
return builder.AppendLine(serializationLogic).ToString();
}
/// <summary>
/// Gets the list of modules/classes which need to be included.

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

@ -37,7 +37,10 @@ def @(Model.Name)(@(Model.MethodParameterDeclaration))
promise = promise.then do |response|
# Defining deserialization method.
deserialize_method = lambda do |parsed_response|
@(Model.DeserializePollingResponse("parsed_response", Model.ReturnType.Body))
@if(Model.ReturnType.Body != null)
{
@:@(Model.DeserializePollingResponse("parsed_response", Model.ReturnType.Body))
}
end
@EmptyLine
@ -79,7 +82,10 @@ def @(Model.Name)(@(Model.MethodParameterDeclaration))
promise = promise.then do |response|
# Defining deserialization method.
deserialize_method = lambda do |parsed_response|
@(Model.DeserializePollingResponse("parsed_response", Model.ReturnType.Body))
@if(Model.ReturnType.Body != null)
{
@:@(Model.DeserializePollingResponse("parsed_response", Model.ReturnType.Body))
}
end
@EmptyLine

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

@ -15,6 +15,8 @@ describe String do
client = AutoRestSwaggerBATService.new(@credentials, @base_url)
@string_client = StringModule::String.new(client)
@enum_client = StringModule::Enum.new(client)
end
it 'should create test service' do
@ -62,4 +64,12 @@ describe String do
expect(result.response.status).to eq(200)
expect(result.body).to be_nil
end
it 'should support valid enum valid value' do
result = @enum_client.get_not_expandable_async().value!
expect(result.response.status).to eq(200)
expect(result.response.body).to include('red color')
end
it 'should correctly handle invalid values for enum' do
expect { @enum_client.put_not_expandable_async('orange color').value! }.to raise_error(MsRest::ValidationError)
end
end

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

@ -2,13 +2,15 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Rest.Generator.ClientModel;
using System.Globalization;
using System.Linq;
using Microsoft.Rest.Generator.ClientModel;
namespace Microsoft.Rest.Generator.Ruby.TemplateModels
{
using Utilities;
using System.Collections.Generic;
using System.Reflection;
using Utilities;
/// <summary>
/// Keeps a few aux method used across all templates/models.
@ -262,224 +264,6 @@ namespace Microsoft.Rest.Generator.Ruby.TemplateModels
return client.ModelTypes.Any(mt => mt.Extensions.Count == 0);
}
/// <summary>
/// Generates Ruby code in form of string for deserializing object of given type.
/// </summary>
/// <param name="type">Type of object needs to be deserialized.</param>
/// <param name="scope">Current scope.</param>
/// <param name="valueReference">Reference to object which needs to be deserialized.</param>
/// <returns>Generated Ruby code in form of string.</returns>
public static string DeserializeType(
this IType type,
IScopeProvider scope,
string valueReference)
{
var composite = type as CompositeType;
var sequence = type as SequenceType;
var dictionary = type as DictionaryType;
var primary = type as PrimaryType;
var enumType = type as EnumType;
var builder = new IndentedStringBuilder(" ");
if (primary != null)
{
if (primary.Type == KnownPrimaryType.Int || primary.Type == KnownPrimaryType.Long)
{
return builder.AppendLine("{0} = Integer({0}) unless {0}.to_s.empty?", valueReference).ToString();
}
if (primary.Type == KnownPrimaryType.Double)
{
return builder.AppendLine("{0} = Float({0}) unless {0}.to_s.empty?", valueReference).ToString();
}
if (primary.Type == KnownPrimaryType.ByteArray)
{
return builder.AppendLine("{0} = Base64.strict_decode64({0}).unpack('C*') unless {0}.to_s.empty?", valueReference).ToString();
}
if (primary.Type == KnownPrimaryType.Date)
{
return builder.AppendLine("{0} = MsRest::Serialization.deserialize_date({0}) unless {0}.to_s.empty?", valueReference).ToString();
}
if (primary.Type == KnownPrimaryType.DateTime)
{
return builder.AppendLine("{0} = DateTime.parse({0}) unless {0}.to_s.empty?", valueReference).ToString();
}
if (primary.Type == KnownPrimaryType.DateTimeRfc1123)
{
return builder.AppendLine("{0} = DateTime.parse({0}) unless {0}.to_s.empty?", valueReference).ToString();
}
if (primary.Type == KnownPrimaryType.UnixTime)
{
return builder.AppendLine("{0} = DateTime.strptime({0}.to_s, '%s') unless {0}.to_s.empty?", valueReference).ToString();
}
}
else if (enumType != null && !string.IsNullOrEmpty(enumType.Name))
{
return builder
.AppendLine("if (!{0}.nil? && !{0}.empty?)", valueReference)
.AppendLine(
" enum_is_valid = {0}.constants.any? {{ |e| {0}.const_get(e).to_s.downcase == {1}.downcase }}",
enumType.Name, valueReference)
.AppendLine(
" warn 'Enum {0} does not contain ' + {1}.downcase + ', but was received from the server.' unless enum_is_valid", enumType.Name, valueReference)
.AppendLine("end")
.ToString();
}
else if (sequence != null)
{
var elementVar = scope.GetUniqueName("element");
var innerSerialization = sequence.ElementType.DeserializeType(scope, elementVar);
if (!string.IsNullOrEmpty(innerSerialization))
{
return
builder
.AppendLine("unless {0}.nil?", valueReference)
.Indent()
.AppendLine("deserialized_{0} = []", sequence.Name.ToLower())
.AppendLine("{0}.each do |{1}|", valueReference, elementVar)
.Indent()
.AppendLine(innerSerialization)
.AppendLine("deserialized_{0}.push({1})", sequence.Name.ToLower(), elementVar)
.Outdent()
.AppendLine("end")
.AppendLine("{0} = deserialized_{1}", valueReference, sequence.Name.ToLower())
.Outdent()
.AppendLine("end")
.ToString();
}
}
else if (dictionary != null)
{
var valueVar = scope.GetUniqueName("valueElement");
var innerSerialization = dictionary.ValueType.DeserializeType(scope, valueVar);
if (!string.IsNullOrEmpty(innerSerialization))
{
return builder.AppendLine("unless {0}.nil?", valueReference)
.Indent()
.AppendLine("{0}.each do |key, {1}|", valueReference, valueVar)
.Indent()
.AppendLine(innerSerialization)
.AppendLine("{0}[key] = {1}", valueReference, valueVar)
.Outdent()
.AppendLine("end")
.Outdent()
.AppendLine("end").ToString();
}
}
else if (composite != null)
{
return builder.AppendLine("unless {0}.nil?", valueReference)
.Indent()
.AppendLine("{0} = {1}.deserialize_object({0})", valueReference, composite.Name)
.Outdent()
.AppendLine("end").ToString();
}
return string.Empty;
}
/// <summary>
/// Generates Ruby code in form of string for serializing object of given type.
/// </summary>
/// <param name="type">Type of object needs to be serialized.</param>
/// <param name="scope">Current scope.</param>
/// <param name="valueReference">Reference to object which needs to serialized.</param>
/// <returns>Generated Ruby code in form of string.</returns>
public static string SerializeType(
this IType type,
IScopeProvider scope,
string valueReference)
{
var composite = type as CompositeType;
var sequence = type as SequenceType;
var dictionary = type as DictionaryType;
var primary = type as PrimaryType;
var builder = new IndentedStringBuilder(" ");
if (primary != null)
{
if (primary.Type == KnownPrimaryType.ByteArray)
{
return builder.AppendLine("{0} = Base64.strict_encode64({0}.pack('c*'))", valueReference).ToString();
}
if (primary.Type == KnownPrimaryType.DateTime)
{
return builder.AppendLine("{0} = {0}.new_offset(0).strftime('%FT%TZ')", valueReference).ToString();
}
if (primary.Type == KnownPrimaryType.DateTimeRfc1123)
{
return builder.AppendLine("{0} = {0}.new_offset(0).strftime('%a, %d %b %Y %H:%M:%S GMT')", valueReference).ToString();
}
if (primary.Type == KnownPrimaryType.UnixTime)
{
return builder.AppendLine("{0} = {0}.new_offset(0).strftime('%s')", valueReference).ToString();
}
}
else if (sequence != null)
{
var elementVar = scope.GetUniqueName("element");
var innerSerialization = sequence.ElementType.SerializeType(scope, elementVar);
if (!string.IsNullOrEmpty(innerSerialization))
{
return
builder
.AppendLine("unless {0}.nil?", valueReference)
.Indent()
.AppendLine("serialized{0} = []", sequence.Name)
.AppendLine("{0}.each do |{1}|", valueReference, elementVar)
.Indent()
.AppendLine(innerSerialization)
.AppendLine("serialized{0}.push({1})", sequence.Name.ToPascalCase(), elementVar)
.Outdent()
.AppendLine("end")
.AppendLine("{0} = serialized{1}", valueReference, sequence.Name.ToPascalCase())
.Outdent()
.AppendLine("end")
.ToString();
}
}
else if (dictionary != null)
{
var valueVar = scope.GetUniqueName("valueElement");
var innerSerialization = dictionary.ValueType.SerializeType(scope, valueVar);
if (!string.IsNullOrEmpty(innerSerialization))
{
return builder.AppendLine("unless {0}.nil?", valueReference)
.Indent()
.AppendLine("{0}.each {{ |key, {1}|", valueReference, valueVar)
.Indent()
.AppendLine(innerSerialization)
.AppendLine("{0}[key] = {1}", valueReference, valueVar)
.Outdent()
.AppendLine("}")
.Outdent()
.AppendLine("end").ToString();
}
}
else if (composite != null)
{
return builder.AppendLine("unless {0}.nil?", valueReference)
.Indent()
.AppendLine("{0} = {1}.serialize_object({0})", valueReference, composite.Name)
.Outdent()
.AppendLine("end").ToString();
}
return string.Empty;
}
/// <summary>
/// Determines whether one composite type derives directly or indirectly from another.
/// </summary>
@ -493,5 +277,478 @@ namespace Microsoft.Rest.Generator.Ruby.TemplateModels
(type.BaseModelType.Equals(possibleAncestorType) ||
type.BaseModelType.DerivesFrom(possibleAncestorType));
}
/// <summary>
/// Constructs blueprint of the given <paramref name="type"/>.
/// </summary>
/// <param name="type">Type for which mapper being generated.</param>
/// <param name="serializedName">Serialized name to be used.</param>
/// <param name="parameter">Parameter of the composite type to construct the parameter constraints.</param>
/// <param name="expandComposite">Expand composite type if <c>true</c> otherwise specify class_name in the mapper.</param>
/// <returns>Mapper for the <paramref name="type"/> as string.</returns>
/// <exception cref="ArgumentNullException">Thrown when a required parameter is null.</exception>
/// <example>
/// One of the example of the mapper is
/// {
/// required: false,
/// serialized_name: 'Fish',
/// type: {
/// name: 'Composite',
/// polymorphic_discriminator: 'fishtype',
/// uber_parent: 'Fish',
/// class_name: 'Fish',
/// model_properties: {
/// species: {
/// required: false,
/// serialized_name: 'species',
/// type: {
/// name: 'String'
/// }
/// },
/// length: {
/// required: true,
/// serialized_name: 'length',
/// type: {
/// name: 'Double'
/// }
/// },
/// siblings: {
/// required: false,
/// serialized_name: 'siblings',
/// type: {
/// name: 'Sequence',
/// element: {
/// required: false,
/// serialized_name: 'FishElementType',
/// type: {
/// name: 'Composite',
/// polymorphic_discriminator: 'fishtype',
/// uber_parent: 'Fish',
/// class_name: 'Fish'
/// }
/// }
/// }
/// }
/// }
/// }
/// }
/// </example>
public static string ConstructMapper(this IType type, string serializedName, IParameter parameter, bool expandComposite)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
var builder = new IndentedStringBuilder(" ");
CompositeType composite = type as CompositeType;
SequenceType sequence = type as SequenceType;
DictionaryType dictionary = type as DictionaryType;
PrimaryType primary = type as PrimaryType;
EnumType enumType = type as EnumType;
if (enumType != null && enumType.ModelAsString)
{
primary = new PrimaryType(KnownPrimaryType.String);
}
builder.AppendLine("").Indent();
builder.AppendLine(type.AddMetaData(serializedName, parameter));
if (primary != null)
{
builder.AppendLine(primary.ContructMapperForPrimaryType());
}
else if (enumType != null && enumType.Name != null)
{
builder.AppendLine(enumType.ContructMapperForEnumType());
}
else if (sequence != null)
{
builder.AppendLine(sequence.ContructMapperForSequenceType());
}
else if (dictionary != null)
{
builder.AppendLine(dictionary.ContructMapperForDictionaryType());
}
else if (composite != null)
{
builder.AppendLine(composite.ContructMapperForCompositeType(expandComposite));
}
else
{
throw new NotImplementedException(string.Format(CultureInfo.InvariantCulture, "{0} is not a supported Type.", type));
}
return builder.ToString();
}
/// <summary>
/// Adds metadata to the given <paramref name="type"/>.
/// </summary>
/// <param name="type">Type for which metadata being generated.</param>
/// <param name="serializedName">Serialized name to be used.</param>
/// <param name="parameter">Parameter of the composite type to construct the parameter constraints.</param>
/// <returns>Metadata as string.</returns>
/// <exception cref="ArgumentNullException">Thrown when a required parameter is null.</exception>
/// <example>
/// The below example shows possible mapper string for IParameter for IType.
/// required: true | false, -- whether this property is required or not
/// read_only: true | false, -- whether this property is read only or not. Default is false
/// is_constant: true | false, -- whether this property is constant or not. Default is false
/// serialized_name: 'name' -- serialized name of the property if provided
/// default_value: 'value' -- default value of the property if provided
/// constraints: { -- constraints of the property
/// key: value, -- constraint name and value if any
/// ***: *****
/// }
/// </example>
private static string AddMetaData(this IType type, string serializedName, IParameter parameter)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
if (serializedName == null)
{
throw new ArgumentNullException(nameof(serializedName));
}
IndentedStringBuilder builder = new IndentedStringBuilder(" ");
Dictionary<Constraint, string> constraints = null;
string defaultValue = null;
bool isRequired = false;
bool isConstant = false;
bool isReadOnly = false;
var property = parameter as Property;
if (property != null)
{
isReadOnly = property.IsReadOnly;
}
if (parameter != null)
{
defaultValue = parameter.DefaultValue;
isRequired = parameter.IsRequired;
isConstant = parameter.IsConstant;
constraints = parameter.Constraints;
}
CompositeType composite = type as CompositeType;
if (composite != null && composite.ContainsConstantProperties && isRequired)
{
defaultValue = "{}";
}
if (isRequired)
{
builder.AppendLine("required: true,");
}
else
{
builder.AppendLine("required: false,");
}
if (isReadOnly)
{
builder.AppendLine("read_only: true,");
}
if (isConstant)
{
builder.AppendLine("is_constant: true,");
}
if (serializedName != null)
{
builder.AppendLine("serialized_name: '{0}',", serializedName);
}
if (defaultValue != null)
{
builder.AppendLine("default_value: {0},", defaultValue);
}
if (constraints != null && constraints.Count > 0)
{
builder.AppendLine("constraints: {").Indent();
var keys = constraints.Keys.ToList<Constraint>();
for (int j = 0; j < keys.Count; j++)
{
var constraintValue = constraints[keys[j]];
if (keys[j] == Constraint.Pattern)
{
constraintValue = string.Format(CultureInfo.InvariantCulture, "'{0}'", constraintValue);
}
if (j != keys.Count - 1)
{
builder.AppendLine("{0}: {1},", keys[j], constraintValue);
}
else
{
builder.AppendLine("{0}: {1}", keys[j], constraintValue);
}
}
builder.Outdent()
.AppendLine("},");
}
return builder.ToString();
}
/// <summary>
/// Constructs blueprint of the given <paramref name="primary"/>.
/// </summary>
/// <param name="primary">PrimaryType for which mapper being generated.</param>
/// <returns>Mapper for the <paramref name="primary"/> as string.</returns>
/// <exception cref="ArgumentNullException">Thrown when a required parameter is null.</exception>
/// <example>
/// The below example shows possible mapper string for PrimaryType.
/// type: {
/// name: 'Boolean' -- This value should be one of the KnownPrimaryType
/// }
/// </example>
private static string ContructMapperForPrimaryType(this PrimaryType primary)
{
if (primary == null)
{
throw new ArgumentNullException(nameof(primary));
}
IndentedStringBuilder builder = new IndentedStringBuilder(" ");
if (primary.Type == KnownPrimaryType.Boolean)
{
builder.AppendLine("type: {").Indent().AppendLine("name: 'Boolean'").Outdent().AppendLine("}");
}
else if (primary.Type == KnownPrimaryType.Double)
{
builder.AppendLine("type: {").Indent().AppendLine("name: 'Double'").Outdent().AppendLine("}");
}
else if (primary.Type == KnownPrimaryType.Int || primary.Type == KnownPrimaryType.Long ||
primary.Type == KnownPrimaryType.Decimal)
{
builder.AppendLine("type: {").Indent().AppendLine("name: 'Number'").Outdent().AppendLine("}");
}
else if (primary.Type == KnownPrimaryType.String || primary.Type == KnownPrimaryType.Uuid)
{
builder.AppendLine("type: {").Indent().AppendLine("name: 'String'").Outdent().AppendLine("}");
}
else if (primary.Type == KnownPrimaryType.ByteArray)
{
builder.AppendLine("type: {").Indent().AppendLine("name: 'ByteArray'").Outdent().AppendLine("}");
}
else if (primary.Type == KnownPrimaryType.Base64Url)
{
builder.AppendLine("type: {").Indent().AppendLine("name: 'Base64Url'").Outdent().AppendLine("}");
}
else if (primary.Type == KnownPrimaryType.Date)
{
builder.AppendLine("type: {").Indent().AppendLine("name: 'Date'").Outdent().AppendLine("}");
}
else if (primary.Type == KnownPrimaryType.DateTime)
{
builder.AppendLine("type: {").Indent().AppendLine("name: 'DateTime'").Outdent().AppendLine("}");
}
else if (primary.Type == KnownPrimaryType.DateTimeRfc1123)
{
builder.AppendLine("type: {").Indent().AppendLine("name: 'DateTimeRfc1123'").Outdent().AppendLine("}");
}
else if (primary.Type == KnownPrimaryType.TimeSpan)
{
builder.AppendLine("type: {").Indent().AppendLine("name: 'TimeSpan'").Outdent().AppendLine("}");
}
else if (primary.Type == KnownPrimaryType.UnixTime)
{
builder.AppendLine("type: {").Indent().AppendLine("name: 'UnixTime'").Outdent().AppendLine("}");
}
else if (primary.Type == KnownPrimaryType.Object)
{
builder.AppendLine("type: {").Indent().AppendLine("name: 'Object'").Outdent().AppendLine("}");
}
else if (primary.Type == KnownPrimaryType.Stream)
{
builder.AppendLine("type: {").Indent().AppendLine("name: 'Stream'").Outdent().AppendLine("}");
}
else
{
throw new NotImplementedException(string.Format(CultureInfo.InvariantCulture, "{0} is not a supported primary Type for {1}.", primary.Type, primary.SerializedName));
}
return builder.ToString();
}
/// <summary>
/// Constructs blueprint of the given <paramref name="enumeration"/>.
/// </summary>
/// <param name="enumeration">EnumType for which mapper being generated.</param>
/// <returns>Mapper for the <paramref name="enumeration"/> as string.</returns>
/// <exception cref="ArgumentNullException">Thrown when a required parameter is null.</exception>
/// <example>
/// The below example shows possible mapper string for EnumType.
/// type: {
/// name: 'Enum',
/// module: 'module_name' -- name of the module to be looked for enum values
/// }
/// </example>
private static string ContructMapperForEnumType(this EnumType enumeration)
{
if (enumeration == null)
{
throw new ArgumentNullException(nameof(enumeration));
}
IndentedStringBuilder builder = new IndentedStringBuilder(" ");
builder.AppendLine("type: {").Indent()
.AppendLine("name: 'Enum',")
.AppendLine("module: '{0}'", enumeration.Name).Outdent()
.AppendLine("}");
return builder.ToString();
}
/// <summary>
/// Constructs blueprint of the given <paramref name="sequence"/>.
/// </summary>
/// <param name="sequence">SequenceType for which mapper being generated.</param>
/// <returns>Mapper for the <paramref name="sequence"/> as string.</returns>
/// <exception cref="ArgumentNullException">Thrown when a required parameter is null.</exception>
/// <example>
/// The below example shows possible mapper string for SequenceType.
/// type: {
/// name: 'Sequence',
/// element: {
/// *** -- mapper of the IType from the sequence element
/// }
/// }
/// </example>
private static string ContructMapperForSequenceType(this SequenceType sequence)
{
if (sequence == null)
{
throw new ArgumentNullException(nameof(sequence));
}
IndentedStringBuilder builder = new IndentedStringBuilder(" ");
builder.AppendLine("type: {").Indent()
.AppendLine("name: 'Sequence',")
.AppendLine("element: {").Indent()
.AppendLine("{0}", sequence.ElementType.ConstructMapper(sequence.ElementType.Name + "ElementType", null, false)).Outdent()
.AppendLine("}").Outdent()
.AppendLine("}");
return builder.ToString();
}
/// <summary>
/// Constructs blueprint of the given <paramref name="dictionary"/>.
/// </summary>
/// <param name="dictionary">DictionaryType for which mapper being generated.</param>
/// <returns>Mapper for the <paramref name="dictionary"/> as string.</returns>
/// <exception cref="ArgumentNullException">Thrown when a required parameter is null.</exception>
/// <example>
/// The below example shows possible mapper string for DictionaryType.
/// type: {
/// name: 'Dictionary',
/// value: {
/// *** -- mapper of the IType from the value type of dictionary
/// }
/// }
/// </example>
private static string ContructMapperForDictionaryType(this DictionaryType dictionary)
{
if (dictionary == null)
{
throw new ArgumentNullException(nameof(dictionary));
}
IndentedStringBuilder builder = new IndentedStringBuilder(" ");
builder.AppendLine("type: {").Indent()
.AppendLine("name: 'Dictionary',")
.AppendLine("value: {").Indent()
.AppendLine("{0}", dictionary.ValueType.ConstructMapper(dictionary.ValueType.Name + "ElementType", null, false)).Outdent()
.AppendLine("}").Outdent()
.AppendLine("}");
return builder.ToString();
}
/// <summary>
/// Constructs blueprint of the given <paramref name="composite"/>.
/// </summary>
/// <param name="composite">CompositeType for which mapper being generated.</param>
/// <param name="expandComposite">Expand composite type if <c>true</c> otherwise specify class_name in the mapper.</param>
/// <returns>Mapper for the <paramref name="composite"/> as string.</returns>
/// <exception cref="ArgumentNullException">Thrown when a required parameter is null.</exception>
/// <example>
/// The below example shows possible mapper string for CompositeType.
/// type: {
/// name: 'Composite',
/// polymorphic_discriminator: 'property_name', -- name of the property for polymorphic discriminator
/// Used only when x-ms-discriminator-value applied
/// uber_parent: 'parent_class_name', -- name of the topmost level class on inheritance hierarchy
/// Used only when x-ms-discriminator-value applied
/// class_name: 'class_name', -- name of the modeled class
/// Used when <paramref name="expandComposite"/> is false
/// model_properties: { -- expanded properties of the model
/// Used when <paramref name="expandComposite"/> is true
/// property_name : { -- name of the property of this composite type
/// *** -- mapper of the IType from the type of the property
/// }
/// }
/// }
/// </example>
private static string ContructMapperForCompositeType(this CompositeType composite, bool expandComposite)
{
if (composite == null)
{
throw new ArgumentNullException(nameof(composite));
}
IndentedStringBuilder builder = new IndentedStringBuilder(" ");
builder.AppendLine("type: {").Indent()
.AppendLine("name: 'Composite',");
if (composite.PolymorphicDiscriminator != null)
{
builder.AppendLine("polymorphic_discriminator: '{0}',", composite.PolymorphicDiscriminator);
var polymorphicType = composite;
while (polymorphicType.BaseModelType != null)
{
polymorphicType = polymorphicType.BaseModelType;
}
builder.AppendLine("uber_parent: '{0}',", polymorphicType.Name);
}
if (!expandComposite)
{
builder.AppendLine("class_name: '{0}'", composite.Name).Outdent().AppendLine("}");
}
else
{
builder.AppendLine("class_name: '{0}',", composite.Name)
.AppendLine("model_properties: {").Indent();
var composedPropertyList = new List<Property>(composite.ComposedProperties);
for (var i = 0; i < composedPropertyList.Count; i++)
{
var prop = composedPropertyList[i];
var serializedPropertyName = prop.SerializedName;
if (i != composedPropertyList.Count - 1)
{
builder.AppendLine("{0}: {{{1}}},", prop.Name, prop.Type.ConstructMapper(serializedPropertyName, prop, false));
}
else
{
builder.AppendLine("{0}: {{{1}}}", prop.Name, prop.Type.ConstructMapper(serializedPropertyName, prop, false));
}
}
// end of modelProperties and type
builder.Outdent().
AppendLine("}").Outdent().
AppendLine("}");
}
return builder.ToString();
}
}
}

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

@ -122,8 +122,11 @@ namespace Microsoft.Rest.Generator.Ruby
/// </summary>
public virtual IEnumerable<ParameterTemplateModel> EncodingPathParams
{
get { return AllPathParams.Where(p => !(p.Extensions.ContainsKey(Generator.Extensions.SkipUrlEncodingExtension) &&
String.Equals(p.Extensions[Generator.Extensions.SkipUrlEncodingExtension].ToString(), "true", StringComparison.OrdinalIgnoreCase))); }
get
{
return AllPathParams.Where(p => !(p.Extensions.ContainsKey(Generator.Extensions.SkipUrlEncodingExtension) &&
String.Equals(p.Extensions[Generator.Extensions.SkipUrlEncodingExtension].ToString(), "true", StringComparison.OrdinalIgnoreCase)));
}
}
/// <summary>
@ -133,7 +136,7 @@ namespace Microsoft.Rest.Generator.Ruby
{
get
{
return AllPathParams.Where(p =>
return AllPathParams.Where(p =>
(p.Extensions.ContainsKey(Generator.Extensions.SkipUrlEncodingExtension) &&
String.Equals(p.Extensions[Generator.Extensions.SkipUrlEncodingExtension].ToString(), "true", StringComparison.OrdinalIgnoreCase) &&
!p.Extensions.ContainsKey("hostParameter")));
@ -207,7 +210,7 @@ namespace Microsoft.Rest.Generator.Ruby
/// Gets the list of method paramater templates.
/// </summary>
public List<ParameterTemplateModel> ParameterTemplateModels { get; private set; }
/// <summary>
/// Gets the list of parameter which need to be included into HTTP header.
/// </summary>
@ -271,7 +274,7 @@ namespace Microsoft.Rest.Generator.Ruby
PrimaryType type = parameter.Type as PrimaryType;
if (type != null)
{
if (type.Type == KnownPrimaryType.Boolean || type.Type == KnownPrimaryType.Double ||
if (type.Type == KnownPrimaryType.Boolean || type.Type == KnownPrimaryType.Double ||
type.Type == KnownPrimaryType.Int || type.Type == KnownPrimaryType.Long || type.Type == KnownPrimaryType.String)
{
format = "{0} = " + parameter.DefaultValue;
@ -369,32 +372,12 @@ namespace Microsoft.Rest.Generator.Ruby
builder.AppendLine("{0} = {1}.to_s.empty? ? nil : JSON.load({1})", tempVariable, inputVariable);
// Secondly parse each js object into appropriate Ruby type (DateTime, Byte array, etc.)
// and overwrite temporary variable variable value.
string deserializationLogic = type.DeserializeType(this.Scope, tempVariable);
// and overwrite temporary variable value.
string deserializationLogic = GetDeserializationString(type, outputVariable, tempVariable);
builder.AppendLine(deserializationLogic);
// Assigning value of temporary variable to the output variable.
return builder.AppendLine("{0} = {1}", outputVariable, tempVariable).ToString();
}
/// <summary>
/// Creates a code in form of string which serializes given input variable of given type.
/// </summary>
/// <param name="inputVariable">The input variable.</param>
/// <param name="type">The type of input variable.</param>
/// <param name="outputVariable">The output variable.</param>
/// <returns>The serialization code.</returns>
public virtual string CreateSerializationString(string inputVariable, IType type, string outputVariable)
{
var builder = new IndentedStringBuilder(" ");
// Firstly recursively serialize each component of the object.
string serializationLogic = type.SerializeType(this.Scope, inputVariable);
builder.AppendLine(serializationLogic);
// After that - generate JSON object after serializing each component.
return builder.AppendLine("{0} = {1} != nil ? JSON.generate({1}, quirks_mode: true) : nil", outputVariable, inputVariable).ToString();
return builder.ToString();
}
/// <summary>
@ -517,5 +500,61 @@ namespace Microsoft.Rest.Generator.Ruby
}
return urlPathParamName;
}
/// <summary>
/// Constructs mapper for the request body.
/// </summary>
/// <param name="outputVariable">Name of the output variable.</param>
/// <returns>Mapper for the request body as string.</returns>
public string ConstructRequestBodyMapper(string outputVariable = "request_mapper")
{
var builder = new IndentedStringBuilder(" ");
if (RequestBody.Type is CompositeType)
{
builder.AppendLine("{0} = {1}.mapper()", outputVariable, RequestBody.Type.Name);
}
else
{
builder.AppendLine("{0} = {{{1}}}", outputVariable,
RequestBody.Type.ConstructMapper(RequestBody.SerializedName, RequestBody, false));
}
return builder.ToString();
}
/// <summary>
/// Creates deserialization logic for the given <paramref name="type"/>.
/// </summary>
/// <param name="type">Type for which deserialization logic being constructed.</param>
/// <param name="valueReference">Reference variable name.</param>
/// <param name="responseVariable">Response variable name.</param>
/// <returns>Deserialization logic for the given <paramref name="type"/> as string.</returns>
/// <exception cref="ArgumentNullException">Thrown when a required parameter is null.</exception>
public string GetDeserializationString(IType type, string valueReference = "result", string responseVariable = "parsed_response")
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
var builder = new IndentedStringBuilder(" ");
if (type is CompositeType)
{
builder.AppendLine("result_mapper = {0}.mapper()", type.Name);
}
else
{
builder.AppendLine("result_mapper = {{{0}}}", type.ConstructMapper(responseVariable, null, false));
}
if (Group == null)
{
builder.AppendLine("{1} = self.deserialize(result_mapper, {0}, '{1}')", responseVariable, valueReference);
}
else
{
builder.AppendLine("{1} = @client.deserialize(result_mapper, {0}, '{1}')", responseVariable, valueReference);
}
return builder.ToString();
}
}
}

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

@ -4,8 +4,8 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.Rest.Generator.ClientModel;
using Microsoft.Rest.Generator.Utilities;
using Microsoft.Rest.Generator.Ruby.TemplateModels;
using Microsoft.Rest.Generator.Utilities;
namespace Microsoft.Rest.Generator.Ruby
{
@ -61,7 +61,7 @@ namespace Microsoft.Rest.Generator.Ruby
{
get
{
return new List<string> {};
return new List<string> { };
}
}
@ -91,7 +91,7 @@ namespace Microsoft.Rest.Generator.Ruby
/// </summary>
public string PolymorphicDiscriminatorProperty
{
get
get
{
if (string.IsNullOrEmpty(this.PolymorphicDiscriminator) && this.parent != null)
{
@ -124,6 +124,22 @@ namespace Microsoft.Rest.Generator.Ruby
PropertyTemplateModels = new List<PropertyTemplateModel>();
source.Properties.ForEach(p => PropertyTemplateModels.Add(new PropertyTemplateModel(p)));
if (!string.IsNullOrEmpty(source.PolymorphicDiscriminator))
{
if (!source.Properties.Any(p => p.Name == source.PolymorphicDiscriminator))
{
var polymorphicProperty = new Property
{
IsRequired = true,
Name = source.PolymorphicDiscriminator,
SerializedName = source.PolymorphicDiscriminator,
Documentation = "Polymorhpic Discriminator",
Type = new PrimaryType(KnownPrimaryType.String)
};
source.Properties.Add(polymorphicProperty);
}
}
if (source.BaseModelType != null)
{
this.parent = new ModelTemplateModel(source.BaseModelType, allTypes);
@ -132,36 +148,6 @@ namespace Microsoft.Rest.Generator.Ruby
this.allTypes = allTypes;
}
/// <summary>
/// Generates code for model serialization.
/// </summary>
/// <param name="variableName">Variable serialize model from.</param>
/// <param name="type">The type of the model.</param>
/// <returns>The code for serialization in string format.</returns>
public virtual string SerializeProperty(string variableName, IType type)
{
var builder = new IndentedStringBuilder(" ");
string serializationLogic = type.SerializeType(this.Scope, variableName);
builder.AppendLine(serializationLogic);
return builder.ToString();
}
/// <summary>
/// Generates code for model deserialization.
/// </summary>
/// <param name="variableName">Variable deserialize model from.</param>
/// <param name="type">The type of the model.</param>
/// <returns>The code for вуserialization in string format.</returns>
public virtual string DeserializeProperty(string variableName, IType type)
{
var builder = new IndentedStringBuilder(" ");
string serializationLogic = type.DeserializeType(this.Scope, variableName);
return builder.AppendLine(serializationLogic).ToString();
}
/// <summary>
/// Returns code for declaring inheritance.
/// </summary>
@ -170,5 +156,17 @@ namespace Microsoft.Rest.Generator.Ruby
{
return this.BaseModelType != null ? " < " + this.BaseModelType.Name : string.Empty;
}
/// <summary>
/// Constructs mapper for the model class.
/// </summary>
/// <returns>Mapper as string for this model class.</returns>
public virtual string ConstructModelMapper()
{
var modelMapper = this.ConstructMapper(SerializedName, null, true);
var builder = new IndentedStringBuilder(" ");
builder.AppendLine("{{{0}}}", modelMapper);
return builder.ToString();
}
}
}

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

@ -95,11 +95,7 @@ def @(Model.Name)_async(@(Model.MethodParameterDeclaration))
if (parameter.IsConstant)
{
@:@(parameter.Name) = @(parameter.DefaultValue)
}
else
{
@:@(parameter.Type.ValidateType(Model.Scope, parameter.Name))
}
}
}
request_headers = {}
@if (Model.Parameters.Any(p => p.Location == ParameterLocation.Header))
@ -120,9 +116,13 @@ def @(Model.Name)_async(@(Model.MethodParameterDeclaration))
@if (Model.RequestBody != null)
{
@EmptyLine
@:# Serialize Request
@:request_headers['Content-Type'] = '@(Model.RequestContentType)'
@:@Model.CreateSerializationString(Model.RequestBody.Name, Model.RequestBody.Type, "request_content")
@EmptyLine
@:# Serialize Request
@:@Model.ConstructRequestBodyMapper("request_mapper")
@:request_content = @(Model.ClientReference).serialize(request_mapper, @(Model.RequestBody.Name), '@(Model.RequestBody.Name)')
@:request_content = request_content != nil ? JSON.generate(request_content, quirks_mode: true) : nil
@EmptyLine
}
path_template = '@Model.Url'

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

@ -31,7 +31,18 @@ module @(Settings.Namespace)
@:@@@@discriminatorMap["@derivedType.SerializedName"] = "@derivedType.Name.ToLowerInvariant()"
}
}
@if (Model.IsPolymorphic)
{
@EmptyLine
@:def initialize
@: @@@Model.PolymorphicDiscriminatorProperty = "@Model.SerializedName"
@:end
@EmptyLine
@:attr_accessor :@Model.PolymorphicDiscriminatorProperty
@EmptyLine
}
@foreach (var property in Model.PropertyTemplateModels)
{
@:@WrapComment("# ", string.Format("@return {0}{1}", property.Type.GetYardDocumentation(), property.Documentation))
@ -41,115 +52,14 @@ module @(Settings.Namespace)
@:
}
@EmptyLine
#
# Validate the object. Throws ValidationError if validation fails.
@WrapComment("# ", string.Format("Mapper for {0} class as Ruby Hash.", Model.Name))
# This will be used for serialization/deserialization.
#
def validate
@{
bool anythingToValidate = false;
foreach (var property in Model.Properties.Where(p => p.IsRequired && !p.IsReadOnly && p.Type.IsNullable()))
{
anythingToValidate = true;
@:fail MsRest::ValidationError, 'property @property.Name is nil' if @@@(property.Name).nil?
}
foreach (var property in Model.Properties.Where(p => !(p.Type is PrimaryType)))
{
anythingToValidate = true;
@:@property.Type.ValidateType(Model.Scope, string.Format("@{0}", property.Name))
@:
}
if (!anythingToValidate)
{
@:# Nothing to validate
}
}
def self.mapper()
@(Model.ConstructModelMapper())
end
@EmptyLine
#
# Serializes given Model object into Ruby Hash.
@WrapComment("# ", string.Format("@param {0} {1}", "object", "Model object to serialize."))
@WrapComment("# ", string.Format("@return [Hash] {0}", "Serialized object in form of Ruby Hash."))
#
def self.serialize_object(object)
object.validate
output_object = {}
@if (Model.IsPolymorphic)
{
@EmptyLine
@:unless object.@(Model.PolymorphicDiscriminatorProperty).nil? or object.@(Model.PolymorphicDiscriminatorProperty) == "@Model.SerializedName"
@:class_name = @@@@discriminatorMap[object.@Model.PolymorphicDiscriminatorProperty].capitalize
@:class_instance = Models.const_get(class_name)
@foreach (var ns in Model.ClassNamespaces)
{
@:class_instance = @(ns).const_get(class_name) if class_instance.nil?
}
@:output_object = class_instance.serialize_object(object)
@:else
@:output_object['@Model.PolymorphicDiscriminatorProperty'] = object.@Model.PolymorphicDiscriminatorProperty
@:end
}
@foreach (var property in Model.ComposedProperties.OrderByDescending(x => x.IsRequired))
{
@EmptyLine
@:serialized_property = @("object." + property.Name)
@:@Model.SerializeProperty("serialized_property", property.Type)
@:output_object['@(property.SerializedName)'] = serialized_property unless serialized_property.nil?
}
@EmptyLine
output_object
end
@EmptyLine
#
# Deserializes given Ruby Hash into Model object.
@WrapComment("# ", string.Format("@param {0} [Hash] {1}", "object", "Ruby Hash object to deserialize."))
@WrapComment("# ", string.Format("@return [{0}] {1}", Model.Name, "Deserialized object."))
#
def self.deserialize_object(object)
return if object.nil?
output_object = @(Model.Name).new
@if (Model.IsPolymorphic)
{
@EmptyLine
@:unless object['@(Model.PolymorphicDiscriminatorProperty)'].nil? or object['@(Model.PolymorphicDiscriminatorProperty)'] == "@Model.SerializedName"
@:class_name = @@@@discriminatorMap[object['@Model.PolymorphicDiscriminatorProperty']].capitalize
@:class_instance = Models.const_get(class_name)
@foreach (var ns in Model.ClassNamespaces)
{
@:class_instance = @(ns).const_get(class_name) if class_instance.nil?
}
@:output_object = class_instance.deserialize_object(object)
@:else
@:output_object.@Model.PolymorphicDiscriminatorProperty = object['@Model.PolymorphicDiscriminatorProperty']
@:end
}
@foreach (var property in Model.ComposedProperties.OrderByDescending(x => x.IsRequired))
{
@EmptyLine
@:deserialized_property = @(string.Format("object['{0}']", property.SerializedName))
@:@Model.DeserializeProperty("deserialized_property", property.Type)
@:output_object.@(property.Name) = deserialized_property
}
@EmptyLine
output_object
end
@if (Model.IsPolymorphic)
{
@EmptyLine
@:def initialize
@: @@@Model.PolymorphicDiscriminatorProperty = "@Model.SerializedName"
@:end
@EmptyLine
@:attr_accessor :@Model.PolymorphicDiscriminatorProperty
}
end
end
end

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

@ -16,6 +16,7 @@ module @Settings.Namespace
{
@:include @(Settings.Namespace)::Models
}
include MsRest::Serialization
@foreach (var include in Model.Includes)
{
@:include @include

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

@ -23,67 +23,61 @@ module MsRestAzure
# @return [Hash{String => String}] the tags attached to resources (optional).
attr_accessor :tags
#
# Serializes given resource object into hash.
# @param object [Resource] resource object to serialize.
#
# @return [Hash] hash representation of resource.
def self.serialize_object(object)
object.validate
output_object = {}
serialized_property = object.id
output_object['id'] = serialized_property unless serialized_property.nil?
serialized_property = object.name
output_object['name'] = serialized_property unless serialized_property.nil?
serialized_property = object.type
output_object['type'] = serialized_property unless serialized_property.nil?
serialized_property = object.location
output_object['location'] = serialized_property unless serialized_property.nil?
serialized_property = object.tags
output_object['tags'] = serialized_property unless serialized_property.nil?
output_object
def self.mapper
{
required: false,
serialized_name: 'Resource',
type: {
name: 'Composite',
class_name: 'Resource',
model_properties: {
id: {
required: false,
serialized_name: 'id',
type: {
name: 'String'
}
},
name: {
required: false,
read_only: true,
serialized_name: 'name',
type: {
name: 'String'
}
},
type: {
required: false,
read_only: true,
serialized_name: 'type',
type: {
name: 'String'
}
},
location: {
required: false,
serialized_name: 'location',
type: {
name: 'String'
}
},
tags: {
required: false,
serialized_name: 'tags',
type: {
name: 'Dictionary',
value: {
required: false,
serialized_name: 'StringElementType',
type: {
name: 'String'
}
}
}
}
}
}
}
end
#
# Deserializes given hash object into resource.
# @param object [Hash] resource in hash representation to deserialize.
#
# @return [Resource] deserialized resource.
def self.deserialize_object(object)
return if object.nil?
output_object = Resource.new
deserialized_property = object['id']
output_object.id = deserialized_property
deserialized_property = object['name']
output_object.name = deserialized_property
deserialized_property = object['type']
output_object.type = deserialized_property
deserialized_property = object['location']
output_object.location = deserialized_property
deserialized_property = object['tags']
output_object.tags = deserialized_property
output_object.validate
output_object
end
#
# Validates the resource. Throws error if there is any property is incorrect.
#
def validate
fail MsRest::ValidationError, 'Location cannot be nil in the Resource object' if @location.nil?
end
end
end

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

@ -11,41 +11,24 @@ module MsRestAzure
# @return [String] the id of the subresource.
attr_accessor :id
#
# Serializes given subresource object into hash.
# @param object [SubResource] subresource object to serialize.
#
# @return [Hash] hash representation of subresource.
def self.serialize_object(object)
object.validate
output_object = {}
serialized_property = object.id
output_object['id'] = serialized_property unless serialized_property.nil?
output_object
end
#
# Deserializes given hash object into subresource.
# @param object [Hash] subresource in hash representation to deserialize.
#
# @return [SubResource] deserialized subresource.
def self.deserialize_object(object)
return if object.nil?
output_object = SubResource.new
deserialized_property = object['id']
output_object.id = deserialized_property
output_object.validate
output_object
end
#
# Validates the subresource. Throws error if there is any property is incorrect.
#
def validate
def self.mapper
{
required: false,
serialized_name: 'SubResource',
type: {
name: 'Composite',
class_name: 'SubResource',
model_properties: {
id: {
required: false,
serialized_name: 'id',
type: {
name: 'String'
}
}
}
}
}
end
end
end

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

@ -6,8 +6,15 @@ require 'rspec'
require 'ms_rest_azure'
module MsRestAzure
class Helper
include MsRest::Serialization
end
describe Resource do
before (:all) do
@helper = Helper.new
end
it 'should serialize Resource correctly' do
resource = Resource.new
resource.id = 'id'
@ -19,7 +26,8 @@ module MsRestAzure
'tag2' => 'tag2_value'
}
res = Resource.serialize_object(resource)
allow_any_instance_of(MsRest::Serialization::Serialization).to receive(:get_model).and_return(Resource)
res = @helper.serialize(Resource.mapper(), resource, 'resource')
expect(res).to be_a(Hash)
expect(res['id']).to eq('id')
@ -41,7 +49,8 @@ module MsRestAzure
}
}
res = Resource.deserialize_object(resource_hash)
allow_any_instance_of(MsRest::Serialization::Serialization).to receive(:get_model).and_return(Resource)
res = @helper.deserialize(Resource.mapper(), resource_hash, 'resource_hash')
expect(res).to be_a(Resource)
expect(res.id).to eq('id')
@ -50,16 +59,5 @@ module MsRestAzure
expect(res.location).to eq('location')
expect(res.tags).to eq({ 'tag1' => 'tag1_value', 'tag2' => 'tag2_value' })
end
it 'should throw error if location isn\'t provided' do
resource_hash = {
'id' => 'id',
'name' => 'name',
'type' => 'type'
}
expect { Resource.deserialize_object(resource_hash) }.to raise_error(MsRest::ValidationError)
end
end
end

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

@ -6,13 +6,21 @@ require 'rspec'
require 'ms_rest_azure'
module MsRestAzure
class Helper
include MsRest::Serialization
end
describe SubResource do
before (:all) do
@helper = Helper.new
end
it 'should serialize SubResource correctly' do
sub_resource = SubResource.new
sub_resource.id = 'the_id'
sub_resource_serialized = SubResource.serialize_object(sub_resource)
allow_any_instance_of(MsRest::Serialization::Serialization).to receive(:get_model).and_return(SubResource)
sub_resource_serialized = @helper.serialize(SubResource.mapper(), sub_resource, 'sub_resource')
expect(sub_resource_serialized).to be_a(Hash)
expect(sub_resource_serialized['id']).to eq('the_id')
@ -22,12 +30,11 @@ module MsRestAzure
sub_resource_hash = {
'id' => 'the_id'
}
sub_resource = SubResource.deserialize_object(sub_resource_hash)
allow_any_instance_of(MsRest::Serialization::Serialization).to receive(:get_model).and_return(SubResource)
sub_resource = @helper.deserialize(SubResource.mapper(), sub_resource_hash, 'sub_resource_hash')
expect(sub_resource).to be_a(SubResource)
expect(sub_resource.id).to eq('the_id')
end
end
end

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

@ -24,4 +24,5 @@ require 'ms_rest/http_operation_error'
require 'ms_rest/retry_policy_middleware'
require 'ms_rest/service_client'
module MsRest; end
module MsRest end
module MsRest::Serialization end

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

@ -3,20 +3,364 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
module MsRest
# Base module for Ruby serialization and deserialization.
#
# Class which keeps the auxiliary for (de)serializing JSON requests and responses from server.
#
class Serialization
# Provides methods to serialize Ruby object into Ruby Hash and
# to deserialize Ruby Hash into Ruby object.
module Serialization
#
# Deserialize the response from the server using the mapper.
#
# @param mapper [Hash] Ruby Hash object to represent expected structure of the response_body.
# @param response_body [Hash] Ruby Hash object to deserialize.
# @param object_name [String] Name of the deserialized object.
#
def deserialize(mapper, response_body, object_name)
serialization = Serialization.new(self)
serialization.deserialize(mapper, response_body, object_name)
end
#
# Deserializes given string value into Ruby Date object.
# @param [String] string_value string value to deserialize.
# Serialize the Ruby object into Ruby Hash to send it to the server using the mapper.
#
# @return [Date] deserialized Date object.
def self.deserialize_date(string_value)
result = Timeliness.parse(string_value, :strict => true)
fail DeserializationError.new('Error occured in deserializing the response', nil, nil, string_value) if result.nil?
return ::Date.parse(result.to_s)
# @param mapper [Hash] Ruby Hash object to represent expected structure of the object.
# @param object [Object] Ruby object to serialize.
# @param object_name [String] Name of the serialized object.
#
def serialize(mapper, object, object_name)
serialization = Serialization.new(self)
serialization.serialize(mapper, object, object_name)
end
#
# Class to handle serialization & deserialization.
#
class Serialization
def initialize(context)
@context = context
end
#
# Deserialize the response from the server using the mapper.
#
# @param mapper [Hash] Ruby Hash object to represent expected structure of the response_body.
# @param response_body [Hash] Ruby Hash object to deserialize.
# @param object_name [String] Name of the deserialized object.
#
def deserialize(mapper, response_body, object_name)
return response_body if response_body.nil?
object_name = mapper[:serialized_name] unless object_name.nil?
mapper_type = mapper[:type][:name]
if !mapper_type.match(/^(Number|Double|ByteArray|Boolean|Date|DateTime|DateTimeRfc1123|UnixTime|Enum|String|Object|Stream)$/i).nil?
payload = deserialize_primary_type(mapper, response_body)
elsif !mapper_type.match(/^Dictionary$/i).nil?
payload = deserialize_dictionary_type(mapper, response_body, object_name)
elsif !mapper_type.match(/^Composite$/i).nil?
payload = deserialize_composite_type(mapper, response_body, object_name)
elsif !mapper_type.match(/^Sequence$/i).nil?
payload = deserialize_sequence_type(mapper, response_body, object_name)
else
payload = ""
end
payload = mapper[:default_value] if mapper[:is_constant]
payload
end
#
# Deserialize the response of known primary type from the server using the mapper.
#
# @param mapper [Hash] Ruby Hash object to represent expected structure of the response_body.
# @param response_body [Hash] Ruby Hash object to deserialize.
#
def deserialize_primary_type(mapper, response_body)
result = ""
case mapper[:type][:name]
when 'Number'
result = Integer(response_body) unless response_body.to_s.empty?
when 'Double'
result = Float(response_body) unless response_body.to_s.empty?
when 'ByteArray'
result = Base64.strict_decode64(response_body).unpack('C*') unless response_body.to_s.empty?
when 'String', 'Boolean', 'Object', 'Stream'
result = response_body
when 'Enum'
unless response_body.nil? || response_body.empty?
unless enum_is_valid(mapper, response_body)
warn "Enum #{model} does not contain #{response_body.downcase}, but was received from the server."
end
end
result = response_body
when 'Date'
unless response_body.to_s.empty?
result = Timeliness.parse(response_body, :strict => true)
fail DeserializationError.new('Error occured in deserializing the response_body', nil, nil, response_body) if result.nil?
result = ::Date.parse(result.to_s)
end
when 'DateTime', 'DateTimeRfc1123'
result = DateTime.parse(response_body) unless response_body.to_s.empty?
when 'UnixTime'
result = DateTime.strptime(response_body.to_s, '%s') unless response_body.to_s.empty?
else
result
end
result
end
#
# Deserialize the response of dictionary type from the server using the mapper.
#
# @param mapper [Hash] Ruby Hash object to represent expected structure of the response_body.
# @param response_body [Hash] Ruby Hash object to deserialize.
# @param object_name [String] Name of the deserialized object.
#
def deserialize_dictionary_type(mapper, response_body, object_name)
if mapper[:type][:value].nil? || !mapper[:type][:value].is_a?(Hash)
fail DeserializationError.new("'value' metadata for a dictionary type must be defined in the mapper and it must be of type Hash in #{object_name}", nil, nil, response_body)
end
result = Hash.new
response_body.each do |key, val|
result[key] = deserialize(mapper[:type][:value], val, object_name)
end
result
end
#
# Deserialize the response of composite type from the server using the mapper.
#
# @param mapper [Hash] Ruby Hash object to represent expected structure of the response_body.
# @param response_body [Hash] Ruby Hash object to deserialize.
# @param object_name [String] Name of the deserialized object.
#
def deserialize_composite_type(mapper, response_body, object_name)
if mapper[:type][:class_name].nil?
fail DeserializationError.new("'class_name' metadata for a composite type must be defined in the mapper and it must be of type Hash in #{object_name}", nil, nil, response_body)
end
if !mapper[:type][:polymorphic_discriminator].nil?
# Handle polymorphic types
parent_class = get_model(mapper[:type][:class_name])
discriminator = parent_class.class_eval("@@discriminatorMap")
model_name = response_body["#{mapper[:type][:polymorphic_discriminator]}"]
model_class = get_model(discriminator[model_name].capitalize)
else
model_class = get_model(mapper[:type][:class_name])
end
result = model_class.new
model_mapper = model_class.mapper()
model_props = model_mapper[:type][:model_properties]
unless model_props.nil?
model_props.each do |key, val|
result.instance_variable_set("@#{key}", deserialize(val, response_body[val[:serialized_name].to_s], object_name))
end
end
result
end
#
# Deserialize the response of sequence type from the server using the mapper.
#
# @param mapper [Hash] Ruby Hash object to represent expected structure of the response_body.
# @param response_body [Hash] Ruby Hash object to deserialize.
# @param object_name [String] Name of the deserialized object.
#
def deserialize_sequence_type(mapper, response_body, object_name)
if mapper[:type][:element].nil? || !mapper[:type][:element].is_a?(Hash)
fail DeserializationError.new("'element' metadata for a sequence type must be defined in the mapper and it must be of type Hash in #{object_name}", nil, nil, response_body)
end
return response_body if response_body.nil?
result = []
response_body.each do |element|
result.push(deserialize(mapper[:type][:element], element, object_name))
end
result
end
#
# Serialize the Ruby object into Ruby Hash to send it to the server using the mapper.
#
# @param mapper [Hash] Ruby Hash object to represent expected structure of the object.
# @param object [Object] Ruby object to serialize.
# @param object_name [String] Name of the serialized object.
#
def serialize(mapper, object, object_name)
object_name = mapper[:serialized_name] unless object_name.nil?
if mapper[:required] && object.nil? && !mapper[:is_constant]
fail ValidationError, "#{object_name} is required and cannot be nil"
end
if !mapper[:required] && object.nil?
return object
end
# Set defaults
unless mapper[:default_value].nil?
object = mapper[:default_value] if object.nil?
end
object = mapper[:default_value] if mapper[:is_constant]
payload = Hash.new
mapper_type = mapper[:type][:name]
if !mapper_type.match(/^(Number|Double|ByteArray|Boolean|Date|DateTime|DateTimeRfc1123|UnixTime|Enum|String|Object|Stream)$/i).nil?
payload = serialize_primary_type(mapper, object)
elsif !mapper_type.match(/^Dictionary$/i).nil?
payload = serialize_dictionary_type(mapper, object, object_name)
elsif !mapper_type.match(/^Composite$/i).nil?
payload = serialize_composite_type(mapper, object, object_name)
elsif !mapper_type.match(/^Sequence$/i).nil?
payload = serialize_sequence_type(mapper, object, object_name)
end
payload
end
#
# Serialize the Ruby object of known primary type into Ruby Hash to send it to the server using the mapper.
#
# @param mapper [Hash] Ruby Hash object to represent expected structure of the object.
# @param object [Object] Ruby object to serialize.
#
def serialize_primary_type(mapper, object)
mapper_type = mapper[:type][:name]
payload = nil
case mapper_type
when 'Number', 'Double', 'String', 'Date', 'Boolean', 'Object', 'Stream'
payload = object != nil ? object : nil
when 'Enum'
unless object.nil? || object.empty?
unless enum_is_valid(mapper, object)
fail ValidationError, "Enum #{mapper[:type][:module]} does not contain #{object.to_s}, but trying to send it to the server."
end
end
payload = object != nil ? object : nil
when 'ByteArray'
payload = Base64.strict_encode64(object.pack('c*'))
when 'DateTime'
payload = object.new_offset(0).strftime('%FT%TZ')
when 'DateTimeRfc1123'
payload = object.new_offset(0).strftime('%a, %d %b %Y %H:%M:%S GMT')
when 'UnixTime'
payload = object.new_offset(0).strftime('%s') unless object.nil?
end
payload
end
#
# Serialize the Ruby object of dictionary type into Ruby Hash to send it to the server using the mapper.
#
# @param mapper [Hash] Ruby Hash object to represent expected structure of the object.
# @param object [Object] Ruby object to serialize.
# @param object_name [String] Name of the serialized object.
#
def serialize_dictionary_type(mapper, object, object_name)
unless object.is_a?(Hash)
fail DeserializationError.new("#{object_name} must be of type Hash", nil, nil, object)
end
unless mapper[:type][:value].nil? || mapper[:type][:value].is_a?(Hash)
fail DeserializationError.new("'value' metadata for a dictionary type must be defined in the mapper and it must be of type Hash in #{object_name}", nil, nil, object)
end
payload = Hash.new
object.each do |key, value|
if !value.nil? && value.respond_to?(:validate)
value.validate
end
payload[key] = serialize(mapper[:type][:value], value, object_name)
end
payload
end
#
# Serialize the Ruby object of composite type into Ruby Hash to send it to the server using the mapper.
#
# @param mapper [Hash] Ruby Hash object to represent expected structure of the object.
# @param object [Object] Ruby object to serialize.
# @param object_name [String] Name of the serialized object.
#
def serialize_composite_type(mapper, object, object_name)
if !mapper[:type][:polymorphic_discriminator].nil?
# Handle polymorphic types
model_name = object.class.to_s.split('::')[-1]
model_class = get_model(model_name)
else
model_class = get_model(mapper[:type][:class_name])
end
payload = Hash.new
model_mapper = model_class.mapper()
model_props = model_mapper[:type][:model_properties]
unless model_props.nil?
model_props.each do |key, value|
instance_variable = object.instance_variable_get("@#{key}")
if !instance_variable.nil? && instance_variable.respond_to?(:validate)
instance_variable.validate
end
sub_payload = serialize(value, instance_variable, object_name)
payload[value[:serialized_name].to_s] = sub_payload unless instance_variable.nil?
end
end
payload
end
#
# Serialize the Ruby object of sequence type into Ruby Hash to send it to the server using the mapper.
#
# @param mapper [Hash] Ruby Hash object to represent expected structure of the object.
# @param object [Object] Ruby object to serialize.
# @param object_name [String] Name of the serialized object.
#
def serialize_sequence_type(mapper, object, object_name)
unless object.is_a?(Array)
fail DeserializationError.new("#{object_name} must be of type of Array", nil, nil, object)
end
unless mapper[:type][:element].nil? || mapper[:type][:element].is_a?(Hash)
fail DeserializationError.new("'element' metadata for a sequence type must be defined in the mapper and it must be of type Hash in #{object_name}", nil, nil, object)
end
payload = Array.new
object.each do |element|
if !element.nil? && element.respond_to?(:validate)
element.validate
end
payload.push(serialize(mapper[:type][:element], element, object_name))
end
payload
end
#
# Retrieves model of the model_name
#
# @param model_name [String] Name of the model to retrieve.
#
def get_model(model_name)
Object.const_get(@context.class.to_s.split('::')[0...-1].join('::') + "::Models::#{model_name}")
end
#
# Checks whether given enum_value is valid for the mapper or not
#
# @param mapper [Hash] Ruby Hash object containing meta data
# @param enum_value [String] Enum value to validate
#
def enum_is_valid(mapper, enum_value)
model = get_model(mapper[:type][:module])
model.constants.any? { |e| model.const_get(e).to_s.downcase == enum_value.downcase }
end
end
end
end

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

@ -1,25 +0,0 @@
# encoding: utf-8
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
require 'rspec'
require 'ms_rest'
module MsRest
describe Serialization do
it 'should parse correct date' do
parsed_date = Serialization.deserialize_date('1/1/2000')
expect(parsed_date).to eq(Date.new(2000, 1, 1))
end
it 'should throw error if incorrect date is provided' do
expect { Serialization.deserialize_date('13/1/2000') }.to raise_error(DeserializationError)
end
it 'should throw error if incorrect date format is provided' do
expect { Serialization.deserialize_date('invalid_date') }.to raise_error(DeserializationError)
end
end
end