Connectors: fix default value logic and export functions to yml (#1913)

This commit is contained in:
Luc Genetier 2023-10-04 16:44:29 +02:00 коммит произвёл GitHub
Родитель c2f9bbdad5
Коммит 7874752d05
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 483 добавлений и 73 удалений

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

@ -355,14 +355,12 @@ Output/
# Tool folder
tool/
# Report folder
report/
# ApiCompat suppression files
suppress.*.xml
# Connector analysis reports
/report/Analysis.txt
/report/Report.json
/report/Report_*.json
# global.json
/src/global.json
/global.json

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

@ -827,6 +827,11 @@ namespace Microsoft.PowerFx.Connectors
// Only used by ConnectorTexlFunction
private DType[] GetParamTypes()
{
if (RequiredParameters == null)
{
return Array.Empty<DType>();
}
IEnumerable<DType> parameterTypes = RequiredParameters.Select(parameter => parameter.FormulaType._type);
if (OptionalParameters.Any())
{

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

@ -67,7 +67,7 @@ namespace Microsoft.PowerFx.Connectors
}
else if (param.Schema.Default != null)
{
if (OpenApiExtensions.TryGetOpenApiValue(param.Schema.Default, out FormulaValue defaultValue))
if (OpenApiExtensions.TryGetOpenApiValue(param.Schema.Default, null, out FormulaValue defaultValue))
{
bodyParts.Add(param.Name, (param.Schema, defaultValue));
}
@ -239,9 +239,19 @@ namespace Microsoft.PowerFx.Connectors
lst.Remove(field1);
lst.Add(field2);
}
else if (field1.Value is BlankValue)
{
lst.Remove(field1);
lst.Add(field2);
}
else if (field2.Value is BlankValue)
{
lst.Remove(field2);
lst.Add(field1);
}
else
{
throw new ArgumentException($"Cannot merge {field1.Name} of type {field1.Value.GetType().Name} with {field2.Name} of type {field2.Value.GetType().Name}");
throw new ArgumentException($"Cannot merge '{field1.Name}' of type {field1.Value.GetType().Name} with '{field2.Name}' of type {field2.Value.GetType().Name}");
}
}
}

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

