Skip to content

Commit

Permalink
Add ReturnEnumsAsPrimitive connector settings (#2807)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
LucGenetier authored Jan 8, 2025
1 parent a957fcd commit f0af967
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 45 deletions.
10 changes: 10 additions & 0 deletions src/libraries/Microsoft.PowerFx.Connectors/OpenApiExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
/// </summary>
public bool SupportXMsEnumValues { get; init; } = false;
public bool SupportXMsEnumValues { get; init; } = false;

/// <summary>
/// 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.
/// </summary>
public bool ReturnEnumsAsPrimitive { get; init; } = false;

public ConnectorCompatibility Compatibility { get; init; } = ConnectorCompatibility.Default;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ internal ConnectorType(ISwaggerSchema schema, ISwaggerParameter openApiParameter
{
if (list != null && list.Any())
{
EnumValues = list.Select<KeyValuePair<DName, DName>, 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<KeyValuePair<DName, DName>, 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
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ConnectorFunction> functions = OpenApiParser.GetFunctions(new ConnectorSettings("MSNWeather") { Compatibility = connectorCompatibility, SupportXMsEnumValues = supportXMsEnumValues }, testConnector._apiDocument).ToList();
List<ConnectorFunction> 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);
Expand All @@ -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<ConnectorFunction> functions = OpenApiParser.GetFunctions(new ConnectorSettings("DimeScheduler") { Compatibility = connectorCompatibility, SupportXMsEnumValues = supportXMsEnumValues }, testConnector._apiDocument).ToList();
List<ConnectorFunction> 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);
Expand All @@ -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<ConnectorFunction> functions = OpenApiParser.GetFunctions(new ConnectorSettings("ACSL") { Compatibility = connectorCompatibility, SupportXMsEnumValues = supportXMsEnumValues }, testConnector._apiDocument).ToList();
List<ConnectorFunction> 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
Expand All @@ -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<object>(
connectorCompatibility != ConnectorCompatibility.Default
? "![conversations:![conversationItems:*[audioTimings:*[duration:w, offset:w, word:s], id:s, itn:s, language:s, lexical:s, maskedItn:s, participantId:s, role:s, text:s], domain:s, language:s]]"
: "![conversations:![conversationItems:*[audioTimings:*[duration:w, offset:w, word:s], id:s, itn:s, language:s, lexical:s, maskedItn:s, modality:s, participantId:s, role:s, text:s], domain:s, language:s]]",
connectorType.FormulaType._type.ToString());
: "![conversations:![conversationItems:*[audioTimings:*[duration:w, offset:w, word:s], id:s, itn:s, language:s, lexical:s, maskedItn:s, modality:s, participantId:s, role:s, text:s], domain:s, language:s]]",
connectorType.FormulaType._type.ToString());

if (connectorCompatibility == ConnectorCompatibility.CdpCompatibility || supportXMsEnumValues)
{
Expand Down Expand Up @@ -1969,16 +2023,16 @@ public async Task SendEmail()
public async Task AiSensitivityTest()
{
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SendMail.json", _output);
OpenApiDocument apiDoc = testConnector._apiDocument;
OpenApiDocument apiDoc = testConnector._apiDocument;

ConnectorSettings connectorSettings = new ConnectorSettings("exob")
{
{
Compatibility = ConnectorCompatibility.SwaggerCompatibility,
AllowUnsupportedFunctions = true,
IncludeInternalFunctions = true,
ReturnUnknownRecordFieldsAsUntypedObjects = true
ReturnUnknownRecordFieldsAsUntypedObjects = true
};

List<ConnectorFunction> functions = OpenApiParser.GetFunctions(connectorSettings, apiDoc).OrderBy(f => f.Name).ToList();

ConnectorFunction sendmail = functions.First(f => f.Name == "SendEmailV3");
Expand Down

0 comments on commit f0af967

Please sign in to comment.