Fix dynamic connector extensions & update internal testing (#2146)

Fix dynamic connector extensions management in making 'parameters'
optional despite the spec which says it's required
Update internal tests
Add Guardian files
Update .gitignore file
Fix BuildLocalPackages.cmd script
This commit is contained in:
Luc Genetier 2024-01-16 19:03:56 +01:00 коммит произвёл GitHub
Родитель 382540a26b
Коммит cdcef30472
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
19 изменённых файлов: 372 добавлений и 292 удалений

5
.gdn/.gdnsettings Normal file
Просмотреть файл

@ -0,0 +1,5 @@
{
"jobs": [],
"commands": [],
"tools": []
}

11
.gdn/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,11 @@
## Ignore Guardian internal files
.r/
rc/
rs/
i/
p/
c/
o/
## Ignore Guardian Local settings
LocalSettings.gdn.json

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

@ -364,3 +364,11 @@ suppress.*.xml
# global.json
/src/global.json
/global.json
# Guardian results
.gdn/r
.gdn/internal.gdnhistory
# Yaml output
YamlOutput/

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

@ -2,11 +2,10 @@
setlocal enabledelayedexpansion
set MSBUILDARGS=-p:PublishRepositoryUrl=true -p:GeneratePackages=true -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:InternalBuild=true -p:Configuration=Debug -p:Platform="Any CPU"
set MSBUILD=C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\amd64\MSBuild.exe
for /f "usebackq tokens=*" %%i in (`"%programfiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe`) do (
@REM Restore dependencies first if needed
"%%i" -t:restore
@REM Run build and generate nuget packages
"%%i" %MSBUILDARGS%
exit /b !errorlevel!
)
@REM Restore dependencies first if needed
"%MSBUILD%" -t:restore
@REM Run build and generate nuget packages
"%MSBUILD%" %MSBUILDARGS%

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

@ -773,23 +773,22 @@ namespace Microsoft.PowerFx.Connectors
// https://learn.microsoft.com/en-us/connectors/custom-connectors/openapi-extensions#use-dynamic-values
if (param?.Extensions != null && param.Extensions.TryGetValue(XMsDynamicValues, out IOpenApiExtension ext) && ext is OpenApiObject apiObj)
{
if (apiObj.TryGetValue("parameters", out IOpenApiAny op_prms) && op_prms is OpenApiObject opPrms)
{
ConnectorDynamicValue cdv = new (opPrms);
// Parameters is required in the spec but there are examples where it's not specified and we'll support this condition with an empty list
OpenApiObject op_prms = apiObj.TryGetValue("parameters", out IOpenApiAny openApiAny) && openApiAny is OpenApiObject apiString ? apiString : null;
ConnectorDynamicValue cdv = new (op_prms);
// Mandatory operationId for connectors, except when capibility or builtInOperation are defined
apiObj.WhenPresent("operationId", (opId) => cdv.OperationId = OpenApiHelperFunctions.NormalizeOperationId(opId));
apiObj.WhenPresent("value-title", (opValTitle) => cdv.ValueTitle = opValTitle);
apiObj.WhenPresent("value-path", (opValPath) => cdv.ValuePath = opValPath);
apiObj.WhenPresent("value-collection", (opValCollection) => cdv.ValueCollection = opValCollection);
// Mandatory operationId for connectors, except when capibility or builtInOperation are defined
apiObj.WhenPresent("operationId", (opId) => cdv.OperationId = OpenApiHelperFunctions.NormalizeOperationId(opId));
apiObj.WhenPresent("value-title", (opValTitle) => cdv.ValueTitle = opValTitle);
apiObj.WhenPresent("value-path", (opValPath) => cdv.ValuePath = opValPath);
apiObj.WhenPresent("value-collection", (opValCollection) => cdv.ValueCollection = opValCollection);
// we don't support BuiltInOperations or capabilities right now
// return null to indicate that the call to get suggestions is not needed for this parameter
apiObj.WhenPresent("capability", (string op_capStr) => cdv = null);
apiObj.WhenPresent("builtInOperation", (string op_bioStr) => cdv = null);
// we don't support BuiltInOperations or capabilities right now
// return null to indicate that the call to get suggestions is not needed for this parameter
apiObj.WhenPresent("capability", (string op_capStr) => cdv = null);
apiObj.WhenPresent("builtInOperation", (string op_bioStr) => cdv = null);
return cdv;
}
return cdv;
}
return null;
@ -800,33 +799,32 @@ namespace Microsoft.PowerFx.Connectors
// https://learn.microsoft.com/en-us/connectors/custom-connectors/openapi-extensions#use-dynamic-values
if (param?.Extensions != null && param.Extensions.TryGetValue(XMsDynamicList, out IOpenApiExtension ext) && ext is OpenApiObject apiObj)
{
// Mandatory openrationId for connectors
// Mandatory operationId for connectors
if (apiObj.TryGetValue("operationId", out IOpenApiAny op_id) && op_id is OpenApiString opId)
{
if (apiObj.TryGetValue("parameters", out IOpenApiAny op_prms) && op_prms is OpenApiObject opPrms)
// Parameters is required in the spec but there are examples where it's not specified and we'll support this condition with an empty list
OpenApiObject op_prms = apiObj.TryGetValue("parameters", out IOpenApiAny openApiAny) && openApiAny is OpenApiObject apiString ? apiString : null;
ConnectorDynamicList cdl = new (op_prms)
{
ConnectorDynamicList cdl = new (opPrms)
{
OperationId = OpenApiHelperFunctions.NormalizeOperationId(opId.Value),
};
OperationId = OpenApiHelperFunctions.NormalizeOperationId(opId.Value),
};
if (apiObj.TryGetValue("itemTitlePath", out IOpenApiAny op_valtitle) && op_valtitle is OpenApiString opValTitle)
{
cdl.ItemTitlePath = opValTitle.Value;
}
if (apiObj.TryGetValue("itemsPath", out IOpenApiAny op_valpath) && op_valpath is OpenApiString opValPath)
{
cdl.ItemPath = opValPath.Value;
}
if (apiObj.TryGetValue("itemValuePath", out IOpenApiAny op_valcoll) && op_valcoll is OpenApiString opValCollection)
{
cdl.ItemValuePath = opValCollection.Value;
}
return cdl;
if (apiObj.TryGetValue("itemTitlePath", out IOpenApiAny op_valtitle) && op_valtitle is OpenApiString opValTitle)
{
cdl.ItemTitlePath = opValTitle.Value;
}
if (apiObj.TryGetValue("itemsPath", out IOpenApiAny op_valpath) && op_valpath is OpenApiString opValPath)
{
cdl.ItemPath = opValPath.Value;
}
if (apiObj.TryGetValue("itemValuePath", out IOpenApiAny op_valcoll) && op_valcoll is OpenApiString opValCollection)
{
cdl.ItemValuePath = opValCollection.Value;
}
return cdl;
}
else
{
@ -839,7 +837,12 @@ namespace Microsoft.PowerFx.Connectors
internal static Dictionary<string, IConnectorExtensionValue> GetParameterMap(this OpenApiObject opPrms, SupportsConnectorErrors errors, bool forceString = false)
{
Dictionary<string, IConnectorExtensionValue> dvParams = new ();
Dictionary<string, IConnectorExtensionValue> dvParams = new ();
if (opPrms == null)
{
return dvParams;
}
foreach (KeyValuePair<string, IOpenApiAny> prm in opPrms)
{
@ -907,23 +910,23 @@ namespace Microsoft.PowerFx.Connectors
// https://learn.microsoft.com/en-us/connectors/custom-connectors/openapi-extensions#use-dynamic-values
if (param?.Extensions != null && param.Extensions.TryGetValue(XMsDynamicSchema, out IOpenApiExtension ext) && ext is OpenApiObject apiObj)
{
// Mandatory openrationId for connectors
// Mandatory operationId for connectors
if (apiObj.TryGetValue("operationId", out IOpenApiAny op_id) && op_id is OpenApiString opId)
{
if (apiObj.TryGetValue("parameters", out IOpenApiAny op_prms) && op_prms is OpenApiObject opPrms)
// Parameters is required in the spec but there are examples where it's not specified and we'll support this condition with an empty list
OpenApiObject op_prms = apiObj.TryGetValue("parameters", out IOpenApiAny openApiAny) && openApiAny is OpenApiObject apiString ? apiString : null;
ConnectorDynamicSchema cds = new (op_prms)
{
ConnectorDynamicSchema cds = new (opPrms)
{
OperationId = OpenApiHelperFunctions.NormalizeOperationId(opId.Value),
};
OperationId = OpenApiHelperFunctions.NormalizeOperationId(opId.Value),
};
if (apiObj.TryGetValue("value-path", out IOpenApiAny op_valpath) && op_valpath is OpenApiString opValPath)
{
cds.ValuePath = opValPath.Value;
}
return cds;
if (apiObj.TryGetValue("value-path", out IOpenApiAny op_valpath) && op_valpath is OpenApiString opValPath)
{
cds.ValuePath = opValPath.Value;
}
return cds;
}
else
{
@ -939,23 +942,23 @@ namespace Microsoft.PowerFx.Connectors
// https://learn.microsoft.com/en-us/connectors/custom-connectors/openapi-extensions#use-dynamic-values
if (param?.Extensions != null && param.Extensions.TryGetValue(XMsDynamicProperties, out IOpenApiExtension ext) && ext is OpenApiObject apiObj)
{
// Mandatory openrationId for connectors
// Mandatory operationId for connectors
if (apiObj.TryGetValue("operationId", out IOpenApiAny op_id) && op_id is OpenApiString opId)
{
if (apiObj.TryGetValue("parameters", out IOpenApiAny op_prms) && op_prms is OpenApiObject opPrms)
// Parameters is required in the spec but there are examples where it's not specified and we'll support this condition with an empty list
OpenApiObject op_prms = apiObj.TryGetValue("parameters", out IOpenApiAny openApiAny) && openApiAny is OpenApiObject apiString ? apiString : null;
ConnectorDynamicProperty cdp = new (op_prms)
{
ConnectorDynamicProperty cdp = new (opPrms)
{
OperationId = OpenApiHelperFunctions.NormalizeOperationId(opId.Value),
};
OperationId = OpenApiHelperFunctions.NormalizeOperationId(opId.Value),
};
if (apiObj.TryGetValue("itemValuePath", out IOpenApiAny op_valpath) && op_valpath is OpenApiString opValPath)
{
cdp.ItemValuePath = opValPath.Value;
}
return cdp;
if (apiObj.TryGetValue("itemValuePath", out IOpenApiAny op_valpath) && op_valpath is OpenApiString opValPath)
{
cdp.ItemValuePath = opValPath.Value;
}
return cdp;
}
else
{

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

@ -84,7 +84,7 @@ namespace Microsoft.PowerFx.Connectors
{
configurationLogger?.LogError($"{nameof(openApiDocument)} is null");
return functions;
}
}
if (!ValidateSupportedOpenApiDocument(openApiDocument, ref connectorIsSupported, ref connectorNotSupportedReason, connectorSettings.FailOnUnknownExtension, configurationLogger))
{
@ -260,7 +260,11 @@ namespace Microsoft.PowerFx.Connectors
// openApiDocument.Components.RequestBodies is ok
// openApiDocument.Components.Responses contains references from "path" definitions
// openApiDocument.Components.Schemas contains global "definitions"
// openApiDocument.Components.SecuritySchemes are critical but as we don't manage them at all, we'll ignore this parameter
if (openApiDocument.Components.SecuritySchemes.Count > 0)
{
logger?.LogInformation($"Unsupported document: {notSupportedReason}");
}
}
if (isSupported && failOnUnknownExtensions)

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

@ -23,12 +23,12 @@ namespace Microsoft.PowerFx.Connectors.Tests
{
public abstract class BaseConnectorTest : PowerFxTest, IDisposable
{
internal ITestOutputHelper _output;
internal ITestOutputHelper _output;
internal string _swaggerFile;
private bool _disposedValue;
public BaseConnectorTest(ITestOutputHelper output, string swaggerFile)
{
{
_swaggerFile = swaggerFile;
_output = output;
}
@ -43,11 +43,11 @@ namespace Microsoft.PowerFx.Connectors.Tests
public virtual ConnectorSettings GetConnectorSettings(bool returnUnknownRecordFieldsAsUntypedObjects = false)
{
return new ConnectorSettings(GetNamespace())
{
Compatibility = returnUnknownRecordFieldsAsUntypedObjects ? ConnectorCompatibility.SwaggerCompatibility : ConnectorCompatibility.PowerAppsCompatibility,
IncludeInternalFunctions = true,
ReturnUnknownRecordFieldsAsUntypedObjects = returnUnknownRecordFieldsAsUntypedObjects
return new ConnectorSettings(GetNamespace())
{
Compatibility = returnUnknownRecordFieldsAsUntypedObjects ? ConnectorCompatibility.SwaggerCompatibility : ConnectorCompatibility.PowerAppsCompatibility,
IncludeInternalFunctions = true,
ReturnUnknownRecordFieldsAsUntypedObjects = returnUnknownRecordFieldsAsUntypedObjects
};
}
@ -73,10 +73,10 @@ namespace Microsoft.PowerFx.Connectors.Tests
internal (LoggingTestServer testConnector, OpenApiDocument apiDoc, PowerFxConfig config, HttpClient httpClient, PowerPlatformConnectorClient client, ConnectorSettings connectorSettings, RuntimeConfig runtimeConfig) GetElements(bool live = false, bool returnUO = false)
{
var testConnector = new LoggingTestServer(_swaggerFile, live);
var testConnector = new LoggingTestServer(_swaggerFile, _output, live);
HttpClient httpClient = new HttpClient(testConnector);
PowerPlatformConnectorClient client = new PowerPlatformConnectorClient(GetEndpoint(), GetEnvironment(), GetConnectionId(), () => GetJWTToken(), httpClient) { SessionId = "9315f316-5182-4260-b333-7a43a36ca3b0" };
PowerFxConfig config = new PowerFxConfig();
OpenApiDocument apiDoc = testConnector._apiDocument;
ConnectorSettings connectorSettings = GetConnectorSettings(returnUO);
@ -108,7 +108,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
: ($@"Responses\{ef.Substring(4)}", (HttpStatusCode)int.Parse(ef.Substring(0, 3)))).ToArray());
}
FormulaValue fv = await engine.EvalAsync(expr, CancellationToken.None, options: new ParserOptions() { AllowsSideEffects = true }, runtimeConfig: runtimeConfig).ConfigureAwait(false);
FormulaValue fv = await engine.EvalAsync(expr, CancellationToken.None, options: new ParserOptions() { AllowsSideEffects = true }, runtimeConfig: runtimeConfig).ConfigureAwait(false);
string network = testConnector._log.ToString();
string urls = string.Join("|", Regex.Matches(network, @"x-ms-request-method: (?<r>[^ \r\n]+)\s*x-ms-request-url: (?<u>[^ \r\n]+)").Select(g => $"{g.Groups["r"].Value}:{g.Groups["u"].Value}"));
@ -150,7 +150,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
else if (expectedResult.StartsWith("DATETIME:"))
{
Assert.True(fv is not ErrorValue, fv is ErrorValue ev ? $"EvalAsync Error: {string.Join(", ", ev.Errors.Select(er => er.Message))}" : null);
DateTimeValue dtv = Assert.IsType<DateTimeValue>(fv);
DateTimeValue dtv = Assert.IsType<DateTimeValue>(fv);
Assert.Equal(DateTime.Parse(expectedResult.Substring(9)).ToUniversalTime(), new ConvertToUTC(GetTimeZoneInfo()).ToUTC(dtv));
}
@ -171,8 +171,8 @@ namespace Microsoft.PowerFx.Connectors.Tests
}
Assert.Equal(xUrls, urls);
Assert.Equal(xBodies, bodies);
Assert.Equal(xBodies, bodies);
if (!string.IsNullOrEmpty(extra))
{
foreach (string e in extra.Split("|"))
@ -237,7 +237,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
if (!_disposedValue)
{
if (disposing)
{
{
}
_disposedValue = true;

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

@ -11,7 +11,6 @@ using System.Threading.Tasks;
using Microsoft.PowerFx.Connectors;
using Microsoft.PowerFx.Connectors.Tests;
using Microsoft.PowerFx.Core.Tests;
using Microsoft.VisualStudio.TestPlatform.Utilities;
using Xunit;
using Xunit.Abstractions;
@ -66,7 +65,7 @@ namespace Microsoft.PowerFx.Tests
var swaggerFile = @"Swagger\TestOpenAPI.json";
Console.Write(i);
using var testConnector = new LoggingTestServer(swaggerFile);
using var testConnector = new LoggingTestServer(swaggerFile, _output);
testConnector.SetResponseFromFile(@"Responses\HttpCall_1.json");
List<ConnectorFunction> functions = OpenApiParser.GetFunctions("Test", testConnector._apiDocument, new ConsoleLogger(_output)).OrderBy(cf => cf.Name).ToList();
@ -82,7 +81,7 @@ namespace Microsoft.PowerFx.Tests
var checkResult = engine.Check(fxQuery, options: _optionsPost);
Assert.True(checkResult.IsSuccess, string.Join("\r\n", checkResult.Errors.Select(er => er.Message)));
var rConfig = new RuntimeConfig();
var rConfig = new RuntimeConfig();
rConfig.SetTimeZone(TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time"));
rConfig.AddRuntimeContext(new TestConnectorRuntimeContext("Test", httpClient, console: _output));
@ -105,7 +104,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task BasicHttpCallNullInvoker()
{
using var testConnector = new LoggingTestServer(@"Swagger\TestOpenAPI.json");
using var testConnector = new LoggingTestServer(@"Swagger\TestOpenAPI.json", _output);
var config = new PowerFxConfig();
var apiDoc = testConnector._apiDocument;
@ -125,7 +124,7 @@ namespace Microsoft.PowerFx.Tests
public void BasicHttpBinding()
{
var config = new PowerFxConfig();
var apiDoc = Helpers.ReadSwagger(@"Swagger\TestOpenAPI.json");
var apiDoc = Helpers.ReadSwagger(@"Swagger\TestOpenAPI.json", _output);
// If we don't pass httpClient, we can still bind, we just can't invoke.
config.AddActionConnector("Test", apiDoc, new ConsoleLogger(_output));
@ -139,7 +138,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task PetStore_MultiServer()
{
using var testConnector = new LoggingTestServer(@"Swagger\PetStore.json");
using var testConnector = new LoggingTestServer(@"Swagger\PetStore.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();

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

@ -32,7 +32,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Fact]
public async Task ConnectorWizardTest()
{
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SQL Server.json");
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SQL Server.json", _output);
using HttpClient httpClient = new HttpClient(testConnector);
using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient(
"tip1-shared-002.azure-apim.net", // endpoint
@ -154,7 +154,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Fact]
public async Task ConnectorWizardTest_InvalidToken()
{
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SQL Server.json");
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SQL Server.json", _output);
using HttpClient httpClient = new HttpClient(testConnector);
using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient(
"tip1-shared-002.azure-apim.net", // endpoint
@ -167,7 +167,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
};
OpenApiDocument apiDoc = testConnector._apiDocument;
IEnumerable<ConnectorFunction> functions = OpenApiParser.GetFunctions("SQL", apiDoc, new ConsoleLogger(_output));
IEnumerable<ConnectorFunction> functions = OpenApiParser.GetFunctions("SQL", apiDoc, new ConsoleLogger(_output));
ConnectorFunction executeProcedureV2 = functions.First(cf => cf.Name == "ExecuteProcedureV2");
BaseRuntimeConnectorContext context = new TestConnectorRuntimeContext("SQL", client, throwOnError: true, console: _output);

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

@ -10,16 +10,16 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers;
using Microsoft.OpenApi.Validations;
using Microsoft.PowerFx.Core.Tests;
using Microsoft.PowerFx.Intellisense;
using Microsoft.PowerFx.Types;
using Microsoft.VisualStudio.TestPlatform.Utilities;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.PowerFx.Connectors.Tests
{
public class DynamicTests
public class DynamicTests
{
public const string Host = @"http://localhost:5189";
public const string SwaggerFile = @"http://localhost:5189/swagger/v1/swagger.json";
@ -32,18 +32,35 @@ namespace Microsoft.PowerFx.Connectors.Tests
_output = output;
}
private void GetFunctionsInternal(out OpenApiDocument doc, out List<ConnectorFunction> functions)
private void GetFunctionsInternal(ITestOutputHelper output, out OpenApiDocument doc, out List<ConnectorFunction> functions)
{
functions = null;
doc = null;
try
try
{
// lock managed by XUnit
if (!skip)
{
using WebClient webClient = new WebClient();
doc = new OpenApiStreamReader().Read(webClient.OpenRead(SwaggerFile), out var diagnostic);
doc = new OpenApiStreamReader().Read(webClient.OpenRead(SwaggerFile), out var diag);
if (diag != null && diag.Errors.Count > 0)
{
foreach (OpenApiError error in diag.Errors)
{
if (error is OpenApiValidatorError vError)
{
output.WriteLine($"[OpenApi Error] {vError.RuleName} {vError.Pointer} {vError.Message}");
}
else
{
// Could be OpenApiError or OpenApiReferenceError
output.WriteLine($"[OpenApi Error] {error.Pointer} {error.Message}");
}
}
}
functions = OpenApiParser.GetFunctions("Test", doc, new ConsoleLogger(_output)).OrderBy(cf => cf.Name).ToList();
}
}
@ -80,7 +97,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[SkippableFact]
public async Task DynamicValues_GetWithDynamicValuesStatic_RequiredParameters()
{
GetFunctionsInternal(out OpenApiDocument doc, out List<ConnectorFunction> functions);
GetFunctionsInternal(_output, out OpenApiDocument doc, out List<ConnectorFunction> functions);
ConnectorFunction getWithDynamicValuesStatic = functions.First(cf => cf.Name == "GetWithDynamicValuesStatic");
Assert.True(getWithDynamicValuesStatic.RequiredParameters[0].SupportsDynamicIntellisense);
@ -89,7 +106,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
string expr = "Test.GetWithDynamicValuesStatic(";
CheckResult result = engine.Check(expr, symbolTable: null);
IIntellisenseResult suggestions = engine.Suggest(result, expr.Length, services);
Assert.Equal("14|24", string.Join("|", suggestions.Suggestions.Select(s => s.DisplayText.Text)));
@ -100,7 +117,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[SkippableFact]
public async Task DynamicValues_GetWithDynamicValuesStatic_OptionalParameters()
{
GetFunctionsInternal(out OpenApiDocument doc, out List<ConnectorFunction> functions);
GetFunctionsInternal(_output, out OpenApiDocument doc, out List<ConnectorFunction> functions);
ConnectorFunction getWithDynamicValuesStaticOptional = functions.First(cf => cf.Name == "GetWithDynamicValuesStaticOptional");
Assert.True(getWithDynamicValuesStaticOptional.OptionalParameters[0].SupportsDynamicIntellisense);
@ -122,7 +139,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[SkippableFact]
public async Task DynamicValues_GetWithDynamicValuesStaticNoValueCollection_RequiredParameters()
{
GetFunctionsInternal(out OpenApiDocument doc, out List<ConnectorFunction> functions);
GetFunctionsInternal(_output, out OpenApiDocument doc, out List<ConnectorFunction> functions);
ConnectorFunction getWithDynamicValuesStaticNoValueCollection = functions.First(cf => cf.Name == "GetWithDynamicValuesStaticNoValueCollection");
Assert.True(getWithDynamicValuesStaticNoValueCollection.RequiredParameters[0].SupportsDynamicIntellisense);
@ -142,7 +159,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[SkippableFact]
public async Task DynamicValues_GetWithDynamicValuesStaticInvalidValueCollection_RequiredParameters()
{
GetFunctionsInternal(out OpenApiDocument doc, out List<ConnectorFunction> functions);
GetFunctionsInternal(_output, out OpenApiDocument doc, out List<ConnectorFunction> functions);
ConnectorFunction getWithDynamicValuesStaticInvalidValueCollection = functions.First(cf => cf.Name == "GetWithDynamicValuesStaticInvalidValueCollection");
Assert.True(getWithDynamicValuesStaticInvalidValueCollection.RequiredParameters[0].SupportsDynamicIntellisense);
@ -162,7 +179,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[SkippableFact]
public async Task DynamicValues_GetWithDynamicValuesStaticInvalidDisplayName_RequiredParameters()
{
GetFunctionsInternal(out OpenApiDocument doc, out List<ConnectorFunction> functions);
GetFunctionsInternal(_output, out OpenApiDocument doc, out List<ConnectorFunction> functions);
ConnectorFunction getWithDynamicValuesStaticInvalidDisplayName = functions.First(cf => cf.Name == "GetWithDynamicValuesStaticInvalidDisplayName");
Assert.True(getWithDynamicValuesStaticInvalidDisplayName.RequiredParameters[0].SupportsDynamicIntellisense);
@ -182,7 +199,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[SkippableFact]
public async Task DynamicValues_GetWithDynamicValuesStaticInvalidValuePath_RequiredParameters()
{
GetFunctionsInternal(out OpenApiDocument doc, out List<ConnectorFunction> functions);
GetFunctionsInternal(_output, out OpenApiDocument doc, out List<ConnectorFunction> functions);
ConnectorFunction getWithDynamicValuesStaticInvalidValuePath = functions.First(cf => cf.Name == "GetWithDynamicValuesStaticInvalidValuePath");
Assert.True(getWithDynamicValuesStaticInvalidValuePath.RequiredParameters[0].SupportsDynamicIntellisense);
@ -202,7 +219,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[SkippableFact]
public async Task DynamicValues_GetWithDynamicValuesDynamic_RequiredParameters()
{
GetFunctionsInternal(out OpenApiDocument doc, out List<ConnectorFunction> functions);
GetFunctionsInternal(_output, out OpenApiDocument doc, out List<ConnectorFunction> functions);
ConnectorFunction getWithDynamicValuesDynamic = functions.First(cf => cf.Name == "GetWithDynamicValuesDynamic");
Assert.True(getWithDynamicValuesDynamic.RequiredParameters[1].SupportsDynamicIntellisense);
@ -222,7 +239,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[SkippableFact]
public async Task DynamicValues_GetWithDynamicValuesMultipleDynamic_RequiredParameters()
{
GetFunctionsInternal(out OpenApiDocument doc, out List<ConnectorFunction> functions);
GetFunctionsInternal(_output, out OpenApiDocument doc, out List<ConnectorFunction> functions);
ConnectorFunction getWithDynamicValuesMultipleDynamic = functions.First(cf => cf.Name == "GetWithDynamicValuesMultipleDynamic");
Assert.True(getWithDynamicValuesMultipleDynamic.RequiredParameters[2].SupportsDynamicIntellisense);
@ -255,13 +272,13 @@ namespace Microsoft.PowerFx.Connectors.Tests
Assert.Equal("![Index:w, Name:s, PrimaryKey:s]", innerType.FormulaType.ToStringWithDisplayNames());
ConnectorEnhancedSuggestions ces = await getWithDynamicValuesMultipleDynamic.GetConnectorSuggestionsAsync(Array.Empty<NamedValue>(), innerType.Fields.First(field => field.Name == "Index"), connectorContext, CancellationToken.None).ConfigureAwait(false);
Assert.Equal("110|120", string.Join("|", ces.ConnectorSuggestions.Suggestions.Select(cs => cs.DisplayName)));
Assert.Equal("110|120", string.Join("|", ces.ConnectorSuggestions.Suggestions.Select(cs => cs.DisplayName)));
}
[SkippableFact]
public async Task DynamicValues_GetWithDynamicListStatic_RequiredParameters()
{
GetFunctionsInternal(out OpenApiDocument doc, out List<ConnectorFunction> functions);
GetFunctionsInternal(_output, out OpenApiDocument doc, out List<ConnectorFunction> functions);
ConnectorFunction getWithDynamicListStatic = functions.First(cf => cf.Name == "GetWithDynamicListStatic");
Assert.True(getWithDynamicListStatic.RequiredParameters[0].SupportsDynamicIntellisense);
@ -281,7 +298,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[SkippableFact]
public async Task DynamicValues_GetWithDynamicListDynamic_RequiredParameters()
{
GetFunctionsInternal(out OpenApiDocument doc, out List<ConnectorFunction> functions);
GetFunctionsInternal(_output, out OpenApiDocument doc, out List<ConnectorFunction> functions);
ConnectorFunction getWithDynamicListDynamic = functions.First(cf => cf.Name == "GetWithDynamicListDynamic");
Assert.True(getWithDynamicListDynamic.RequiredParameters[1].SupportsDynamicIntellisense);

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

@ -6,7 +6,9 @@ using System.IO;
using System.Linq;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers;
using Microsoft.OpenApi.Validations;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.PowerFx.Tests
{
@ -45,26 +47,35 @@ namespace Microsoft.PowerFx.Tests
}
else
{
using var streamReader = new StreamReader(stream);
return streamReader.ReadToEnd();
using var streamReader = new StreamReader(stream);
return streamReader.ReadToEnd();
}
}
}
// Get a swagger file from the embedded resources.
public static OpenApiDocument ReadSwagger(string name)
public static OpenApiDocument ReadSwagger(string name, ITestOutputHelper output)
{
using (var stream = GetStream(name))
using var stream = GetStream(name);
OpenApiDocument doc = new OpenApiStreamReader().Read(stream, out OpenApiDiagnostic diag);
if (diag != null && diag.Errors.Count > 0)
{
var doc = new OpenApiStreamReader().Read(stream, out OpenApiDiagnostic diag);
if ((doc == null || doc.Paths == null || doc.Paths.Count == 0) && diag != null && diag.Errors.Count > 0)
{
throw new InvalidDataException($"Unable to parse Swagger file: {string.Join(", ", diag.Errors.Select(err => err.Message))}");
foreach (OpenApiError error in diag.Errors)
{
if (error is OpenApiValidatorError vError)
{
output.WriteLine($"[OpenApi Error] {vError.RuleName} {vError.Pointer} {vError.Message}");
}
else
{
// Could be OpenApiError or OpenApiReferenceError
output.WriteLine($"[OpenApi Error] {error.Pointer} {error.Message}");
}
}
return doc;
}
return doc;
}
}
}

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

@ -13,6 +13,7 @@ using System.Threading.Tasks;
using Microsoft.OpenApi.Models;
using Microsoft.PowerFx.Connectors;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.PowerFx.Tests
{
@ -26,12 +27,12 @@ namespace Microsoft.PowerFx.Tests
public OpenApiDocument _apiDocument;
public bool SendAsyncCalled = false;
public bool Live = false;
public HttpClient LiveClient = null;
public HttpClient LiveClient = null;
public LoggingTestServer(string swaggerName, bool live = false)
public LoggingTestServer(string swaggerName, ITestOutputHelper output, bool live = false)
{
Live = live;
_apiDocument = Helpers.ReadSwagger(swaggerName);
_apiDocument = Helpers.ReadSwagger(swaggerName, output);
if (live)
{
@ -189,7 +190,7 @@ namespace Microsoft.PowerFx.Tests
{
clone.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
return await LiveClient.SendAsync(clone, cancellationToken).ConfigureAwait(false);
}

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

@ -9,7 +9,6 @@ using System.Threading.Tasks;
using Microsoft.OpenApi.Models;
using Microsoft.PowerFx.Intellisense;
using Microsoft.PowerFx.Tests;
using Microsoft.VisualStudio.TestPlatform.Utilities;
using Xunit;
using Xunit.Abstractions;
@ -47,7 +46,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
public void ConnectorIntellisenseTest(int responseIndex, int queryIndex, string expression, string expectedSuggestions)
{
// These tests are exercising 'x-ms-dynamic-values' extension property
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SQL Server.json");
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SQL Server.json", _output);
OpenApiDocument apiDoc = testConnector._apiDocument;
PowerFxConfig config = new PowerFxConfig(Features.PowerFxV1);
@ -128,7 +127,7 @@ $@"POST https://tip1-shared-002.azure-apim.net/invoke
public void ConnectorIntellisenseTest2(int responseIndex, int networkCall, string expression, string expectedSuggestions)
{
// These tests are exercising 'x-ms-dynamic-schema' extension property
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SQL Server.json");
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SQL Server.json", _output);
OpenApiDocument apiDoc = testConnector._apiDocument;
PowerFxConfig config = new PowerFxConfig(Features.PowerFxV1);
@ -185,7 +184,7 @@ $@"POST https://tip1-shared-002.azure-apim.net/invoke
public void ConnectorIntellisenseTestLSP(int responseIndex, int networkCall, string expression, string expectedSuggestions)
{
// These tests are exercising 'x-ms-dynamic-schema' extension property
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SQL Server.json");
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SQL Server.json", _output);
OpenApiDocument apiDoc = testConnector._apiDocument;
PowerFxConfig config = new PowerFxConfig(Features.PowerFxV1);
@ -248,7 +247,7 @@ $@"POST https://tip1-shared-002.azure-apim.net/invoke
[Fact]
public async Task ConnectorIntellisenseTest3()
{
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SharePoint.json");
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SharePoint.json", _output);
OpenApiDocument apiDoc = testConnector._apiDocument;
PowerFxConfig config = new PowerFxConfig();
string token = @"eyJ0eXA...";
@ -283,7 +282,7 @@ $@"POST https://tip1-shared-002.azure-apim.net/invoke
{
// This Path can be found in the Swagger file SQL Server.json
var deprecatedFunctionExample = "SQL.ExecutePassThroughNativeQuery";
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SQL Server.json");
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SQL Server.json", _output);
OpenApiDocument apiDoc = testConnector._apiDocument;
PowerFxConfig config = new PowerFxConfig(Features.PowerFxV1);

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

@ -38,34 +38,45 @@ namespace Microsoft.PowerFx.Connectors.Tests
#endif
public void TestAllConnectors()
{
string outFolder = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, @"..\..\..\..\..\.."));
string srcFolder = Path.GetFullPath(Path.Combine(outFolder, ".."));
(string outFolder, string srcFolder) = GetFolders();
string reportFolder = @"report";
string reportName = @$"{reportFolder}\Analysis.txt";
string jsonReport = @$"{reportFolder}\Report.json";
// New report name every second
string jsonReport2 = @$"report\Report_{Math.Round(DateTime.UtcNow.Ticks / 1e7):00000000000}.json";
string jsonReport2 = @$"{reportFolder}\Report_{Math.Round(DateTime.UtcNow.Ticks / 1e7):00000000000}.json";
string outFolderPath = Path.Combine(outFolder, reportFolder);
if (Directory.Exists(outFolderPath))
{
Directory.Delete(outFolderPath, true);
}
// On build servers: ENV: C:\__w\1\s\pfx\src\tests\Microsoft.PowerFx.Connectors.Tests\bin\Release\netcoreapp3.1
// Locally : ENV: C:\Data\Power-Fx\src\tests\Microsoft.PowerFx.Connectors.Tests\bin\Debug\netcoreapp3.1
_output.WriteLine($"ENV: {Environment.CurrentDirectory}");
_output.WriteLine($"OUT: {outFolder}");
_output.WriteLine($"SRC: {srcFolder}");
BaseConnectorTest.EmptyFolder(outFolderPath);
Directory.CreateDirectory(Path.Combine(outFolder, "report"));
GenerateReport(reportFolder, reportName, outFolder, srcFolder);
AnalyzeReport(reportName, outFolder, srcFolder, jsonReport);
File.Copy(Path.Combine(outFolder, jsonReport), Path.Combine(outFolder, jsonReport2));
}
private (string outFolder, string srcFolder) GetFolders()
{
string outFolder = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, @"..\..\..\..\..\.."));
string srcFolder = Path.GetFullPath(Path.Combine(outFolder, ".."));
// On build servers: ENV: C:\__w\1\s\pfx\src\tests\Microsoft.PowerFx.Connectors.Tests\bin\Release\netcoreapp3.1
// Locally : ENV: C:\Data\Power-Fx\src\tests\Microsoft.PowerFx.Connectors.Tests\bin\Debug\netcoreapp3.1
_output.WriteLine($"ENV: {Environment.CurrentDirectory}");
// On build servers: OUT: C:\__w\1\s\pfx
// Locally : OUT: C:\Data\Power-Fx
_output.WriteLine($"OUT: {outFolder}");
// On build servers: SRC: C:\__w\1\s
// Locally : SRC: C:\Data
_output.WriteLine($"SRC: {srcFolder}");
return (outFolder, srcFolder);
}
private void AnalyzeReport(string reportName, string outFolder, string srcFolder, string jsonReport)
{
List<Connector> connectors = new ();
@ -356,23 +367,22 @@ namespace Microsoft.PowerFx.Connectors.Tests
try
{
ConsoleLogger logger = new ConsoleLogger(_output);
OpenApiDocument doc = Helpers.ReadSwagger(swaggerFile);
OpenApiDocument doc = Helpers.ReadSwagger(swaggerFile, _output);
ConnectorSettings connectorSettings = new ConnectorSettings("Connector") { AllowUnsupportedFunctions = true, IncludeInternalFunctions = true };
ConnectorSettings swaggerConnectorSettings = new ConnectorSettings("Connector") { AllowUnsupportedFunctions = true, IncludeInternalFunctions = true, Compatibility = ConnectorCompatibility.SwaggerCompatibility };
title = $"{doc.Info.Title} [{swaggerFile}]";
// Check we can get the functions
IEnumerable<ConnectorFunction> functions = OpenApiParser.GetFunctions("C", doc, logger);
IEnumerable<ConnectorFunction> functions1 = OpenApiParser.GetFunctions(connectorSettings, doc, logger);
allFunctions.Add(title, functions);
allFunctions.Add(title, functions1);
var config = new PowerFxConfig();
using var client = new PowerPlatformConnectorClient("firstrelease-001.azure-apim.net", "839eace6-59ab-4243-97ec-a5b8fcc104e4", "72c42ee1b3c7403c8e73aa9c02a7fbcc", () => "Some JWT token")
{
SessionId = "ce55fe97-6e74-4f56-b8cf-529e275b253f"
};
// Check we can add the service (more comprehensive test)
config.AddActionConnector("Connector", doc, logger);
config.AddActionConnector(connectorSettings, doc, logger);
IEnumerable<ConnectorFunction> functions2 = OpenApiParser.GetFunctions(new ConnectorSettings("C1") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, doc);
IEnumerable<ConnectorFunction> functions2 = OpenApiParser.GetFunctions(swaggerConnectorSettings, doc);
string cFolder = Path.Combine(outFolder, reportFolder, doc.Info.Title);
int ix = 2;
@ -383,7 +393,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
Directory.CreateDirectory(cFolder);
foreach (ConnectorFunction cf1 in functions)
foreach (ConnectorFunction cf1 in functions1)
{
ConnectorFunction cf2 = functions2.First(f => f.Name == cf1.Name);
@ -512,7 +522,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
public void TestConnector1()
{
string swaggerFile = @"c:\data\AAPT-connectors\src\ConnectorPlatform\build-system\SharedTestAssets\Assets\BaselineBuild\locPublish\Connectors\AzureAD\apidefinition.swagger.json";
OpenApiDocument doc = Helpers.ReadSwagger(swaggerFile);
OpenApiDocument doc = Helpers.ReadSwagger(swaggerFile, _output);
IEnumerable<ConnectorFunction> functions = OpenApiParser.GetFunctions("C", doc, new ConsoleLogger(_output));
var config = new PowerFxConfig();
@ -538,22 +548,16 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Theory(Skip = "Need files from AAPT-connector, PowerPlatformConnectors and Power-Fx-TexlFunctions-Baseline projects")]
#endif
[InlineData("Library")] // Default Power-Fx library
[InlineData("Aapt-Ppc", 0, "apidefinition*swagger*.json", @"C:\Data\aapt", @"C:\Data\ppc")]
[InlineData("Baseline", 1, "*.json", @"C:\Data\Power-Fx-TexlFunctions-Baseline\Swaggers")]
[InlineData("Aapt-Ppc", 0, "apidefinition*swagger*.json", @"aapt\src", @"ppc")]
[InlineData("Baseline", 1, "*.json", @"Power-Fx-TexlFunctions-Baseline\Swaggers")]
public void GenerateYamlFiles(string reference, int folderExclusionIndex = -1, string pattern = null, params string[] folders)
{
string outFolder = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, @"..\..\..\..\..\.."));
string srcFolder = Path.GetFullPath(Path.Combine(outFolder, ".."));
// On build servers: ENV: C:\__w\1\s\pfx\src\tests\Microsoft.PowerFx.Connectors.Tests\bin\Release\netcoreapp3.1
// Locally : ENV: C:\Data\Power-Fx\src\tests\Microsoft.PowerFx.Connectors.Tests\bin\Debug\netcoreapp3.1
_output.WriteLine($"ENV: {Environment.CurrentDirectory}");
_output.WriteLine($"OUT: {outFolder}");
_output.WriteLine($"SRC: {srcFolder}");
(string outFolder, string srcFolder) = GetFolders();
string outFolderPath = Path.Combine(outFolder, "YamlOutput");
BaseConnectorTest.EmptyFolder(Path.Combine(outFolderPath, reference));
// if no folder, use Library
if (folders.Length == 0)
{
#pragma warning disable CS0618 // Type or member is obsolete
@ -569,20 +573,22 @@ namespace Microsoft.PowerFx.Connectors.Tests
SwaggerLocatorSettings swaggerLocationSettings = folderExclusionIndex == 0 ? null : new SwaggerLocatorSettings(new List<string>());
ConsoleLogger logger = new ConsoleLogger(_output);
ConnectorSettings connectorSettings = new ConnectorSettings("FakeNamespace")
{
AllowUnsupportedFunctions = true,
IncludeInternalFunctions = true,
FailOnUnknownExtension = false,
Compatibility = ConnectorCompatibility.PowerAppsCompatibility
};
ConnectorSettings connectorSettings = new ConnectorSettings("FakeNamespace")
{
AllowUnsupportedFunctions = true,
IncludeInternalFunctions = true,
FailOnUnknownExtension = false,
Compatibility = ConnectorCompatibility.PowerAppsCompatibility
};
string[] rootedFolders = folders.Select(f => Path.Combine(srcFolder, f)).ToArray();
// Step 1: Identify the list of swagger files to consider
// The output of LocateSwaggerFilesWithDocuments is a dictionary where
// - Key is the connector display name
// - Value is a (folder, location, document) tuple where folder is the source folder at the origin of the swagger identification, location is the exact swagger file location, document is the corresponding OpenApiDocument
// LocateSwaggerFiles could also be used here and would only return a Dictionary<displayName, location> (no source folder or document)
Dictionary<string, (string folder, string location, OpenApiDocument document)> swaggerFiles = SwaggerFileIdentification.LocateSwaggerFilesWithDocuments(folders, pattern, swaggerLocationSettings);
Dictionary<string, (string folder, string location, OpenApiDocument document)> swaggerFiles = SwaggerFileIdentification.LocateSwaggerFilesWithDocuments(rootedFolders, pattern, swaggerLocationSettings);
_output.WriteLine($"Number of connectors found: {swaggerFiles.Count()}");
foreach (KeyValuePair<string, (string folder, string location, OpenApiDocument document)> connector in swaggerFiles)
@ -646,6 +652,8 @@ namespace Microsoft.PowerFx.Connectors.Tests
}
func.IsBehavior = connectorFunction.IsBehavior;
func.IsSupported = connectorFunction.IsSupported;
func.NotSupportedReason = connectorFunction.NotSupportedReason;
func.IsDeprecated = connectorFunction.IsDeprecated;
func.IsInternal = connectorFunction.IsInternal;
func.IsPageable = connectorFunction.IsPageable;

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

@ -11,13 +11,22 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers;
using Microsoft.OpenApi.Validations;
using Microsoft.PowerFx.Types;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.PowerFx.Connectors.Tests
{
public class LivePublicSwaggerTests
{
private readonly ITestOutputHelper _output;
public LivePublicSwaggerTests(ITestOutputHelper output)
{
_output = output;
}
[Fact(Skip = "These APIs are rate limited and HTTP error 429 is possible")]
public async Task RealTest()
{
@ -30,7 +39,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
// "https://api.apis.guru/v2/specs/weatherbit.io/2.0.0/swagger.json"
// "https://www.weatherbit.io/static/swagger.json"
OpenApiDocument doc = await ReadSwaggerFromUrl(swaggerUrl).ConfigureAwait(false);
OpenApiDocument doc = await ReadSwaggerFromUrl(swaggerUrl, _output).ConfigureAwait(false);
// No BaseAdress specified, we'll use the 1st HTTPS one found in the swagger file
using var client = new HttpClient(); // public auth
@ -72,13 +81,13 @@ namespace Microsoft.PowerFx.Connectors.Tests
// Note that these APIs are rate limited and HTTP error 429 is possible
var swaggerUrl = "https://api.apis.guru/v2/specs/math.tools/1.5/openapi.json";
OpenApiDocument doc = await ReadSwaggerFromUrl(swaggerUrl).ConfigureAwait(false);
OpenApiDocument doc = await ReadSwaggerFromUrl(swaggerUrl, _output).ConfigureAwait(false);
// Here we specify the BaseAddress
using var client = new HttpClient() { BaseAddress = new Uri("https://api.math.tools") };
// FailOnUnknownExtension in connectorsettings is set to false (default) as this swagger uses some extensions we don't honnor like x-apisguru-categories, x-origin, x-providerName
var funcs = config.AddActionConnector(new ConnectorSettings("Math"), doc);
var funcs = config.AddActionConnector(new ConnectorSettings("Math"), doc);
var engine = new RecalcEngine(config);
var expr = "Math.numbersbasebinary(632506623)";
@ -129,7 +138,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
// https://date.nager.at/
var swaggerUrl = "https://date.nager.at/swagger/v3/swagger.json";
OpenApiDocument doc = await ReadSwaggerFromUrl(swaggerUrl).ConfigureAwait(false);
OpenApiDocument doc = await ReadSwaggerFromUrl(swaggerUrl, _output).ConfigureAwait(false);
using var client = new HttpClient() { BaseAddress = new Uri("https://date.nager.at") };
var funcs = config.AddActionConnector("Holiday", doc);
@ -143,7 +152,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
Assert.True(ok, string.Join(", ", check.Errors.Select(er => er.Message)));
var runtimeConfig = new RuntimeConfig().AddRuntimeContext(new TestConnectorRuntimeContext("Holiday", client));
FormulaValue result = await engine.EvalAsync(expr, CancellationToken.None, runtimeConfig: runtimeConfig).ConfigureAwait(false);
FormulaValue result = await engine.EvalAsync(expr, CancellationToken.None, runtimeConfig: runtimeConfig).ConfigureAwait(false);
if (result is ErrorValue ev)
{
@ -173,8 +182,8 @@ namespace Microsoft.PowerFx.Connectors.Tests
public async Task RealTest4()
{
var config = new PowerFxConfig();
OpenApiDocument docXkcd = await ReadSwaggerFromUrl(@"https://api.apis.guru/v2/specs/xkcd.com/1.0.0/openapi.json").ConfigureAwait(false);
OpenApiDocument docWorldTime = await ReadSwaggerFromUrl(@"https://api.apis.guru/v2/specs/worldtimeapi.org/20210108/openapi.json").ConfigureAwait(false);
OpenApiDocument docXkcd = await ReadSwaggerFromUrl(@"https://api.apis.guru/v2/specs/xkcd.com/1.0.0/openapi.json", _output).ConfigureAwait(false);
OpenApiDocument docWorldTime = await ReadSwaggerFromUrl(@"https://api.apis.guru/v2/specs/worldtimeapi.org/20210108/openapi.json", _output).ConfigureAwait(false);
using var clientXkcd = new HttpClient() { BaseAddress = new Uri(@"http://xkcd.com/") };
using var clientWorldTime = new HttpClient() { BaseAddress = new Uri(@"http://worldtimeapi.org/api/") };
@ -182,7 +191,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
var funcsWorldTime = config.AddActionConnector(new ConnectorSettings("WorldTime"), docWorldTime);
var engine = new RecalcEngine(config);
var runtimeConfig = new RuntimeConfig().AddRuntimeContext(new TestConnectorRuntimeContext("Xkcd", clientXkcd).Add("WorldTime", clientWorldTime));
var runtimeConfig = new RuntimeConfig().AddRuntimeContext(new TestConnectorRuntimeContext("Xkcd", clientXkcd).Add("WorldTime", clientWorldTime));
FormulaValue fv1 = await engine.EvalAsync(@"Xkcd.comicIdinfo0json(1).transcript", CancellationToken.None, runtimeConfig: runtimeConfig).ConfigureAwait(false);
string transcript = ((StringValue)fv1).Value.Replace("\n", "\r\n");
@ -201,7 +210,7 @@ Boy: I wonder where I'll float next?
public async Task FailOnUnknownExtensionTest()
{
var config = new PowerFxConfig();
OpenApiDocument docXkcd = await ReadSwaggerFromUrl(@"https://api.apis.guru/v2/specs/xkcd.com/1.0.0/openapi.json").ConfigureAwait(false);
OpenApiDocument docXkcd = await ReadSwaggerFromUrl(@"https://api.apis.guru/v2/specs/xkcd.com/1.0.0/openapi.json", _output).ConfigureAwait(false);
IReadOnlyList<ConnectorFunction> funcsXkcd = config.AddActionConnector(new ConnectorSettings("Xkcd") { FailOnUnknownExtension = true }, docXkcd);
@ -217,22 +226,31 @@ Boy: I wonder where I'll float next?
}
// Get a swagger file from the embedded resources.
private static async Task<OpenApiDocument> ReadSwaggerFromUrl(string url)
private static async Task<OpenApiDocument> ReadSwaggerFromUrl(string url, ITestOutputHelper output)
{
using var http = new HttpClient();
using (var stream = await http.GetStreamAsync(new Uri(url)).ConfigureAwait(false))
using HttpClient http = new HttpClient();
using Stream stream = await http.GetStreamAsync(new Uri(url)).ConfigureAwait(false);
ReadResult rr = await new OpenApiStreamReader().ReadAsync(stream, CancellationToken.None).ConfigureAwait(false);
OpenApiDiagnostic diag = rr.OpenApiDiagnostic;
OpenApiDocument doc = rr.OpenApiDocument;
if (diag != null && diag.Errors.Count > 0)
{
ReadResult rr = await new OpenApiStreamReader().ReadAsync(stream, CancellationToken.None).ConfigureAwait(false);
OpenApiDiagnostic diag = rr.OpenApiDiagnostic;
OpenApiDocument doc = rr.OpenApiDocument;
if ((doc == null || doc.Paths == null || doc.Paths.Count == 0) && diag != null && diag.Errors.Count > 0)
foreach (OpenApiError error in diag.Errors)
{
throw new InvalidDataException($"Unable to parse Swagger file: {string.Join(", ", diag.Errors.Select(err => err.Message))}");
if (error is OpenApiValidatorError vError)
{
output.WriteLine($"[OpenApi Error] {vError.RuleName} {vError.Pointer} {vError.Message}");
}
else
{
// Could be OpenApiError or OpenApiReferenceError
output.WriteLine($"[OpenApi Error] {error.Pointer} {error.Message}");
}
}
return doc;
}
return doc;
}
}
}

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

@ -20,7 +20,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Fact]
public void ConnectorLogger_Test1()
{
{
ConsoleLogger logger = new ConsoleLogger(_output);
IReadOnlyList<ConnectorFunction> functions = (null as PowerFxConfig).AddActionConnector(null as string, null, logger);
@ -51,7 +51,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
PowerFxConfig config = new PowerFxConfig(Features.PowerFxV1);
IReadOnlyList<ConnectorFunction> functions = config.AddActionConnector(null as ConnectorSettings, null, logger);
Assert.Empty(functions);
Assert.Empty(functions);
Assert.Equal(
@"[INFO ] Entering in ConfigExtensions.AddActionConnector, with ConnectorSettings <null>|" +
@"[ERROR] connectorSettings is null|" +
@ -69,7 +69,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
Assert.Equal(
@"[INFO ] Entering in ConfigExtensions.AddActionConnector, with ConnectorSettings Namespace <Namespace is null>, MaxRows 1000, FailOnUnknownExtension False, AllowUnsupportedFunctions False, IncludeInternalFunctions False, |" +
@"[ERROR] connectorSettings.Namespace is null|" +
@"[INFO ] Exiting ConfigExtensions.AddActionConnector, returning 0 functions", logger.GetLogs());
@"[INFO ] Exiting ConfigExtensions.AddActionConnector, returning 0 functions", logger.GetLogs());
}
[Fact]
@ -83,7 +83,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
Assert.Equal(
@"[INFO ] Entering in ConfigExtensions.AddActionConnector, with ConnectorSettings Namespace Test, MaxRows 1000, FailOnUnknownExtension False, AllowUnsupportedFunctions False, IncludeInternalFunctions False, |" +
@"[ERROR] openApiDocument is null|" +
@"[INFO ] Exiting ConfigExtensions.AddActionConnector, returning 0 functions", logger.GetLogs());
@"[INFO ] Exiting ConfigExtensions.AddActionConnector, returning 0 functions", logger.GetLogs());
}
[Fact]
@ -97,7 +97,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
Assert.Equal(
@"[INFO ] Entering in ConfigExtensions.AddActionConnector, with ConnectorSettings Namespace Test, MaxRows 1000, FailOnUnknownExtension False, AllowUnsupportedFunctions False, IncludeInternalFunctions False, |" +
@"[ERROR] OpenApiDocument is invalid, it has no Path|" +
@"[INFO ] Exiting ConfigExtensions.AddActionConnector, returning 0 functions", logger.GetLogs());
@"[INFO ] Exiting ConfigExtensions.AddActionConnector, returning 0 functions", logger.GetLogs());
}
[Fact]
@ -111,15 +111,15 @@ namespace Microsoft.PowerFx.Connectors.Tests
Assert.Equal(
@"[INFO ] Entering in ConfigExtensions.AddActionConnector, with ConnectorSettings Namespace , MaxRows 1000, FailOnUnknownExtension False, AllowUnsupportedFunctions False, IncludeInternalFunctions False, |" +
@"[ERROR] connectorSettings.Namespace is not a valid DName|" +
@"[INFO ] Exiting ConfigExtensions.AddActionConnector, returning 0 functions", logger.GetLogs());
@"[INFO ] Exiting ConfigExtensions.AddActionConnector, returning 0 functions", logger.GetLogs());
}
[Fact]
public void ConnectorLogger_Test8()
{
ConsoleLogger logger = new ConsoleLogger(_output);
PowerFxConfig config = new PowerFxConfig(Features.PowerFxV1);
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SQL Server.json");
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SQL Server.json", _output);
IReadOnlyList<ConnectorFunction> functions = config.AddActionConnector(new ConnectorSettings("SQL"), testConnector._apiDocument, logger);
Assert.NotEmpty(functions);

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

@ -29,7 +29,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Fact]
public void ACSL_GetFunctionNames()
{
OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Azure Cognitive Service for Language.json");
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);
@ -51,7 +51,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Fact]
public void ACSL_GetFunctionNames22()
{
OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Azure Cognitive Service for Language v2.2.json");
OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Azure Cognitive Service for Language v2.2.json", _output);
List<ConnectorFunction> functions = OpenApiParser.GetFunctions("ACSL", doc, new ConsoleLogger(_output)).OrderBy(cf => cf.Name).ToList();
ConnectorFunction detectSentimentV3 = functions.First(cf => cf.Name == "DetectSentimentV3");
@ -62,12 +62,12 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Fact]
public void ACSL_Load()
{
OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Azure Cognitive Service for Language.json");
OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Azure Cognitive Service for Language.json", _output);
(List<ConnectorFunction> connectorFunctions, List<ConnectorTexlFunction> 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");
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}")));
@ -75,7 +75,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
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");
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);
}
@ -83,7 +83,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Fact]
public void SF_TextCsv()
{
OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\SalesForce.json");
OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\SalesForce.json", _output);
(List<ConnectorFunction> connectorFunctions, List<ConnectorTexlFunction> texlFunctions) = OpenApiParser.ParseInternal(new ConnectorSettings("SF") { Compatibility = ConnectorCompatibility.SwaggerCompatibility }, doc, new ConsoleLogger(_output));
// function returns text/csv
@ -99,7 +99,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Fact]
public void ACSL_GetFunctionParameters_PowerAppsCompatibility()
{
OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Azure Cognitive Service for Language.json");
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);
@ -212,7 +212,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Fact]
public void ACSL_GetFunctionParameters_SwaggerCompatibility()
{
OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Azure Cognitive Service for Language.json");
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);
@ -221,11 +221,11 @@ namespace Microsoft.PowerFx.Connectors.Tests
RecordType analysisInputRecordType = Extensions.MakeRecordType(
("conversationItem", Extensions.MakeRecordType(
("language", FormulaType.String),
("language", FormulaType.String),
("text", FormulaType.String))));
RecordType parametersRecordType = Extensions.MakeRecordType(
("deploymentName", FormulaType.String),
("projectName", FormulaType.String),
("deploymentName", FormulaType.String),
("projectName", FormulaType.String),
("verbose", FormulaType.Boolean));
Assert.Equal(2, function.RequiredParameters.Length);
@ -320,11 +320,11 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Fact]
public async Task ACSL_InvokeFunction()
{
using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language.json");
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);
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());
@ -351,7 +351,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
client.Dispose();
testConnector.Dispose();
using var testConnector2 = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language.json");
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)
@ -407,7 +407,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Fact]
public async Task AzureOpenAiGetFunctions()
{
using var testConnector = new LoggingTestServer(@"Swagger\Azure Open AI.json");
using var testConnector = new LoggingTestServer(@"Swagger\Azure Open AI.json", _output);
OpenApiDocument apiDoc = testConnector._apiDocument;
PowerFxConfig pfxConfig = new PowerFxConfig(Features.PowerFxV1);
@ -426,7 +426,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Fact]
public async Task ACSL_InvokeFunction_v21()
{
using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language v2.1.json");
using var testConnector = new LoggingTestServer(@"Swagger\Azure Cognitive Service for Language v2.1.json", _output);
OpenApiDocument apiDoc = testConnector._apiDocument;
ConsoleLogger logger = new ConsoleLogger(_output);
@ -636,7 +636,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Fact]
public void LQA_Load()
{
OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Language - Question Answering.json");
OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\Language - Question Answering.json", _output);
(List<ConnectorFunction> connectorFunctions, List<ConnectorTexlFunction> texlFunctions) = OpenApiParser.ParseInternal(new ConnectorSettings("LQA"), doc, new ConsoleLogger(_output));
Assert.Contains(texlFunctions, func => func.Namespace.Name.Value == "LQA" && func.Name == "GetAnswersFromText");
}
@ -644,7 +644,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Fact]
public void SQL_Load()
{
OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\SQL Server.json");
OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\SQL Server.json", _output);
(List<ConnectorFunction> connectorFunctions, List<ConnectorTexlFunction> texlFunctions) = OpenApiParser.ParseInternal(new ConnectorSettings("SQL") { IncludeInternalFunctions = true }, doc, new ConsoleLogger(_output));
Assert.Contains(texlFunctions, func => func.Namespace.Name.Value == "SQL" && func.Name == "GetProcedureV2");
}
@ -652,7 +652,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Fact]
public void Dataverse_Sample()
{
OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\DataverseSample.json");
OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\DataverseSample.json", _output);
ConnectorFunction[] functions = OpenApiParser.GetFunctions("DV", doc, new ConsoleLogger(_output)).ToArray();
Assert.NotNull(functions);
@ -704,7 +704,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Fact]
public void VisibilityTest()
{
OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\AzureBlobStorage.json");
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");
@ -742,7 +742,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
public void DynamicReturnValueTest()
{
using HttpClient httpClient = new ();
OpenApiDocument doc = Helpers.ReadSwagger(@"Swagger\SQL Server.json");
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");
@ -797,7 +797,7 @@ namespace Microsoft.PowerFx.Connectors.Tests
[Fact]
public async Task DirectIntellisenseTest()
{
using var testConnector = new LoggingTestServer(@"Swagger\SQL Server.json");
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" };
@ -909,7 +909,7 @@ POST https://tip1002-002.azure-apihub.net/invoke
[Fact]
public async Task DataverseTest()
{
using var testConnector = new LoggingTestServer(@"Swagger\Dataverse.json");
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" };
@ -986,7 +986,7 @@ POST https://tip1-shared.azure-apim.net/invoke
public async Task DataverseTest2(string swaggerFile)
{
PowerFxConfig powerFxConfig = new PowerFxConfig();
OpenApiDocument doc = Helpers.ReadSwagger(swaggerFile);
OpenApiDocument doc = Helpers.ReadSwagger(swaggerFile, _output);
OpenApiParser.GetFunctions("namespace", doc); // missing logger
powerFxConfig.AddActionConnector("namespace", doc);
@ -995,18 +995,18 @@ POST https://tip1-shared.azure-apim.net/invoke
[Fact]
public async Task CardsForPowerApps_Invoke()
{
using var testConnector = new LoggingTestServer(@"Swagger\CardsForPowerApps.json");
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
},
new ConnectorSettings("DV")
{
Compatibility = ConnectorCompatibility.SwaggerCompatibility,
ReturnUnknownRecordFieldsAsUntypedObjects = true
},
testConnector._apiDocument).ToArray();
ConnectorFunction createCardInstance = functions.First(f => f.Name == "CreateCardInstance");
@ -1044,7 +1044,7 @@ POST https://tip1-shared.azure-apim.net/invoke
[Fact]
public async Task CardsForPowerApps_Suggestion()
{
using var testConnector = new LoggingTestServer(@"Swagger\CardsForPowerApps.json");
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" };
@ -1071,7 +1071,7 @@ POST https://tip1-shared.azure-apim.net/invoke
[Fact]
public async Task Teams_GetMessageDetails_WithComplexParameterReference()
{
using var testConnector = new LoggingTestServer(@"Swagger\Teams.json");
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" };
@ -1092,7 +1092,7 @@ POST https://tip1-shared.azure-apim.net/invoke
CancellationToken.None).ConfigureAwait(false);
var bodyConnectorType = parameters.ParametersWithSuggestions[2].ConnectorType;
ConnectorParameterWithSuggestions suggestions = parameters.ParametersWithSuggestions[2];
testConnector.SetResponseFromFile(@"Responses\Teams_GetMessageDetails_GetSuggestionsForChannel.json");

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

@ -44,7 +44,7 @@ namespace Microsoft.PowerFx.Tests
[InlineData(false)]
public async Task MSNWeatherConnector_CurrentWeather(bool useSwaggerParameter)
{
using var testConnector = new LoggingTestServer(@"Swagger\MSNWeather.json");
using var testConnector = new LoggingTestServer(@"Swagger\MSNWeather.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();
@ -111,13 +111,13 @@ namespace Microsoft.PowerFx.Tests
[InlineData(100, null)] // Continue
[InlineData(200, null)] // Ok
[InlineData(202, null)] // Accepted
[InlineData(305, "Use Proxy")]
[InlineData(411, "Length Required")]
[InlineData(500, "Internal Server Error")]
[InlineData(502, "Bad Gateway")]
[InlineData(305, "Use Proxy")]
[InlineData(411, "Length Required")]
[InlineData(500, "Internal Server Error")]
[InlineData(502, "Bad Gateway")]
public async Task Connector_GenerateErrors(int statusCode, string reasonPhrase)
{
using var testConnector = new LoggingTestServer(@"Swagger\TestConnector12.json");
using var testConnector = new LoggingTestServer(@"Swagger\TestConnector12.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();
@ -209,7 +209,7 @@ namespace Microsoft.PowerFx.Tests
[InlineData(false, true)]
public async Task AzureBlobConnector_UploadFile(bool useSwaggerParameter, bool useHttpsPrefix)
{
using var testConnector = new LoggingTestServer(@"Swagger\AzureBlobStorage.json");
using var testConnector = new LoggingTestServer(@"Swagger\AzureBlobStorage.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();
var token = @"eyJ0eX...";
@ -280,7 +280,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task AzureBlobConnector_UseOfDeprecatedFunction()
{
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\AzureBlobStorage.json");
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\AzureBlobStorage.json", _output);
OpenApiDocument apiDoc = testConnector._apiDocument;
PowerFxConfig config = new PowerFxConfig();
string token = @"eyJ0eXAi...";
@ -321,7 +321,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task AzureBlobConnector_Paging()
{
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\AzureBlobStorage.json");
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\AzureBlobStorage.json", _output);
OpenApiDocument apiDoc = testConnector._apiDocument;
PowerFxConfig config = new PowerFxConfig();
string token = @"eyJ0eX...";
@ -350,7 +350,7 @@ namespace Microsoft.PowerFx.Tests
[InlineData("Office365Outlook.FindMeetingTimesV2(")]
public void IntellisenseHelpStringsOptionalParms(string expr)
{
var apiDocOutlook = Helpers.ReadSwagger(@"Swagger\Office_365_Outlook.json");
var apiDocOutlook = Helpers.ReadSwagger(@"Swagger\Office_365_Outlook.json", _output);
var config = new PowerFxConfig();
config.AddActionConnector("Office365Outlook", apiDocOutlook, new ConsoleLogger(_output));
@ -372,7 +372,7 @@ namespace Microsoft.PowerFx.Tests
[InlineData("Behavior(); MSNWeather.CurrentWeather(", false, true)]
public void IntellisenseHelpStrings(string expr, bool withAllowSideEffects, bool expectedBehaviorError)
{
var apiDoc = Helpers.ReadSwagger(@"Swagger\MSNWeather.json");
var apiDoc = Helpers.ReadSwagger(@"Swagger\MSNWeather.json", _output);
var config = new PowerFxConfig();
config.AddActionConnector("MSNWeather", apiDoc, new ConsoleLogger(_output));
@ -447,7 +447,7 @@ namespace Microsoft.PowerFx.Tests
Assert.NotNull(ppcl2);
Assert.Equal("firstrelease-001.azure-apim.net:117", ppcl2.Endpoint);
using var testConnector = new LoggingTestServer(@"Swagger\AzureBlobStorage.json");
using var testConnector = new LoggingTestServer(@"Swagger\AzureBlobStorage.json", _output);
var apiDoc = testConnector._apiDocument;
using var ppcl3 = new PowerPlatformConnectorClient(
@ -460,7 +460,7 @@ namespace Microsoft.PowerFx.Tests
Assert.NotNull(ppcl3);
Assert.Equal("localhost:23340", ppcl3.Endpoint);
using var testConnector2 = new LoggingTestServer(@"Swagger\TestOpenAPI.json");
using var testConnector2 = new LoggingTestServer(@"Swagger\TestOpenAPI.json", _output);
var apiDoc2 = testConnector2._apiDocument;
var ex2 = Assert.Throws<PowerFxConnectorException>(() => new PowerPlatformConnectorClient(
@ -476,7 +476,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task Office365Users_UserProfile_V2()
{
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Users.json");
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Users.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();
@ -507,7 +507,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task Office365Users_MyProfile_V2()
{
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Users.json");
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Users.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();
@ -537,7 +537,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task Office365Users_DirectReports_V2()
{
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Users.json");
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Users.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();
@ -567,7 +567,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task Office365Users_SearchUsers_V2()
{
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Users.json");
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Users.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();
@ -597,7 +597,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task Office365Users_UseDates()
{
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json");
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();
@ -638,7 +638,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task Office365Outlook_GetEmailsV2()
{
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json");
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();
@ -690,7 +690,7 @@ namespace Microsoft.PowerFx.Tests
[InlineData(2, @"Office365Outlook.V4CalendarPostItem(""Calendar"", ""Subject"", DateTime(2023, 6, 2, 11, 00, 00), DateTime(2023, 6, 2, 11, 30, 00), ""(UTC+01:00) Brussels, Copenhagen, Madrid, Paris"")")]
public async Task Office365Outlook_V4CalendarPostItem(int id, string expr)
{
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json");
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();
@ -741,7 +741,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task Office365Outlook_ExportEmailV2()
{
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json");
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();
@ -786,7 +786,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task Office365Outlook_FindMeetingTimesV2()
{
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json");
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();
@ -833,7 +833,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task Office365Outlook_FlagV2()
{
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json");
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();
@ -880,7 +880,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task Office365Outlook_GetMailTipsV2()
{
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json");
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();
@ -927,7 +927,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task Office365Outlook_GetRoomListsV2()
{
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json");
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();
@ -975,7 +975,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task Office365Outlook_Load()
{
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json");
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();
@ -1010,7 +1010,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task Office365Outlook_GetRooms()
{
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json");
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Outlook.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig(Features.PowerFxV1);
@ -1060,7 +1060,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task BingMaps_GetRouteV3()
{
using var testConnector = new LoggingTestServer(@"Swagger\Bing_Maps.json");
using var testConnector = new LoggingTestServer(@"Swagger\Bing_Maps.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();
@ -1108,7 +1108,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task SQL_GetStoredProcs()
{
using var testConnector = new LoggingTestServer(@"Swagger\SQL Server.json");
using var testConnector = new LoggingTestServer(@"Swagger\SQL Server.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig(Features.PowerFxV1);
@ -1157,7 +1157,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task SQL_ExecuteStoredProc()
{
using var testConnector = new LoggingTestServer(@"Swagger\SQL Server.json");
using var testConnector = new LoggingTestServer(@"Swagger\SQL Server.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig(Features.PowerFxV1);
@ -1206,7 +1206,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task SQL_ExecuteStoredProc_WithUserAgent()
{
using var testConnector = new LoggingTestServer(@"Swagger\SQL Server.json");
using var testConnector = new LoggingTestServer(@"Swagger\SQL Server.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig(Features.PowerFxV1);
@ -1256,7 +1256,7 @@ namespace Microsoft.PowerFx.Tests
[Fact]
public async Task SharePointOnlineTest()
{
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SharePoint.json");
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SharePoint.json", _output);
OpenApiDocument apiDoc = testConnector._apiDocument;
PowerFxConfig config = new PowerFxConfig();
string token = @"eyJ0eXA...";
@ -1274,14 +1274,14 @@ namespace Microsoft.PowerFx.Tests
Assert.Equal(101 - 51, functions.Count(f => f.IsInternal));
IEnumerable<ConnectorFunction> funcInfos = config.AddActionConnector(
new ConnectorSettings("SP")
{
new ConnectorSettings("SP")
{
// This shouldn't be used with expressions but this is OK here as this is a tabular connector and has no impact for this connector/these functions
Compatibility = ConnectorCompatibility.SwaggerCompatibility,
IncludeInternalFunctions = true,
ReturnUnknownRecordFieldsAsUntypedObjects = true
IncludeInternalFunctions = true,
ReturnUnknownRecordFieldsAsUntypedObjects = true
},
apiDoc,
apiDoc,
new ConsoleLogger(_output));
RecalcEngine engine = new RecalcEngine(config);
RuntimeConfig rc = new RuntimeConfig().AddRuntimeContext(new TestConnectorRuntimeContext("SP", ppClient, console: _output));
@ -1305,7 +1305,7 @@ namespace Microsoft.PowerFx.Tests
Assert.Equal("1e54c4b5-2a59-4a2a-9633-cc611a2ff718", ((StringValue)((TableValue)fv4).Rows.Skip(1).First().Value.GetField("Name")).Value);
testConnector.SetResponseFromFile(@"Responses\SPO_Response5.json");
FormulaValue fv5 = await engine.EvalAsync($@"SP.GetItems(""{dataset}"", ""{table}"", {{'$top': 4}})", CancellationToken.None, runtimeConfig: rc).ConfigureAwait(false);
FormulaValue fv5 = await engine.EvalAsync($@"SP.GetItems(""{dataset}"", ""{table}"", {{'$top': 4}})", CancellationToken.None, runtimeConfig: rc).ConfigureAwait(false);
Assert.Equal("Shared Documents/Document.docx", ((UntypedObjectValue)((RecordValue)((TableValue)((RecordValue)fv5).GetField("value")).Rows.First().Value).GetField("{FullPath}")).Impl.GetString());
string version = PowerPlatformConnectorClient.Version;
@ -1367,7 +1367,7 @@ POST https://tip1-shared-002.azure-apim.net/invoke
[Fact]
public async Task ExcelOnlineTest()
{
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\ExcelOnlineBusiness.swagger.json");
using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\ExcelOnlineBusiness.swagger.json", _output);
OpenApiDocument apiDoc = testConnector._apiDocument;
PowerFxConfig config = new PowerFxConfig();
string token = @"eyJ0eXAiOiJ...";
@ -1375,12 +1375,12 @@ POST https://tip1-shared-002.azure-apim.net/invoke
using HttpClient httpClient = new HttpClient(testConnector);
using PowerPlatformConnectorClient ppClient = new PowerPlatformConnectorClient("https://tip1-shared-002.azure-apim.net", "36897fc0-0c0c-eee5-ac94-e12765496c20" /* env */, "b20e87387f9149e884bdf0b0c87a67e8" /* connId */, () => $"{token}", httpClient) { SessionId = "547d471f-c04c-4c4a-b3af-337ab0637a0d" };
ConnectorSettings connectorSettings = new ConnectorSettings("exob")
ConnectorSettings connectorSettings = new ConnectorSettings("exob")
{
// This shouldn't be used with expressions but this is OK here as this is a tabular connector
Compatibility = ConnectorCompatibility.SwaggerCompatibility,
AllowUnsupportedFunctions = true,
IncludeInternalFunctions = true,
AllowUnsupportedFunctions = true,
IncludeInternalFunctions = true,
ReturnUnknownRecordFieldsAsUntypedObjects = true
};
List<ConnectorFunction> functions = OpenApiParser.GetFunctions(connectorSettings, apiDoc).OrderBy(f => f.Name).ToList();
@ -1430,7 +1430,7 @@ POST https://tip1-shared-002.azure-apim.net/invoke
[Fact]
public async Task DataverseTest_WithComplexMapping()
{
using var testConnector = new LoggingTestServer(@"Swagger\Dataverse.json");
using var testConnector = new LoggingTestServer(@"Swagger\Dataverse.json", _output);
using var httpClient = new HttpClient(testConnector);
using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("https://tip1002-002.azure-apihub.net", "b29c41cf-173b-e469-830b-4f00163d296b" /* environment Id */, "82728ddb6bfa461ea3e50e17da8ab164" /* connectionId */, () => "eyJ0eXAiOiJKV1QiLCJ...", httpClient) { SessionId = "a41bd03b-6c3c-4509-a844-e8c51b61f878" };
@ -1478,7 +1478,7 @@ POST https://tip1-shared-002.azure-apim.net/invoke
[InlineData(ConnectorCompatibility.SwaggerCompatibility, "Office365Users.SearchUserV2(", "SearchUserV2({ searchTerm:String,top:Decimal,isSearchTermRequired:Boolean })")]
public async Task ConnectorCompatibilityIntellisenseTest(ConnectorCompatibility compact, string expression, string expected)
{
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Users.json");
using var testConnector = new LoggingTestServer(@"Swagger\Office_365_Users.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig();

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

@ -67,13 +67,13 @@ namespace Microsoft.PowerFx.TexlFunctionExporter
int distinctDocCount = docs.Select(t => t.document.HashCode).Distinct().Count();
// if single document or all documents with same hashcode
if (distinctDocCount == 1)
if (distinctDocCount == 1)
{
list2.Add(swagger.Key, docs.First());
}
// if 2 docs have same description + with one in aapt & one in ppc folders, take the one in 1st folder which aapt version (internal version)
else if (distinctDocCount == 2)
else if (distinctDocCount == 2)
{
var first = docs.First();
var second = docs.Last();
@ -117,6 +117,12 @@ namespace Microsoft.PowerFx.TexlFunctionExporter
try
{
OpenApiDocument doc = ReadSwagger(swaggerFile);
if (doc == null || doc.Info == null)
{
return;
}
string title = doc.Info.Title;
var item = (folder, swaggerFile, doc);
@ -136,17 +142,8 @@ namespace Microsoft.PowerFx.TexlFunctionExporter
public static OpenApiDocument ReadSwagger(string name)
{
using (var stream = File.OpenRead(name))
{
var doc = new OpenApiStreamReader().Read(stream, out OpenApiDiagnostic diag);
if ((doc == null || doc.Paths == null || doc.Paths.Count == 0) && diag != null && diag.Errors.Count > 0)
{
throw new InvalidDataException($"Unable to parse Swagger file: {string.Join(", ", diag.Errors.Select(err => err.Message))}");
}
return doc;
}
using FileStream stream = File.OpenRead(name);
return new OpenApiStreamReader().Read(stream, out OpenApiDiagnostic diag);
}
}