@ -123,47 +123,104 @@ namespace Microsoft.PowerFx.Connectors
}
public static bool TryGetDefaultValue(this OpenApiSchema schema, FormulaType formulaType, out FormulaValue defaultValue)
{
IOpenApiAny openApiDefaultValue = schema.Default;
if (openApiDefaultValue == null)
{
if (formulaType is RecordType rt && schema.Properties != null)
{
List<NamedValue> values = new List<NamedValue>();
foreach (NamedFormulaType nft in rt.GetFieldTypes())
{
string columnName = nft.Name.Value;
if (schema.Properties.ContainsKey(columnName))
{
if (schema.Properties[columnName].TryGetDefaultValue(nft.Type, out FormulaValue innerDefaultValue))
{
values.Add(new NamedValue(columnName, innerDefaultValue));
}
}
}
if (values.Any())
{
defaultValue = new InMemoryRecordValue(IRContext.NotInSource(rt), values);
return true;
}
}
defaultValue = null;
return false;
{
if (schema.Type == "array" && formulaType is TableType tableType && schema.Items != null)
{
RecordType recordType = tableType.ToRecord();
bool b = schema.Items.TryGetDefaultValue(recordType, out FormulaValue itemDefaultValue);
if (!b || itemDefaultValue is BlankValue)
{
defaultValue = FormulaValue.NewTable(recordType);
return true;
}
if (itemDefaultValue is RecordValue itemDefaultRecordValue)
{
defaultValue = FormulaValue.NewTable(recordType, itemDefaultRecordValue);
return true;
}
defaultValue = FormulaValue.NewTable(recordType, FormulaValue.NewRecordFromFields(new NamedValue[] { new NamedValue(TableValue.ValueName, itemDefaultValue) }));
return true;
}
return TryGetOpenApiValue(openApiDefaultValue, out defaultValue);
if (schema.Type == "object" || schema.Default == null)
{
if (formulaType is RecordType recordType2 && schema.Properties != null)
{
List<NamedValue> values = new List<NamedValue>();
foreach (NamedFormulaType namedFormulaType in recordType2.GetFieldTypes())
{
string columnName = namedFormulaType.Name.Value;
if (schema.Properties.ContainsKey(columnName))
{
if (schema.Properties[columnName].TryGetDefaultValue(namedFormulaType.Type, out FormulaValue innerDefaultValue))
{
values.Add(new NamedValue(columnName, innerDefaultValue));
}
else
{
values.Add(new NamedValue(columnName, FormulaValue.NewBlank(namedFormulaType.Type)));
}
}
}
defaultValue = values.Any(v => v.Value is not BlankValue) ? FormulaValue.NewRecordFromFields(values) : FormulaValue.NewBlank(recordType2);
return true;
}
if (schema.Default == null)
{
defaultValue = null;
return false;
}
}
return TryGetOpenApiValue(schema.Default, formulaType, out defaultValue);
}
internal static bool TryGetOpenApiValue(IOpenApiAny openApiAny, out FormulaValue formulaValue)
internal static bool TryGetOpenApiValue(IOpenApiAny openApiAny, FormulaType formulaType, out FormulaValue formulaValue)
{
if (openApiAny is OpenApiString str)
{
formulaValue = FormulaValue.New(str.Value);
{
formulaValue = null;
if (formulaType != null && formulaType is not StringType && formulaType is not RecordType)
{
if (formulaType is BooleanType && bool.TryParse(str.Value, out bool b))
{
formulaValue = FormulaValue.New(b);
}
else if (formulaType is DecimalType && decimal.TryParse(str.Value, out decimal d))
{
formulaValue = FormulaValue.New(d);
}
else if (formulaType is DateTimeType)
{
if (DateTime.TryParse(str.Value, out DateTime dt))
{
formulaValue = FormulaValue.New(dt);
}
throw new PowerFxConnectorException($"Unsupported DateTime format: {str.Value}");
}
else if (formulaType is NumberType && double.TryParse(str.Value, out double dbl))
{
formulaValue = FormulaValue.New(dbl);
}
else if (formulaType is OptionSetValueType osvt && osvt.TryGetValue(new DName(str.Value), out OptionSetValue osv))
{
formulaValue = osv;
}
}
if (formulaValue == null)
{
formulaValue = FormulaValue.New(str.Value);
}
}
else if (openApiAny is OpenApiInteger intVal)
{
@ -210,7 +267,17 @@ namespace Microsoft.PowerFx.Connectors
foreach (IOpenApiAny element in arr)
{
bool ba = TryGetOpenApiValue(element, out FormulaValue fv);
FormulaType newType = null;
if (formulaType != null)
{
if (formulaType is TableType tableType)
{
newType = tableType.ToRecord();
}
}
bool ba = TryGetOpenApiValue(element, newType, out FormulaValue fv);
if (!ba)
{
formulaValue = null;
@ -232,7 +299,7 @@ namespace Microsoft.PowerFx.Connectors
foreach (KeyValuePair<string, IOpenApiAny> kvp in o)
{
if (TryGetOpenApiValue(kvp.Value, out FormulaValue fv))
if (TryGetOpenApiValue(kvp.Value, null, out FormulaValue fv))
{
dvParams[kvp.Key] = fv;
}
@ -390,7 +457,7 @@ namespace Microsoft.PowerFx.Connectors
return new ConnectorType(schema, openApiParameter, FormulaType.UntypedObject);
}
chain.Push(innerA);
chain.Push(innerA);
ConnectorType arrayType = new OpenApiParameter() { Name = "Array", Required = true, Schema = schema.Items, Extensions = schema.Items.Extensions }.ToConnectorType(chain, level + 1);
chain.Pop();
@ -536,13 +603,13 @@ namespace Microsoft.PowerFx.Connectors
}
if (response == null)
{
// No return type. Void() method.
{
// No return type. Void() method.
return (new ConnectorType(), null);
}
if (response.Content.Count == 0)
{
{
OpenApiSchema schema = new OpenApiSchema() { Type = "string", Format = "binary" };
ConnectorType connectorType = new OpenApiParameter() { Name = "response", Required = true, Schema = schema, Extensions = response.Extensions }.ToConnectorType();
return (connectorType, null);
@ -727,7 +794,7 @@ namespace Microsoft.PowerFx.Connectors
foreach (KeyValuePair<string, IOpenApiAny> prm in opPrms)
{
if (!OpenApiExtensions.TryGetOpenApiValue(prm.Value, out FormulaValue fv))
if (!OpenApiExtensions.TryGetOpenApiValue(prm.Value, null, out FormulaValue fv))
{
throw new NotImplementedException($"Unsupported param with OpenApi type {prm.Value.GetType().FullName}, key = {prm.Key}");
}

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

@ -383,6 +383,7 @@ namespace Microsoft.PowerFx.Connectors
isSupported = false;
notSupportedReason = $"OpenApiParameter is null";
logger?.LogWarning($"OperationId {op.OperationId} has a null OpenApiParameter");
return;
}
if (param.Deprecated)

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

@ -24,7 +24,7 @@ namespace Microsoft.PowerFx.Connectors
public string Title => Schema.Title;
public FormulaType FormulaType => UseHiddenTypes ? ConnectorType.HiddenRecordType : ConnectorType.FormulaType;
internal RecordType HiddenRecordType => ConnectorType.HiddenRecordType;
public string Summary => ConnectorExtensions.Summary;
@ -36,8 +36,7 @@ namespace Microsoft.PowerFx.Connectors
Schema = openApiParameter.Schema;
UseHiddenTypes = useHiddenTypes;
ConnectorType = openApiParameter.ToConnectorType();
DefaultValue = openApiParameter.Schema.TryGetDefaultValue(FormulaType, out FormulaValue defaultValue) ? defaultValue : null;
DefaultValue = openApiParameter.Schema.TryGetDefaultValue(FormulaType, out FormulaValue defaultValue) && defaultValue is not BlankValue ? defaultValue : null;
ConnectorExtensions = new ConnectorExtensions(openApiParameter, bodyExtensions);
}

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

@ -96,7 +96,7 @@ namespace Microsoft.PowerFx.Connectors
if (IsEnum)
{
EnumValues = schema.Enum.Select(oaa => OpenApiExtensions.TryGetOpenApiValue(oaa, out FormulaValue fv) ? fv : throw new NotSupportedException($"Invalid conversion for type {oaa.GetType().Name} in enum")).ToArray();
EnumValues = schema.Enum.Select(oaa => OpenApiExtensions.TryGetOpenApiValue(oaa, null, out FormulaValue fv) ? fv : throw new NotSupportedException($"Invalid conversion for type {oaa.GetType().Name} in enum")).ToArray();
EnumDisplayNames = schema.Extensions != null && schema.Extensions.TryGetValue(XMsEnumDisplayName, out IOpenApiExtension enumNames) && enumNames is OpenApiArray oaa
? oaa.Cast<OpenApiString>().Select(oas => oas.Value).ToArray()
: Array.Empty<string>();

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

@ -4,15 +4,17 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
using Microsoft.OpenApi.Models;
using Microsoft.PowerFx.Tests;
using Microsoft.VisualStudio.TestPlatform.Utilities;
using Microsoft.PowerFx.Types;
using Xunit;
using Xunit.Abstractions;
using YamlDotNet.Serialization;
namespace Microsoft.PowerFx.Connectors.Tests
{
@ -35,12 +37,19 @@ namespace Microsoft.PowerFx.Connectors.Tests
{
string outFolder = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, @"..\..\..\..\..\.."));
string srcFolder = Path.GetFullPath(Path.Combine(outFolder, ".."));
string reportName = @"report\Analysis.txt";
string jsonReport = @"report\Report.json";
string reportFolder = @"report";
string reportName = @$"{reportFolder}\Analysis.txt";
string jsonReport = @$"{reportFolder}\Report.json";
// New report name every second
string jsonReport2 = @$"report\Report_{Math.Round(DateTime.UtcNow.Ticks / 1e7):00000000000}.json";
string outFolderPath = Path.Combine(outFolder, reportFolder);
if (Directory.Exists(outFolderPath))
{
Directory.Delete(outFolderPath, true);
}
// On build servers: ENV: C:\__w\1\s\pfx\src\tests\Microsoft.PowerFx.Connectors.Tests\bin\Release\netcoreapp3.1
// Locally : ENV: C:\Data\Power-Fx\src\tests\Microsoft.PowerFx.Connectors.Tests\bin\Debug\netcoreapp3.1
_output.WriteLine($"ENV: {Environment.CurrentDirectory}");
@ -48,7 +57,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
_output.WriteLine($"SRC: {srcFolder}");
Directory.CreateDirectory(Path.Combine(outFolder, "report"));
GenerateReport(reportName, outFolder, srcFolder);
GenerateReport(reportFolder, reportName, outFolder, srcFolder);
AnalyzeReport(reportName, outFolder, srcFolder, jsonReport);
File.Copy(Path.Combine(outFolder, jsonReport), Path.Combine(outFolder, jsonReport2));
@ -325,7 +334,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
White = 3, // #FFFFFF
}
private void GenerateReport(string reportName, string outFolder, string srcFolder)
private void GenerateReport(string reportFolder, string reportName, string outFolder, string srcFolder)
{
int i = 0;
int j = 0;
@ -359,28 +368,47 @@ namespace Microsoft.PowerFx.Connectors.Tests
// Check we can add the service (more comprehensive test)
config.AddActionConnector("Connector", doc);
IEnumerable<ConnectorFunction> functions2 = OpenApiParser.GetFunctions(new ConnectorSettings("C1") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, doc);
IEnumerable<ConnectorFunction> functions2 = OpenApiParser.GetFunctions(new ConnectorSettings("C1") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, doc);
string cFolder = Path.Combine(outFolder, reportFolder, doc.Info.Title);
int ix = 2;
while (Directory.Exists(cFolder))
{
cFolder = Path.Combine(outFolder, reportFolder, doc.Info.Title) + $"_{ix++}";
}
Directory.CreateDirectory(cFolder);
foreach (ConnectorFunction cf1 in functions)
{
ConnectorFunction cf2 = functions2.First(f => f.Name == cf1.Name);
string rp1 = string.Join(", ", cf1.RequiredParameters.Select(rp => rp.Name));
string rp2 = string.Join(", ", cf2.RequiredParameters.Select(rp => rp.Name));
if (rp1 != rp2)
if (cf1.RequiredParameters != null && cf2.RequiredParameters != null)
{
string s = $"Function {cf1.Name} - Required parameters are different: [{rp1}] -- [{rp2}]";
if (w2.ContainsKey(title))
string rp1 = string.Join(", ", cf1.RequiredParameters.Select(rp => rp.Name));
string rp2 = string.Join(", ", cf2.RequiredParameters.Select(rp => rp.Name));
if (rp1 != rp2)
{
w2[title].Add(s);
string s = $"Function {cf1.Name} - Required parameters are different: [{rp1}] -- [{rp2}]";
if (w2.ContainsKey(title))
{
w2[title].Add(s);
}
else
{
w2.Add(title, new List<string>() { s });
}
}
else
{
w2.Add(title, new List<string>() { s });
}
}
dynamic obj = cf1.ToExpando(swaggerFile);
var serializer = new SerializerBuilder().Build();
string yaml = serializer.Serialize(obj);
string functionFile = Path.Combine(cFolder, cf1.OriginalName.Replace("/", "_") + ".yaml");
File.WriteAllText(functionFile, yaml);
}
}
catch (Exception ex)
@ -400,7 +428,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
}
}
using StreamWriter writer2 = new StreamWriter(Path.Combine(outFolder, "ConnectorComparison.txt"), append: false);
using StreamWriter writer2 = new StreamWriter(Path.Combine(outFolder, reportFolder, "ConnectorComparison.txt"), append: false);
foreach (KeyValuePair<string, List<string>> kvp in w2.OrderBy(kvp => kvp.Key))
{
@ -509,5 +537,306 @@ namespace Microsoft.PowerFx.Connectors.Tests
console.WriteLine(obj.ToString());
}
}
public static ExpandoObject ToExpando(this ConnectorFunction connectorFunction, string swaggerFile)
{
dynamic func = new ExpandoObject();
func.Name = connectorFunction.Name;
func.OperationId = connectorFunction.OriginalName;
func.Method = connectorFunction.HttpMethod.ToString().ToUpperInvariant();
func.Path = connectorFunction.OperationPath;
func.SwaggerFile = swaggerFile;
if (!string.IsNullOrEmpty(connectorFunction.Description))
{
func.Description = connectorFunction.Description;
}
if (!string.IsNullOrEmpty(connectorFunction.Summary))
{
func.Summary = connectorFunction.Summary;
}
func.IsBehavior = connectorFunction.IsBehavior;
func.IsDeprecated = connectorFunction.IsDeprecated;
func.IsInternal = connectorFunction.IsInternal;
func.IsPageable = connectorFunction.IsPageable;
if (connectorFunction.RequiresUserConfirmation)
{
func.RequiresUserConfirmation = connectorFunction.RequiresUserConfirmation;
}
if (!string.IsNullOrEmpty(connectorFunction.Visibility))
{
func.Visibility = connectorFunction.Visibility;
}
func.ReturnType = connectorFunction.ReturnType._type.ToString();
func.ReturnType_Detailed = connectorFunction.ConnectorReturnType == null ? (dynamic)"null" : connectorFunction.ConnectorReturnType.ToExpando(noname: true);
func.ArityMin = connectorFunction.ArityMin;
func.ArityMax = connectorFunction.ArityMax;
func.RequiredParameters = connectorFunction.RequiredParameters == null ? (dynamic)"null" : connectorFunction.RequiredParameters.Select(rp => rp.ToExpando()).ToList();
func.OptionalParameters = connectorFunction.OptionalParameters == null ? (dynamic)"null" : connectorFunction.OptionalParameters.Select(op => op.ToExpando()).ToList();
return func;
}
internal static ExpandoObject ToExpando(this ConnectorParameter connectorParam)
{
dynamic cParam = new ExpandoObject();
cParam.Name = connectorParam.Name;
if (!string.IsNullOrEmpty(connectorParam.Description))
{
cParam.Description = connectorParam.Description;
}
if (connectorParam.Location != null)
{
cParam.Location = connectorParam.Location.ToString();
}
cParam.FormulaType = connectorParam.FormulaType._type.ToString();
cParam.Type = connectorParam.ConnectorType.ToExpando();
if (connectorParam.DefaultValue != null)
{
cParam.DefaultValue = connectorParam.DefaultValue.ToExpression();
}
if (!string.IsNullOrEmpty(connectorParam.Title))
{
cParam.Title = connectorParam.Title;
}
if (connectorParam.ConnectorExtensions.ExplicitInput)
{
cParam.ExplicitInput = connectorParam.ConnectorExtensions.ExplicitInput;
}
return cParam;
}
internal static ExpandoObject ToExpando(this ConnectorType connectorType, bool noname = false)
{
dynamic cType = new ExpandoObject();
if (!noname)
{
cType.Name = connectorType.Name;
if (!string.IsNullOrEmpty(connectorType.DisplayName))
{
cType.DisplayName = connectorType.DisplayName;
}
if (!string.IsNullOrEmpty(connectorType.Description))
{
cType.Description = connectorType.Description;
}
}
cType.IsRequired = connectorType.IsRequired;
if (connectorType.Fields != null && connectorType.Fields.Any())
{
cType.Fields = connectorType.Fields.Select(fieldCT => fieldCT.ToExpando()).ToList();
}
cType.FormulaType = connectorType.FormulaType._type.ToString();
if (connectorType.ExplicitInput)
{
cType.ExplicitInput = connectorType.ExplicitInput;
}
cType.IsEnum = connectorType.IsEnum;
if (connectorType.IsEnum)
{
bool hasDisplayNames = connectorType.EnumDisplayNames != null && connectorType.EnumDisplayNames.Length > 0;
cType.EnumValues = connectorType.EnumValues.Select<FormulaValue, object>((ev, i) => GetEnumExpando(hasDisplayNames ? connectorType.EnumDisplayNames[i] : null, ev.ToObject().ToString(), ev.Type._type.ToString())).ToList();
}
if (connectorType.Visibility != Visibility.None && connectorType.Visibility != Visibility.Unknown)
{
cType.Visibility = connectorType.Visibility.ToString();
}
if (connectorType.DynamicList != null)
{
cType.DynamicList = connectorType.DynamicList.ToExpando();
}
if (connectorType.DynamicProperty != null)
{
cType.DynamicProperty = connectorType.DynamicProperty.ToExpando();
}
if (connectorType.DynamicSchema != null)
{
cType.DynamicSchema = connectorType.DynamicSchema.ToExpando();
}
if (connectorType.DynamicValues != null)
{
cType.DynamicValues = connectorType.DynamicValues.ToExpando();
}
return cType;
}
internal static ExpandoObject GetEnumExpando(string displayName, string value, string type)
{
dynamic e = new ExpandoObject();
if (!string.IsNullOrEmpty(displayName))
{
e.DisplayName = displayName;
}
e.Value = value;
e.Type = type;
return e;
}
internal static ExpandoObject ToExpando(this ConnectorDynamicList dynamicList)
{
dynamic dList = new ExpandoObject();
dList.OperationId = dynamicList.OperationId;
if (!string.IsNullOrEmpty(dynamicList.ItemValuePath))
{
dList.ItemValuePath = dynamicList.ItemValuePath;
}
if (!string.IsNullOrEmpty(dynamicList.ItemPath))
{
dList.ItemPath = dynamicList.ItemPath;
}
if (!string.IsNullOrEmpty(dynamicList.ItemTitlePath))
{
dList.ItemTitlePath = dynamicList.ItemTitlePath;
}
if (dynamicList.ParameterMap != null)
{
dList.Map = dynamicList.ParameterMap.ToExpando();
}
return dList;
}
internal static ExpandoObject ToExpando(this ConnectorDynamicProperty dynamicProp)
{
dynamic dProp = new ExpandoObject();
dProp.OperationId = dynamicProp.OperationId;
if (!string.IsNullOrEmpty(dynamicProp.ItemValuePath))
{
dProp.ItemValuePath = dynamicProp.ItemValuePath;
}
if (dynamicProp.ParameterMap != null)
{
dProp.Map = dynamicProp.ParameterMap.ToExpando();
}
return dProp;
}
internal static ExpandoObject ToExpando(this ConnectorDynamicSchema dynamicSchema)
{
dynamic dSchema = new ExpandoObject();
dSchema.OperationId = dynamicSchema.OperationId;
if (!string.IsNullOrEmpty(dynamicSchema.ValuePath))
{
dSchema.ValuePath = dynamicSchema.ValuePath;
}
if (dynamicSchema.ParameterMap != null)
{
dSchema.Map = dynamicSchema.ParameterMap.ToExpando();
}
return dSchema;
}
internal static ExpandoObject ToExpando(this ConnectorDynamicValue dynamicValue)
{
dynamic dValue = new ExpandoObject();
dValue.OperationId = dynamicValue.OperationId;
if (!string.IsNullOrEmpty(dynamicValue.ValuePath))
{
dValue.ValuePath = dynamicValue.ValuePath;
}
if (!string.IsNullOrEmpty(dynamicValue.ValueTitle))
{
dValue.ValueTitle = dynamicValue.ValueTitle;
}
if (!string.IsNullOrEmpty(dynamicValue.ValueCollection))
{
dValue.ValueCollection = dynamicValue.ValueCollection;
}
if (dynamicValue.ParameterMap != null)
{
dValue.Map = dynamicValue.ParameterMap.ToExpando();
}
return dValue;
}
internal static ExpandoObject ToExpando(this Dictionary<string, IConnectorExtensionValue> paramMap)
{
IDictionary<string, object> dMap = new ExpandoObject() as IDictionary<string, object>;
foreach (var kvp in paramMap)
{
if (kvp.Value is StaticConnectorExtensionValue scev)
{
dMap[kvp.Key] = GetStaticExt(scev.Value.ToExpression(), scev.Value.Type._type.ToString());
}
else if (kvp.Value is DynamicConnectorExtensionValue dcev)
{
dMap[kvp.Key] = GetDynamicExt(dcev.Reference);
}
}
return (dynamic)dMap;
}
internal static ExpandoObject GetStaticExt(string value, string type)
{
dynamic s = new ExpandoObject();
s.Value = value;
s.Type = type;
return s;
}
internal static ExpandoObject GetDynamicExt(string @ref)
{
dynamic s = new ExpandoObject();
s.Reference = @ref;
return s;
}
}
}

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

@ -102,7 +102,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
RecordValue rv2 = engine.Eval(@"{a:""x""}") as RecordValue;
var ex = Assert.Throws<ArgumentException>(() => HttpFunctionInvoker.MergeRecords(rv1, rv2));
Assert.Equal("Cannot merge a of type DecimalValue with a of type StringValue", ex.Message);
Assert.Equal("Cannot merge 'a' of type DecimalValue with 'a' of type StringValue", ex.Message);
}
[Fact]

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

@ -18,6 +18,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Xunit.SkippableFact" Version="1.4.13" />
<PackageReference Include="YamlDotNet" Version="13.4.0" />
</ItemGroup>
<ItemGroup>