From f0af967deb8a79e719dd29a835d97563f185b5d2 Mon Sep 17 00:00:00 2001
From: Luc Genetier <69138830+LucGenetier@users.noreply.github.com>
Date: Wed, 8 Jan 2025 16:45:38 +0100
Subject: [PATCH] Add ReturnEnumsAsPrimitive connector settings (#2807)
When SupportXMsEnumValues is set to true, we want to force
'modelAsString' to false via ReturnEnumsAsPrimitive flag
This will keep the x-ms-enum-* extensions to be parsed while creating a
consistent FormulaType as string or decimal
---
.../OpenApiExtensions.cs | 10 ++
.../Public/ConnectorSettings.cs | 11 +-
.../Public/ConnectorType.cs | 4 +-
.../PowerPlatformConnectorTests.cs | 136 ++++++++++++------
4 files changed, 116 insertions(+), 45 deletions(-)
diff --git a/src/libraries/Microsoft.PowerFx.Connectors/OpenApiExtensions.cs b/src/libraries/Microsoft.PowerFx.Connectors/OpenApiExtensions.cs
index 0f6fc9fe06..3a8656c951 100644
--- a/src/libraries/Microsoft.PowerFx.Connectors/OpenApiExtensions.cs
+++ b/src/libraries/Microsoft.PowerFx.Connectors/OpenApiExtensions.cs
@@ -716,6 +716,11 @@ private static ConnectorType TryGetOptionSet(ISwaggerParameter openApiParameter,
return new ConnectorType(schema, openApiParameter, FormulaType.String, list: list, isNumber: isNumber);
}
+ if (settings.Settings.ReturnEnumsAsPrimitive)
+ {
+ return new ConnectorType(schema, openApiParameter, isNumber ? FormulaType.Decimal : FormulaType.String, list: list, isNumber: isNumber);
+ }
+
return new ConnectorType(schema, openApiParameter, optionSet.FormulaType);
}
@@ -737,6 +742,11 @@ private static ConnectorType TryGetOptionSet(ISwaggerParameter openApiParameter,
return new ConnectorType(schema, openApiParameter, FormulaType.String, list: dic);
}
+ if (settings.Settings.ReturnEnumsAsPrimitive)
+ {
+ return new ConnectorType(schema, openApiParameter, isNumber ? FormulaType.Decimal : FormulaType.String, list: list, isNumber: isNumber);
+ }
+
return new ConnectorType(schema, openApiParameter, optionSet.FormulaType);
}
else
diff --git a/src/libraries/Microsoft.PowerFx.Connectors/Public/ConnectorSettings.cs b/src/libraries/Microsoft.PowerFx.Connectors/Public/ConnectorSettings.cs
index 9605730c4b..c40b808bc0 100644
--- a/src/libraries/Microsoft.PowerFx.Connectors/Public/ConnectorSettings.cs
+++ b/src/libraries/Microsoft.PowerFx.Connectors/Public/ConnectorSettings.cs
@@ -14,7 +14,8 @@ public class ConnectorSettings
internal static readonly ConnectorSettings DefaultCdp = new ConnectorSettings(null)
{
Compatibility = ConnectorCompatibility.CdpCompatibility,
- SupportXMsEnumValues = true
+ SupportXMsEnumValues = true,
+ ReturnEnumsAsPrimitive = false
};
public ConnectorSettings(string @namespace)
@@ -85,7 +86,13 @@ public bool ExposeInternalParamsWithoutDefaultValue
/// By default action connectors won't parse x-ms-enum-values.
/// Only CDP connectors will have this enabled by default.
///
- public bool SupportXMsEnumValues { get; init; } = false;
+ public bool SupportXMsEnumValues { get; init; } = false;
+
+ ///
+ /// This flag will force all enums to be returns as FormulaType.String or FormulaType.Decimal regardless of x-ms-enum-*.
+ /// This flag is only in effect when SupportXMsEnumValues is true.
+ ///
+ public bool ReturnEnumsAsPrimitive { get; init; } = false;
public ConnectorCompatibility Compatibility { get; init; } = ConnectorCompatibility.Default;
}
diff --git a/src/libraries/Microsoft.PowerFx.Connectors/Public/ConnectorType.cs b/src/libraries/Microsoft.PowerFx.Connectors/Public/ConnectorType.cs
index 3ac353475e..83e8204eb3 100644
--- a/src/libraries/Microsoft.PowerFx.Connectors/Public/ConnectorType.cs
+++ b/src/libraries/Microsoft.PowerFx.Connectors/Public/ConnectorType.cs
@@ -162,8 +162,8 @@ internal ConnectorType(ISwaggerSchema schema, ISwaggerParameter openApiParameter
{
if (list != null && list.Any())
{
- EnumValues = list.Select, FormulaValue>(kvp => isNumber ? FormulaValue.New(decimal.Parse(kvp.Value.Value, CultureInfo.InvariantCulture)) : FormulaValue.New(kvp.Value)).ToArray();
- EnumDisplayNames = list.Select(list => list.Key.Value).ToArray();
+ EnumValues = list.Select, FormulaValue>(kvp => isNumber ? FormulaValue.New(decimal.Parse(kvp.Key.Value, CultureInfo.InvariantCulture)) : FormulaValue.New(kvp.Key)).ToArray();
+ EnumDisplayNames = list.Select(list => list.Value.Value).ToArray();
}
else
{
diff --git a/src/tests/Microsoft.PowerFx.Connectors.Tests.Shared/PowerPlatformConnectorTests.cs b/src/tests/Microsoft.PowerFx.Connectors.Tests.Shared/PowerPlatformConnectorTests.cs
index ce52cbe1cf..f5817a9ea9 100644
--- a/src/tests/Microsoft.PowerFx.Connectors.Tests.Shared/PowerPlatformConnectorTests.cs
+++ b/src/tests/Microsoft.PowerFx.Connectors.Tests.Shared/PowerPlatformConnectorTests.cs
@@ -46,16 +46,30 @@ private static void AssertEqual(string expected, string actual)
}
[Theory]
- [InlineData(ConnectorCompatibility.SwaggerCompatibility, true)]
- [InlineData(ConnectorCompatibility.SwaggerCompatibility, false)]
- [InlineData(ConnectorCompatibility.PowerAppsCompatibility, true)]
- [InlineData(ConnectorCompatibility.PowerAppsCompatibility, false)]
- [InlineData(ConnectorCompatibility.CdpCompatibility, true)]
- [InlineData(ConnectorCompatibility.CdpCompatibility, false)]
- public void MSNWeather_OptionSets(ConnectorCompatibility connectorCompatibility, bool supportXMsEnumValues)
+ [InlineData(ConnectorCompatibility.SwaggerCompatibility, true, false)]
+ [InlineData(ConnectorCompatibility.SwaggerCompatibility, false, false)]
+ [InlineData(ConnectorCompatibility.SwaggerCompatibility, true, true)]
+ [InlineData(ConnectorCompatibility.SwaggerCompatibility, false, true)]
+ [InlineData(ConnectorCompatibility.PowerAppsCompatibility, true, false)]
+ [InlineData(ConnectorCompatibility.PowerAppsCompatibility, false, false)]
+ [InlineData(ConnectorCompatibility.PowerAppsCompatibility, true, true)]
+ [InlineData(ConnectorCompatibility.PowerAppsCompatibility, false, true)]
+ [InlineData(ConnectorCompatibility.CdpCompatibility, true, false)]
+ [InlineData(ConnectorCompatibility.CdpCompatibility, false, false)]
+ [InlineData(ConnectorCompatibility.CdpCompatibility, true, true)]
+ [InlineData(ConnectorCompatibility.CdpCompatibility, false, true)]
+ public void MSNWeather_OptionSets(ConnectorCompatibility connectorCompatibility, bool supportXMsEnumValues, bool returnEnumsAsPrimitive)
{
using var testConnector = new LoggingTestServer(@"Swagger\MSNWeather.json", _output);
- List functions = OpenApiParser.GetFunctions(new ConnectorSettings("MSNWeather") { Compatibility = connectorCompatibility, SupportXMsEnumValues = supportXMsEnumValues }, testConnector._apiDocument).ToList();
+ List functions = OpenApiParser.GetFunctions(
+ new ConnectorSettings("MSNWeather")
+ {
+ Compatibility = connectorCompatibility,
+ SupportXMsEnumValues = supportXMsEnumValues,
+ ReturnEnumsAsPrimitive = returnEnumsAsPrimitive
+ },
+ testConnector._apiDocument).ToList();
+
ConnectorFunction currentWeather = functions.First(f => f.Name == "CurrentWeather");
Assert.Equal(2, currentWeather.RequiredParameters.Length);
@@ -69,35 +83,56 @@ public void MSNWeather_OptionSets(ConnectorCompatibility connectorCompatibility,
if (connectorCompatibility == ConnectorCompatibility.CdpCompatibility || supportXMsEnumValues)
{
- Assert.Equal(FormulaType.OptionSetValue, currentWeather.RequiredParameters[1].FormulaType);
+ if (returnEnumsAsPrimitive)
+ {
+ Assert.Equal(FormulaType.String, currentWeather.RequiredParameters[1].FormulaType);
+ }
+ else
+ {
+ Assert.Equal(FormulaType.OptionSetValue, currentWeather.RequiredParameters[1].FormulaType);
+ }
// Dictionary is used here, so we need to reorder
Assert.Equal("Imperial,Metric", string.Join(",", currentWeather.RequiredParameters[1].ConnectorType.Enum.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Key)));
Assert.Equal("I,C", string.Join(",", currentWeather.RequiredParameters[1].ConnectorType.Enum.OrderBy(kvp => kvp.Key).Select(kvp => (kvp.Value as StringValue).Value)));
- Assert.Equal("Imperial,Metric", string.Join(",", currentWeather.RequiredParameters[1].ConnectorType.EnumDisplayNames));
+ Assert.Equal("Imperial,Metric", string.Join(",", currentWeather.RequiredParameters[1].ConnectorType.EnumDisplayNames));
}
else
{
- Assert.Equal(FormulaType.String, currentWeather.RequiredParameters[1].FormulaType);
+ Assert.Equal(FormulaType.String, currentWeather.RequiredParameters[1].FormulaType);
}
Assert.Equal("I,C", string.Join(",", currentWeather.RequiredParameters[1].ConnectorType.EnumValues.Select(fv => (fv as StringValue).Value)));
}
[Theory]
- [InlineData(ConnectorCompatibility.SwaggerCompatibility, true)]
- [InlineData(ConnectorCompatibility.SwaggerCompatibility, false)]
- [InlineData(ConnectorCompatibility.PowerAppsCompatibility, true)]
- [InlineData(ConnectorCompatibility.PowerAppsCompatibility, false)]
- [InlineData(ConnectorCompatibility.CdpCompatibility, true)]
- [InlineData(ConnectorCompatibility.CdpCompatibility, false)]
+ [InlineData(ConnectorCompatibility.SwaggerCompatibility, true, false)]
+ [InlineData(ConnectorCompatibility.SwaggerCompatibility, false, false)]
+ [InlineData(ConnectorCompatibility.SwaggerCompatibility, true, true)]
+ [InlineData(ConnectorCompatibility.SwaggerCompatibility, false, true)]
+ [InlineData(ConnectorCompatibility.PowerAppsCompatibility, true, false)]
+ [InlineData(ConnectorCompatibility.PowerAppsCompatibility, false, false)]
+ [InlineData(ConnectorCompatibility.PowerAppsCompatibility, true, true)]
+ [InlineData(ConnectorCompatibility.PowerAppsCompatibility, false, true)]
+ [InlineData(ConnectorCompatibility.CdpCompatibility, true, false)]
+ [InlineData(ConnectorCompatibility.CdpCompatibility, false, false)]
+ [InlineData(ConnectorCompatibility.CdpCompatibility, true, true)]
+ [InlineData(ConnectorCompatibility.CdpCompatibility, false, true)]
// Option set with numeric logical names
- public void DimeScheduler_OptionSets(ConnectorCompatibility connectorCompatibility, bool supportXMsEnumValues)
+ public void DimeScheduler_OptionSets(ConnectorCompatibility connectorCompatibility, bool supportXMsEnumValues, bool returnEnumsAsPrimitive)
{
using var testConnector = new LoggingTestServer(@"Swagger\Dime.Scheduler.json", _output);
- List functions = OpenApiParser.GetFunctions(new ConnectorSettings("DimeScheduler") { Compatibility = connectorCompatibility, SupportXMsEnumValues = supportXMsEnumValues }, testConnector._apiDocument).ToList();
+ List functions = OpenApiParser.GetFunctions(
+ new ConnectorSettings("DimeScheduler")
+ {
+ Compatibility = connectorCompatibility,
+ SupportXMsEnumValues = supportXMsEnumValues,
+ ReturnEnumsAsPrimitive = returnEnumsAsPrimitive
+ },
+ testConnector._apiDocument).ToList();
+
ConnectorFunction actionUriUpsert = functions.First(f => f.Name == "actionUriUpsert");
Assert.Equal(5, actionUriUpsert.OptionalParameters.Length);
@@ -106,35 +141,54 @@ public void DimeScheduler_OptionSets(ConnectorCompatibility connectorCompatibili
if (connectorCompatibility == ConnectorCompatibility.CdpCompatibility || supportXMsEnumValues)
{
- Assert.Equal(FormulaType.OptionSetValue, actionUriUpsert.OptionalParameters[2].FormulaType);
+ if (returnEnumsAsPrimitive)
+ {
+ Assert.Equal(FormulaType.Decimal, actionUriUpsert.OptionalParameters[2].FormulaType);
+ }
+ else
+ {
+ Assert.Equal(FormulaType.OptionSetValue, actionUriUpsert.OptionalParameters[2].FormulaType);
+ }
// Dictionary is used here, so we need to reorder
Assert.Equal("Planning Board,Appointment,Task,Map", string.Join(",", actionUriUpsert.OptionalParameters[2].ConnectorType.Enum.Select(kvp => kvp.Key)));
Assert.Equal("0,1,2,3", string.Join(",", actionUriUpsert.OptionalParameters[2].ConnectorType.Enum.Select(kvp => (kvp.Value as DecimalValue).Value)));
- Assert.Equal("Planning Board,Appointment,Task,Map", string.Join(",", actionUriUpsert.OptionalParameters[2].ConnectorType.EnumDisplayNames));
+ Assert.Equal("Planning Board,Appointment,Task,Map", string.Join(",", actionUriUpsert.OptionalParameters[2].ConnectorType.EnumDisplayNames));
}
else
{
- Assert.Equal(FormulaType.Decimal, actionUriUpsert.OptionalParameters[2].FormulaType);
+ Assert.Equal(FormulaType.Decimal, actionUriUpsert.OptionalParameters[2].FormulaType);
}
Assert.Equal("0,1,2,3", string.Join(",", actionUriUpsert.OptionalParameters[2].ConnectorType.EnumValues.Select(fv => (fv as DecimalValue).Value)));
}
[Theory]
- [InlineData(ConnectorCompatibility.SwaggerCompatibility, true)]
- [InlineData(ConnectorCompatibility.SwaggerCompatibility, false)]
- [InlineData(ConnectorCompatibility.PowerAppsCompatibility, true)]
- [InlineData(ConnectorCompatibility.PowerAppsCompatibility, false)]
- [InlineData(ConnectorCompatibility.CdpCompatibility, true)]
- [InlineData(ConnectorCompatibility.CdpCompatibility, false)]
-
- // Option set with numeric logical names
- public void ACSL_OptionSets(ConnectorCompatibility connectorCompatibility, bool supportXMsEnumValues)
+ [InlineData(ConnectorCompatibility.SwaggerCompatibility, true, false)]
+ [InlineData(ConnectorCompatibility.SwaggerCompatibility, false, false)]
+ [InlineData(ConnectorCompatibility.SwaggerCompatibility, true, true)]
+ [InlineData(ConnectorCompatibility.SwaggerCompatibility, false, true)]
+ [InlineData(ConnectorCompatibility.PowerAppsCompatibility, true, false)]
+ [InlineData(ConnectorCompatibility.PowerAppsCompatibility, false, false)]
+ [InlineData(ConnectorCompatibility.PowerAppsCompatibility, true, true)]
+ [InlineData(ConnectorCompatibility.PowerAppsCompatibility, false, true)]
+ [InlineData(ConnectorCompatibility.CdpCompatibility, true, false)]
+ [InlineData(ConnectorCompatibility.CdpCompatibility, false, false)]
+ [InlineData(ConnectorCompatibility.CdpCompatibility, true, true)]
+ [InlineData(ConnectorCompatibility.CdpCompatibility, false, true)]
+ public void ACSL_OptionSets(ConnectorCompatibility connectorCompatibility, bool supportXMsEnumValues, bool returnEnumsAsPrimitive)
{
using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language v2.2.json", _output);
- List functions = OpenApiParser.GetFunctions(new ConnectorSettings("ACSL") { Compatibility = connectorCompatibility, SupportXMsEnumValues = supportXMsEnumValues }, testConnector._apiDocument).ToList();
+ List functions = OpenApiParser.GetFunctions(
+ new ConnectorSettings("ACSL")
+ {
+ Compatibility = connectorCompatibility,
+ SupportXMsEnumValues = supportXMsEnumValues,
+ ReturnEnumsAsPrimitive = returnEnumsAsPrimitive
+ },
+ testConnector._apiDocument).ToList();
+
ConnectorFunction analyzeConversationTranscriptSubmitJob = functions.Single(f => f.Name == "AnalyzeConversationTranscriptSubmitJob");
// AnalyzeConversationTranscript_SubmitJob defined at line 1226 of swagger file
@@ -143,19 +197,19 @@ public void ACSL_OptionSets(ConnectorCompatibility connectorCompatibility, bool
// conversations parameter at line 2577, defined at line 2580(TranscriptConversation)
// conversationItems parameter at line 2585, defined at line 2624(TranscriptConversationItem)
// role parameter at line 2641 is having extension x-ms-enum with modelAsString set to true
- ConnectorType connectorType = analyzeConversationTranscriptSubmitJob.RequiredParameters[0].ConnectorType;
+ ConnectorType connectorType = analyzeConversationTranscriptSubmitJob.RequiredParameters[0].ConnectorType;
ConnectorType role = connectorType.Fields[0].Fields[0].Fields[0].Fields[connectorCompatibility == ConnectorCompatibility.Default ? 4 : 3];
Assert.Equal("role", role.Name);
// Type is always a string here as to be an optionset, we need (ConnectorCompatibility = CdpCompatibility or SupportXMsEnumValues = true) AND (modelAsString = false)
Assert.Equal(FormulaType.String, role.FormulaType);
Assert.True(role.IsEnum);
-
+
Assert.Equal