diff --git a/src/libraries/Microsoft.PowerFx.Connectors/ConnectorFunction.cs b/src/libraries/Microsoft.PowerFx.Connectors/ConnectorFunction.cs index 302b957a83..652f837939 100644 --- a/src/libraries/Microsoft.PowerFx.Connectors/ConnectorFunction.cs +++ b/src/libraries/Microsoft.PowerFx.Connectors/ConnectorFunction.cs @@ -1449,23 +1449,25 @@ private FormulaValue[] GetArguments(ConnectorDynamicApi dynamicApi, NamedValue[] ConnectorFunction functionToBeCalled = EnsureConnectorFunction(dynamicApi, GlobalContext.FunctionList).ConnectorFunction; - foreach (ConnectorParameter connectorParameter in functionToBeCalled.RequiredParameters) + var parameters = functionToBeCalled.RequiredParameters.Union(functionToBeCalled.OptionalParameters); + + foreach (ConnectorParameter connectorParameter in parameters) { - string requiredParameterName = connectorParameter.Name; + string parameterName = connectorParameter.Name; - // TODO: properly implement the ability to reference child properties instead of simplistic check "GetLastPart(kvp.Key) == requiredParameterName" + // TODO: properly implement the ability to reference child properties instead of simplistic check "GetLastPart(kvp.Key) == parameterName" // doc: https://learn.microsoft.com/en-us/connectors/custom-connectors/openapi-extensions // e.g. make this example working: // "destinationInputParam1/property1": { // "parameterReference": "sourceInputParam1/property1" // } - if (dynamicApi.ParameterMap.FirstOrDefault(kvp => kvp.Value is StaticConnectorExtensionValue && GetLastPart(kvp.Key) == requiredParameterName).Value is StaticConnectorExtensionValue sValue) + if (dynamicApi.ParameterMap.FirstOrDefault(kvp => kvp.Value is StaticConnectorExtensionValue && GetLastPart(kvp.Key) == parameterName).Value is StaticConnectorExtensionValue sValue) { arguments.Add(sValue.Value); continue; } - KeyValuePair dValue = dynamicApi.ParameterMap.FirstOrDefault(kvp => kvp.Value is DynamicConnectorExtensionValue dv && GetLastPart(kvp.Key) == requiredParameterName); + KeyValuePair dValue = dynamicApi.ParameterMap.FirstOrDefault(kvp => kvp.Value is DynamicConnectorExtensionValue dv && GetLastPart(kvp.Key) == parameterName); string[] referenceList = ((DynamicConnectorExtensionValue)dValue.Value).Reference.Split('/'); var parameterToUse = knownParameters.FirstOrDefault(nv => nv.Name == referenceList.First())?.Value; diff --git a/src/libraries/Microsoft.PowerFx.Connectors/Execution/HttpFunctionInvoker.cs b/src/libraries/Microsoft.PowerFx.Connectors/Execution/HttpFunctionInvoker.cs index 4008e1e384..65acdf257d 100644 --- a/src/libraries/Microsoft.PowerFx.Connectors/Execution/HttpFunctionInvoker.cs +++ b/src/libraries/Microsoft.PowerFx.Connectors/Execution/HttpFunctionInvoker.cs @@ -209,7 +209,6 @@ public async Task BuildRequest(IReadOnlyList a public Dictionary ConvertToNamedParameters(IReadOnlyList args) { // First N are required params. - // Last param is a record with each field being an optional. // Parameter names are case sensitive. Dictionary map = new (); @@ -225,15 +224,16 @@ public Dictionary ConvertToNamedParameters(IReadOnlyList ConvertToNamedParameters(IReadOnlyList 0 && args.Count > _function.RequiredParameters.Length) - { - FormulaValue optionalArg = args[args.Count - 1]; - - // Objects are always flattenned - if (optionalArg is RecordValue record) - { - foreach (NamedValue field in record.Fields) - { - if (map.ContainsKey(field.Name)) - { - // if optional parameters are defined and a default value is already present - map[field.Name] = field.Value; - } - else - { - map.Add(field.Name, field.Value); - } - } - } - else - { - // Type check should have caught this. - throw new PowerFxConnectorException($"Optional arguments must be the last argument and a record"); - } - } - return map; } diff --git a/src/tests/Microsoft.PowerFx.Connectors.Tests.Shared/Microsoft.PowerFx.Connectors.Tests.Shared.projitems b/src/tests/Microsoft.PowerFx.Connectors.Tests.Shared/Microsoft.PowerFx.Connectors.Tests.Shared.projitems index d8468a3770..b630bb084f 100644 --- a/src/tests/Microsoft.PowerFx.Connectors.Tests.Shared/Microsoft.PowerFx.Connectors.Tests.Shared.projitems +++ b/src/tests/Microsoft.PowerFx.Connectors.Tests.Shared/Microsoft.PowerFx.Connectors.Tests.Shared.projitems @@ -320,6 +320,7 @@ + diff --git a/src/tests/Microsoft.PowerFx.Connectors.Tests.Shared/OpenApiParserTests.cs b/src/tests/Microsoft.PowerFx.Connectors.Tests.Shared/OpenApiParserTests.cs index 5643dadacc..78232cda93 100644 --- a/src/tests/Microsoft.PowerFx.Connectors.Tests.Shared/OpenApiParserTests.cs +++ b/src/tests/Microsoft.PowerFx.Connectors.Tests.Shared/OpenApiParserTests.cs @@ -1,1441 +1,1440 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.OpenApi.Models; -using Microsoft.PowerFx.Core; -using Microsoft.PowerFx.Core.Tests; -using Microsoft.PowerFx.Core.Types; -using Microsoft.PowerFx.Tests; -using Microsoft.PowerFx.Types; -using Newtonsoft.Json; -using Xunit; -using Xunit.Abstractions; - -namespace Microsoft.PowerFx.Connectors.Tests -{ - public class OpenApiParserTests - { - private readonly ITestOutputHelper _output; - - public OpenApiParserTests(ITestOutputHelper output) - { - _output = output; - } - - [Fact] - public void ACSL_GetFunctionNames() - { - OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Azure Cognitive Service for Language.json", _output); - - // OpenAPI spec: Info.Title is required - Assert.Equal("Azure Cognitive Service for Language", doc.Info.Title); - - // OpenAPI spec: Info.Description is optional - Assert.Equal("Azure Cognitive Service for Language, previously known as 'Text Analytics' connector detects language, sentiment and more of the text you provide.", doc.Info.Description); - - List functions = OpenApiParser.GetFunctions("ACSL", doc, new ConsoleLogger(_output)).OrderBy(cf => cf.Name).ToList(); - Assert.Equal(44, functions.Count); - ConnectorFunction conversationAnalysisAnalyzeConversationConversation = functions[14]; - - Assert.Equal("ConversationAnalysisAnalyzeConversationConversation", conversationAnalysisAnalyzeConversationConversation.Name); - Assert.Equal("Analyzes the input conversation utterance.", conversationAnalysisAnalyzeConversationConversation.Description); - Assert.Equal("Conversations (CLU) (2022-05-01)", conversationAnalysisAnalyzeConversationConversation.Summary); - Assert.Equal("/apim/cognitiveservicestextanalytics/{connectionId}/language/:analyze-conversations", conversationAnalysisAnalyzeConversationConversation.OperationPath); - Assert.Equal(HttpMethod.Post, conversationAnalysisAnalyzeConversationConversation.HttpMethod); - } - - [Fact] - public void ACSL_GetFunctionNames22() - { - OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Azure Cognitive Service for Language v2.2.json", _output); - List functions = OpenApiParser.GetFunctions("ACSL", doc, new ConsoleLogger(_output)).OrderBy(cf => cf.Name).ToList(); - ConnectorFunction detectSentimentV3 = functions.First(cf => cf.Name == "DetectSentimentV3"); - - Assert.Equal("Documents", detectSentimentV3.OptionalParameters[0].Summary); - Assert.Equal("The documents to analyze.", detectSentimentV3.OptionalParameters[0].Description); - } - - [Fact] - public void DocuSign_ListEnvelopes_DefaultValue() - { - OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\DocuSign.json", _output); - List functions = OpenApiParser.GetFunctions(new ConnectorSettings("DocuSign") { IncludeInternalFunctions = true, AllowUnsupportedFunctions = true }, doc, new ConsoleLogger(_output)).OrderBy(cf => cf.Name).ToList(); - ConnectorFunction listEnvelopes = functions.First(cf => cf.Name == "ListEnvelopes"); - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.OpenApi.Models; +using Microsoft.PowerFx.Core; +using Microsoft.PowerFx.Core.Tests; +using Microsoft.PowerFx.Core.Types; +using Microsoft.PowerFx.Tests; +using Microsoft.PowerFx.Types; +using Newtonsoft.Json; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.PowerFx.Connectors.Tests +{ + public class OpenApiParserTests + { + private readonly ITestOutputHelper _output; + + public OpenApiParserTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void ACSL_GetFunctionNames() + { + OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Azure Cognitive Service for Language.json", _output); + + // OpenAPI spec: Info.Title is required + Assert.Equal("Azure Cognitive Service for Language", doc.Info.Title); + + // OpenAPI spec: Info.Description is optional + Assert.Equal("Azure Cognitive Service for Language, previously known as 'Text Analytics' connector detects language, sentiment and more of the text you provide.", doc.Info.Description); + + List functions = OpenApiParser.GetFunctions("ACSL", doc, new ConsoleLogger(_output)).OrderBy(cf => cf.Name).ToList(); + Assert.Equal(44, functions.Count); + ConnectorFunction conversationAnalysisAnalyzeConversationConversation = functions[14]; + + Assert.Equal("ConversationAnalysisAnalyzeConversationConversation", conversationAnalysisAnalyzeConversationConversation.Name); + Assert.Equal("Analyzes the input conversation utterance.", conversationAnalysisAnalyzeConversationConversation.Description); + Assert.Equal("Conversations (CLU) (2022-05-01)", conversationAnalysisAnalyzeConversationConversation.Summary); + Assert.Equal("/apim/cognitiveservicestextanalytics/{connectionId}/language/:analyze-conversations", conversationAnalysisAnalyzeConversationConversation.OperationPath); + Assert.Equal(HttpMethod.Post, conversationAnalysisAnalyzeConversationConversation.HttpMethod); + } + + [Fact] + public void ACSL_GetFunctionNames22() + { + OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Azure Cognitive Service for Language v2.2.json", _output); + List functions = OpenApiParser.GetFunctions("ACSL", doc, new ConsoleLogger(_output)).OrderBy(cf => cf.Name).ToList(); + ConnectorFunction detectSentimentV3 = functions.First(cf => cf.Name == "DetectSentimentV3"); + + Assert.Equal("Documents", detectSentimentV3.OptionalParameters[0].Summary); + Assert.Equal("The documents to analyze.", detectSentimentV3.OptionalParameters[0].Description); + } + + [Fact] + public void DocuSign_ListEnvelopes_DefaultValue() + { + OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\DocuSign.json", _output); + List functions = OpenApiParser.GetFunctions(new ConnectorSettings("DocuSign") { IncludeInternalFunctions = true, AllowUnsupportedFunctions = true }, doc, new ConsoleLogger(_output)).OrderBy(cf => cf.Name).ToList(); + ConnectorFunction listEnvelopes = functions.First(cf => cf.Name == "ListEnvelopes"); + Assert.True(listEnvelopes.IsSupported, listEnvelopes.NotSupportedReason); Assert.Equal("from_date", listEnvelopes.OptionalParameters[10].Name); DateTimeValue dtv = Assert.IsType(listEnvelopes.OptionalParameters[10].DefaultValue); - Assert.Equal(new DateTime(2000, 1, 2, 12, 45, 0, DateTimeKind.Utc), dtv.GetConvertedValue(TimeZoneInfo.Utc)); - } - - [Fact] - public void SwaggerWithArrayBody() - { - OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\TestSwagger1.json", _output); - List functions = OpenApiParser.GetFunctions(new ConnectorSettings("swagger") { AllowUnsupportedFunctions = true, IncludeInternalFunctions = true }, doc, new ConsoleLogger(_output)).OrderBy(cf => cf.Name).ToList(); + Assert.Equal(new DateTime(2000, 1, 2, 12, 45, 0, DateTimeKind.Utc), dtv.GetConvertedValue(TimeZoneInfo.Utc)); + } + + [Fact] + public void SwaggerWithArrayBody() + { + OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\TestSwagger1.json", _output); + List functions = OpenApiParser.GetFunctions(new ConnectorSettings("swagger") { AllowUnsupportedFunctions = true, IncludeInternalFunctions = true }, doc, new ConsoleLogger(_output)).OrderBy(cf => cf.Name).ToList(); ConnectorFunction createFileWithTable = functions.First(cf => cf.Name == "CreateFileWithTable"); Assert.Single(createFileWithTable.RequiredParameters); Assert.Equal("rows", createFileWithTable.RequiredParameters[0].Name); - Assert.Equal("*[Value:*[]]", createFileWithTable.RequiredParameters[0].FormulaType.ToStringWithDisplayNames()); - } - - [Fact] - public void ACSL_Load() - { - OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Azure Cognitive Service for Language.json", _output); - (List connectorFunctions, List texlFunctions) = OpenApiParser.ParseInternal(new ConnectorSettings("ACSL"), doc, new ConsoleLogger(_output)); - Assert.Contains(connectorFunctions, func => func.Namespace == "ACSL" && func.Name == "ConversationAnalysisAnalyzeConversationConversation"); - Assert.Contains(texlFunctions, func => func.Namespace.Name.Value == "ACSL" && func.Name == "ConversationAnalysisAnalyzeConversationConversation"); - - ConnectorFunction func1 = connectorFunctions.First(f => f.Name == "AnalyzeTextSubmitJobCustomEntityRecognition"); - Assert.Equal("analysisInput:![documents:*[id:s, language:s, text:s]]|task:![parameters:![deploymentName:s, projectName:s, stringIndexType:s]]", string.Join("|", func1.RequiredParameters.Select(rp => $"{rp.Name}:{rp.FormulaType._type}"))); - Assert.Equal("displayName:s", string.Join("|", func1.OptionalParameters.Select(rp => $"{rp.Name}:{rp.FormulaType._type}"))); - - (connectorFunctions, texlFunctions) = OpenApiParser.ParseInternal(new ConnectorSettings("ACSL") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, doc, new ConsoleLogger(_output)); - Assert.Contains(connectorFunctions, func => func.Namespace == "ACSL" && func.Name == "ConversationAnalysisAnalyzeConversationConversation"); - Assert.Contains(texlFunctions, func => func.Namespace.Name.Value == "ACSL" && func.Name == "ConversationAnalysisAnalyzeConversationConversation"); - - func1 = connectorFunctions.First(f => f.Name == "AnalyzeTextSubmitJobCustomEntityRecognition"); - Assert.Equal("analysisInput:![documents:*[id:s, language:s, text:s]]|task:![parameters:![deploymentName:s, projectName:s]]", string.Join("|", func1.RequiredParameters.Select(rp => $"{rp.Name}:{rp.FormulaType._type}"))); - Assert.Empty(func1.OptionalParameters); - } - - [Fact] - public void SF_TextCsv() - { - OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\SalesForce.json", _output); - (List connectorFunctions, List texlFunctions) = OpenApiParser.ParseInternal(new ConnectorSettings("SF") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, doc, new ConsoleLogger(_output)); - - // function returns text/csv - ConnectorFunction func1 = connectorFunctions.First(f => f.Name == "GetJobRecordResults"); - ConnectorType returnType = func1.ReturnParameterType; - - // returns a string - Assert.Equal("s", returnType.FormulaType._type.ToString()); - } - -#pragma warning disable SA1118, SA1117, SA1119, SA1137 - - [Fact] - public void ACSL_GetFunctionParameters_PowerAppsCompatibility() - { - OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Azure Cognitive Service for Language.json", _output); - ConnectorFunction function = OpenApiParser.GetFunctions(new ConnectorSettings("ACSL") { Compatibility = ConnectorCompatibility.PowerAppsCompatibility }, doc).OrderBy(cf => cf.Name).ToList()[14]; - - Assert.Equal("ConversationAnalysisAnalyzeConversationConversation", function.Name); - Assert.Equal("ConversationAnalysis_AnalyzeConversation_Conversation", function.OriginalName); - Assert.Equal("/apim/cognitiveservicestextanalytics/{connectionId}/language/:analyze-conversations", function.OperationPath); - - Assert.Equal(3, function.RequiredParameters.Length); - Assert.Equal(2, function.HiddenRequiredParameters.Length); - Assert.Empty(function.OptionalParameters); - - RecordType analysisInputRecordType = Extensions.MakeRecordType( - ("conversationItem", Extensions.MakeRecordType( - ("language", FormulaType.String), - ("modality", FormulaType.String), - ("text", FormulaType.String)))); - RecordType parametersRecordType = Extensions.MakeRecordType( - ("deploymentName", FormulaType.String), - ("directTarget", FormulaType.String), - ("isLoggingEnabled", FormulaType.Boolean), - ("projectName", FormulaType.String), - ("stringIndexType", FormulaType.String), - ("targetProjectParameters", FormulaType.UntypedObject), - ("verbose", FormulaType.Boolean)); - - // -- Parameter 1 -- - Assert.Equal("kind", function.RequiredParameters[0].Name); - Assert.Equal(FormulaType.String, function.RequiredParameters[0].FormulaType); - Assert.Equal("A single conversational task to execute.", function.RequiredParameters[0].Description); - Assert.Equal("Conversation", function.RequiredParameters[0].DefaultValue.ToObject()); - - // -- Parameter 2 -- - Assert.Equal("analysisInput", function.RequiredParameters[1].Name); - Assert.Equal(analysisInputRecordType, function.RequiredParameters[1].FormulaType); - Assert.Equal("A single conversational task to execute.", function.RequiredParameters[1].Description); - Assert.Null(function.RequiredParameters[1].DefaultValue); - Assert.NotNull(function.RequiredParameters[1].ConnectorType); - Assert.Equal("analysisInput", function.RequiredParameters[1].ConnectorType.Name); - Assert.Null(function.RequiredParameters[1].ConnectorType.DisplayName); - Assert.Equal("The input ConversationItem and its optional parameters", function.RequiredParameters[1].ConnectorType.Description); - Assert.Equal(analysisInputRecordType, function.RequiredParameters[1].ConnectorType.FormulaType); - Assert.True(function.RequiredParameters[1].ConnectorType.IsRequired); - Assert.Single(function.RequiredParameters[1].ConnectorType.Fields); - Assert.Equal("conversationItem", function.RequiredParameters[1].ConnectorType.Fields[0].Name); - Assert.Null(function.RequiredParameters[1].ConnectorType.Fields[0].DisplayName); - Assert.Equal("The abstract base for a user input formatted conversation (e.g., Text, Transcript).", function.RequiredParameters[1].ConnectorType.Fields[0].Description); - Assert.True(function.RequiredParameters[1].ConnectorType.Fields[0].IsRequired); - - // -- Parameter 3 -- - Assert.Equal("parameters", function.RequiredParameters[2].Name); - Assert.Equal(parametersRecordType, function.RequiredParameters[2].FormulaType); - Assert.Equal("A single conversational task to execute.", function.RequiredParameters[2].Description); - Assert.Equal(@"{""deploymentName"":null,""directTarget"":null,""isLoggingEnabled"":null,""projectName"":null,""stringIndexType"":""TextElement_V8"",""targetProjectParameters"":null,""verbose"":null}", System.Text.Json.JsonSerializer.Serialize(function.RequiredParameters[2].DefaultValue.ToObject())); - - RecordType analysisInputRecordTypeH = Extensions.MakeRecordType( - ("conversationItem", Extensions.MakeRecordType( - ("id", FormulaType.String), - ("participantId", FormulaType.String)))); - - // -- Hidden Required Parameter 1 -- - Assert.Equal("api-version", function.HiddenRequiredParameters[0].Name); - Assert.Equal(FormulaType.String, function.HiddenRequiredParameters[0].FormulaType); - Assert.Equal("Client API version.", function.HiddenRequiredParameters[0].Description); - Assert.Equal("2022-05-01", function.HiddenRequiredParameters[0].DefaultValue.ToObject()); - - // -- Hidden Required Parameter 2 -- - Assert.Equal("analysisInput", function.HiddenRequiredParameters[1].Name); - Assert.Equal(analysisInputRecordTypeH, function.HiddenRequiredParameters[1].FormulaType); - Assert.Equal("A single conversational task to execute.", function.HiddenRequiredParameters[1].Description); - Assert.Equal(@"{""conversationItem"":{""id"":""0"",""participantId"":""0""}}", System.Text.Json.JsonSerializer.Serialize(function.HiddenRequiredParameters[1].DefaultValue.ToObject())); - - Assert.Equal(3, function.ArityMin); - Assert.Equal(3, function.ArityMax); - - // -- Return Type -- - FormulaType returnType = function.ReturnType; - - RecordType expectedReturnType = Extensions.MakeRecordType( - ("kind", FormulaType.String), - ("result", Extensions.MakeRecordType( - ("detectedLanguage", FormulaType.String), - ("prediction", Extensions.MakeRecordType( - ("entities", Extensions.MakeTableType( - ("category", FormulaType.String), - ("confidenceScore", FormulaType.Decimal), - ("extraInformation", FormulaType.UntypedObject), // property has a discriminator - ("length", FormulaType.Decimal), - ("offset", FormulaType.Decimal), - ("resolutions", FormulaType.UntypedObject), // property has a discriminator - ("text", FormulaType.String))), - ("intents", Extensions.MakeTableType( - ("category", FormulaType.String), - ("confidenceScore", FormulaType.Decimal))), - ("projectKind", FormulaType.String), - ("topIntent", FormulaType.String))), - ("query", FormulaType.String)))); - - string rt3Name = expectedReturnType.ToStringWithDisplayNames(); - string returnTypeName = expectedReturnType.ToStringWithDisplayNames(); - - Assert.Equal(rt3Name, returnTypeName); - Assert.True((FormulaType)expectedReturnType == returnType); - - ConnectorType connectorReturnType = function.ReturnParameterType; - Assert.NotNull(connectorReturnType); - Assert.Equal((FormulaType)expectedReturnType, connectorReturnType.FormulaType); - Assert.Equal(2, connectorReturnType.Fields.Length); - Assert.Equal("The results of a Conversation task.", connectorReturnType.Description); - } - - [Fact] - public void ACSL_GetFunctionParameters_SwaggerCompatibility() - { - OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Azure Cognitive Service for Language.json", _output); - ConnectorFunction function = OpenApiParser.GetFunctions(new ConnectorSettings("ACSL") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, doc).OrderBy(cf => cf.Name).ToList()[14]; - - Assert.Equal("ConversationAnalysisAnalyzeConversationConversation", function.Name); - Assert.Equal("ConversationAnalysis_AnalyzeConversation_Conversation", function.OriginalName); - Assert.Equal("/apim/cognitiveservicestextanalytics/{connectionId}/language/:analyze-conversations", function.OperationPath); - - RecordType analysisInputRecordType = Extensions.MakeRecordType( - ("conversationItem", Extensions.MakeRecordType( - ("language", FormulaType.String), - ("text", FormulaType.String)))); - RecordType parametersRecordType = Extensions.MakeRecordType( - ("deploymentName", FormulaType.String), - ("projectName", FormulaType.String), - ("verbose", FormulaType.Boolean)); - - Assert.Equal(2, function.RequiredParameters.Length); - Assert.Equal(3, function.HiddenRequiredParameters.Length); - Assert.Empty(function.OptionalParameters); - - // -- Parameter 1 -- - Assert.Equal("analysisInput", function.RequiredParameters[0].Name); - Assert.Equal(analysisInputRecordType, function.RequiredParameters[0].FormulaType); - Assert.Equal("A single conversational task to execute.", function.RequiredParameters[0].Description); - Assert.Null(function.RequiredParameters[0].DefaultValue); - Assert.NotNull(function.RequiredParameters[0].ConnectorType); - Assert.Equal("analysisInput", function.RequiredParameters[0].ConnectorType.Name); - Assert.Null(function.RequiredParameters[0].ConnectorType.DisplayName); - Assert.Equal("The input ConversationItem and its optional parameters", function.RequiredParameters[0].ConnectorType.Description); - Assert.Equal(analysisInputRecordType, function.RequiredParameters[0].ConnectorType.FormulaType); - Assert.True(function.RequiredParameters[0].ConnectorType.IsRequired); - Assert.Single(function.RequiredParameters[0].ConnectorType.Fields); - Assert.Equal("conversationItem", function.RequiredParameters[0].ConnectorType.Fields[0].Name); - Assert.Null(function.RequiredParameters[0].ConnectorType.Fields[0].DisplayName); - Assert.Equal("The abstract base for a user input formatted conversation (e.g., Text, Transcript).", function.RequiredParameters[0].ConnectorType.Fields[0].Description); - Assert.True(function.RequiredParameters[0].ConnectorType.Fields[0].IsRequired); - - // -- Parameter 2 -- - Assert.Equal("parameters", function.RequiredParameters[1].Name); - Assert.Equal(parametersRecordType, function.RequiredParameters[1].FormulaType); - Assert.Equal("A single conversational task to execute.", function.RequiredParameters[1].Description); - Assert.Null(function.RequiredParameters[1].DefaultValue); - - RecordType analysisInputRecordTypeH = Extensions.MakeRecordType( - ("conversationItem", Extensions.MakeRecordType( - ("id", FormulaType.String), - ("participantId", FormulaType.String)))); - - // -- Hidden Required Parameter 1 -- - Assert.Equal("api-version", function.HiddenRequiredParameters[0].Name); - Assert.Equal(FormulaType.String, function.HiddenRequiredParameters[0].FormulaType); - Assert.Equal("Client API version.", function.HiddenRequiredParameters[0].Description); - Assert.Equal("2022-05-01", function.HiddenRequiredParameters[0].DefaultValue.ToObject()); - - // -- Hidden Required Parameter 2 -- - Assert.Equal("kind", function.HiddenRequiredParameters[1].Name); - Assert.Equal(FormulaType.String, function.HiddenRequiredParameters[1].FormulaType); - Assert.Equal("A single conversational task to execute.", function.HiddenRequiredParameters[1].Description); - Assert.Equal("Conversation", function.HiddenRequiredParameters[1].DefaultValue.ToObject()); - - // -- Hidden Required Parameter 3 -- - Assert.Equal("analysisInput", function.HiddenRequiredParameters[2].Name); - Assert.Equal(analysisInputRecordTypeH, function.HiddenRequiredParameters[2].FormulaType); - Assert.Equal("A single conversational task to execute.", function.HiddenRequiredParameters[2].Description); - Assert.Equal(@"{""conversationItem"":{""id"":""0"",""participantId"":""0""}}", System.Text.Json.JsonSerializer.Serialize(function.HiddenRequiredParameters[2].DefaultValue.ToObject())); - - Assert.Equal(2, function.ArityMin); - Assert.Equal(2, function.ArityMax); - - // -- Return Type -- - FormulaType returnType = function.ReturnType; - - RecordType expectedReturnType = Extensions.MakeRecordType( - ("kind", FormulaType.String), - ("result", Extensions.MakeRecordType( - ("detectedLanguage", FormulaType.String), - ("prediction", Extensions.MakeRecordType( - ("entities", Extensions.MakeTableType( - ("category", FormulaType.String), - ("confidenceScore", FormulaType.Decimal), - ("extraInformation", FormulaType.UntypedObject), // property has a discriminator - ("length", FormulaType.Decimal), - ("offset", FormulaType.Decimal), - ("resolutions", FormulaType.UntypedObject), // property has a discriminator - ("text", FormulaType.String))), - ("intents", Extensions.MakeTableType( - ("category", FormulaType.String), - ("confidenceScore", FormulaType.Decimal))), - ("projectKind", FormulaType.String), - ("topIntent", FormulaType.String))), - ("query", FormulaType.String)))); - - string rt3Name = expectedReturnType.ToStringWithDisplayNames(); - string returnTypeName = expectedReturnType.ToStringWithDisplayNames(); - - Assert.Equal(rt3Name, returnTypeName); - Assert.True((FormulaType)expectedReturnType == returnType); - - ConnectorType connectorReturnType = function.ReturnParameterType; - Assert.NotNull(connectorReturnType); - Assert.Equal((FormulaType)expectedReturnType, connectorReturnType.FormulaType); - Assert.Equal(2, connectorReturnType.Fields.Length); - Assert.Equal("The results of a Conversation task.", connectorReturnType.Description); - } - - [Fact] - public async Task ACSL_InvokeFunctionWithoutOutputsWithOutputOverride() - { - using var testConnector = new LoggingTestServer(@"Swagger\TestConnectorNoOutput.json", _output); - OpenApiDocument apiDoc = testConnector._apiDocument; - ConsoleLogger logger = new ConsoleLogger(_output); - - PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); - ConnectorFunction function = OpenApiParser.GetFunctions(new ConnectorSettings("ACSL") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, apiDoc).OrderBy(cf => cf.Name).ToList()[0]; - Assert.Equal("AnalyzeConversationTextSubmitJob", function.Name); - Assert.Equal("s", function.ReturnType.ToStringWithDisplayNames()); - - using var testConnector2 = new LoggingTestServer(@"Swagger\TestConnectorNoOutput.json", _output); - using var httpClient2 = new HttpClient(testConnector2); - testConnector2.SetResponseFromFile(@"Responses\TestConnectorDateTimeFormatResponse.json"); - using PowerPlatformConnectorClient client2 = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient2) - { - SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", - }; - - BaseRuntimeConnectorContext context2 = new TestConnectorRuntimeContext("ACSL", client2, console: _output); - - DType.TryParse("![createdDateTime:d, displayName:d]", out DType dtype); - var expectedFormulaType = FormulaType.Build(dtype); - - FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[0], context2, expectedFormulaType, CancellationToken.None); - - RecordValue httpResultValue = (RecordValue)httpResult; - FormulaValue displayName = httpResultValue.GetField("displayName"); - FormulaValue createdDateTime = httpResultValue.GetField("createdDateTime"); - - Assert.NotNull(httpResult); - Assert.True(httpResult is RecordValue); - Assert.True(displayName is DateTimeValue); - Assert.True(createdDateTime is DateTimeValue); - Assert.True(function.ReturnType != expectedFormulaType); + Assert.Equal("*[Value:*[]]", createFileWithTable.RequiredParameters[0].FormulaType.ToStringWithDisplayNames()); + } + + [Fact] + public void ACSL_Load() + { + OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Azure Cognitive Service for Language.json", _output); + (List connectorFunctions, List texlFunctions) = OpenApiParser.ParseInternal(new ConnectorSettings("ACSL"), doc, new ConsoleLogger(_output)); + Assert.Contains(connectorFunctions, func => func.Namespace == "ACSL" && func.Name == "ConversationAnalysisAnalyzeConversationConversation"); + Assert.Contains(texlFunctions, func => func.Namespace.Name.Value == "ACSL" && func.Name == "ConversationAnalysisAnalyzeConversationConversation"); + + ConnectorFunction func1 = connectorFunctions.First(f => f.Name == "AnalyzeTextSubmitJobCustomEntityRecognition"); + Assert.Equal("analysisInput:![documents:*[id:s, language:s, text:s]]|task:![parameters:![deploymentName:s, projectName:s, stringIndexType:s]]", string.Join("|", func1.RequiredParameters.Select(rp => $"{rp.Name}:{rp.FormulaType._type}"))); + Assert.Equal("displayName:s", string.Join("|", func1.OptionalParameters.Select(rp => $"{rp.Name}:{rp.FormulaType._type}"))); + + (connectorFunctions, texlFunctions) = OpenApiParser.ParseInternal(new ConnectorSettings("ACSL") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, doc, new ConsoleLogger(_output)); + Assert.Contains(connectorFunctions, func => func.Namespace == "ACSL" && func.Name == "ConversationAnalysisAnalyzeConversationConversation"); + Assert.Contains(texlFunctions, func => func.Namespace.Name.Value == "ACSL" && func.Name == "ConversationAnalysisAnalyzeConversationConversation"); + + func1 = connectorFunctions.First(f => f.Name == "AnalyzeTextSubmitJobCustomEntityRecognition"); + Assert.Equal("analysisInput:![documents:*[id:s, language:s, text:s]]|task:![parameters:![deploymentName:s, projectName:s]]", string.Join("|", func1.RequiredParameters.Select(rp => $"{rp.Name}:{rp.FormulaType._type}"))); + Assert.Empty(func1.OptionalParameters); + } + + [Fact] + public void SF_TextCsv() + { + OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\SalesForce.json", _output); + (List connectorFunctions, List texlFunctions) = OpenApiParser.ParseInternal(new ConnectorSettings("SF") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, doc, new ConsoleLogger(_output)); + + // function returns text/csv + ConnectorFunction func1 = connectorFunctions.First(f => f.Name == "GetJobRecordResults"); + ConnectorType returnType = func1.ReturnParameterType; + + // returns a string + Assert.Equal("s", returnType.FormulaType._type.ToString()); + } + +#pragma warning disable SA1118, SA1117, SA1119, SA1137 + + [Fact] + public void ACSL_GetFunctionParameters_PowerAppsCompatibility() + { + OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Azure Cognitive Service for Language.json", _output); + ConnectorFunction function = OpenApiParser.GetFunctions(new ConnectorSettings("ACSL") { Compatibility = ConnectorCompatibility.PowerAppsCompatibility }, doc).OrderBy(cf => cf.Name).ToList()[14]; + + Assert.Equal("ConversationAnalysisAnalyzeConversationConversation", function.Name); + Assert.Equal("ConversationAnalysis_AnalyzeConversation_Conversation", function.OriginalName); + Assert.Equal("/apim/cognitiveservicestextanalytics/{connectionId}/language/:analyze-conversations", function.OperationPath); + + Assert.Equal(3, function.RequiredParameters.Length); + Assert.Equal(2, function.HiddenRequiredParameters.Length); + Assert.Empty(function.OptionalParameters); + + RecordType analysisInputRecordType = Extensions.MakeRecordType( + ("conversationItem", Extensions.MakeRecordType( + ("language", FormulaType.String), + ("modality", FormulaType.String), + ("text", FormulaType.String)))); + RecordType parametersRecordType = Extensions.MakeRecordType( + ("deploymentName", FormulaType.String), + ("directTarget", FormulaType.String), + ("isLoggingEnabled", FormulaType.Boolean), + ("projectName", FormulaType.String), + ("stringIndexType", FormulaType.String), + ("targetProjectParameters", FormulaType.UntypedObject), + ("verbose", FormulaType.Boolean)); + + // -- Parameter 1 -- + Assert.Equal("kind", function.RequiredParameters[0].Name); + Assert.Equal(FormulaType.String, function.RequiredParameters[0].FormulaType); + Assert.Equal("A single conversational task to execute.", function.RequiredParameters[0].Description); + Assert.Equal("Conversation", function.RequiredParameters[0].DefaultValue.ToObject()); + + // -- Parameter 2 -- + Assert.Equal("analysisInput", function.RequiredParameters[1].Name); + Assert.Equal(analysisInputRecordType, function.RequiredParameters[1].FormulaType); + Assert.Equal("A single conversational task to execute.", function.RequiredParameters[1].Description); + Assert.Null(function.RequiredParameters[1].DefaultValue); + Assert.NotNull(function.RequiredParameters[1].ConnectorType); + Assert.Equal("analysisInput", function.RequiredParameters[1].ConnectorType.Name); + Assert.Null(function.RequiredParameters[1].ConnectorType.DisplayName); + Assert.Equal("The input ConversationItem and its optional parameters", function.RequiredParameters[1].ConnectorType.Description); + Assert.Equal(analysisInputRecordType, function.RequiredParameters[1].ConnectorType.FormulaType); + Assert.True(function.RequiredParameters[1].ConnectorType.IsRequired); + Assert.Single(function.RequiredParameters[1].ConnectorType.Fields); + Assert.Equal("conversationItem", function.RequiredParameters[1].ConnectorType.Fields[0].Name); + Assert.Null(function.RequiredParameters[1].ConnectorType.Fields[0].DisplayName); + Assert.Equal("The abstract base for a user input formatted conversation (e.g., Text, Transcript).", function.RequiredParameters[1].ConnectorType.Fields[0].Description); + Assert.True(function.RequiredParameters[1].ConnectorType.Fields[0].IsRequired); + + // -- Parameter 3 -- + Assert.Equal("parameters", function.RequiredParameters[2].Name); + Assert.Equal(parametersRecordType, function.RequiredParameters[2].FormulaType); + Assert.Equal("A single conversational task to execute.", function.RequiredParameters[2].Description); + Assert.Equal(@"{""deploymentName"":null,""directTarget"":null,""isLoggingEnabled"":null,""projectName"":null,""stringIndexType"":""TextElement_V8"",""targetProjectParameters"":null,""verbose"":null}", System.Text.Json.JsonSerializer.Serialize(function.RequiredParameters[2].DefaultValue.ToObject())); + + RecordType analysisInputRecordTypeH = Extensions.MakeRecordType( + ("conversationItem", Extensions.MakeRecordType( + ("id", FormulaType.String), + ("participantId", FormulaType.String)))); + + // -- Hidden Required Parameter 1 -- + Assert.Equal("api-version", function.HiddenRequiredParameters[0].Name); + Assert.Equal(FormulaType.String, function.HiddenRequiredParameters[0].FormulaType); + Assert.Equal("Client API version.", function.HiddenRequiredParameters[0].Description); + Assert.Equal("2022-05-01", function.HiddenRequiredParameters[0].DefaultValue.ToObject()); + + // -- Hidden Required Parameter 2 -- + Assert.Equal("analysisInput", function.HiddenRequiredParameters[1].Name); + Assert.Equal(analysisInputRecordTypeH, function.HiddenRequiredParameters[1].FormulaType); + Assert.Equal("A single conversational task to execute.", function.HiddenRequiredParameters[1].Description); + Assert.Equal(@"{""conversationItem"":{""id"":""0"",""participantId"":""0""}}", System.Text.Json.JsonSerializer.Serialize(function.HiddenRequiredParameters[1].DefaultValue.ToObject())); + + Assert.Equal(3, function.ArityMin); + Assert.Equal(3, function.ArityMax); + + // -- Return Type -- + FormulaType returnType = function.ReturnType; + + RecordType expectedReturnType = Extensions.MakeRecordType( + ("kind", FormulaType.String), + ("result", Extensions.MakeRecordType( + ("detectedLanguage", FormulaType.String), + ("prediction", Extensions.MakeRecordType( + ("entities", Extensions.MakeTableType( + ("category", FormulaType.String), + ("confidenceScore", FormulaType.Decimal), + ("extraInformation", FormulaType.UntypedObject), // property has a discriminator + ("length", FormulaType.Decimal), + ("offset", FormulaType.Decimal), + ("resolutions", FormulaType.UntypedObject), // property has a discriminator + ("text", FormulaType.String))), + ("intents", Extensions.MakeTableType( + ("category", FormulaType.String), + ("confidenceScore", FormulaType.Decimal))), + ("projectKind", FormulaType.String), + ("topIntent", FormulaType.String))), + ("query", FormulaType.String)))); + + string rt3Name = expectedReturnType.ToStringWithDisplayNames(); + string returnTypeName = expectedReturnType.ToStringWithDisplayNames(); + + Assert.Equal(rt3Name, returnTypeName); + Assert.True((FormulaType)expectedReturnType == returnType); + + ConnectorType connectorReturnType = function.ReturnParameterType; + Assert.NotNull(connectorReturnType); + Assert.Equal((FormulaType)expectedReturnType, connectorReturnType.FormulaType); + Assert.Equal(2, connectorReturnType.Fields.Length); + Assert.Equal("The results of a Conversation task.", connectorReturnType.Description); + } + + [Fact] + public void ACSL_GetFunctionParameters_SwaggerCompatibility() + { + OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Azure Cognitive Service for Language.json", _output); + ConnectorFunction function = OpenApiParser.GetFunctions(new ConnectorSettings("ACSL") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, doc).OrderBy(cf => cf.Name).ToList()[14]; + + Assert.Equal("ConversationAnalysisAnalyzeConversationConversation", function.Name); + Assert.Equal("ConversationAnalysis_AnalyzeConversation_Conversation", function.OriginalName); + Assert.Equal("/apim/cognitiveservicestextanalytics/{connectionId}/language/:analyze-conversations", function.OperationPath); + + RecordType analysisInputRecordType = Extensions.MakeRecordType( + ("conversationItem", Extensions.MakeRecordType( + ("language", FormulaType.String), + ("text", FormulaType.String)))); + RecordType parametersRecordType = Extensions.MakeRecordType( + ("deploymentName", FormulaType.String), + ("projectName", FormulaType.String), + ("verbose", FormulaType.Boolean)); + + Assert.Equal(2, function.RequiredParameters.Length); + Assert.Equal(3, function.HiddenRequiredParameters.Length); + Assert.Empty(function.OptionalParameters); + + // -- Parameter 1 -- + Assert.Equal("analysisInput", function.RequiredParameters[0].Name); + Assert.Equal(analysisInputRecordType, function.RequiredParameters[0].FormulaType); + Assert.Equal("A single conversational task to execute.", function.RequiredParameters[0].Description); + Assert.Null(function.RequiredParameters[0].DefaultValue); + Assert.NotNull(function.RequiredParameters[0].ConnectorType); + Assert.Equal("analysisInput", function.RequiredParameters[0].ConnectorType.Name); + Assert.Null(function.RequiredParameters[0].ConnectorType.DisplayName); + Assert.Equal("The input ConversationItem and its optional parameters", function.RequiredParameters[0].ConnectorType.Description); + Assert.Equal(analysisInputRecordType, function.RequiredParameters[0].ConnectorType.FormulaType); + Assert.True(function.RequiredParameters[0].ConnectorType.IsRequired); + Assert.Single(function.RequiredParameters[0].ConnectorType.Fields); + Assert.Equal("conversationItem", function.RequiredParameters[0].ConnectorType.Fields[0].Name); + Assert.Null(function.RequiredParameters[0].ConnectorType.Fields[0].DisplayName); + Assert.Equal("The abstract base for a user input formatted conversation (e.g., Text, Transcript).", function.RequiredParameters[0].ConnectorType.Fields[0].Description); + Assert.True(function.RequiredParameters[0].ConnectorType.Fields[0].IsRequired); + + // -- Parameter 2 -- + Assert.Equal("parameters", function.RequiredParameters[1].Name); + Assert.Equal(parametersRecordType, function.RequiredParameters[1].FormulaType); + Assert.Equal("A single conversational task to execute.", function.RequiredParameters[1].Description); + Assert.Null(function.RequiredParameters[1].DefaultValue); + + RecordType analysisInputRecordTypeH = Extensions.MakeRecordType( + ("conversationItem", Extensions.MakeRecordType( + ("id", FormulaType.String), + ("participantId", FormulaType.String)))); + + // -- Hidden Required Parameter 1 -- + Assert.Equal("api-version", function.HiddenRequiredParameters[0].Name); + Assert.Equal(FormulaType.String, function.HiddenRequiredParameters[0].FormulaType); + Assert.Equal("Client API version.", function.HiddenRequiredParameters[0].Description); + Assert.Equal("2022-05-01", function.HiddenRequiredParameters[0].DefaultValue.ToObject()); + + // -- Hidden Required Parameter 2 -- + Assert.Equal("kind", function.HiddenRequiredParameters[1].Name); + Assert.Equal(FormulaType.String, function.HiddenRequiredParameters[1].FormulaType); + Assert.Equal("A single conversational task to execute.", function.HiddenRequiredParameters[1].Description); + Assert.Equal("Conversation", function.HiddenRequiredParameters[1].DefaultValue.ToObject()); + + // -- Hidden Required Parameter 3 -- + Assert.Equal("analysisInput", function.HiddenRequiredParameters[2].Name); + Assert.Equal(analysisInputRecordTypeH, function.HiddenRequiredParameters[2].FormulaType); + Assert.Equal("A single conversational task to execute.", function.HiddenRequiredParameters[2].Description); + Assert.Equal(@"{""conversationItem"":{""id"":""0"",""participantId"":""0""}}", System.Text.Json.JsonSerializer.Serialize(function.HiddenRequiredParameters[2].DefaultValue.ToObject())); + + Assert.Equal(2, function.ArityMin); + Assert.Equal(2, function.ArityMax); + + // -- Return Type -- + FormulaType returnType = function.ReturnType; + + RecordType expectedReturnType = Extensions.MakeRecordType( + ("kind", FormulaType.String), + ("result", Extensions.MakeRecordType( + ("detectedLanguage", FormulaType.String), + ("prediction", Extensions.MakeRecordType( + ("entities", Extensions.MakeTableType( + ("category", FormulaType.String), + ("confidenceScore", FormulaType.Decimal), + ("extraInformation", FormulaType.UntypedObject), // property has a discriminator + ("length", FormulaType.Decimal), + ("offset", FormulaType.Decimal), + ("resolutions", FormulaType.UntypedObject), // property has a discriminator + ("text", FormulaType.String))), + ("intents", Extensions.MakeTableType( + ("category", FormulaType.String), + ("confidenceScore", FormulaType.Decimal))), + ("projectKind", FormulaType.String), + ("topIntent", FormulaType.String))), + ("query", FormulaType.String)))); + + string rt3Name = expectedReturnType.ToStringWithDisplayNames(); + string returnTypeName = expectedReturnType.ToStringWithDisplayNames(); + + Assert.Equal(rt3Name, returnTypeName); + Assert.True((FormulaType)expectedReturnType == returnType); + + ConnectorType connectorReturnType = function.ReturnParameterType; + Assert.NotNull(connectorReturnType); + Assert.Equal((FormulaType)expectedReturnType, connectorReturnType.FormulaType); + Assert.Equal(2, connectorReturnType.Fields.Length); + Assert.Equal("The results of a Conversation task.", connectorReturnType.Description); + } + + [Fact] + public async Task ACSL_InvokeFunctionWithoutOutputsWithOutputOverride() + { + using var testConnector = new LoggingTestServer(@"Swagger\TestConnectorNoOutput.json", _output); + OpenApiDocument apiDoc = testConnector._apiDocument; + ConsoleLogger logger = new ConsoleLogger(_output); + + PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); + ConnectorFunction function = OpenApiParser.GetFunctions(new ConnectorSettings("ACSL") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, apiDoc).OrderBy(cf => cf.Name).ToList()[0]; + Assert.Equal("AnalyzeConversationTextSubmitJob", function.Name); + Assert.Equal("s", function.ReturnType.ToStringWithDisplayNames()); + + using var testConnector2 = new LoggingTestServer(@"Swagger\TestConnectorNoOutput.json", _output); + using var httpClient2 = new HttpClient(testConnector2); + testConnector2.SetResponseFromFile(@"Responses\TestConnectorDateTimeFormatResponse.json"); + using PowerPlatformConnectorClient client2 = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient2) + { + SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", + }; + + BaseRuntimeConnectorContext context2 = new TestConnectorRuntimeContext("ACSL", client2, console: _output); + + DType.TryParse("![createdDateTime:d, displayName:d]", out DType dtype); + var expectedFormulaType = FormulaType.Build(dtype); + + FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[0], context2, expectedFormulaType, CancellationToken.None); + + RecordValue httpResultValue = (RecordValue)httpResult; + FormulaValue displayName = httpResultValue.GetField("displayName"); + FormulaValue createdDateTime = httpResultValue.GetField("createdDateTime"); + + Assert.NotNull(httpResult); + Assert.True(httpResult is RecordValue); + Assert.True(displayName is DateTimeValue); + Assert.True(createdDateTime is DateTimeValue); + Assert.True(function.ReturnType != expectedFormulaType); + } + + [Fact] + public async Task ACSL_InvokeFunctionWithOutputOverride() + { + // this test is asserting that we can provide the expected output type that should be used during deserialization + // this is useful in a situation when the output type is dynamic and is not available in the swagger + using var testConnector = new LoggingTestServer(@"Swagger\TestConnectorDateTimeFormat.json", _output); + OpenApiDocument apiDoc = testConnector._apiDocument; + ConsoleLogger logger = new ConsoleLogger(_output); + + PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); + ConnectorFunction function = OpenApiParser.GetFunctions(new ConnectorSettings("ACSL") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, apiDoc).OrderBy(cf => cf.Name).ToList()[0]; + Assert.Equal("AnalyzeConversationTextSubmitJob", function.Name); + Assert.Equal("![createdDateTime`'Created Date':d, displayName:s]", function.ReturnType.ToStringWithDisplayNames()); + + using var testConnector2 = new LoggingTestServer(@"Swagger\TestConnectorDateTimeFormat.json", _output); + using var httpClient2 = new HttpClient(testConnector2); + testConnector2.SetResponseFromFile(@"Responses\TestConnectorDateTimeFormatResponse.json"); + using PowerPlatformConnectorClient client2 = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient2) + { + SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", + }; + + BaseRuntimeConnectorContext context2 = new TestConnectorRuntimeContext("ACSL", client2, console: _output); + + DType.TryParse("![createdDateTime:d, displayName:d]", out DType dtype); + var expectedFormulaType = FormulaType.Build(dtype); + + FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[0], context2, expectedFormulaType, CancellationToken.None); + + RecordValue httpResultValue = (RecordValue)httpResult; + FormulaValue displayName = httpResultValue.GetField("displayName"); + FormulaValue createdDateTime = httpResultValue.GetField("createdDateTime"); + + Assert.NotNull(httpResult); + Assert.True(httpResult is RecordValue); + Assert.True(displayName is DateTimeValue); + Assert.True(createdDateTime is DateTimeValue); + Assert.True(function.ReturnType != expectedFormulaType); + } + + [Fact] + public async Task ACSL_InvokeFunction() + { + using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language.json", _output); + OpenApiDocument apiDoc = testConnector._apiDocument; + ConsoleLogger logger = new ConsoleLogger(_output); + + PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); + ConnectorFunction function = OpenApiParser.GetFunctions(new ConnectorSettings("ACSL") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, apiDoc).OrderBy(cf => cf.Name).ToList()[14]; + Assert.Equal("ConversationAnalysisAnalyzeConversationConversation", function.Name); + Assert.Equal("![kind:s, result:![detectedLanguage:s, prediction:![entities:*[category:s, confidenceScore:w, extraInformation:O, length:w, offset:w, resolutions:O, text:s], intents:*[category:s, confidenceScore:w], projectKind:s, topIntent:s], query:s]]", function.ReturnType.ToStringWithDisplayNames()); + + RecalcEngine engine = new RecalcEngine(pfxConfig); + + string analysisInput = @"{ conversationItem: { modality: ""text"", language: ""en-us"", text: ""Book me a flight for Munich"" } }"; + string parameters = @"{ deploymentName: ""deploy1"", projectName: ""project1"", verbose: true, stringIndexType: ""TextElement_V8"" }"; + FormulaValue analysisInputParam = engine.Eval(analysisInput); + FormulaValue parametersParam = engine.Eval(parameters); + FormulaValue kind = FormulaValue.New("Conversation"); + + using var httpClient = new HttpClient(testConnector); + testConnector.SetResponseFromFile(@"Responses\Azure Cognitive Service for Language_Response.json"); + using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient) + { + SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", + }; + + BaseRuntimeConnectorContext context = new TestConnectorRuntimeContext("ACSL", client, console: _output); + + FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[] { analysisInputParam, parametersParam }, context, CancellationToken.None); + httpClient.Dispose(); + client.Dispose(); + testConnector.Dispose(); + + using var testConnector2 = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language.json", _output); + using var httpClient2 = new HttpClient(testConnector2); + testConnector2.SetResponseFromFile(@"Responses\Azure Cognitive Service for Language_Response.json"); + using PowerPlatformConnectorClient client2 = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient2) + { + SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", + }; + + BaseRuntimeConnectorContext context2 = new TestConnectorRuntimeContext("ACSL", client2, console: _output); + + FormulaValue httpResult2 = await function.InvokeAsync(new FormulaValue[] { analysisInputParam, parametersParam }, context2, CancellationToken.None); + + Assert.NotNull(httpResult2); + Assert.True(httpResult2 is RecordValue); + + RecordValue httpResultValue = (RecordValue)httpResult2; + RecordValue resultValue = (RecordValue)httpResultValue.GetField("result"); + RecordValue predictionValue = (RecordValue)resultValue.GetField("prediction"); + TableValue entitiesValue = (TableValue)predictionValue.GetField("entities"); + RecordValue entityValue = (RecordValue)entitiesValue.Rows.First().Value; + FormulaValue resolutionsValue = entityValue.GetField("resolutions"); + + Assert.True(resolutionsValue is UntypedObjectValue); + + UntypedObjectValue resolutionUO = (UntypedObjectValue)resolutionsValue; + IUntypedObject impl = resolutionUO.Impl; + Assert.NotNull(impl); + Assert.Equal(1, impl.GetArrayLength()); + + bool b = impl[0].TryGetProperty("resolutionKind", out IUntypedObject resolutionKind); + Assert.True(b); + Assert.Equal("NumberResolution", resolutionKind.GetString()); + + string input = testConnector2._log.ToString(); + var version = PowerPlatformConnectorClient.Version; + var expectedInput = +@$"POST https://lucgen-apim.azure-api.net/invoke + authority: lucgen-apim.azure-api.net + Authorization: Bearer No Auth + path: /invoke + scheme: https + x-ms-client-environment-id: /providers/Microsoft.PowerApps/environments/aaa373836ffd4915bf6eefd63d164adc + x-ms-client-session-id: a41bd03b-6c3c-4509-a844-e8c51b61f878 + x-ms-request-method: POST + x-ms-request-url: /apim/cognitiveservicestextanalytics/16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b/language/:analyze-conversations?api-version=2022-05-01 + x-ms-user-agent: PowerFx/{version} + [content-header] Content-Type: application/json; charset=utf-8 + [body] {{""kind"":""Conversation"",""analysisInput"":{{""conversationItem"":{{""id"":""0"",""participantId"":""0"",""language"":""en-us"",""modality"":""text"",""text"":""Book me a flight for Munich""}}}},""parameters"":{{""projectName"":""project1"",""deploymentName"":""deploy1"",""verbose"":true,""stringIndexType"":""TextElement_V8""}}}} +"; + + Assert.Equal(expectedInput.Replace("\r\n", "\n").Replace("\r", "\n"), input.Replace("\r\n", "\n").Replace("\r", "\n")); } - [Fact] - public async Task ACSL_InvokeFunctionWithOutputOverride() - { - // this test is asserting that we can provide the expected output type that should be used during deserialization - // this is useful in a situation when the output type is dynamic and is not available in the swagger - using var testConnector = new LoggingTestServer(@"Swagger\TestConnectorDateTimeFormat.json", _output); - OpenApiDocument apiDoc = testConnector._apiDocument; - ConsoleLogger logger = new ConsoleLogger(_output); - - PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); - ConnectorFunction function = OpenApiParser.GetFunctions(new ConnectorSettings("ACSL") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, apiDoc).OrderBy(cf => cf.Name).ToList()[0]; - Assert.Equal("AnalyzeConversationTextSubmitJob", function.Name); - Assert.Equal("![createdDateTime`'Created Date':d, displayName:s]", function.ReturnType.ToStringWithDisplayNames()); - - using var testConnector2 = new LoggingTestServer(@"Swagger\TestConnectorDateTimeFormat.json", _output); - using var httpClient2 = new HttpClient(testConnector2); - testConnector2.SetResponseFromFile(@"Responses\TestConnectorDateTimeFormatResponse.json"); - using PowerPlatformConnectorClient client2 = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient2) - { - SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", - }; - - BaseRuntimeConnectorContext context2 = new TestConnectorRuntimeContext("ACSL", client2, console: _output); - - DType.TryParse("![createdDateTime:d, displayName:d]", out DType dtype); - var expectedFormulaType = FormulaType.Build(dtype); - - FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[0], context2, expectedFormulaType, CancellationToken.None); - - RecordValue httpResultValue = (RecordValue)httpResult; - FormulaValue displayName = httpResultValue.GetField("displayName"); - FormulaValue createdDateTime = httpResultValue.GetField("createdDateTime"); - - Assert.NotNull(httpResult); - Assert.True(httpResult is RecordValue); - Assert.True(displayName is DateTimeValue); - Assert.True(createdDateTime is DateTimeValue); - Assert.True(function.ReturnType != expectedFormulaType); - } - - [Fact] - public async Task ACSL_InvokeFunction() - { - using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language.json", _output); - OpenApiDocument apiDoc = testConnector._apiDocument; - ConsoleLogger logger = new ConsoleLogger(_output); - - PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); - ConnectorFunction function = OpenApiParser.GetFunctions(new ConnectorSettings("ACSL") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, apiDoc).OrderBy(cf => cf.Name).ToList()[14]; - Assert.Equal("ConversationAnalysisAnalyzeConversationConversation", function.Name); - Assert.Equal("![kind:s, result:![detectedLanguage:s, prediction:![entities:*[category:s, confidenceScore:w, extraInformation:O, length:w, offset:w, resolutions:O, text:s], intents:*[category:s, confidenceScore:w], projectKind:s, topIntent:s], query:s]]", function.ReturnType.ToStringWithDisplayNames()); - - RecalcEngine engine = new RecalcEngine(pfxConfig); - - string analysisInput = @"{ conversationItem: { modality: ""text"", language: ""en-us"", text: ""Book me a flight for Munich"" } }"; - string parameters = @"{ deploymentName: ""deploy1"", projectName: ""project1"", verbose: true, stringIndexType: ""TextElement_V8"" }"; - FormulaValue analysisInputParam = engine.Eval(analysisInput); - FormulaValue parametersParam = engine.Eval(parameters); - FormulaValue kind = FormulaValue.New("Conversation"); - - using var httpClient = new HttpClient(testConnector); - testConnector.SetResponseFromFile(@"Responses\Azure Cognitive Service for Language_Response.json"); - using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient) - { - SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", - }; - - BaseRuntimeConnectorContext context = new TestConnectorRuntimeContext("ACSL", client, console: _output); - - FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[] { analysisInputParam, parametersParam }, context, CancellationToken.None); - httpClient.Dispose(); - client.Dispose(); - testConnector.Dispose(); - - using var testConnector2 = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language.json", _output); - using var httpClient2 = new HttpClient(testConnector2); - testConnector2.SetResponseFromFile(@"Responses\Azure Cognitive Service for Language_Response.json"); - using PowerPlatformConnectorClient client2 = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient2) - { - SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", - }; - - BaseRuntimeConnectorContext context2 = new TestConnectorRuntimeContext("ACSL", client2, console: _output); - - FormulaValue httpResult2 = await function.InvokeAsync(new FormulaValue[] { analysisInputParam, parametersParam }, context2, CancellationToken.None); - - Assert.NotNull(httpResult2); - Assert.True(httpResult2 is RecordValue); - - RecordValue httpResultValue = (RecordValue)httpResult2; - RecordValue resultValue = (RecordValue)httpResultValue.GetField("result"); - RecordValue predictionValue = (RecordValue)resultValue.GetField("prediction"); - TableValue entitiesValue = (TableValue)predictionValue.GetField("entities"); - RecordValue entityValue = (RecordValue)entitiesValue.Rows.First().Value; - FormulaValue resolutionsValue = entityValue.GetField("resolutions"); - - Assert.True(resolutionsValue is UntypedObjectValue); - - UntypedObjectValue resolutionUO = (UntypedObjectValue)resolutionsValue; - IUntypedObject impl = resolutionUO.Impl; - Assert.NotNull(impl); - Assert.Equal(1, impl.GetArrayLength()); - - bool b = impl[0].TryGetProperty("resolutionKind", out IUntypedObject resolutionKind); - Assert.True(b); - Assert.Equal("NumberResolution", resolutionKind.GetString()); - - string input = testConnector2._log.ToString(); - var version = PowerPlatformConnectorClient.Version; - var expectedInput = -@$"POST https://lucgen-apim.azure-api.net/invoke - authority: lucgen-apim.azure-api.net - Authorization: Bearer No Auth - path: /invoke - scheme: https - x-ms-client-environment-id: /providers/Microsoft.PowerApps/environments/aaa373836ffd4915bf6eefd63d164adc - x-ms-client-session-id: a41bd03b-6c3c-4509-a844-e8c51b61f878 - x-ms-request-method: POST - x-ms-request-url: /apim/cognitiveservicestextanalytics/16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b/language/:analyze-conversations?api-version=2022-05-01 - x-ms-user-agent: PowerFx/{version} - [content-header] Content-Type: application/json; charset=utf-8 - [body] {{""kind"":""Conversation"",""analysisInput"":{{""conversationItem"":{{""id"":""0"",""participantId"":""0"",""language"":""en-us"",""modality"":""text"",""text"":""Book me a flight for Munich""}}}},""parameters"":{{""projectName"":""project1"",""deploymentName"":""deploy1"",""verbose"":true,""stringIndexType"":""TextElement_V8""}}}} -"; - - Assert.Equal(expectedInput.Replace("\r\n", "\n").Replace("\r", "\n"), input.Replace("\r\n", "\n").Replace("\r", "\n")); - } - - [Fact] - public async Task AzureOpenAiGetFunctions() - { - using var testConnector = new LoggingTestServer(@"Swagger\Azure Open AI.json", _output); - OpenApiDocument apiDoc = testConnector._apiDocument; - - PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); - ConnectorFunction[] functions = OpenApiParser.GetFunctions("OpenAI", apiDoc, new ConsoleLogger(_output)).OrderBy(cf => cf.Name).ToArray(); - - Assert.Equal("ChatCompletionsCreate", functions[0].Name); - Assert.Equal("![choices:*[finish_reason:s, index:w, message:![content:s, role:s]], created:w, id:s, model:s, object:s, usage:![completion_tokens:w, prompt_tokens:w, total_tokens:w]]", functions[0].ReturnType.ToStringWithDisplayNames()); - - Assert.Equal("CompletionsCreate", functions[1].Name); - Assert.Equal("![choices:*[finish_reason:s, index:w, logprobs:![text_offset:*[Value:w], token_logprobs:*[Value:w], tokens:*[Value:s], top_logprobs:*[]], text:s], created:w, id:s, model:s, object:s, usage:![completion_tokens:w, prompt_tokens:w, total_tokens:w]]", functions[1].ReturnType.ToStringWithDisplayNames()); - - Assert.Equal("ExtensionsChatCompletionsCreate", functions[2].Name); - Assert.Equal("![choices:*[content_filter_results:![error:![code:s, message:s], hate:![filtered:b, severity:s], self_harm:![filtered:b, severity:s], sexual:![filtered:b, severity:s], violence:![filtered:b, severity:s]], finish_reason:s, index:w, messages:*[content:s, end_turn:b, index:w, recipient:s, role:s]], created:w, id:s, model:s, object:s, prompt_filter_results:*[content_filter_results:![error:![code:s, message:s], hate:![filtered:b, severity:s], self_harm:![filtered:b, severity:s], sexual:![filtered:b, severity:s], violence:![filtered:b, severity:s]], prompt_index:w], usage:![completion_tokens:w, prompt_tokens:w, total_tokens:w]]", functions[2].ReturnType.ToStringWithDisplayNames()); - } - - [Fact] - public async Task ACSL_InvokeFunction_v21() - { - using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language v2.1.json", _output); - OpenApiDocument apiDoc = testConnector._apiDocument; - ConsoleLogger logger = new ConsoleLogger(_output); - - PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); - using var httpClient = new HttpClient(testConnector); - testConnector.SetResponseFromFile(@"Responses\Azure Cognitive Service for Language v2.1_Response.json"); - - var xx = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList(); - ConnectorFunction function = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList()[11]; - Assert.Equal("ConversationAnalysisAnalyzeConversationConversation", function.Name); - Assert.Equal("![kind:s, result:![detectedLanguage:s, prediction:![entities:*[category:s, confidenceScore:w, extraInformation:O, length:w, multipleResolutions:b, offset:w, resolutions:O, text:s, topResolution:O], intents:*[category:s, confidenceScore:w], projectKind:s, topIntent:s], query:s]]", function.ReturnType.ToStringWithDisplayNames()); - - RecalcEngine engine = new RecalcEngine(pfxConfig); - - string analysisInput = @"{ conversationItem: { modality: ""text"", language: ""en-us"", text: ""Book me a flight for Munich"" } }"; - string parameters = @"{ deploymentName: ""deploy1"", projectName: ""project1"", verbose: true, stringIndexType: ""TextElement_V8"" }"; - FormulaValue analysisInputParam = engine.Eval(analysisInput); - FormulaValue parametersParam = engine.Eval(parameters); - FormulaValue kind = FormulaValue.New("Conversation"); - - using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", }; - BaseRuntimeConnectorContext context = new TestConnectorRuntimeContext("ACSL", client, console: _output); - - FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[] { kind, analysisInputParam, parametersParam }, context, CancellationToken.None); - - Assert.NotNull(httpResult); - Assert.True(httpResult is RecordValue); - - RecordValue httpResultValue = (RecordValue)httpResult; - RecordValue resultValue = (RecordValue)httpResultValue.GetField("result"); - RecordValue predictionValue = (RecordValue)resultValue.GetField("prediction"); - TableValue entitiesValue = (TableValue)predictionValue.GetField("entities"); - IEnumerable> rows = entitiesValue.Rows; - - // Get second entity - RecordValue entityValue2 = rows.Skip(1).First().Value; - FormulaValue resolutionsValue = entityValue2.GetField("resolutions"); - - Assert.True(resolutionsValue is UntypedObjectValue); - UOValueVisitor visitor1 = new UOValueVisitor(); - resolutionsValue.Visit(visitor1); - - Assert.Equal("[\"{\\\"resolutionKind\\\":\\\"DateTimeResolution\\\",\\\"value\\\":\\\"2023-02-25\\\"}\"]", visitor1.Result); - - FormulaValue topResolutionValue1 = entityValue2.GetField("topResolution"); - Assert.True(topResolutionValue1 is UntypedObjectValue); - - UOValueVisitor visitor2 = new UOValueVisitor(); - topResolutionValue1.Visit(visitor2); - - Assert.Equal("{\"resolutionKind\":\"DateTimeResolution\",\"value\":\"2023-02-25\"}", visitor2.Result); - } - - [Fact] - public async Task ACSL_InvokeFunction_v21_WithInvalidResponse() - { - using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language v2.1.json", _output); - OpenApiDocument apiDoc = testConnector._apiDocument; - ConsoleLogger logger = new ConsoleLogger(_output); - - PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); - using var httpClient = new HttpClient(testConnector); - - // 'intents' is a record instead of being a table - // Expecting Table but received a Record, in result/prediction/intents - testConnector.SetResponseFromFile(@"Responses\Azure Cognitive Service For Language v2.1_Invalid Response.json"); - - var xx = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList(); - ConnectorFunction function = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList()[11]; - Assert.Equal("ConversationAnalysisAnalyzeConversationConversation", function.Name); - Assert.Equal("![kind:s, result:![detectedLanguage:s, prediction:![entities:*[category:s, confidenceScore:w, extraInformation:O, length:w, multipleResolutions:b, offset:w, resolutions:O, text:s, topResolution:O], intents:*[category:s, confidenceScore:w], projectKind:s, topIntent:s], query:s]]", function.ReturnType.ToStringWithDisplayNames()); - - RecalcEngine engine = new RecalcEngine(pfxConfig); - - string analysisInput = @"{ conversationItem: { modality: ""text"", language: ""en-us"", text: ""Book me a flight for Munich"" } }"; - string parameters = @"{ deploymentName: ""deploy1"", projectName: ""project1"", verbose: true, stringIndexType: ""TextElement_V8"" }"; - FormulaValue analysisInputParam = engine.Eval(analysisInput); - FormulaValue parametersParam = engine.Eval(parameters); - FormulaValue kind = FormulaValue.New("Conversation"); - - using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", }; - BaseRuntimeConnectorContext context = new TestConnectorRuntimeContext("ACSL", client, console: _output); - - FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[] { kind, analysisInputParam, parametersParam }, context, CancellationToken.None); - - Assert.NotNull(httpResult); - Assert.True(httpResult is RecordValue); - - RecordValue httpResultValue = (RecordValue)httpResult; - RecordValue resultValue = (RecordValue)httpResultValue.GetField("result"); - RecordValue predictionValue = (RecordValue)resultValue.GetField("prediction"); - TableValue entitiesValue = (TableValue)predictionValue.GetField("entities"); - IEnumerable> rows = entitiesValue.Rows; - - // Get second entity - RecordValue entityValue2 = rows.Skip(1).First().Value; - FormulaValue resolutionsValue = entityValue2.GetField("resolutions"); - - Assert.True(resolutionsValue is UntypedObjectValue); - UOValueVisitor visitor1 = new UOValueVisitor(); - resolutionsValue.Visit(visitor1); - - Assert.Equal("[\"{\\\"resolutionKind\\\":\\\"DateTimeResolution\\\",\\\"value\\\":\\\"2023-02-25\\\"}\"]", visitor1.Result); - - FormulaValue topResolutionValue1 = entityValue2.GetField("topResolution"); - Assert.True(topResolutionValue1 is UntypedObjectValue); - - UOValueVisitor visitor2 = new UOValueVisitor(); - topResolutionValue1.Visit(visitor2); - - Assert.Equal("{\"resolutionKind\":\"DateTimeResolution\",\"value\":\"2023-02-25\"}", visitor2.Result); - } - - [Fact] - public async Task ACSL_InvokeFunction_v21_WithInvalidResponse2() - { - using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language v2.1.json", _output); - OpenApiDocument apiDoc = testConnector._apiDocument; - ConsoleLogger logger = new ConsoleLogger(_output); - - PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); - using var httpClient = new HttpClient(testConnector); - - // "intents": 99 - testConnector.SetResponseFromFile(@"Responses\Azure Cognitive Service For Language v2.1_Invalid Response 2.json"); - - var xx = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList(); + [Fact] + public async Task AzureOpenAiGetFunctions() + { + using var testConnector = new LoggingTestServer(@"Swagger\Azure Open AI.json", _output); + OpenApiDocument apiDoc = testConnector._apiDocument; + + PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); + ConnectorFunction[] functions = OpenApiParser.GetFunctions("OpenAI", apiDoc, new ConsoleLogger(_output)).OrderBy(cf => cf.Name).ToArray(); + + Assert.Equal("ChatCompletionsCreate", functions[0].Name); + Assert.Equal("![choices:*[finish_reason:s, index:w, message:![content:s, role:s]], created:w, id:s, model:s, object:s, usage:![completion_tokens:w, prompt_tokens:w, total_tokens:w]]", functions[0].ReturnType.ToStringWithDisplayNames()); + + Assert.Equal("CompletionsCreate", functions[1].Name); + Assert.Equal("![choices:*[finish_reason:s, index:w, logprobs:![text_offset:*[Value:w], token_logprobs:*[Value:w], tokens:*[Value:s], top_logprobs:*[]], text:s], created:w, id:s, model:s, object:s, usage:![completion_tokens:w, prompt_tokens:w, total_tokens:w]]", functions[1].ReturnType.ToStringWithDisplayNames()); + + Assert.Equal("ExtensionsChatCompletionsCreate", functions[2].Name); + Assert.Equal("![choices:*[content_filter_results:![error:![code:s, message:s], hate:![filtered:b, severity:s], self_harm:![filtered:b, severity:s], sexual:![filtered:b, severity:s], violence:![filtered:b, severity:s]], finish_reason:s, index:w, messages:*[content:s, end_turn:b, index:w, recipient:s, role:s]], created:w, id:s, model:s, object:s, prompt_filter_results:*[content_filter_results:![error:![code:s, message:s], hate:![filtered:b, severity:s], self_harm:![filtered:b, severity:s], sexual:![filtered:b, severity:s], violence:![filtered:b, severity:s]], prompt_index:w], usage:![completion_tokens:w, prompt_tokens:w, total_tokens:w]]", functions[2].ReturnType.ToStringWithDisplayNames()); + } + + [Fact] + public async Task ACSL_InvokeFunction_v21() + { + using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language v2.1.json", _output); + OpenApiDocument apiDoc = testConnector._apiDocument; + ConsoleLogger logger = new ConsoleLogger(_output); + + PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); + using var httpClient = new HttpClient(testConnector); + testConnector.SetResponseFromFile(@"Responses\Azure Cognitive Service for Language v2.1_Response.json"); + + var xx = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList(); + ConnectorFunction function = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList()[11]; + Assert.Equal("ConversationAnalysisAnalyzeConversationConversation", function.Name); + Assert.Equal("![kind:s, result:![detectedLanguage:s, prediction:![entities:*[category:s, confidenceScore:w, extraInformation:O, length:w, multipleResolutions:b, offset:w, resolutions:O, text:s, topResolution:O], intents:*[category:s, confidenceScore:w], projectKind:s, topIntent:s], query:s]]", function.ReturnType.ToStringWithDisplayNames()); + + RecalcEngine engine = new RecalcEngine(pfxConfig); + + string analysisInput = @"{ conversationItem: { modality: ""text"", language: ""en-us"", text: ""Book me a flight for Munich"" } }"; + string parameters = @"{ deploymentName: ""deploy1"", projectName: ""project1"", verbose: true, stringIndexType: ""TextElement_V8"" }"; + FormulaValue analysisInputParam = engine.Eval(analysisInput); + FormulaValue parametersParam = engine.Eval(parameters); + FormulaValue kind = FormulaValue.New("Conversation"); + + using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", }; + BaseRuntimeConnectorContext context = new TestConnectorRuntimeContext("ACSL", client, console: _output); + + FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[] { kind, analysisInputParam, parametersParam }, context, CancellationToken.None); + + Assert.NotNull(httpResult); + Assert.True(httpResult is RecordValue); + + RecordValue httpResultValue = (RecordValue)httpResult; + RecordValue resultValue = (RecordValue)httpResultValue.GetField("result"); + RecordValue predictionValue = (RecordValue)resultValue.GetField("prediction"); + TableValue entitiesValue = (TableValue)predictionValue.GetField("entities"); + IEnumerable> rows = entitiesValue.Rows; + + // Get second entity + RecordValue entityValue2 = rows.Skip(1).First().Value; + FormulaValue resolutionsValue = entityValue2.GetField("resolutions"); + + Assert.True(resolutionsValue is UntypedObjectValue); + UOValueVisitor visitor1 = new UOValueVisitor(); + resolutionsValue.Visit(visitor1); + + Assert.Equal("[\"{\\\"resolutionKind\\\":\\\"DateTimeResolution\\\",\\\"value\\\":\\\"2023-02-25\\\"}\"]", visitor1.Result); + + FormulaValue topResolutionValue1 = entityValue2.GetField("topResolution"); + Assert.True(topResolutionValue1 is UntypedObjectValue); + + UOValueVisitor visitor2 = new UOValueVisitor(); + topResolutionValue1.Visit(visitor2); + + Assert.Equal("{\"resolutionKind\":\"DateTimeResolution\",\"value\":\"2023-02-25\"}", visitor2.Result); + } + + [Fact] + public async Task ACSL_InvokeFunction_v21_WithInvalidResponse() + { + using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language v2.1.json", _output); + OpenApiDocument apiDoc = testConnector._apiDocument; + ConsoleLogger logger = new ConsoleLogger(_output); + + PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); + using var httpClient = new HttpClient(testConnector); + + // 'intents' is a record instead of being a table + // Expecting Table but received a Record, in result/prediction/intents + testConnector.SetResponseFromFile(@"Responses\Azure Cognitive Service For Language v2.1_Invalid Response.json"); + + var xx = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList(); + ConnectorFunction function = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList()[11]; + Assert.Equal("ConversationAnalysisAnalyzeConversationConversation", function.Name); + Assert.Equal("![kind:s, result:![detectedLanguage:s, prediction:![entities:*[category:s, confidenceScore:w, extraInformation:O, length:w, multipleResolutions:b, offset:w, resolutions:O, text:s, topResolution:O], intents:*[category:s, confidenceScore:w], projectKind:s, topIntent:s], query:s]]", function.ReturnType.ToStringWithDisplayNames()); + + RecalcEngine engine = new RecalcEngine(pfxConfig); + + string analysisInput = @"{ conversationItem: { modality: ""text"", language: ""en-us"", text: ""Book me a flight for Munich"" } }"; + string parameters = @"{ deploymentName: ""deploy1"", projectName: ""project1"", verbose: true, stringIndexType: ""TextElement_V8"" }"; + FormulaValue analysisInputParam = engine.Eval(analysisInput); + FormulaValue parametersParam = engine.Eval(parameters); + FormulaValue kind = FormulaValue.New("Conversation"); + + using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", }; + BaseRuntimeConnectorContext context = new TestConnectorRuntimeContext("ACSL", client, console: _output); + + FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[] { kind, analysisInputParam, parametersParam }, context, CancellationToken.None); + + Assert.NotNull(httpResult); + Assert.True(httpResult is RecordValue); + + RecordValue httpResultValue = (RecordValue)httpResult; + RecordValue resultValue = (RecordValue)httpResultValue.GetField("result"); + RecordValue predictionValue = (RecordValue)resultValue.GetField("prediction"); + TableValue entitiesValue = (TableValue)predictionValue.GetField("entities"); + IEnumerable> rows = entitiesValue.Rows; + + // Get second entity + RecordValue entityValue2 = rows.Skip(1).First().Value; + FormulaValue resolutionsValue = entityValue2.GetField("resolutions"); + + Assert.True(resolutionsValue is UntypedObjectValue); + UOValueVisitor visitor1 = new UOValueVisitor(); + resolutionsValue.Visit(visitor1); + + Assert.Equal("[\"{\\\"resolutionKind\\\":\\\"DateTimeResolution\\\",\\\"value\\\":\\\"2023-02-25\\\"}\"]", visitor1.Result); + + FormulaValue topResolutionValue1 = entityValue2.GetField("topResolution"); + Assert.True(topResolutionValue1 is UntypedObjectValue); + + UOValueVisitor visitor2 = new UOValueVisitor(); + topResolutionValue1.Visit(visitor2); + + Assert.Equal("{\"resolutionKind\":\"DateTimeResolution\",\"value\":\"2023-02-25\"}", visitor2.Result); + } + + [Fact] + public async Task ACSL_InvokeFunction_v21_WithInvalidResponse2() + { + using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language v2.1.json", _output); + OpenApiDocument apiDoc = testConnector._apiDocument; + ConsoleLogger logger = new ConsoleLogger(_output); + + PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); + using var httpClient = new HttpClient(testConnector); + + // "intents": 99 + testConnector.SetResponseFromFile(@"Responses\Azure Cognitive Service For Language v2.1_Invalid Response 2.json"); + + var xx = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList(); ConnectorFunction function = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList()[11]; - RecalcEngine engine = new RecalcEngine(pfxConfig); - - string analysisInput = @"{ conversationItem: { modality: ""text"", language: ""en-us"", text: ""Book me a flight for Munich"" } }"; - string parameters = @"{ deploymentName: ""deploy1"", projectName: ""project1"", verbose: true, stringIndexType: ""TextElement_V8"" }"; - FormulaValue analysisInputParam = engine.Eval(analysisInput); - FormulaValue parametersParam = engine.Eval(parameters); - FormulaValue kind = FormulaValue.New("Conversation"); - - using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", }; - BaseRuntimeConnectorContext context = new TestConnectorRuntimeContext("ACSL", client, console: _output); - - FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[] { kind, analysisInputParam, parametersParam }, context, CancellationToken.None); - - ErrorValue ev = Assert.IsType(httpResult); - Assert.Equal(ErrorKind.InvalidArgument, ev.Errors.First().Kind); - Assert.Equal("ACSL.ConversationAnalysisAnalyzeConversationConversation failed: PowerFxJsonException Expecting Table but received a Number, in result/prediction/intents", ev.Errors.First().Message); - } - - [Fact] - public async Task ACSL_InvokeFunction_v21_WithInvalidResponse3() - { - using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language v2.1.json", _output); - OpenApiDocument apiDoc = testConnector._apiDocument; - ConsoleLogger logger = new ConsoleLogger(_output); - - PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); - using var httpClient = new HttpClient(testConnector); - - // "category" is an 1-element array instead of a string - testConnector.SetResponseFromFile(@"Responses\Azure Cognitive Service For Language v2.1_Invalid Response 3.json"); - - var xx = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList(); - ConnectorFunction function = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList()[11]; - RecalcEngine engine = new RecalcEngine(pfxConfig); - - string analysisInput = @"{ conversationItem: { modality: ""text"", language: ""en-us"", text: ""Book me a flight for Munich"" } }"; - string parameters = @"{ deploymentName: ""deploy1"", projectName: ""project1"", verbose: true, stringIndexType: ""TextElement_V8"" }"; - FormulaValue analysisInputParam = engine.Eval(analysisInput); - FormulaValue parametersParam = engine.Eval(parameters); - FormulaValue kind = FormulaValue.New("Conversation"); - - using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", }; - BaseRuntimeConnectorContext context = new TestConnectorRuntimeContext("ACSL", client, console: _output); - - FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[] { kind, analysisInputParam, parametersParam }, context, CancellationToken.None); - - // valid result + RecalcEngine engine = new RecalcEngine(pfxConfig); + + string analysisInput = @"{ conversationItem: { modality: ""text"", language: ""en-us"", text: ""Book me a flight for Munich"" } }"; + string parameters = @"{ deploymentName: ""deploy1"", projectName: ""project1"", verbose: true, stringIndexType: ""TextElement_V8"" }"; + FormulaValue analysisInputParam = engine.Eval(analysisInput); + FormulaValue parametersParam = engine.Eval(parameters); + FormulaValue kind = FormulaValue.New("Conversation"); + + using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", }; + BaseRuntimeConnectorContext context = new TestConnectorRuntimeContext("ACSL", client, console: _output); + + FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[] { kind, analysisInputParam, parametersParam }, context, CancellationToken.None); + + ErrorValue ev = Assert.IsType(httpResult); + Assert.Equal(ErrorKind.InvalidArgument, ev.Errors.First().Kind); + Assert.Equal("ACSL.ConversationAnalysisAnalyzeConversationConversation failed: PowerFxJsonException Expecting Table but received a Number, in result/prediction/intents", ev.Errors.First().Message); + } + + [Fact] + public async Task ACSL_InvokeFunction_v21_WithInvalidResponse3() + { + using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language v2.1.json", _output); + OpenApiDocument apiDoc = testConnector._apiDocument; + ConsoleLogger logger = new ConsoleLogger(_output); + + PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); + using var httpClient = new HttpClient(testConnector); + + // "category" is an 1-element array instead of a string + testConnector.SetResponseFromFile(@"Responses\Azure Cognitive Service For Language v2.1_Invalid Response 3.json"); + + var xx = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList(); + ConnectorFunction function = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList()[11]; + RecalcEngine engine = new RecalcEngine(pfxConfig); + + string analysisInput = @"{ conversationItem: { modality: ""text"", language: ""en-us"", text: ""Book me a flight for Munich"" } }"; + string parameters = @"{ deploymentName: ""deploy1"", projectName: ""project1"", verbose: true, stringIndexType: ""TextElement_V8"" }"; + FormulaValue analysisInputParam = engine.Eval(analysisInput); + FormulaValue parametersParam = engine.Eval(parameters); + FormulaValue kind = FormulaValue.New("Conversation"); + + using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", }; + BaseRuntimeConnectorContext context = new TestConnectorRuntimeContext("ACSL", client, console: _output); + + FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[] { kind, analysisInputParam, parametersParam }, context, CancellationToken.None); + + // valid result + Assert.IsAssignableFrom(httpResult); + } + + [Fact] + public async Task ACSL_InvokeFunction_v21_WithInvalidResponse4() + { + using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language v2.1.json", _output); + OpenApiDocument apiDoc = testConnector._apiDocument; + ConsoleLogger logger = new ConsoleLogger(_output); + + PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); + using var httpClient = new HttpClient(testConnector); + + // "category" is an 2-element array instead of a string + testConnector.SetResponseFromFile(@"Responses\Azure Cognitive Service For Language v2.1_Invalid Response 4.json"); + + var xx = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList(); + ConnectorFunction function = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList()[11]; + RecalcEngine engine = new RecalcEngine(pfxConfig); + + string analysisInput = @"{ conversationItem: { modality: ""text"", language: ""en-us"", text: ""Book me a flight for Munich"" } }"; + string parameters = @"{ deploymentName: ""deploy1"", projectName: ""project1"", verbose: true, stringIndexType: ""TextElement_V8"" }"; + FormulaValue analysisInputParam = engine.Eval(analysisInput); + FormulaValue parametersParam = engine.Eval(parameters); + FormulaValue kind = FormulaValue.New("Conversation"); + + using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", }; + BaseRuntimeConnectorContext context = new TestConnectorRuntimeContext("ACSL", client, console: _output); + + FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[] { kind, analysisInputParam, parametersParam }, context, CancellationToken.None); + + ErrorValue ev = Assert.IsType(httpResult); + Assert.Equal(ErrorKind.InvalidArgument, ev.Errors.First().Kind); + Assert.Equal("ACSL.ConversationAnalysisAnalyzeConversationConversation failed: PowerFxJsonException Expecting String but received a Table with 2 elements, in result/prediction/entities/category", ev.Errors.First().Message); + } + + [Fact] + public async Task ACSL_InvokeFunction_v21_WithInvalidResponse5() + { + using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language v2.1.json", _output); + OpenApiDocument apiDoc = testConnector._apiDocument; + ConsoleLogger logger = new ConsoleLogger(_output); + + PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); + using var httpClient = new HttpClient(testConnector); + + // "intent" is an array of arrays of records + testConnector.SetResponseFromFile(@"Responses\Azure Cognitive Service For Language v2.1_Invalid Response 5.json"); + + var xx = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList(); + ConnectorFunction function = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList()[11]; + RecalcEngine engine = new RecalcEngine(pfxConfig); + + string analysisInput = @"{ conversationItem: { modality: ""text"", language: ""en-us"", text: ""Book me a flight for Munich"" } }"; + string parameters = @"{ deploymentName: ""deploy1"", projectName: ""project1"", verbose: true, stringIndexType: ""TextElement_V8"" }"; + FormulaValue analysisInputParam = engine.Eval(analysisInput); + FormulaValue parametersParam = engine.Eval(parameters); + FormulaValue kind = FormulaValue.New("Conversation"); + + using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", }; + BaseRuntimeConnectorContext context = new TestConnectorRuntimeContext("ACSL", client, console: _output); + + FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[] { kind, analysisInputParam, parametersParam }, context, CancellationToken.None); + + // valid result Assert.IsAssignableFrom(httpResult); - } - - [Fact] - public async Task ACSL_InvokeFunction_v21_WithInvalidResponse4() - { - using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language v2.1.json", _output); - OpenApiDocument apiDoc = testConnector._apiDocument; - ConsoleLogger logger = new ConsoleLogger(_output); - - PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); - using var httpClient = new HttpClient(testConnector); - - // "category" is an 2-element array instead of a string - testConnector.SetResponseFromFile(@"Responses\Azure Cognitive Service For Language v2.1_Invalid Response 4.json"); - - var xx = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList(); - ConnectorFunction function = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList()[11]; - RecalcEngine engine = new RecalcEngine(pfxConfig); - - string analysisInput = @"{ conversationItem: { modality: ""text"", language: ""en-us"", text: ""Book me a flight for Munich"" } }"; - string parameters = @"{ deploymentName: ""deploy1"", projectName: ""project1"", verbose: true, stringIndexType: ""TextElement_V8"" }"; - FormulaValue analysisInputParam = engine.Eval(analysisInput); - FormulaValue parametersParam = engine.Eval(parameters); - FormulaValue kind = FormulaValue.New("Conversation"); - - using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", }; - BaseRuntimeConnectorContext context = new TestConnectorRuntimeContext("ACSL", client, console: _output); - - FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[] { kind, analysisInputParam, parametersParam }, context, CancellationToken.None); - - ErrorValue ev = Assert.IsType(httpResult); - Assert.Equal(ErrorKind.InvalidArgument, ev.Errors.First().Kind); - Assert.Equal("ACSL.ConversationAnalysisAnalyzeConversationConversation failed: PowerFxJsonException Expecting String but received a Table with 2 elements, in result/prediction/entities/category", ev.Errors.First().Message); - } - - [Fact] - public async Task ACSL_InvokeFunction_v21_WithInvalidResponse5() - { - using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language v2.1.json", _output); - OpenApiDocument apiDoc = testConnector._apiDocument; - ConsoleLogger logger = new ConsoleLogger(_output); - - PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1); - using var httpClient = new HttpClient(testConnector); - - // "intent" is an array of arrays of records - testConnector.SetResponseFromFile(@"Responses\Azure Cognitive Service For Language v2.1_Invalid Response 5.json"); - - var xx = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList(); - ConnectorFunction function = OpenApiParser.GetFunctions("ACSL", apiDoc, logger).OrderBy(cf => cf.Name).ToList()[11]; - RecalcEngine engine = new RecalcEngine(pfxConfig); - - string analysisInput = @"{ conversationItem: { modality: ""text"", language: ""en-us"", text: ""Book me a flight for Munich"" } }"; - string parameters = @"{ deploymentName: ""deploy1"", projectName: ""project1"", verbose: true, stringIndexType: ""TextElement_V8"" }"; - FormulaValue analysisInputParam = engine.Eval(analysisInput); - FormulaValue parametersParam = engine.Eval(parameters); - FormulaValue kind = FormulaValue.New("Conversation"); - - using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://lucgen-apim.azure-api.net", "aaa373836ffd4915bf6eefd63d164adc" /* environment Id */, "16e7c181-2f8d-4cae-b1f0-179c5c4e4d8b" /* connectionId */, () => "No Auth", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878", }; - BaseRuntimeConnectorContext context = new TestConnectorRuntimeContext("ACSL", client, console: _output); - - FormulaValue httpResult = await function.InvokeAsync(new FormulaValue[] { kind, analysisInputParam, parametersParam }, context, CancellationToken.None); - - // valid result - Assert.IsAssignableFrom(httpResult); - } - - internal class UOValueVisitor : IValueVisitor - { - public string Result { get; private set; } - - public void Visit(BlankValue value) - { - Result = string.Empty; - } - - public void Visit(NumberValue value) - { - Result = value.Value.ToString(); - } - - public void Visit(DecimalValue value) - { - Result = value.Value.ToString(); - } - - public void Visit(BooleanValue value) - { - Result = value.Value.ToString(); - } - - public void Visit(StringValue value) - { - Result = value.Value; - } - - public void Visit(TimeValue value) - { - Result = value.Value.ToString(); - } - - public void Visit(DateValue value) - { - Result = value.GetConvertedValue(null).ToString(); - } - - public void Visit(DateTimeValue value) - { - Result = value.GetConvertedValue(null).ToString(); - } - - public void Visit(ErrorValue value) - { - Result = string.Empty; - } - - public void Visit(RecordValue value) - { - var fieldBuilder = new Dictionary(); - - foreach (var item in value.Fields) - { - item.Value.Visit(this); - fieldBuilder.Add(item.Name, Result!); - } - - Result = JsonConvert.SerializeObject(fieldBuilder); - } - - public void Visit(TableValue value) - { - var rows = new List(); - - foreach (var row in value.Rows) - { - row.ToFormulaValue().Visit(this); - rows.Add(Result); - } - - Result = JsonConvert.SerializeObject(rows); - } - - public void Visit(UntypedObjectValue value) - { - Visit(value.Impl); - } - - public void Visit(OptionSetValue value) - { - Result = value.DisplayName; - } - - public void Visit(ColorValue value) - { - Result = string.Empty; - } - - public void Visit(GuidValue value) - { - Result = value.Value.ToString(); - } - - public void Visit(BlobValue value) - { - Result = value.Content.GetAsBase64Async(CancellationToken.None).Result; - } - - private void Visit(IUntypedObject untypedObject) - { - var type = untypedObject.Type; - - if (type is StringType) - { - Result = untypedObject.GetString(); - } - else if (type is NumberType) - { - Result = untypedObject.GetDouble().ToString(); - } - else if (type is BooleanType) - { - Result = untypedObject.GetBoolean().ToString(); - } - else if (type is ExternalType externalType) - { - if (externalType.Kind == ExternalTypeKind.Array) - { - var rows = new List(); - - for (var i = 0; i < untypedObject.GetArrayLength(); i++) - { - var row = untypedObject[i]; - Visit(row); - rows.Add(Result); - } - - Result = JsonConvert.SerializeObject(rows); - } - else if (externalType.Kind == ExternalTypeKind.Object) - { - var fieldBuilder = new Dictionary(); - - foreach (var key in new[] { "extraInformationKind", "numberKind", "resolutionKind", "value" }) - { - if (untypedObject.TryGetProperty(key, out var result)) - { - Visit(result); - fieldBuilder.Add(key, Result!); - } - } - - Result = JsonConvert.SerializeObject(fieldBuilder); - } - else - { - Result = string.Empty; - } - } - else - { - Result = string.Empty; - } - } - } - - [Fact] - public void LQA_Load() - { - OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Language - Question Answering.json", _output); - (List connectorFunctions, List texlFunctions) = OpenApiParser.ParseInternal(new ConnectorSettings("LQA"), doc, new ConsoleLogger(_output)); - Assert.Contains(texlFunctions, func => func.Namespace.Name.Value == "LQA" && func.Name == "GetAnswersFromText"); - } - - [Fact] - public void SQL_Load() - { - OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\SQL Server.json", _output); - (List connectorFunctions, List texlFunctions) = OpenApiParser.ParseInternal(new ConnectorSettings("SQL") { IncludeInternalFunctions = true }, doc, new ConsoleLogger(_output)); - Assert.Contains(texlFunctions, func => func.Namespace.Name.Value == "SQL" && func.Name == "GetProcedureV2"); - } - -#if !NET462 - [Fact] - public void Dataverse_Sample() - { - OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\DataverseSample.json", _output); - ConnectorFunction[] functions = OpenApiParser.GetFunctions("DV", doc, new ConsoleLogger(_output)).ToArray(); - - Assert.NotNull(functions); - Assert.Equal(3, functions.Count()); - - Assert.Equal(new List() { "GetLead", "PostLead", "QualifyLead" }, functions.Select(f => f.Name).ToList()); - Assert.Equal(new List() { "GetLead", "PostLead", "QualifyLead" }, functions.Select(f => f.OriginalName).ToList()); - - // "x-ms-require-user-confirmation" - Assert.Equal(new List() { false, true, true }, functions.Select(f => f.RequiresUserConfirmation).ToList()); - - // "x-ms-explicit-input" in "QualifyLead" function parameters - Assert.Equal(4, functions[2].RequiredParameters.Length); - Assert.False(functions[2].RequiredParameters[0].ConnectorType.ExplicitInput); // "leadId" - Assert.True(functions[2].RequiredParameters[1].ConnectorType.ExplicitInput); // "CreateAccount" - Assert.True(functions[2].RequiredParameters[2].ConnectorType.ExplicitInput); // "CreateContact" - Assert.True(functions[2].RequiredParameters[3].ConnectorType.ExplicitInput); // "CreateOpportunity" - Assert.Single(functions[2].HiddenRequiredParameters); - Assert.False(functions[2].HiddenRequiredParameters[0].ConnectorType.ExplicitInput); // "Status" - Assert.Empty(functions[2].OptionalParameters); - - // "x-ms-visibility" - (0..3).ForAll(i => Assert.Equal(Visibility.None, functions[2].RequiredParameters[i].ConnectorType.Visibility)); - Assert.Equal(Visibility.Internal, functions[2].HiddenRequiredParameters[0].ConnectorType.Visibility); // "Status" - - // "enum" - Assert.Equal(FormulaType.Decimal, functions[1].OptionalParameters[2].ConnectorType.FormulaType); // "leadsourcecode" + } + + internal class UOValueVisitor : IValueVisitor + { + public string Result { get; private set; } + + public void Visit(BlankValue value) + { + Result = string.Empty; + } + + public void Visit(NumberValue value) + { + Result = value.Value.ToString(); + } + + public void Visit(DecimalValue value) + { + Result = value.Value.ToString(); + } + + public void Visit(BooleanValue value) + { + Result = value.Value.ToString(); + } + + public void Visit(StringValue value) + { + Result = value.Value; + } + + public void Visit(TimeValue value) + { + Result = value.Value.ToString(); + } + + public void Visit(DateValue value) + { + Result = value.GetConvertedValue(null).ToString(); + } + + public void Visit(DateTimeValue value) + { + Result = value.GetConvertedValue(null).ToString(); + } + + public void Visit(ErrorValue value) + { + Result = string.Empty; + } + + public void Visit(RecordValue value) + { + var fieldBuilder = new Dictionary(); + + foreach (var item in value.Fields) + { + item.Value.Visit(this); + fieldBuilder.Add(item.Name, Result!); + } + + Result = JsonConvert.SerializeObject(fieldBuilder); + } + + public void Visit(TableValue value) + { + var rows = new List(); + + foreach (var row in value.Rows) + { + row.ToFormulaValue().Visit(this); + rows.Add(Result); + } + + Result = JsonConvert.SerializeObject(rows); + } + + public void Visit(UntypedObjectValue value) + { + Visit(value.Impl); + } + + public void Visit(OptionSetValue value) + { + Result = value.DisplayName; + } + + public void Visit(ColorValue value) + { + Result = string.Empty; + } + + public void Visit(GuidValue value) + { + Result = value.Value.ToString(); + } + + public void Visit(BlobValue value) + { + Result = value.Content.GetAsBase64Async(CancellationToken.None).Result; + } + + private void Visit(IUntypedObject untypedObject) + { + var type = untypedObject.Type; + + if (type is StringType) + { + Result = untypedObject.GetString(); + } + else if (type is NumberType) + { + Result = untypedObject.GetDouble().ToString(); + } + else if (type is BooleanType) + { + Result = untypedObject.GetBoolean().ToString(); + } + else if (type is ExternalType externalType) + { + if (externalType.Kind == ExternalTypeKind.Array) + { + var rows = new List(); + + for (var i = 0; i < untypedObject.GetArrayLength(); i++) + { + var row = untypedObject[i]; + Visit(row); + rows.Add(Result); + } + + Result = JsonConvert.SerializeObject(rows); + } + else if (externalType.Kind == ExternalTypeKind.Object) + { + var fieldBuilder = new Dictionary(); + + foreach (var key in new[] { "extraInformationKind", "numberKind", "resolutionKind", "value" }) + { + if (untypedObject.TryGetProperty(key, out var result)) + { + Visit(result); + fieldBuilder.Add(key, Result!); + } + } + + Result = JsonConvert.SerializeObject(fieldBuilder); + } + else + { + Result = string.Empty; + } + } + else + { + Result = string.Empty; + } + } + } + + [Fact] + public void LQA_Load() + { + OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Language - Question Answering.json", _output); + (List connectorFunctions, List texlFunctions) = OpenApiParser.ParseInternal(new ConnectorSettings("LQA"), doc, new ConsoleLogger(_output)); + Assert.Contains(texlFunctions, func => func.Namespace.Name.Value == "LQA" && func.Name == "GetAnswersFromText"); + } + + [Fact] + public void SQL_Load() + { + OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\SQL Server.json", _output); + (List connectorFunctions, List texlFunctions) = OpenApiParser.ParseInternal(new ConnectorSettings("SQL") { IncludeInternalFunctions = true }, doc, new ConsoleLogger(_output)); + Assert.Contains(texlFunctions, func => func.Namespace.Name.Value == "SQL" && func.Name == "GetProcedureV2"); + } + +#if !NET462 + [Fact] + public void Dataverse_Sample() + { + OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\DataverseSample.json", _output); + ConnectorFunction[] functions = OpenApiParser.GetFunctions("DV", doc, new ConsoleLogger(_output)).ToArray(); + + Assert.NotNull(functions); + Assert.Equal(3, functions.Count()); + + Assert.Equal(new List() { "GetLead", "PostLead", "QualifyLead" }, functions.Select(f => f.Name).ToList()); + Assert.Equal(new List() { "GetLead", "PostLead", "QualifyLead" }, functions.Select(f => f.OriginalName).ToList()); + + // "x-ms-require-user-confirmation" + Assert.Equal(new List() { false, true, true }, functions.Select(f => f.RequiresUserConfirmation).ToList()); + + // "x-ms-explicit-input" in "QualifyLead" function parameters + Assert.Equal(4, functions[2].RequiredParameters.Length); + Assert.False(functions[2].RequiredParameters[0].ConnectorType.ExplicitInput); // "leadId" + Assert.True(functions[2].RequiredParameters[1].ConnectorType.ExplicitInput); // "CreateAccount" + Assert.True(functions[2].RequiredParameters[2].ConnectorType.ExplicitInput); // "CreateContact" + Assert.True(functions[2].RequiredParameters[3].ConnectorType.ExplicitInput); // "CreateOpportunity" + Assert.Single(functions[2].HiddenRequiredParameters); + Assert.False(functions[2].HiddenRequiredParameters[0].ConnectorType.ExplicitInput); // "Status" + Assert.Empty(functions[2].OptionalParameters); + + // "x-ms-visibility" + (0..3).ForAll(i => Assert.Equal(Visibility.None, functions[2].RequiredParameters[i].ConnectorType.Visibility)); + Assert.Equal(Visibility.Internal, functions[2].HiddenRequiredParameters[0].ConnectorType.Visibility); // "Status" + + // "enum" + Assert.Equal(FormulaType.Decimal, functions[1].OptionalParameters[2].ConnectorType.FormulaType); // "leadsourcecode" Assert.True(functions[1].OptionalParameters[2].ConnectorType.IsEnum); - Assert.Equal(Enumerable.Range(1, 10).Select(i => (decimal)i).ToArray(), functions[1].OptionalParameters[2].ConnectorType.EnumValues.Select(fv => (decimal)fv.ToObject())); - Assert.Equal("Advertisement, Employee Referral, External Referral, Partner, Public Relations, Seminar, Trade Show, Web, Word of Mouth, Other", string.Join(", ", functions[1].OptionalParameters[2].ConnectorType.EnumDisplayNames)); - Assert.Equal(4m, functions[1].OptionalParameters[2].ConnectorType.Enum["Partner"].ToObject()); - - // "x-ms-enum-display-name" - Assert.NotNull(functions[1].OptionalParameters[2].ConnectorType.EnumDisplayNames); - Assert.Equal("Advertisement", functions[1].OptionalParameters[2].ConnectorType.EnumDisplayNames[0]); - Assert.Equal("Employee Referral", functions[1].OptionalParameters[2].ConnectorType.EnumDisplayNames[1]); - - Assert.True(functions[1].RequiredParameters[2].ConnectorType.IsEnum); // "msdyn_company@odata.bind" - Assert.Equal("2b629105-4a26-4607-97a5-0715059e0a55", functions[1].RequiredParameters[2].ConnectorType.EnumValues[0].ToObject()); - Assert.Equal("5cacddd3-d47f-4023-a68e-0ce3e0d401fb", functions[1].RequiredParameters[2].ConnectorType.EnumValues[1].ToObject()); - Assert.Equal("INMF", functions[1].RequiredParameters[2].ConnectorType.EnumDisplayNames[0]); + Assert.Equal(Enumerable.Range(1, 10).Select(i => (decimal)i).ToArray(), functions[1].OptionalParameters[2].ConnectorType.EnumValues.Select(fv => (decimal)fv.ToObject())); + Assert.Equal("Advertisement, Employee Referral, External Referral, Partner, Public Relations, Seminar, Trade Show, Web, Word of Mouth, Other", string.Join(", ", functions[1].OptionalParameters[2].ConnectorType.EnumDisplayNames)); + Assert.Equal(4m, functions[1].OptionalParameters[2].ConnectorType.Enum["Partner"].ToObject()); + + // "x-ms-enum-display-name" + Assert.NotNull(functions[1].OptionalParameters[2].ConnectorType.EnumDisplayNames); + Assert.Equal("Advertisement", functions[1].OptionalParameters[2].ConnectorType.EnumDisplayNames[0]); + Assert.Equal("Employee Referral", functions[1].OptionalParameters[2].ConnectorType.EnumDisplayNames[1]); + + Assert.True(functions[1].RequiredParameters[2].ConnectorType.IsEnum); // "msdyn_company@odata.bind" + Assert.Equal("2b629105-4a26-4607-97a5-0715059e0a55", functions[1].RequiredParameters[2].ConnectorType.EnumValues[0].ToObject()); + Assert.Equal("5cacddd3-d47f-4023-a68e-0ce3e0d401fb", functions[1].RequiredParameters[2].ConnectorType.EnumValues[1].ToObject()); + Assert.Equal("INMF", functions[1].RequiredParameters[2].ConnectorType.EnumDisplayNames[0]); Assert.Equal("MYMF", functions[1].RequiredParameters[2].ConnectorType.EnumDisplayNames[1]); - } - - [Fact] - public void VisibilityTest() - { - OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\AzureBlobStorage.json", _output); - ConnectorFunction[] functions = OpenApiParser.GetFunctions("AzBlob", doc, new ConsoleLogger(_output)).ToArray(); - - ConnectorFunction createFileV2 = functions.First(f => f.Name == "CreateFileV2"); - - Assert.Equal(4, createFileV2.RequiredParameters.Length); - Assert.Equal(3, createFileV2.OptionalParameters.Length); - Assert.Empty(createFileV2.HiddenRequiredParameters); - - Assert.Equal("important", createFileV2.Visibility); - - Assert.Equal("dataset", createFileV2.RequiredParameters[0].Name); - Assert.Equal("folderPath", createFileV2.RequiredParameters[1].Name); - Assert.Equal("name", createFileV2.RequiredParameters[2].Name); - Assert.Equal("file", createFileV2.RequiredParameters[3].Name); - Assert.Equal(FormulaType.Blob, createFileV2.RequiredParameters[3].FormulaType); - (0..3).ForAll(i => Assert.Equal(Visibility.None, createFileV2.RequiredParameters[i].ConnectorType.Visibility)); - - Assert.Equal("queryParametersSingleEncoded", createFileV2.OptionalParameters[0].Name); - Assert.Equal("Content-Type", createFileV2.OptionalParameters[1].Name); - Assert.Equal("ReadFileMetadataFromServer", createFileV2.OptionalParameters[2].Name); - Assert.Equal(Visibility.Internal, createFileV2.OptionalParameters[0].ConnectorType.Visibility); - Assert.Equal(Visibility.Advanced, createFileV2.OptionalParameters[1].ConnectorType.Visibility); - Assert.Equal(Visibility.Internal, createFileV2.OptionalParameters[2].ConnectorType.Visibility); - - Assert.Equal(Visibility.None, createFileV2.ReturnParameterType.Visibility); - - ConnectorFunction listFolderV4 = functions.First(f => f.Name == "ListFolderV4"); - - Assert.Equal(Visibility.None, listFolderV4.ReturnParameterType.Visibility); - Assert.Equal(Visibility.None, listFolderV4.ReturnParameterType.Fields[0].Visibility); - Assert.Equal(Visibility.Advanced, listFolderV4.ReturnParameterType.Fields[1].Visibility); - Assert.Equal(Visibility.Advanced, listFolderV4.ReturnParameterType.Fields[2].Visibility); - } -#endif - - [Fact] - public void DynamicReturnValueTest() - { - using HttpClient httpClient = new (); - OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\SQL Server.json", _output); - ConnectorFunction[] functions = OpenApiParser.GetFunctions("SQL", doc, new ConsoleLogger(_output)).ToArray(); - - ConnectorFunction createFileV2 = functions.First(f => f.Name == "ExecuteProcedureV2"); - - Assert.Equal(4, createFileV2.RequiredParameters.Length); - Assert.Empty(createFileV2.OptionalParameters); - Assert.Empty(createFileV2.HiddenRequiredParameters); - - Assert.NotNull(createFileV2.DynamicReturnSchema); - Assert.Null(createFileV2.DynamicReturnProperty); - - Assert.Equal("GetProcedureV2", createFileV2.DynamicReturnSchema.OperationId); - Assert.NotNull(createFileV2.DynamicReturnSchema.ConnectorFunction); - Assert.Equal("GetProcedureV2", createFileV2.DynamicReturnSchema.ConnectorFunction.Name); - Assert.Equal("schema/procedureresultschema", createFileV2.DynamicReturnSchema.ValuePath); - Assert.Equal(3, createFileV2.DynamicReturnSchema.ParameterMap.Count); - - Assert.True(createFileV2.DynamicReturnSchema.ParameterMap["server"] is DynamicConnectorExtensionValue dv1 && dv1.Reference == "server"); - Assert.True(createFileV2.DynamicReturnSchema.ParameterMap["database"] is DynamicConnectorExtensionValue dv2 && dv2.Reference == "database"); - Assert.True(createFileV2.DynamicReturnSchema.ParameterMap["procedure"] is DynamicConnectorExtensionValue dv3 && dv3.Reference == "procedure"); - - ConnectorFunction executePassThroughNativeQueryV2 = functions.First(f => f.Name == "ExecutePassThroughNativeQueryV2"); - - Assert.Equal(2, executePassThroughNativeQueryV2.RequiredParameters.Length); - Assert.Equal(3, executePassThroughNativeQueryV2.OptionalParameters.Length); - Assert.Empty(executePassThroughNativeQueryV2.HiddenRequiredParameters); - - Assert.NotNull(executePassThroughNativeQueryV2.DynamicReturnSchema); - Assert.NotNull(executePassThroughNativeQueryV2.DynamicReturnProperty); - - Assert.Equal("GetPassThroughNativeQueryMetadataV2", executePassThroughNativeQueryV2.DynamicReturnSchema.OperationId); - Assert.NotNull(executePassThroughNativeQueryV2.DynamicReturnSchema.ConnectorFunction); - Assert.Equal("GetPassThroughNativeQueryMetadataV2", executePassThroughNativeQueryV2.DynamicReturnSchema.ConnectorFunction.Name); - Assert.Equal("schema/queryresults", executePassThroughNativeQueryV2.DynamicReturnSchema.ValuePath); - Assert.Equal(4, executePassThroughNativeQueryV2.DynamicReturnSchema.ParameterMap.Count); - Assert.True(executePassThroughNativeQueryV2.DynamicReturnSchema.ParameterMap["server"] is DynamicConnectorExtensionValue dv4 && dv4.Reference == "server"); - Assert.True(executePassThroughNativeQueryV2.DynamicReturnSchema.ParameterMap["database"] is DynamicConnectorExtensionValue dv5 && dv5.Reference == "database"); - Assert.True(executePassThroughNativeQueryV2.DynamicReturnSchema.ParameterMap["query"] is DynamicConnectorExtensionValue dv6 && dv6.Reference == "query"); - Assert.True(executePassThroughNativeQueryV2.DynamicReturnSchema.ParameterMap["formalParameters"] is DynamicConnectorExtensionValue dv7 && dv7.Reference == "formalParameters"); - - Assert.Equal("GetPassThroughNativeQueryMetadataV2", executePassThroughNativeQueryV2.DynamicReturnProperty.OperationId); - Assert.NotNull(executePassThroughNativeQueryV2.DynamicReturnProperty.ConnectorFunction); - Assert.Equal("GetPassThroughNativeQueryMetadataV2", executePassThroughNativeQueryV2.DynamicReturnProperty.ConnectorFunction.Name); - Assert.Equal("schema/queryresults", executePassThroughNativeQueryV2.DynamicReturnProperty.ItemValuePath); - Assert.Equal(4, executePassThroughNativeQueryV2.DynamicReturnProperty.ParameterMap.Count); - Assert.True(executePassThroughNativeQueryV2.DynamicReturnProperty.ParameterMap["server"] is DynamicConnectorExtensionValue dv8 && dv8.Reference == "server"); - Assert.True(executePassThroughNativeQueryV2.DynamicReturnProperty.ParameterMap["database"] is DynamicConnectorExtensionValue dv9 && dv9.Reference == "database"); - Assert.True(executePassThroughNativeQueryV2.DynamicReturnProperty.ParameterMap["query/query"] is DynamicConnectorExtensionValue dv10 && dv10.Reference == "query/query"); - Assert.True(executePassThroughNativeQueryV2.DynamicReturnProperty.ParameterMap["query/formalParameters"] is DynamicConnectorExtensionValue dv11 && dv11.Reference == "query/formalParameters"); - } - - [Fact] - public async Task DirectIntellisenseTest() - { - using var testConnector = new LoggingTestServer(@"Swagger\SQL Server.json", _output); - using var httpClient = new HttpClient(testConnector); - using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://tip1002-002.azure-apihub.net", "ddadf2c7-ebdd-ec01-a5d1-502dc07f04b4" /* environment Id */, "4bf9a87fc9054b6db3a4d07a1c1f5a5b" /* connectionId */, () => "eyJ0eXAi...", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878" }; - - BaseRuntimeConnectorContext runtimeContext = new TestConnectorRuntimeContext("SQL", client, console: _output); - - ConnectorFunction[] functions = OpenApiParser.GetFunctions("SQL", testConnector._apiDocument, new ConsoleLogger(_output)).ToArray(); - ConnectorFunction executeProcedureV2 = functions.First(f => f.Name == "ExecuteProcedureV2"); - - Assert.True(executeProcedureV2.RequiredParameters[0].SupportsDynamicIntellisense); - Assert.True(executeProcedureV2.RequiredParameters[1].SupportsDynamicIntellisense); - Assert.True(executeProcedureV2.RequiredParameters[2].SupportsDynamicIntellisense); - Assert.True(executeProcedureV2.RequiredParameters[3].SupportsDynamicIntellisense); - - // Keeping only for debugging - //FormulaValue result = await executeProcedureV2.InvokeAync(client, new FormulaValue[] - //{ - // FormulaValue.New("pfxdev-sql.database.windows.net"), - // FormulaValue.New("connectortest"), - // FormulaValue.New("sp_1"), - // FormulaValue.NewRecordFromFields(new NamedValue[] { new NamedValue("p1", FormulaValue.New(38)) }) - //}, CancellationToken.None); - - testConnector.SetResponseFromFile(@"Responses\SQL Server Intellisense Response 3.json"); - ConnectorParameters parameters1 = await executeProcedureV2.GetParameterSuggestionsAsync( - new NamedValue[] - { - new NamedValue("server", FormulaValue.New("pfxdev-sql.database.windows.net")), - new NamedValue("database", FormulaValue.New("connectortest")) - }, - executeProcedureV2.RequiredParameters[2], // procedure - runtimeContext, - CancellationToken.None); - - ConnectorParameterWithSuggestions suggestions1 = parameters1.ParametersWithSuggestions[2]; - Assert.NotNull(suggestions1); - Assert.NotNull(suggestions1.Suggestions); - Assert.Equal(2, suggestions1.Suggestions.Count()); - - testConnector.SetResponseFromFile(@"Responses\SQL Server Intellisense Response2 1.json"); - ConnectorParameters parameters2 = await executeProcedureV2.GetParameterSuggestionsAsync( - new NamedValue[] - { - new NamedValue("server", FormulaValue.New("pfxdev-sql.database.windows.net")), - new NamedValue("database", FormulaValue.New("connectortest")), - new NamedValue("procedure", FormulaValue.New("sp_1")) - }, - executeProcedureV2.RequiredParameters[3], // parameters - runtimeContext, - CancellationToken.None); - - ConnectorParameterWithSuggestions suggestions2 = parameters2.ParametersWithSuggestions[3]; - Assert.NotNull(suggestions2); - Assert.NotNull(suggestions2.Suggestions); - Assert.Single(suggestions2.Suggestions); - - Assert.True(executeProcedureV2.ReturnParameterType.SupportsDynamicIntellisense); - - testConnector.SetResponseFromFile(@"Responses\SQL Server Intellisense Response2 1.json"); - ConnectorType returnType = await executeProcedureV2.GetConnectorReturnTypeAsync( - new NamedValue[] - { - new NamedValue("server", FormulaValue.New("pfxdev-sql.database.windows.net")), - new NamedValue("database", FormulaValue.New("connectortest")), - new NamedValue("procedure", FormulaValue.New("sp_1")) - }, - runtimeContext, - CancellationToken.None); - - Assert.NotNull(returnType); - Assert.True(returnType.FormulaType is RecordType); - - string input = testConnector._log.ToString(); - var version = PowerPlatformConnectorClient.Version; - string expected = $@"POST https://tip1002-002.azure-apihub.net/invoke - authority: tip1002-002.azure-apihub.net - Authorization: Bearer eyJ0eXAi... - path: /invoke - scheme: https - x-ms-client-environment-id: /providers/Microsoft.PowerApps/environments/ddadf2c7-ebdd-ec01-a5d1-502dc07f04b4 - x-ms-client-session-id: a41bd03b-6c3c-4509-a844-e8c51b61f878 - x-ms-request-method: GET - x-ms-request-url: /apim/sql/4bf9a87fc9054b6db3a4d07a1c1f5a5b/v2/datasets/pfxdev-sql.database.windows.net,connectortest/procedures - x-ms-user-agent: PowerFx/{version} -POST https://tip1002-002.azure-apihub.net/invoke - authority: tip1002-002.azure-apihub.net - Authorization: Bearer eyJ0eXAi... - path: /invoke - scheme: https - x-ms-client-environment-id: /providers/Microsoft.PowerApps/environments/ddadf2c7-ebdd-ec01-a5d1-502dc07f04b4 - x-ms-client-session-id: a41bd03b-6c3c-4509-a844-e8c51b61f878 - x-ms-request-method: GET - x-ms-request-url: /apim/sql/4bf9a87fc9054b6db3a4d07a1c1f5a5b/v2/$metadata.json/datasets/pfxdev-sql.database.windows.net,connectortest/procedures/sp_1 - x-ms-user-agent: PowerFx/{version} -POST https://tip1002-002.azure-apihub.net/invoke - authority: tip1002-002.azure-apihub.net - Authorization: Bearer eyJ0eXAi... - path: /invoke - scheme: https - x-ms-client-environment-id: /providers/Microsoft.PowerApps/environments/ddadf2c7-ebdd-ec01-a5d1-502dc07f04b4 - x-ms-client-session-id: a41bd03b-6c3c-4509-a844-e8c51b61f878 - x-ms-request-method: GET - x-ms-request-url: /apim/sql/4bf9a87fc9054b6db3a4d07a1c1f5a5b/v2/$metadata.json/datasets/pfxdev-sql.database.windows.net,connectortest/procedures/sp_1 - x-ms-user-agent: PowerFx/{version} -"; - - // Normalize CRLF ==> LF - expected = expected.Replace("\r", string.Empty); - input = input.Replace("\r", string.Empty); - Assert.Equal(expected, input); - } - - [Fact] - public async Task DataverseTest() - { - using var testConnector = new LoggingTestServer(@"Swagger\Dataverse.json", _output); - using var httpClient = new HttpClient(testConnector); - using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://tip1-shared.azure-apim.net", "Default-9f6be790-4a16-4dd6-9850-44a0d2649aef" /* environment Id */, "461a30624723445c9ba87313d8bbefa3" /* connectionId */, () => "eyJ0eXAiO...", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878" }; - - BaseRuntimeConnectorContext runtimeContext = new TestConnectorRuntimeContext("DV", client, console: _output); - - ConnectorFunction[] functions = OpenApiParser.GetFunctions(new ConnectorSettings("DV") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, testConnector._apiDocument).ToArray(); - ConnectorFunction createRecord = functions.First(f => f.Name == "CreateRecordWithOrganization"); - - testConnector.SetResponseFromFile(@"Responses\Dataverse_Response_1.json"); - ConnectorParameters parameters1 = await createRecord.GetParameterSuggestionsAsync( - new NamedValue[] - { - new NamedValue("organization", FormulaValue.New("https://org283e9949.crm10.dynamics.com")) - }, - createRecord.RequiredParameters[1], // entityName - runtimeContext, - CancellationToken.None); - - ConnectorParameterWithSuggestions suggestions1 = parameters1.ParametersWithSuggestions[1]; - Assert.Equal(651, suggestions1.Suggestions.Count); - Assert.Equal("AAD Users", suggestions1.Suggestions[0].DisplayName); - Assert.Equal("aadusers", ((StringValue)suggestions1.Suggestions[0].Suggestion).Value); - - testConnector.SetResponseFromFile(@"Responses\Dataverse_Response_2.json"); - ConnectorParameters parameters2 = await createRecord.GetParameterSuggestionsAsync( - new NamedValue[] - { - new NamedValue("organization", FormulaValue.New("https://org283e9949.crm10.dynamics.com")), - new NamedValue("entityName", FormulaValue.New("accounts")) - }, - createRecord.RequiredParameters[2], // item - runtimeContext, - CancellationToken.None); - - ConnectorParameterWithSuggestions suggestions2 = parameters2.ParametersWithSuggestions[2]; - Assert.Equal(119, suggestions2.Suggestions.Count); - Assert.Equal("accountcategorycode", suggestions2.Suggestions[0].DisplayName); - Assert.Equal("Decimal", suggestions2.Suggestions[0].Suggestion.Type.ToString()); - - string input = testConnector._log.ToString(); - var version = PowerPlatformConnectorClient.Version; - string expected = @$"POST https://tip1-shared.azure-apim.net/invoke - authority: tip1-shared.azure-apim.net - Authorization: Bearer eyJ0eXAiO... - organization: https://org283e9949.crm10.dynamics.com - path: /invoke - scheme: https - x-ms-client-environment-id: /providers/Microsoft.PowerApps/environments/Default-9f6be790-4a16-4dd6-9850-44a0d2649aef - x-ms-client-session-id: a41bd03b-6c3c-4509-a844-e8c51b61f878 - x-ms-request-method: POST - x-ms-request-url: /apim/commondataserviceforapps/461a30624723445c9ba87313d8bbefa3/v1.0/$metadata.json/GetEntityListEnum/GetEntitiesWithOrganization - x-ms-user-agent: PowerFx/{version} -POST https://tip1-shared.azure-apim.net/invoke - authority: tip1-shared.azure-apim.net - Authorization: Bearer eyJ0eXAiO... - organization: https://org283e9949.crm10.dynamics.com - path: /invoke - scheme: https - x-ms-client-environment-id: /providers/Microsoft.PowerApps/environments/Default-9f6be790-4a16-4dd6-9850-44a0d2649aef - x-ms-client-session-id: a41bd03b-6c3c-4509-a844-e8c51b61f878 - x-ms-request-method: GET - x-ms-request-url: /apim/commondataserviceforapps/461a30624723445c9ba87313d8bbefa3/v1.0/$metadata.json/entities/accounts/postitem - x-ms-user-agent: PowerFx/{version} -"; - - // Normalize CRLF ==> LF - expected = expected.Replace("\r", string.Empty); - input = input.Replace("\r", string.Empty); - Assert.Equal(expected, input); - } - - [Theory] - - // Very slow -- [InlineData(@"Swagger\Dataverse 2.json")] - // Very slow -- [InlineData(@"Swagger\Dataverse 3.json")] - [InlineData(@"Swagger\PowerPlatformForAdmins.json")] - public async Task DataverseTest2(string swaggerFile) - { - PowerFxConfig powerFxConfig = new PowerFxConfig(); - OpenApiDocument doc = Helpers.ReadSwagger(swaggerFile, _output); - - OpenApiParser.GetFunctions("namespace", doc); // missing logger - powerFxConfig.AddActionConnector("namespace", doc); - } - - [Fact] - public async Task CardsForPowerApps_Invoke() - { - using var testConnector = new LoggingTestServer(@"Swagger\CardsForPowerApps.json", _output); - using var httpClient = new HttpClient(testConnector); - using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://tip1002-002.azure-apihub.net", "7592282b-e371-e3f6-8e04-e8f23e64227c" /* environment Id */, "shared-cardsforpower-eafc4fa0-c560-4eba-a5b2-3e1ebc63193a" /* connectionId */, () => "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dC...", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878" }; - - BaseRuntimeConnectorContext runtimeContext = new TestConnectorRuntimeContext("DV", client, console: _output); - - ConnectorFunction[] functions = OpenApiParser.GetFunctions( - new ConnectorSettings("DV") - { - Compatibility = ConnectorCompatibility.SwaggerCompatibility, - ReturnUnknownRecordFieldsAsUntypedObjects = true - }, - testConnector._apiDocument).ToArray(); - ConnectorFunction createCardInstance = functions.First(f => f.Name == "CreateCardInstance"); - - testConnector.SetResponseFromFile(@"Responses\CardsForPowerApps_CreateCardInstance.json"); - var result = await createCardInstance.InvokeAsync( - new FormulaValue[] - { - FormulaValue.New("card"), - FormulaValue.NewRecordFromFields( - new NamedValue("inputs", FormulaValue.NewRecordFromFields( - new NamedValue("property1", FormulaValue.New("test1")), - new NamedValue("property2", FormulaValue.New("test2"))))), - }, - runtimeContext, - CancellationToken.None); - - string input = testConnector._log.ToString(); - Assert.Equal("AdaptiveCard", (((RecordValue)result).GetField("type") as UntypedObjectValue).Impl.GetString()); - var expected = - $@"POST https://tip1002-002.azure-apihub.net/invoke - authority: tip1002-002.azure-apihub.net - Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dC... - path: /invoke - scheme: https - x-ms-client-environment-id: /providers/Microsoft.PowerApps/environments/7592282b-e371-e3f6-8e04-e8f23e64227c - x-ms-client-session-id: a41bd03b-6c3c-4509-a844-e8c51b61f878 - x-ms-request-method: POST - x-ms-request-url: /apim/cardsforpowerapps/shared-cardsforpower-eafc4fa0-c560-4eba-a5b2-3e1ebc63193a/cards/cards/card/instances - x-ms-user-agent: PowerFx/{PowerPlatformConnectorClient.Version} - [content-header] Content-Type: application/json; charset=utf-8 - [body] {{""inputs"":{{""property1"":""test1"",""property2"":""test2""}}}} -"; - - // Normalize CRLF ==> LF - expected = expected.Replace("\r", string.Empty); - input = input.Replace("\r", string.Empty); - Assert.Equal(expected, input); - } - - [Fact] - public async Task CardsForPowerApps_Suggestion() - { - using var testConnector = new LoggingTestServer(@"Swagger\CardsForPowerApps.json", _output); - using var httpClient = new HttpClient(testConnector); - using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://tip1002-002.azure-apihub.net", "7592282b-e371-e3f6-8e04-e8f23e64227c" /* environment Id */, "shared-cardsforpower-eafc4fa0-c560-4eba-a5b2-3e1ebc63193a" /* connectionId */, () => "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dC...", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878" }; - - BaseRuntimeConnectorContext runtimeContext = new TestConnectorRuntimeContext("DV", client, console: _output); - - ConnectorFunction[] functions = OpenApiParser.GetFunctions(new ConnectorSettings("DV") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, testConnector._apiDocument).ToArray(); - ConnectorFunction createCardInstance = functions.First(f => f.Name == "CreateCardInstance"); - - testConnector.SetResponseFromFile(@"Responses\CardsForPowerApps_Suggestions.json"); - ConnectorParameters parameters = await createCardInstance.GetParameterSuggestionsAsync( - new NamedValue[] - { - }, - createCardInstance.RequiredParameters[0], // cardid - runtimeContext, - CancellationToken.None); - - ConnectorParameterWithSuggestions suggestions = parameters.ParametersWithSuggestions[0]; - Assert.Equal(2, suggestions.Suggestions.Count); - Assert.Equal("test", suggestions.Suggestions[0].DisplayName); - Assert.Equal("testWithInputs", suggestions.Suggestions[1].DisplayName); - } - - [Fact] - public async Task Teams_GetMessageDetails_WithComplexParameterReference() - { - using var testConnector = new LoggingTestServer(@"Swagger\Teams.json", _output); - using var httpClient = new HttpClient(testConnector); - using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://tip1002-002.azure-apihub.net", "7592282b-e371-e3f6-8e04-e8f23e64227c" /* environment Id */, "shared-cardsforpower-eafc4fa0-c560-4eba-a5b2-3e1ebc63193a" /* connectionId */, () => "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dC...", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878" }; - - BaseRuntimeConnectorContext runtimeContext = new TestConnectorRuntimeContext("DV", client, console: _output); - - ConnectorFunction[] functions = OpenApiParser.GetFunctions(new ConnectorSettings("DV") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, testConnector._apiDocument).ToArray(); - ConnectorFunction getMessageDetails = functions.First(f => f.Name == "GetMessageDetails"); - - testConnector.SetResponseFromFile(@"Responses\Teams_GetMessageDetails_InputType.json"); - ConnectorParameters parameters = await getMessageDetails.GetParameterSuggestionsAsync( - new NamedValue[] - { - new NamedValue("messageId", FormulaValue.New("messageId")), - new NamedValue("threadType", FormulaValue.New("channel")), - }, - getMessageDetails.RequiredParameters[2], // body - runtimeContext, - CancellationToken.None); - - var bodyConnectorType = parameters.ParametersWithSuggestions[2].ConnectorType; - - ConnectorParameterWithSuggestions suggestions = parameters.ParametersWithSuggestions[2]; - testConnector.SetResponseFromFile(@"Responses\Teams_GetMessageDetails_GetSuggestionsForChannel.json"); - - var connectorTypeWithSuggestions = await getMessageDetails.GetConnectorSuggestionsAsync( - new NamedValue[] - { - new NamedValue("messageId", FormulaValue.New("messageId")), - new NamedValue("threadType", FormulaValue.New("channel")), - new NamedValue("body", FormulaValue.NewRecordFromFields( - new NamedValue("recipient", FormulaValue.NewRecordFromFields( - new NamedValue("groupId", FormulaValue.New("groupIdValue")))))), - }, - bodyConnectorType.Fields[0].Fields[1], // channelId - runtimeContext, - CancellationToken.None); - - Assert.Equal(2, connectorTypeWithSuggestions.ConnectorSuggestions.Suggestions.Count); - Assert.Equal("channelName", connectorTypeWithSuggestions.ConnectorSuggestions.Suggestions[0].DisplayName); - Assert.Equal("channelName2", connectorTypeWithSuggestions.ConnectorSuggestions.Suggestions[1].DisplayName); + } + + [Fact] + public void VisibilityTest() + { + OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\AzureBlobStorage.json", _output); + ConnectorFunction[] functions = OpenApiParser.GetFunctions("AzBlob", doc, new ConsoleLogger(_output)).ToArray(); + + ConnectorFunction createFileV2 = functions.First(f => f.Name == "CreateFileV2"); + + Assert.Equal(4, createFileV2.RequiredParameters.Length); + Assert.Equal(3, createFileV2.OptionalParameters.Length); + Assert.Empty(createFileV2.HiddenRequiredParameters); + + Assert.Equal("important", createFileV2.Visibility); + + Assert.Equal("dataset", createFileV2.RequiredParameters[0].Name); + Assert.Equal("folderPath", createFileV2.RequiredParameters[1].Name); + Assert.Equal("name", createFileV2.RequiredParameters[2].Name); + Assert.Equal("file", createFileV2.RequiredParameters[3].Name); + Assert.Equal(FormulaType.Blob, createFileV2.RequiredParameters[3].FormulaType); + (0..3).ForAll(i => Assert.Equal(Visibility.None, createFileV2.RequiredParameters[i].ConnectorType.Visibility)); + + Assert.Equal("queryParametersSingleEncoded", createFileV2.OptionalParameters[0].Name); + Assert.Equal("Content-Type", createFileV2.OptionalParameters[1].Name); + Assert.Equal("ReadFileMetadataFromServer", createFileV2.OptionalParameters[2].Name); + Assert.Equal(Visibility.Internal, createFileV2.OptionalParameters[0].ConnectorType.Visibility); + Assert.Equal(Visibility.Advanced, createFileV2.OptionalParameters[1].ConnectorType.Visibility); + Assert.Equal(Visibility.Internal, createFileV2.OptionalParameters[2].ConnectorType.Visibility); + + Assert.Equal(Visibility.None, createFileV2.ReturnParameterType.Visibility); + + ConnectorFunction listFolderV4 = functions.First(f => f.Name == "ListFolderV4"); + + Assert.Equal(Visibility.None, listFolderV4.ReturnParameterType.Visibility); + Assert.Equal(Visibility.None, listFolderV4.ReturnParameterType.Fields[0].Visibility); + Assert.Equal(Visibility.Advanced, listFolderV4.ReturnParameterType.Fields[1].Visibility); + Assert.Equal(Visibility.Advanced, listFolderV4.ReturnParameterType.Fields[2].Visibility); + } +#endif + + [Fact] + public void DynamicReturnValueTest() + { + using HttpClient httpClient = new (); + OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\SQL Server.json", _output); + ConnectorFunction[] functions = OpenApiParser.GetFunctions("SQL", doc, new ConsoleLogger(_output)).ToArray(); + + ConnectorFunction createFileV2 = functions.First(f => f.Name == "ExecuteProcedureV2"); + + Assert.Equal(4, createFileV2.RequiredParameters.Length); + Assert.Empty(createFileV2.OptionalParameters); + Assert.Empty(createFileV2.HiddenRequiredParameters); + + Assert.NotNull(createFileV2.DynamicReturnSchema); + Assert.Null(createFileV2.DynamicReturnProperty); + + Assert.Equal("GetProcedureV2", createFileV2.DynamicReturnSchema.OperationId); + Assert.NotNull(createFileV2.DynamicReturnSchema.ConnectorFunction); + Assert.Equal("GetProcedureV2", createFileV2.DynamicReturnSchema.ConnectorFunction.Name); + Assert.Equal("schema/procedureresultschema", createFileV2.DynamicReturnSchema.ValuePath); + Assert.Equal(3, createFileV2.DynamicReturnSchema.ParameterMap.Count); + + Assert.True(createFileV2.DynamicReturnSchema.ParameterMap["server"] is DynamicConnectorExtensionValue dv1 && dv1.Reference == "server"); + Assert.True(createFileV2.DynamicReturnSchema.ParameterMap["database"] is DynamicConnectorExtensionValue dv2 && dv2.Reference == "database"); + Assert.True(createFileV2.DynamicReturnSchema.ParameterMap["procedure"] is DynamicConnectorExtensionValue dv3 && dv3.Reference == "procedure"); + + ConnectorFunction executePassThroughNativeQueryV2 = functions.First(f => f.Name == "ExecutePassThroughNativeQueryV2"); + + Assert.Equal(2, executePassThroughNativeQueryV2.RequiredParameters.Length); + Assert.Equal(3, executePassThroughNativeQueryV2.OptionalParameters.Length); + Assert.Empty(executePassThroughNativeQueryV2.HiddenRequiredParameters); + + Assert.NotNull(executePassThroughNativeQueryV2.DynamicReturnSchema); + Assert.NotNull(executePassThroughNativeQueryV2.DynamicReturnProperty); + + Assert.Equal("GetPassThroughNativeQueryMetadataV2", executePassThroughNativeQueryV2.DynamicReturnSchema.OperationId); + Assert.NotNull(executePassThroughNativeQueryV2.DynamicReturnSchema.ConnectorFunction); + Assert.Equal("GetPassThroughNativeQueryMetadataV2", executePassThroughNativeQueryV2.DynamicReturnSchema.ConnectorFunction.Name); + Assert.Equal("schema/queryresults", executePassThroughNativeQueryV2.DynamicReturnSchema.ValuePath); + Assert.Equal(4, executePassThroughNativeQueryV2.DynamicReturnSchema.ParameterMap.Count); + Assert.True(executePassThroughNativeQueryV2.DynamicReturnSchema.ParameterMap["server"] is DynamicConnectorExtensionValue dv4 && dv4.Reference == "server"); + Assert.True(executePassThroughNativeQueryV2.DynamicReturnSchema.ParameterMap["database"] is DynamicConnectorExtensionValue dv5 && dv5.Reference == "database"); + Assert.True(executePassThroughNativeQueryV2.DynamicReturnSchema.ParameterMap["query"] is DynamicConnectorExtensionValue dv6 && dv6.Reference == "query"); + Assert.True(executePassThroughNativeQueryV2.DynamicReturnSchema.ParameterMap["formalParameters"] is DynamicConnectorExtensionValue dv7 && dv7.Reference == "formalParameters"); + + Assert.Equal("GetPassThroughNativeQueryMetadataV2", executePassThroughNativeQueryV2.DynamicReturnProperty.OperationId); + Assert.NotNull(executePassThroughNativeQueryV2.DynamicReturnProperty.ConnectorFunction); + Assert.Equal("GetPassThroughNativeQueryMetadataV2", executePassThroughNativeQueryV2.DynamicReturnProperty.ConnectorFunction.Name); + Assert.Equal("schema/queryresults", executePassThroughNativeQueryV2.DynamicReturnProperty.ItemValuePath); + Assert.Equal(4, executePassThroughNativeQueryV2.DynamicReturnProperty.ParameterMap.Count); + Assert.True(executePassThroughNativeQueryV2.DynamicReturnProperty.ParameterMap["server"] is DynamicConnectorExtensionValue dv8 && dv8.Reference == "server"); + Assert.True(executePassThroughNativeQueryV2.DynamicReturnProperty.ParameterMap["database"] is DynamicConnectorExtensionValue dv9 && dv9.Reference == "database"); + Assert.True(executePassThroughNativeQueryV2.DynamicReturnProperty.ParameterMap["query/query"] is DynamicConnectorExtensionValue dv10 && dv10.Reference == "query/query"); + Assert.True(executePassThroughNativeQueryV2.DynamicReturnProperty.ParameterMap["query/formalParameters"] is DynamicConnectorExtensionValue dv11 && dv11.Reference == "query/formalParameters"); + } + + [Fact] + public async Task DirectIntellisenseTest() + { + using var testConnector = new LoggingTestServer(@"Swagger\SQL Server.json", _output); + using var httpClient = new HttpClient(testConnector); + using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://tip1002-002.azure-apihub.net", "ddadf2c7-ebdd-ec01-a5d1-502dc07f04b4" /* environment Id */, "4bf9a87fc9054b6db3a4d07a1c1f5a5b" /* connectionId */, () => "eyJ0eXAi...", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878" }; + + BaseRuntimeConnectorContext runtimeContext = new TestConnectorRuntimeContext("SQL", client, console: _output); + + ConnectorFunction[] functions = OpenApiParser.GetFunctions("SQL", testConnector._apiDocument, new ConsoleLogger(_output)).ToArray(); + ConnectorFunction executeProcedureV2 = functions.First(f => f.Name == "ExecuteProcedureV2"); + + Assert.True(executeProcedureV2.RequiredParameters[0].SupportsDynamicIntellisense); + Assert.True(executeProcedureV2.RequiredParameters[1].SupportsDynamicIntellisense); + Assert.True(executeProcedureV2.RequiredParameters[2].SupportsDynamicIntellisense); + Assert.True(executeProcedureV2.RequiredParameters[3].SupportsDynamicIntellisense); + + // Keeping only for debugging + //FormulaValue result = await executeProcedureV2.InvokeAync(client, new FormulaValue[] + //{ + // FormulaValue.New("pfxdev-sql.database.windows.net"), + // FormulaValue.New("connectortest"), + // FormulaValue.New("sp_1"), + // FormulaValue.NewRecordFromFields(new NamedValue[] { new NamedValue("p1", FormulaValue.New(38)) }) + //}, CancellationToken.None); + + testConnector.SetResponseFromFile(@"Responses\SQL Server Intellisense Response 3.json"); + ConnectorParameters parameters1 = await executeProcedureV2.GetParameterSuggestionsAsync( + new NamedValue[] + { + new NamedValue("server", FormulaValue.New("pfxdev-sql.database.windows.net")), + new NamedValue("database", FormulaValue.New("connectortest")) + }, + executeProcedureV2.RequiredParameters[2], // procedure + runtimeContext, + CancellationToken.None); + + ConnectorParameterWithSuggestions suggestions1 = parameters1.ParametersWithSuggestions[2]; + Assert.NotNull(suggestions1); + Assert.NotNull(suggestions1.Suggestions); + Assert.Equal(2, suggestions1.Suggestions.Count()); + + testConnector.SetResponseFromFile(@"Responses\SQL Server Intellisense Response2 1.json"); + ConnectorParameters parameters2 = await executeProcedureV2.GetParameterSuggestionsAsync( + new NamedValue[] + { + new NamedValue("server", FormulaValue.New("pfxdev-sql.database.windows.net")), + new NamedValue("database", FormulaValue.New("connectortest")), + new NamedValue("procedure", FormulaValue.New("sp_1")) + }, + executeProcedureV2.RequiredParameters[3], // parameters + runtimeContext, + CancellationToken.None); + + ConnectorParameterWithSuggestions suggestions2 = parameters2.ParametersWithSuggestions[3]; + Assert.NotNull(suggestions2); + Assert.NotNull(suggestions2.Suggestions); + Assert.Single(suggestions2.Suggestions); + + Assert.True(executeProcedureV2.ReturnParameterType.SupportsDynamicIntellisense); + + testConnector.SetResponseFromFile(@"Responses\SQL Server Intellisense Response2 1.json"); + ConnectorType returnType = await executeProcedureV2.GetConnectorReturnTypeAsync( + new NamedValue[] + { + new NamedValue("server", FormulaValue.New("pfxdev-sql.database.windows.net")), + new NamedValue("database", FormulaValue.New("connectortest")), + new NamedValue("procedure", FormulaValue.New("sp_1")) + }, + runtimeContext, + CancellationToken.None); + + Assert.NotNull(returnType); + Assert.True(returnType.FormulaType is RecordType); + + string input = testConnector._log.ToString(); + var version = PowerPlatformConnectorClient.Version; + string expected = $@"POST https://tip1002-002.azure-apihub.net/invoke + authority: tip1002-002.azure-apihub.net + Authorization: Bearer eyJ0eXAi... + path: /invoke + scheme: https + x-ms-client-environment-id: /providers/Microsoft.PowerApps/environments/ddadf2c7-ebdd-ec01-a5d1-502dc07f04b4 + x-ms-client-session-id: a41bd03b-6c3c-4509-a844-e8c51b61f878 + x-ms-request-method: GET + x-ms-request-url: /apim/sql/4bf9a87fc9054b6db3a4d07a1c1f5a5b/v2/datasets/pfxdev-sql.database.windows.net,connectortest/procedures + x-ms-user-agent: PowerFx/{version} +POST https://tip1002-002.azure-apihub.net/invoke + authority: tip1002-002.azure-apihub.net + Authorization: Bearer eyJ0eXAi... + path: /invoke + scheme: https + x-ms-client-environment-id: /providers/Microsoft.PowerApps/environments/ddadf2c7-ebdd-ec01-a5d1-502dc07f04b4 + x-ms-client-session-id: a41bd03b-6c3c-4509-a844-e8c51b61f878 + x-ms-request-method: GET + x-ms-request-url: /apim/sql/4bf9a87fc9054b6db3a4d07a1c1f5a5b/v2/$metadata.json/datasets/pfxdev-sql.database.windows.net,connectortest/procedures/sp_1 + x-ms-user-agent: PowerFx/{version} +POST https://tip1002-002.azure-apihub.net/invoke + authority: tip1002-002.azure-apihub.net + Authorization: Bearer eyJ0eXAi... + path: /invoke + scheme: https + x-ms-client-environment-id: /providers/Microsoft.PowerApps/environments/ddadf2c7-ebdd-ec01-a5d1-502dc07f04b4 + x-ms-client-session-id: a41bd03b-6c3c-4509-a844-e8c51b61f878 + x-ms-request-method: GET + x-ms-request-url: /apim/sql/4bf9a87fc9054b6db3a4d07a1c1f5a5b/v2/$metadata.json/datasets/pfxdev-sql.database.windows.net,connectortest/procedures/sp_1 + x-ms-user-agent: PowerFx/{version} +"; + + // Normalize CRLF ==> LF + expected = expected.Replace("\r", string.Empty); + input = input.Replace("\r", string.Empty); + Assert.Equal(expected, input); + } + + [Fact] + public async Task DataverseTest() + { + using var testConnector = new LoggingTestServer(@"Swagger\Dataverse.json", _output); + using var httpClient = new HttpClient(testConnector); + using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://tip1-shared.azure-apim.net", "Default-9f6be790-4a16-4dd6-9850-44a0d2649aef" /* environment Id */, "461a30624723445c9ba87313d8bbefa3" /* connectionId */, () => "eyJ0eXAiO...", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878" }; + + BaseRuntimeConnectorContext runtimeContext = new TestConnectorRuntimeContext("DV", client, console: _output); + + ConnectorFunction[] functions = OpenApiParser.GetFunctions(new ConnectorSettings("DV") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, testConnector._apiDocument).ToArray(); + ConnectorFunction createRecord = functions.First(f => f.Name == "CreateRecordWithOrganization"); + + testConnector.SetResponseFromFile(@"Responses\Dataverse_Response_1.json"); + ConnectorParameters parameters1 = await createRecord.GetParameterSuggestionsAsync( + new NamedValue[] + { + new NamedValue("organization", FormulaValue.New("https://org283e9949.crm10.dynamics.com")) + }, + createRecord.RequiredParameters[1], // entityName + runtimeContext, + CancellationToken.None); + + ConnectorParameterWithSuggestions suggestions1 = parameters1.ParametersWithSuggestions[1]; + Assert.Equal(651, suggestions1.Suggestions.Count); + Assert.Equal("AAD Users", suggestions1.Suggestions[0].DisplayName); + Assert.Equal("aadusers", ((StringValue)suggestions1.Suggestions[0].Suggestion).Value); + + testConnector.SetResponseFromFile(@"Responses\Dataverse_Response_2.json"); + ConnectorParameters parameters2 = await createRecord.GetParameterSuggestionsAsync( + new NamedValue[] + { + new NamedValue("organization", FormulaValue.New("https://org283e9949.crm10.dynamics.com")), + new NamedValue("entityName", FormulaValue.New("accounts")) + }, + createRecord.RequiredParameters[2], // item + runtimeContext, + CancellationToken.None); + + ConnectorParameterWithSuggestions suggestions2 = parameters2.ParametersWithSuggestions[2]; + Assert.Equal(119, suggestions2.Suggestions.Count); + Assert.Equal("accountcategorycode", suggestions2.Suggestions[0].DisplayName); + Assert.Equal("Decimal", suggestions2.Suggestions[0].Suggestion.Type.ToString()); + + string input = testConnector._log.ToString(); + var version = PowerPlatformConnectorClient.Version; + string expected = @$"POST https://tip1-shared.azure-apim.net/invoke + authority: tip1-shared.azure-apim.net + Authorization: Bearer eyJ0eXAiO... + organization: https://org283e9949.crm10.dynamics.com + path: /invoke + scheme: https + x-ms-client-environment-id: /providers/Microsoft.PowerApps/environments/Default-9f6be790-4a16-4dd6-9850-44a0d2649aef + x-ms-client-session-id: a41bd03b-6c3c-4509-a844-e8c51b61f878 + x-ms-request-method: POST + x-ms-request-url: /apim/commondataserviceforapps/461a30624723445c9ba87313d8bbefa3/v1.0/$metadata.json/GetEntityListEnum/GetEntitiesWithOrganization + x-ms-user-agent: PowerFx/{version} +POST https://tip1-shared.azure-apim.net/invoke + authority: tip1-shared.azure-apim.net + Authorization: Bearer eyJ0eXAiO... + organization: https://org283e9949.crm10.dynamics.com + path: /invoke + scheme: https + x-ms-client-environment-id: /providers/Microsoft.PowerApps/environments/Default-9f6be790-4a16-4dd6-9850-44a0d2649aef + x-ms-client-session-id: a41bd03b-6c3c-4509-a844-e8c51b61f878 + x-ms-request-method: GET + x-ms-request-url: /apim/commondataserviceforapps/461a30624723445c9ba87313d8bbefa3/v1.0/$metadata.json/entities/accounts/postitem + x-ms-user-agent: PowerFx/{version} +"; + + // Normalize CRLF ==> LF + expected = expected.Replace("\r", string.Empty); + input = input.Replace("\r", string.Empty); + Assert.Equal(expected, input); + } + + [Theory] + + // Very slow -- [InlineData(@"Swagger\Dataverse 2.json")] + // Very slow -- [InlineData(@"Swagger\Dataverse 3.json")] + [InlineData(@"Swagger\PowerPlatformForAdmins.json")] + public async Task DataverseTest2(string swaggerFile) + { + PowerFxConfig powerFxConfig = new PowerFxConfig(); + OpenApiDocument doc = Helpers.ReadSwagger(swaggerFile, _output); + + OpenApiParser.GetFunctions("namespace", doc); // missing logger + powerFxConfig.AddActionConnector("namespace", doc); + } + + [Fact] + public async Task CardsForPowerApps_Invoke() + { + using var testConnector = new LoggingTestServer(@"Swagger\CardsForPowerApps.json", _output); + using var httpClient = new HttpClient(testConnector); + using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://tip1002-002.azure-apihub.net", "7592282b-e371-e3f6-8e04-e8f23e64227c" /* environment Id */, "shared-cardsforpower-eafc4fa0-c560-4eba-a5b2-3e1ebc63193a" /* connectionId */, () => "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dC...", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878" }; + + BaseRuntimeConnectorContext runtimeContext = new TestConnectorRuntimeContext("DV", client, console: _output); + + ConnectorFunction[] functions = OpenApiParser.GetFunctions( + new ConnectorSettings("DV") + { + Compatibility = ConnectorCompatibility.SwaggerCompatibility, + ReturnUnknownRecordFieldsAsUntypedObjects = true + }, + testConnector._apiDocument).ToArray(); + ConnectorFunction createCardInstance = functions.First(f => f.Name == "CreateCardInstance"); + + testConnector.SetResponseFromFile(@"Responses\CardsForPowerApps_CreateCardInstance.json"); + var result = await createCardInstance.InvokeAsync( + new FormulaValue[] + { + FormulaValue.New("card"), + FormulaValue.NewRecordFromFields( + new NamedValue("property1", FormulaValue.New("test1")), + new NamedValue("property2", FormulaValue.New("test2"))), + }, + runtimeContext, + CancellationToken.None); + + string input = testConnector._log.ToString(); + Assert.Equal("AdaptiveCard", (((RecordValue)result).GetField("type") as UntypedObjectValue).Impl.GetString()); + var expected = + $@"POST https://tip1002-002.azure-apihub.net/invoke + authority: tip1002-002.azure-apihub.net + Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dC... + path: /invoke + scheme: https + x-ms-client-environment-id: /providers/Microsoft.PowerApps/environments/7592282b-e371-e3f6-8e04-e8f23e64227c + x-ms-client-session-id: a41bd03b-6c3c-4509-a844-e8c51b61f878 + x-ms-request-method: POST + x-ms-request-url: /apim/cardsforpowerapps/shared-cardsforpower-eafc4fa0-c560-4eba-a5b2-3e1ebc63193a/cards/cards/card/instances + x-ms-user-agent: PowerFx/{PowerPlatformConnectorClient.Version} + [content-header] Content-Type: application/json; charset=utf-8 + [body] {{""inputs"":{{""property1"":""test1"",""property2"":""test2""}}}} +"; + + // Normalize CRLF ==> LF + expected = expected.Replace("\r", string.Empty); + input = input.Replace("\r", string.Empty); + Assert.Equal(expected, input); + } + + [Fact] + public async Task CardsForPowerApps_Suggestion() + { + using var testConnector = new LoggingTestServer(@"Swagger\CardsForPowerApps.json", _output); + using var httpClient = new HttpClient(testConnector); + using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://tip1002-002.azure-apihub.net", "7592282b-e371-e3f6-8e04-e8f23e64227c" /* environment Id */, "shared-cardsforpower-eafc4fa0-c560-4eba-a5b2-3e1ebc63193a" /* connectionId */, () => "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dC...", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878" }; + + BaseRuntimeConnectorContext runtimeContext = new TestConnectorRuntimeContext("DV", client, console: _output); + + ConnectorFunction[] functions = OpenApiParser.GetFunctions(new ConnectorSettings("DV") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, testConnector._apiDocument).ToArray(); + ConnectorFunction createCardInstance = functions.First(f => f.Name == "CreateCardInstance"); + + testConnector.SetResponseFromFile(@"Responses\CardsForPowerApps_Suggestions.json"); + ConnectorParameters parameters = await createCardInstance.GetParameterSuggestionsAsync( + new NamedValue[] + { + }, + createCardInstance.RequiredParameters[0], // cardid + runtimeContext, + CancellationToken.None); + + ConnectorParameterWithSuggestions suggestions = parameters.ParametersWithSuggestions[0]; + Assert.Equal(2, suggestions.Suggestions.Count); + Assert.Equal("test", suggestions.Suggestions[0].DisplayName); + Assert.Equal("testWithInputs", suggestions.Suggestions[1].DisplayName); + } + + [Fact] + public async Task Teams_GetMessageDetails_WithComplexParameterReference() + { + using var testConnector = new LoggingTestServer(@"Swagger\Teams.json", _output); + using var httpClient = new HttpClient(testConnector); + using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://tip1002-002.azure-apihub.net", "7592282b-e371-e3f6-8e04-e8f23e64227c" /* environment Id */, "shared-cardsforpower-eafc4fa0-c560-4eba-a5b2-3e1ebc63193a" /* connectionId */, () => "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dC...", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878" }; + + BaseRuntimeConnectorContext runtimeContext = new TestConnectorRuntimeContext("DV", client, console: _output); + + ConnectorFunction[] functions = OpenApiParser.GetFunctions(new ConnectorSettings("DV") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, testConnector._apiDocument).ToArray(); + ConnectorFunction getMessageDetails = functions.First(f => f.Name == "GetMessageDetails"); + + testConnector.SetResponseFromFile(@"Responses\Teams_GetMessageDetails_InputType.json"); + ConnectorParameters parameters = await getMessageDetails.GetParameterSuggestionsAsync( + new NamedValue[] + { + new NamedValue("messageId", FormulaValue.New("messageId")), + new NamedValue("threadType", FormulaValue.New("channel")), + }, + getMessageDetails.RequiredParameters[2], // body + runtimeContext, + CancellationToken.None); + + var bodyConnectorType = parameters.ParametersWithSuggestions[2].ConnectorType; + + ConnectorParameterWithSuggestions suggestions = parameters.ParametersWithSuggestions[2]; + testConnector.SetResponseFromFile(@"Responses\Teams_GetMessageDetails_GetSuggestionsForChannel.json"); + + var connectorTypeWithSuggestions = await getMessageDetails.GetConnectorSuggestionsAsync( + new NamedValue[] + { + new NamedValue("messageId", FormulaValue.New("messageId")), + new NamedValue("threadType", FormulaValue.New("channel")), + new NamedValue("body", FormulaValue.NewRecordFromFields( + new NamedValue("recipient", FormulaValue.NewRecordFromFields( + new NamedValue("groupId", FormulaValue.New("groupIdValue")))))), + }, + bodyConnectorType.Fields[0].Fields[1], // channelId + runtimeContext, + CancellationToken.None); + + Assert.Equal(2, connectorTypeWithSuggestions.ConnectorSuggestions.Suggestions.Count); + Assert.Equal("channelName", connectorTypeWithSuggestions.ConnectorSuggestions.Suggestions[0].DisplayName); + Assert.Equal("channelName2", connectorTypeWithSuggestions.ConnectorSuggestions.Suggestions[1].DisplayName); } [Fact] public async Task Teams_PostCardAndWaitForResponse() { - using var testConnector = new LoggingTestServer(@"Swagger\Teams.json", _output); - using var httpClient = new HttpClient(testConnector); - using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://tip1002-002.azure-apihub.net", "7592282b-e371-e3f6-8e04-e8f23e64227c" /* environment Id */, "shared-cardsforpower-eafc4fa0-c560-4eba-a5b2-3e1ebc63193a" /* connectionId */, () => "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dC...", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878" }; - - BaseRuntimeConnectorContext runtimeContext = new TestConnectorRuntimeContext("DV", client, console: _output); - - ConnectorFunction[] functionsWithWebhooks = OpenApiParser.GetFunctions(new ConnectorSettings("DV") { Compatibility = ConnectorCompatibility.SwaggerCompatibility, IncludeWebhookFunctions = true }, testConnector._apiDocument).ToArray(); - ConnectorFunction[] functionsWithoutWebhooks = OpenApiParser.GetFunctions(new ConnectorSettings("DV") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, testConnector._apiDocument).ToArray(); + using var testConnector = new LoggingTestServer(@"Swagger\Teams.json", _output); + using var httpClient = new HttpClient(testConnector); + using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://tip1002-002.azure-apihub.net", "7592282b-e371-e3f6-8e04-e8f23e64227c" /* environment Id */, "shared-cardsforpower-eafc4fa0-c560-4eba-a5b2-3e1ebc63193a" /* connectionId */, () => "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dC...", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878" }; + + BaseRuntimeConnectorContext runtimeContext = new TestConnectorRuntimeContext("DV", client, console: _output); + + ConnectorFunction[] functionsWithWebhooks = OpenApiParser.GetFunctions(new ConnectorSettings("DV") { Compatibility = ConnectorCompatibility.SwaggerCompatibility, IncludeWebhookFunctions = true }, testConnector._apiDocument).ToArray(); + ConnectorFunction[] functionsWithoutWebhooks = OpenApiParser.GetFunctions(new ConnectorSettings("DV") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, testConnector._apiDocument).ToArray(); Assert.NotNull(functionsWithWebhooks.FirstOrDefault(f => f.Name == "PostCardAndWaitForResponse")); Assert.Null(functionsWithoutWebhooks.FirstOrDefault(f => f.Name == "PostCardAndWaitForResponse")); @@ -1470,58 +1469,100 @@ public async Task Teams_GetMessageLocation_WithSuggestions() Assert.Equal("Channel", suggestions1.Suggestions[0].DisplayName); Assert.Equal("Group chat", suggestions1.Suggestions[1].DisplayName); Assert.Equal("Chat with Flow bot", suggestions1.Suggestions[2].DisplayName); - } - - [Fact] - public void ABS_GetTriggers() - { - OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\AzureBlobStorage.json", _output); - List triggers = OpenApiParser.GetTriggers("AzureBlobStorage", doc, new ConsoleLogger(_output)).OrderBy(cf => cf.Name).ToList(); - Assert.NotNull(triggers); - Assert.Equal(2, triggers.Count); - var onUpdatedFilesTrigger = triggers.FirstOrDefault(t => t.Name.Equals("OnUpdatedFiles")); - Assert.NotNull(onUpdatedFilesTrigger); - Assert.Equal(HttpMethod.Get, onUpdatedFilesTrigger.HttpMethod); - Assert.Equal("/apim/azureblob/{connectionId}/datasets/default/triggers/batch/onupdatedfile", onUpdatedFilesTrigger.OperationPath); - } - } - - public static class Extensions - { - public static RecordType MakeRecordType(params (string, FormulaType)[] columns) - { - RecordType rt = RecordType.Empty(); - - foreach ((string name, FormulaType type) in columns) - { - rt = rt.Add(name, type); - } - - return rt; - } - - public static TableType MakeTableType(params (string, FormulaType)[] columns) - { - TableType tt = TableType.Empty(); - - foreach ((string name, FormulaType type) in columns) - { - tt = tt.Add(name, type); - } - - return tt; - } - - public static OptionSetValueType MakeOptionSetType(string name, params string[] names) - { - return MakeOptionSet(name, names).FormulaType; - } - - public static OptionSet MakeOptionSet(string name, params string[] names) - { - return new OptionSet(name, DisplayNameUtility.MakeUnique(names.ToDictionary(n => n, n => n))); - } - } - -#pragma warning restore SA1118, SA1117, SA1119, SA1137 -} + } + + [Fact] + public async Task UIPath_StartAndWaitForJobCompletion_WithSuggestions_OptionalInput() + { + using var testConnector = new LoggingTestServer(@"Swagger\UIPath.yaml", _output); + using var httpClient = new HttpClient(testConnector); + + using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://tip1002-002.azure-apihub.net", "7592282b-e371-e3f6-8e04-e8f23e64227c" /* environment Id */, "eafc4fa0-c560-4eba-a5b2-3e1ebc63193a" /* connectionId */, () => "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dC...", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878" }; + BaseRuntimeConnectorContext runtimeContext = new TestConnectorRuntimeContext("UIPath", client, console: _output); + + ConnectorFunction[] functions = OpenApiParser.GetFunctions( + new ConnectorSettings("UIPath") + { + Compatibility = ConnectorCompatibility.SwaggerCompatibility, + ReturnUnknownRecordFieldsAsUntypedObjects = true, + IncludeWebhookFunctions = true, // Note: By default, OpenApiParser will filter out those operations with 'x-ms-notification-content'. MCS needs to set it to true. + AllowSuggestionMappingFallback = false, // Note: Some connector's swagger and actual response is not aligned. This option will try parse suggestions result using fallback property keys. + UseItemDynamicPropertiesSpecialHandling = false, // Enables a fix for certain operations with special handling of the body parameter + SupportXMsEnumValues = true, + ReturnEnumsAsPrimitive = true, + }, + testConnector._apiDocument) + .ToArray(); + + ConnectorFunction startAndWaitForJobCompletion = functions.First(f => f.Name == "StartAndWaitForJobCompletion"); + + var dynamicListSchemaParameter = startAndWaitForJobCompletion.OptionalParameters.Single(p => p.Name == "dynamicListSchema"); + + ConnectorParameters result = await startAndWaitForJobCompletion.GetParameterSuggestionsAsync( + new NamedValue[] + { + new NamedValue("X-UIPATH-OrganizationUnitId", FormulaValue.New(12345)), + new NamedValue("processId", FormulaValue.New(6789)) + }, + dynamicListSchemaParameter, + runtimeContext, + CancellationToken.None); + + var expectedLog = "POST https://tip1002-002.azure-apihub.net/invoke\r\n authority: tip1002-002.azure-apihub.net\r\n Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dC...\r\n path: /invoke\r\n scheme: https\r\n x-ms-client-environment-id: /providers/Microsoft.PowerApps/environments/7592282b-e371-e3f6-8e04-e8f23e64227c\r\n x-ms-client-session-id: a41bd03b-6c3c-4509-a844-e8c51b61f878\r\n x-ms-request-method: GET\r\n x-ms-request-url: /schema/input/6789\r\n x-ms-user-agent: PowerFx/1.99.0-local\r\n X-UIPATH-OrganizationUnitId: 12345\r\n"; + + Assert.Equal(expectedLog, testConnector._log.ToString()); + } + + [Fact] + public void ABS_GetTriggers() + { + OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\AzureBlobStorage.json", _output); + List triggers = OpenApiParser.GetTriggers("AzureBlobStorage", doc, new ConsoleLogger(_output)).OrderBy(cf => cf.Name).ToList(); + Assert.NotNull(triggers); + Assert.Equal(2, triggers.Count); + var onUpdatedFilesTrigger = triggers.FirstOrDefault(t => t.Name.Equals("OnUpdatedFiles")); + Assert.NotNull(onUpdatedFilesTrigger); + Assert.Equal(HttpMethod.Get, onUpdatedFilesTrigger.HttpMethod); + Assert.Equal("/apim/azureblob/{connectionId}/datasets/default/triggers/batch/onupdatedfile", onUpdatedFilesTrigger.OperationPath); + } + } + + public static class Extensions + { + public static RecordType MakeRecordType(params (string, FormulaType)[] columns) + { + RecordType rt = RecordType.Empty(); + + foreach ((string name, FormulaType type) in columns) + { + rt = rt.Add(name, type); + } + + return rt; + } + + public static TableType MakeTableType(params (string, FormulaType)[] columns) + { + TableType tt = TableType.Empty(); + + foreach ((string name, FormulaType type) in columns) + { + tt = tt.Add(name, type); + } + + return tt; + } + + public static OptionSetValueType MakeOptionSetType(string name, params string[] names) + { + return MakeOptionSet(name, names).FormulaType; + } + + public static OptionSet MakeOptionSet(string name, params string[] names) + { + return new OptionSet(name, DisplayNameUtility.MakeUnique(names.ToDictionary(n => n, n => n))); + } + } + +#pragma warning restore SA1118, SA1117, SA1119, SA1137 +} diff --git a/src/tests/Microsoft.PowerFx.Connectors.Tests.Shared/Swagger/UIPath.yaml b/src/tests/Microsoft.PowerFx.Connectors.Tests.Shared/Swagger/UIPath.yaml new file mode 100644 index 0000000000..45747e4e35 --- /dev/null +++ b/src/tests/Microsoft.PowerFx.Connectors.Tests.Shared/Swagger/UIPath.yaml @@ -0,0 +1,1264 @@ +swagger: '2.0' +info: + title: UiPath + description: >- + UiPath allows you to trigger jobs in UiPath Orchestrator. It provides the + ability to initiate job runs and listen for job completion through trigger, + ensuring seamless automation workflows and real-time monitoring. + version: '2.0' + contact: + name: Plugin Ecosystem Team + email: pludevsupport@uipath.com + url: https://www.uipath.com/support +host: cloud.uipath.com +basePath: / +schemes: + - https +x-ms-connector-metadata: + - propertyName: Website + propertyValue: https://www.uipath.com + - propertyName: Privacy policy + propertyValue: https://www.uipath.com/legal/privacy-policy + - propertyName: Categories + propertyValue: AI;Business Intelligence +consumes: [] +produces: [] +parameters: + filter: + in: query + name: $filter + description: >- + Restricts the set of items returned. The maximum number of expressions is + 100. + type: string + x-ms-summary: Filter + x-ms-visibility: advanced + orderBy: + in: query + name: $orderby + description: >- + Specifies the order in which items are returned. The maximum number of + expressions is 5. + type: string + x-ms-summary: Order By + x-ms-visibility: advanced + top: + in: query + name: $top + description: >- + Limits the number of items returned from a collection. The maximum value + is 1000. + type: integer + format: int32 + x-ms-summary: Top + x-ms-visibility: advanced + skip: + in: query + name: $skip + description: >- + Excludes the specified number of items of the queried collection from the + result. + type: integer + format: int32 + x-ms-summary: Skip + x-ms-visibility: advanced +paths: + /startJobAndWaitForCompletion/{processId}: + x-ms-notification-content: + description: Result of Job Completion + schema: + x-ms-dynamic-schema: + operationId: GetProcessOutputSchema + parameters: + processId: + parameter: processId + X-UIPATH-OrganizationUnitId: + parameter: X-UIPATH-OrganizationUnitId + value-path: schema + post: + responses: + '201': + description: Success + x-ms-summary: Start job and wait for completion + summary: Start job and wait for completion + description: Start job and receive the job completion notification. + operationId: StartAndWaitForJobCompletion + x-ms-visibility: important + parameters: + - in: header + name: X-UIPATH-OrganizationUnitId + description: Folder/OrganizationUnit Id + type: integer + format: int64 + required: true + x-ms-summary: Folder + x-ms-dynamic-values: + operationId: GetFoldersForCurrentUser + value-path: Id + value-collection: PageItems + value-title: DisplayName + - name: processId + in: path + required: true + type: number + x-ms-summary: Process Name + description: Select the process name + x-ms-dynamic-values: + operationId: GetReleases + value-path: Id + value-collection: value + value-title: Name + parameters: + X-UIPATH-OrganizationUnitId: + parameter: X-UIPATH-OrganizationUnitId + - name: url + in: header + required: true + type: string + description: Webhook URL + x-ms-summary: Webhook URL + x-ms-notification-url: true + x-ms-visibility: internal + - name: dynamicListSchema + in: body + description: Get the schema of the selected process. + schema: + type: object + x-ms-dynamic-schema: + operationId: GetProcessInputSchema + parameters: + processId: + parameter: processId + X-UIPATH-OrganizationUnitId: + parameter: X-UIPATH-OrganizationUnitId + value-path: schema + x-ms-summary: Process Input Schema + - name: runAsMe + in: query + x-ms-summary: RunAsMe + description: Execute the job under your own identity + type: boolean + default: true + /jobupdate/webhook/{processId}: + x-ms-notification-content: + description: Result of Job Completion + schema: + x-ms-dynamic-schema: + operationId: GetProcessOutputSchema + parameters: + processId: + parameter: processId + X-UIPATH-OrganizationUnitId: + parameter: X-UIPATH-OrganizationUnitId + value-path: schema + post: + responses: + default: + description: default + schema: + x-ms-dynamic-schema: + operationId: GetProcessOutputSchema + parameters: + processId: + parameter: processId + X-UIPATH-OrganizationUnitId: + parameter: X-UIPATH-OrganizationUnitId + value-path: schema + x-ms-summary: Wait for Job Completion + summary: Wait for Job Completion + description: Trigger to receive the job completion notification. + x-ms-visibility: important + parameters: + - in: header + name: X-UIPATH-OrganizationUnitId + description: Folder/OrganizationUnit Id + type: integer + format: int64 + required: true + x-ms-summary: Folder + x-ms-dynamic-values: + operationId: GetFoldersForCurrentUser + value-path: Id + value-collection: PageItems + value-title: DisplayName + - name: body + in: body + required: false + x-ms-summary: Webhook URL + schema: + type: object + properties: + url: + type: string + description: Webhook URL + x-ms-notification-url: true + x-ms-visibility: internal + title: URL + jobKey: + type: string + description: Job Id + title: Job Id + x-ms-summary: Job Id + x-ms-visibility: important + required: + - url + - jobKey + - name: processId + in: path + description: Select the process name + required: true + type: number + x-ms-summary: Process Name + x-ms-dynamic-values: + operationId: GetReleases + value-path: Id + value-collection: value + value-title: Name + parameters: + X-UIPATH-OrganizationUnitId: + parameter: X-UIPATH-OrganizationUnitId + operationId: GetJobUpdate + /jobs/startjob: + post: + consumes: + - application/json;odata.metadata=minimal;odata.streaming=true + - application/json;odata.metadata=minimal;odata.streaming=false + - application/json;odata.metadata=minimal + - application/json;odata.metadata=full;odata.streaming=true + - application/json;odata.metadata=full;odata.streaming=false + - application/json;odata.metadata=full + - application/json;odata.metadata=none;odata.streaming=true + - application/json;odata.metadata=none;odata.streaming=false + - application/json;odata.metadata=none + - application/json;odata.streaming=true + - application/json;odata.streaming=false + - application/json + description: >- + Creates a new job and sets it in Pending state for each robot based on + the input parameters and notifies the respective robots about the + pending job. + operationId: StartJobs + parameters: + - name: dynamicListSchema + in: body + description: Get the schema of the selected process. + schema: + type: object + x-ms-dynamic-schema: + operationId: GetProcessInputSchema + parameters: + processId: + parameter: processId + X-UIPATH-OrganizationUnitId: + parameter: X-UIPATH-OrganizationUnitId + value-path: schema + x-ms-summary: Process Input Schema + - in: header + name: X-UIPATH-OrganizationUnitId + description: Folder/OrganizationUnit Id + type: integer + format: int64 + required: true + x-ms-summary: Folder + x-ms-dynamic-values: + operationId: GetFoldersForCurrentUser + value-path: Id + value-collection: PageItems + value-title: DisplayName + - name: processId + in: query + x-ms-summary: Process Name + description: Select the process you want to run + required: true + type: number + x-ms-dynamic-values: + operationId: GetReleases + value-path: Id + value-collection: value + value-title: Name + parameters: + X-UIPATH-OrganizationUnitId: + parameter: X-UIPATH-OrganizationUnitId + $filter: >- + ((ProcessType eq 'Process') or (ProcessType eq 'Agent') or + (ProcessType eq 'ProcessOrchestration')) + - name: runAsMe + in: query + x-ms-summary: RunAsMe + description: Execute the job under your own identity + type: boolean + default: true + produces: + - application/json + responses: + '201': + description: Success + schema: + $ref: '#/definitions/JobDto' + summary: Start Job + tags: + - Jobs + /odata/Releases: + get: + tags: + - Releases + summary: Get Releases + description: Gets a list of releases. + operationId: GetReleases + x-ms-visibility: internal + produces: + - application/json + parameters: + - in: header + name: X-UIPATH-OrganizationUnitId + description: Folder/OrganizationUnit Id + type: integer + format: int64 + x-ms-summary: Folder Id + required: true + - $ref: '#/parameters/filter' + - $ref: '#/parameters/orderBy' + - $ref: '#/parameters/top' + - $ref: '#/parameters/skip' + responses: + '200': + description: Success + schema: + $ref: '#/definitions/ODataValueOfIEnumerableOfReleaseDto' + /schema/input/{processId}: + get: + description: Get the input schema of the selected process. + summary: Get Process Input Schema + operationId: GetProcessInputSchema + parameters: + - in: header + name: X-UIPATH-OrganizationUnitId + description: Folder/OrganizationUnit Id + type: integer + format: int64 + x-ms-summary: Folder Id + required: false + x-ms-visibility: important + - name: processId + type: number + in: path + description: Process Id of the selected process + required: true + x-ms-summary: Process Name + responses: + '200': + description: OK + schema: + $ref: '#/definitions/ProcessSchemaMetadata' + x-ms-visibility: internal + /schema/output/{processId}: + get: + description: Get Process Output Schema. + summary: Get Process Output Schema + operationId: GetProcessOutputSchema + parameters: + - in: header + name: X-UIPATH-OrganizationUnitId + description: Folder/OrganizationUnit Id + type: integer + format: int64 + x-ms-summary: Folder Id + required: false + x-ms-visibility: important + - name: processId + type: number + in: path + description: Process Id of the selected process + required: true + x-ms-summary: Process Name + responses: + '200': + description: OK + schema: + $ref: '#/definitions/ProcessSchemaMetadata' + x-ms-visibility: internal + /api/Folders/GetAllForCurrentUser: + get: + responses: + '200': + description: OK + schema: + type: object + properties: + PageItems: + type: array + items: + type: object + properties: + Key: + type: string + description: Key + DisplayName: + type: string + description: DisplayName + FullyQualifiedName: + type: string + description: FullyQualifiedName + Description: + type: string + description: Description + FolderType: + type: string + description: FolderType + ParentId: + type: integer + format: int32 + description: ParentId + ParentKey: + type: string + description: ParentKey + Id: + type: integer + format: int32 + description: Id + description: PageItems + Count: + type: integer + format: int32 + description: Count + summary: Get Folders for Current User + description: Get all folders accessible by Current User + operationId: GetFoldersForCurrentUser + x-ms-visibility: internal + parameters: [] +definitions: + ProcessSchemaMetadata: + description: Process Schema metadata + type: object + properties: + schema: + $ref: '#/definitions/Object' + Object: + type: object + properties: {} + ArgumentMetadata: + type: object + properties: + Input: + type: string + Output: + type: string + EntryPointDataVariationDto: + type: object + properties: + Content: + type: string + ContentType: + enum: + - Json + type: string + x-ms-enum: + name: EntryPointDataVariationDtoContentType + modelAsString: false + Id: + format: int64 + type: integer + EntryPointDto: + type: object + properties: + UniqueId: + format: uuid + type: string + Path: + type: string + InputArguments: + type: string + OutputArguments: + type: string + DataVariation: + $ref: '#/definitions/EntryPointDataVariationDto' + Id: + format: int32 + type: integer + EnvironmentDto: + description: A grouping of Robots. + required: + - Name + type: object + properties: + Name: + description: A custom name for the environment. + maxLength: 100 + minLength: 0 + type: string + Description: + description: >- + Used to add additional information about an environment in order to + better identify it. + maxLength: 500 + minLength: 0 + type: string + Robots: + description: The collection of robots associated with the current environment. + type: array + items: + $ref: '#/definitions/SimpleRobotDto' + Type: + description: "DEPRECATED. The environment type specifies how it should be used.\r\nThis property is deprecated and should no longer be used." + enum: + - Dev + - Test + - Prod + type: string + x-ms-enum: + name: EnvironmentDtoType + modelAsString: false + x-deprecated: true + Id: + format: int64 + type: integer + JobDto: + description: Represents a scheduled or manual execution of a process on a robot. + type: object + properties: + Id: + format: int32 + type: integer + description: The Unique Job Id + x-ms-summary: Job Id + Key: + description: The unique job identifier. + type: string + x-ms-summary: Job Key + StartTime: + description: >- + The date and time when the job execution started or null if the job + hasn't started yet. + type: string + x-ms-summary: Start Time + EndTime: + description: >- + The date and time when the job execution ended or null if the job + hasn't ended yet. + type: string + x-ms-summary: End Time + State: + description: The state in which the job is. + enum: + - Pending + - Running + - Stopping + - Terminating + - Faulted + - Successful + - Stopped + - Suspended + - Resumed + type: string + x-ms-summary: Job State + x-ms-enum: + name: JobDtoState + modelAsString: false + JobPriority: + description: Execution priority. + enum: + - Low + - Normal + - High + type: string + x-ms-summary: Job Priority + x-ms-enum: + name: JobDtoJobPriority + modelAsString: false + Robot: + $ref: '#/definitions/SimpleRobotDto' + Release: + $ref: '#/definitions/SimpleReleaseDto' + Source: + description: The Source name of the job. + type: string + x-ms-summary: Source + SourceType: + description: The Source type of the job. + enum: + - Manual + - Schedule + - Agent + - Queue + - StudioWeb + type: string + x-ms-summary: Source Type + x-ms-enum: + name: JobDtoSourceType + modelAsString: false + BatchExecutionKey: + description: >- + The unique identifier grouping multiple jobs. It is usually generated + when the job is created by a schedule. + type: string + x-ms-summary: Batch Execution Key + Info: + description: Additional information about the current job. + type: string + x-ms-summary: Info + CreationTime: + description: The date and time when the job was created. + type: string + x-ms-summary: Creation Time + StartingScheduleId: + description: >- + The Id of the schedule that started the job, or null if the job was + started by the user. + type: string + x-ms-summary: Starting Schedule Id + ReleaseName: + description: The name of the release associated with the current name. + type: string + x-ms-summary: Release Name + Type: + description: >- + The type of the job, Attended if started via the robot, Unattended + otherwise + enum: + - Unattended + - Attended + type: string + x-ms-summary: Type + x-ms-enum: + name: JobDtoType + modelAsString: false + InputArguments: + description: Input parameters in JSON format to be passed to job execution + type: string + x-ms-summary: Input Arguments + OutputArguments: + description: Output parameters in JSON format resulted from job execution + type: string + x-ms-summary: Output Arguments + HostMachineName: + description: The name of the machine where the Robot run the job. + type: string + x-ms-summary: Host Machine Name + HasMediaRecorded: + description: >- + True if any execution media has been recorded for this job, false + otherwise. + type: boolean + PersistenceId: + description: The persistence instance id for a suspended job + type: string + ResumeVersion: + description: Distinguishes between multiple job suspend/resume cycles + type: string + StopStrategy: + enum: + - SoftStop + - Kill + type: string + x-ms-enum: + name: JobDtoStopStrategy + modelAsString: false + RuntimeType: + description: The runtime type of the robot which can pick up the job + enum: + - NonProduction + - Attended + - Unattended + - Studio + - RpaDeveloper + - Development + - StudioX + - CitizenDeveloper + - Headless + - RpaDeveloperPro + - StudioPro + - TestAutomation + type: string + x-ms-enum: + name: JobDtoRuntimeType + modelAsString: false + RequiresUserInteraction: + type: boolean + ReleaseVersionId: + format: int32 + type: integer + EntryPointPath: + description: >- + Path to the entry point workflow (XAML) that will be executed by the + robot + maxLength: 512 + type: string + OrganizationUnitId: + format: int32 + description: Id of the folder this job is part of. + type: integer + OrganizationUnitFullyQualifiedName: + description: Fully qualified name of the folder this job is part of. + type: string + Reference: + description: Reference identifier for the job + type: string + ProcessType: + enum: + - Undefined + - Process + - TestAutomationProcess + type: string + x-ms-enum: + name: JobDtoProcessType + modelAsString: false + Machine: + $ref: '#/definitions/MachineDto' + ProfilingOptions: + description: >- + Options to instruct the robot what profiling info to collect (code + coverage, CPU / memory utilization, etc) + type: string + MachineDto: + description: The Machine that hosts the Robot + required: + - Name + type: object + properties: + LicenseKey: + description: >- + The key is automatically generated from the server for the Robot + machine. For the robot to work, the same key must exist on both the + robot and Orchestrator. + maxLength: 255 + minLength: 0 + type: string + Name: + description: The name of the Machine a Robot is hosted on. + maxLength: 450 + minLength: 0 + type: string + Description: + maxLength: 500 + minLength: 0 + type: string + Type: + description: The type of the Machine (Standard / Template). + enum: + - Standard + - Template + type: string + x-ms-enum: + name: MachineDtoType + modelAsString: false + Scope: + description: The scope of the Machine (Default / Shared / PW / Cloud). + enum: + - Default + - Shared + - PersonalWorkspace + - Cloud + type: string + x-ms-enum: + name: MachineDtoScope + modelAsString: false + NonProductionSlots: + format: int32 + description: Number of NonProduction slots to be reserved at runtime + type: integer + UnattendedSlots: + format: int32 + description: Number of Unattended slots to be reserved at runtime + type: integer + HeadlessSlots: + format: int32 + description: Number of Headless slots to be reserved at runtime + type: integer + TestAutomationSlots: + format: int32 + description: Number of TestAutomation slots to be reserved at runtime + type: integer + Key: + format: uuid + description: >- + An immutable unique identifier that is preserved during tenant + migration + type: string + RobotVersions: + description: The versions of the Robots hosted on the Machine. + type: array + items: + $ref: '#/definitions/MachinesRobotVersionDto' + RobotUsers: + description: Robots assigned to template machine. + type: array + items: + $ref: '#/definitions/RobotUserDto' + AutoScalingProfile: + description: The profile that drives auto scaling. + enum: + - CostEfficient + - Balanced + - Fast + - Custom + type: string + x-ms-enum: + name: MachineDtoAutoScalingProfile + modelAsString: false + Id: + format: int64 + type: integer + MachinesRobotVersionDto: + type: object + properties: + Count: + format: int64 + description: The number of Robots on the Machine with the specified version. + type: integer + Version: + description: The Version of the Robot hosted on the Machine. + type: string + MachineId: + format: int64 + description: The Id of the Machine. + type: integer + ODataValueOfIEnumerableOfReleaseDto: + type: object + properties: + value: + type: array + items: + $ref: '#/definitions/ReleaseDto' + ProcessSettingsDto: + type: string + properties: + ErrorRecordingEnabled: + type: boolean + Duration: + format: int32 + type: integer + Frequency: + format: int32 + type: integer + Quality: + format: int32 + type: integer + AutoStartProcess: + type: boolean + AlwaysRunning: + type: boolean + ReleaseDto: + required: + - Name + - ProcessKey + - ProcessVersion + type: object + properties: + Key: + description: A unique identifier associated to each release. + type: string + ProcessKey: + description: The unique identifier of the process associated with the release. + type: string + ProcessVersion: + description: The version of the process associated with the release. + type: string + IsLatestVersion: + description: >- + States whether the version of process associated with the release is + latest or not. + type: boolean + IsProcessDeleted: + description: >- + States whether the process associated with the release is deleted or + not. + type: boolean + Description: + description: >- + Used to add additional information about a release in order to better + identify it. + type: string + Name: + description: >- + A custom name of the release. The default name format is + ProcessName_EnvironmentName. + type: string + EnvironmentId: + description: The Id of the environment associated with the release. + type: string + EnvironmentName: + description: The name of the environment associated with the release. + type: string + Environment: + $ref: '#/definitions/EnvironmentDto' + EntryPointId: + format: int32 + type: integer + EntryPoint: + $ref: '#/definitions/EntryPointDto' + InputArguments: + description: >- + Input parameters in JSON format to be passed as default values to job + execution. + maxLength: 10000 + type: string + ProcessType: + enum: + - Undefined + - Process + - TestAutomationProcess + type: string + x-ms-enum: + name: ReleaseDtoProcessType + modelAsString: false + SupportsMultipleEntryPoints: + type: boolean + RequiresUserInteraction: + type: boolean + CurrentVersion: + $ref: '#/definitions/ReleaseVersionDto' + ReleaseVersions: + description: The collection of release versions that current release had over time. + type: array + items: + $ref: '#/definitions/ReleaseVersionDto' + Arguments: + $ref: '#/definitions/ArgumentMetadata' + ProcessSettings: + $ref: '#/definitions/ProcessSettingsDto' + AutoUpdate: + type: boolean + FeedId: + type: string + JobPriority: + description: The execution priority. If null, it defaults to Normal. + enum: + - Low + - Normal + - High + type: string + x-ms-enum: + name: ReleaseDtoJobPriority + modelAsString: false + CreationTime: + description: Creation time of this release. + type: string + OrganizationUnitId: + format: int32 + description: Id of the folder this release is part of. + type: integer + OrganizationUnitFullyQualifiedName: + description: Fully qualified name of the folder this release is part of. + type: string + Id: + format: int32 + type: integer + ReleaseVersionDto: + description: >- + Stores data about a version of the various versions of the process + associated with a certain release. If a certain version is associated on + and off with a release a new ReleaseVersion object is created for each + association. + required: + - ReleaseId + - VersionNumber + type: object + properties: + ReleaseId: + format: int64 + description: The Id of the parent release. + type: integer + VersionNumber: + description: The version of process associated with the release. + maxLength: 50 + type: string + CreationTime: + format: date-time + description: The date and time when the version was associated with the release. + type: string + ReleaseName: + description: The name of the process associated with the release. + type: string + Id: + format: int64 + type: integer + RobotUserDto: + required: + - RobotId + type: object + properties: + UserName: + description: The name of the user. + type: string + RobotId: + format: int64 + description: The Id of the Robot. + type: integer + HasTriggers: + description: Whether the machine robot has triggers defined or not. + type: boolean + SimpleReleaseDto: + description: >- + Entity derived from BaseReleaseDto. It shares all the properties of the + base entity except the navigation properties. + required: + - Name + - ProcessKey + - ProcessVersion + type: object + properties: + Key: + description: A unique identifier associated to each release. + type: string + ProcessKey: + description: The unique identifier of the process associated with the release. + type: string + ProcessVersion: + description: The version of the process associated with the release. + type: string + IsLatestVersion: + description: >- + States whether the version of process associated with the release is + latest or not. + type: boolean + IsProcessDeleted: + description: >- + States whether the process associated with the release is deleted or + not. + type: boolean + Description: + description: >- + Used to add additional information about a release in order to better + identify it. + type: string + Name: + description: >- + A custom name of the release. The default name format is + ProcessName_EnvironmentName. + type: string + EnvironmentId: + format: int64 + description: The Id of the environment associated with the release. + type: integer + EnvironmentName: + description: The name of the environment associated with the release. + type: string + Environment: + $ref: '#/definitions/EnvironmentDto' + EntryPointId: + format: int32 + type: integer + EntryPoint: + $ref: '#/definitions/EntryPointDto' + InputArguments: + description: >- + Input parameters in JSON format to be passed as default values to job + execution. + maxLength: 10000 + type: string + ProcessType: + enum: + - Undefined + - Process + - TestAutomationProcess + type: string + x-ms-enum: + name: ReleaseDtoProcessType + modelAsString: false + SupportsMultipleEntryPoints: + type: boolean + RequiresUserInteraction: + type: boolean + CurrentVersion: + $ref: '#/definitions/ReleaseVersionDto' + ReleaseVersions: + description: The collection of release versions that current release had over time. + type: array + items: + $ref: '#/definitions/ReleaseVersionDto' + Arguments: + $ref: '#/definitions/ArgumentMetadata' + ProcessSettings: + $ref: '#/definitions/ProcessSettingsDto' + AutoUpdate: + type: boolean + FeedId: + type: string + JobPriority: + description: The execution priority. If null, it defaults to Normal. + enum: + - Low + - Normal + - High + type: string + x-ms-enum: + name: ReleaseDtoJobPriority + modelAsString: false + CreationTime: + format: date-time + description: Creation time of this release. + type: string + OrganizationUnitId: + format: int64 + description: Id of the folder this release is part of. + type: integer + OrganizationUnitFullyQualifiedName: + description: Fully qualified name of the folder this release is part of. + type: string + Id: + format: int64 + type: integer + SimpleRobotDto: + description: >- + Entity derived from RobotDto. Is shares all the properties of the base + entity except the navigation properties. + required: + - HostingType + - Name + - Type + - Username + type: object + properties: + LicenseKey: + description: >- + The key is automatically generated from the server for the Robot + machine. For the robot to work, the same key must exist on both the + robot and Orchestrator. + maxLength: 255 + minLength: 0 + type: string + MachineName: + description: The name of the machine a Robot is hosted on. + maxLength: 450 + minLength: 0 + type: string + MachineId: + format: int64 + description: The Id of the machine a Robot is hosted on + type: integer + Name: + description: A custom name for the robot. + maxLength: 19 + minLength: 0 + type: string + Username: + description: >- + The machine username. If the user is under a domain, you are required + to also specify it in a DOMAIN\username format. Note: You must use + short domain names, such as desktop\administrator and NOT + desktop.local/administrator. + maxLength: 100 + minLength: 0 + type: string + ExternalName: + description: >- + Contains the value of the key in the external store used to store the + password. + maxLength: 450 + minLength: 0 + type: string + Description: + description: >- + Used to add additional information about a robot in order to better + identify it. + maxLength: 500 + minLength: 0 + type: string + Type: + description: The Robot type. + enum: + - NonProduction + - Attended + - Unattended + - Studio + - RpaDeveloper + - Development + - StudioX + - CitizenDeveloper + - Headless + - RpaDeveloperPro + - StudioPro + - TestAutomation + type: string + x-ms-enum: + name: RobotDtoType + modelAsString: false + HostingType: + description: The Robot hosting type (Standard / Floating). + enum: + - Standard + - Floating + type: string + x-ms-enum: + name: RobotDtoHostingType + modelAsString: false + ProvisionType: + description: The Robot provision type. + enum: + - Manual + - Automatic + type: string + x-ms-enum: + name: RobotDtoProvisionType + modelAsString: false + Password: + description: The Windows password associated with the machine username. + maxLength: 100 + minLength: 0 + type: string + CredentialStoreId: + format: int64 + description: The Credential Store used to store the password. + type: integer + UserId: + format: int64 + description: The associated user's Id. + type: integer + Enabled: + description: >- + Specificies the state of the Robot (enabled/disabled) - a disabled + robot cannot connect to Orchestrator + type: boolean + CredentialType: + description: The robot credentials type (Default/ SmartCard) + enum: + - Default + - SmartCard + - NCipher + - SafeNet + type: string + x-ms-enum: + name: RobotDtoCredentialType + modelAsString: false + Environments: + description: The collection of environments the robot is part of. + type: array + items: + $ref: '#/definitions/EnvironmentDto' + RobotEnvironments: + description: >- + The comma separated textual representation of environment names the + robot is part of. + type: string + ExecutionSettings: + description: >- + A collection of key value pairs containing execution settings for this + robot. + type: object + additionalProperties: {} + IsExternalLicensed: + description: Flag to indicate if the robot uses an external license + type: boolean + LimitConcurrentExecution: + description: Specifies if the robot can be used concurrently on multiple machines + type: boolean + Id: + format: int64 + type: integer +responses: {} +securityDefinitions: + oauth2-auth: + type: oauth2 + flow: accessCode + authorizationUrl: https://cloud.uipath.com/identity_/connect/authorize + tokenUrl: https://cloud.uipath.com/identity_/connect/token + scopes: + openid profile email OrchestratorApiUserAccess IdentityServerApi offline_access: >- + openid profile email OrchestratorApiUserAccess IdentityServerApi + offline_access +security: + - oauth2-auth: + - >- + openid profile email OrchestratorApiUserAccess IdentityServerApi + offline_access +tags: []