Merge branch 'main' into lucgen/zd2

This commit is contained in:
Luc Genetier 2024-11-15 14:46:42 +01:00 коммит произвёл GitHub
Родитель ba6ba14ae7 5893a462cb
Коммит d0719bd721
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
34 изменённых файлов: 587 добавлений и 361 удалений

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

@ -30,6 +30,12 @@
`IsType(UntypedObject, Type)`\
`AsType(UntypedObject, Type)`
## Updated function behaviors:
- TimeValue function (https://github.com/microsoft/Power-Fx/pull/2731)
- Support for am/pm designators: `TimeValue("6:00pm")` now works (used to return an error)
- Better validation: `TimeValue("1")` would return a time value (equivalent to `Time(0,0,0)`), now returns an error
- Better support for wrapping times around the 24-hour mark: `TimeValue("27:00:00")` now returns the same as `Time(3,0,0)`, consistent with Excel's behavior.
## Other:
- Untyped object
- Read a field from an untyped object by index (https://github.com/microsoft/Power-Fx/pull/2555):
@ -39,4 +45,4 @@
- Setting an untyped object via deep mutation is now supported (https://github.com/microsoft/Power-Fx/pull/2548):
`Set(untypedObject.Field, 99)`
`Set(Index(untypedObject, 1).Field, 99) // Reference field by name`
`Set(Index(Index(untypedObject, 1), 1), 99) // Reference field by index`
`Set(Index(Index(untypedObject, 1), 1), 99) // Reference field by index`

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

@ -27,6 +27,8 @@ using Microsoft.PowerFx.Functions;
using Microsoft.PowerFx.Intellisense;
using Microsoft.PowerFx.Types;
using static Microsoft.PowerFx.Connectors.ConnectorHelperFunctions;
#pragma warning disable SA1117
namespace Microsoft.PowerFx.Connectors
{
@ -292,15 +294,16 @@ namespace Microsoft.PowerFx.Connectors
// Those properties are only used by HttpFunctionInvoker
internal ConnectorParameterInternals _internals = null;
private readonly ConnectorLogger _configurationLogger = null;
private readonly ConnectorLogger _configurationLogger = null;
internal ConnectorFunction(OpenApiOperation openApiOperation, bool isSupported, string notSupportedReason, string name, string operationPath, HttpMethod httpMethod, ConnectorSettings connectorSettings, List<ConnectorFunction> functionList, ConnectorLogger configurationLogger, IReadOnlyDictionary<string, FormulaValue> globalValues)
internal ConnectorFunction(OpenApiOperation openApiOperation, bool isSupported, string notSupportedReason, string name, string operationPath, HttpMethod httpMethod, ConnectorSettings connectorSettings, List<ConnectorFunction> functionList,
ConnectorLogger configurationLogger, IReadOnlyDictionary<string, FormulaValue> globalValues)
{
Operation = openApiOperation;
Name = name;
OperationPath = operationPath;
HttpMethod = httpMethod;
ConnectorSettings = connectorSettings;
ConnectorSettings = connectorSettings;
GlobalContext = new ConnectorGlobalContext(functionList ?? throw new ArgumentNullException(nameof(functionList)), globalValues);
_configurationLogger = configurationLogger;
@ -1007,21 +1010,24 @@ namespace Microsoft.PowerFx.Connectors
// Only called by ConnectorTable.GetSchema
// Returns a FormulaType with AssociatedDataSources set (done in AddTabularDataSource)
internal static ConnectorType GetCdpTableType(ICdpTableResolver tableResolver, string connectorName, string valuePath, StringValue stringValue, List<SqlRelationship> sqlRelationships, ConnectorCompatibility compatibility, string datasetName, out string name, out string displayName, out TableDelegationInfo delegationInfo)
internal static ConnectorType GetCdpTableType(ICdpTableResolver tableResolver, string connectorName, string tableName, string valuePath, StringValue stringValue, List<SqlRelationship> sqlRelationships, ConnectorCompatibility compatibility, string datasetName,
out string name, out string displayName, out TableDelegationInfo delegationInfo, out IEnumerable<OptionSet> optionSets)
{
// There are some errors when parsing this Json payload but that's not a problem here as we only need x-ms-capabilities parsing to work
OpenApiReaderSettings oars = new OpenApiReaderSettings() { RuleSet = DefaultValidationRuleSet };
ISwaggerSchema tableSchema = SwaggerSchema.New(new OpenApiStringReader(oars).ReadFragment<OpenApiSchema>(stringValue.Value, OpenApi.OpenApiSpecVersion.OpenApi2_0, out OpenApiDiagnostic _));
ServiceCapabilities serviceCapabilities = tableSchema.GetTableCapabilities();
ConnectorPermission tablePermission = tableSchema.GetPermission();
ConnectorPermission tablePermission = tableSchema.GetPermission();
JsonElement jsonElement = ExtractFromJson(stringValue, valuePath, out name, out displayName);
bool isTableReadOnly = tablePermission == ConnectorPermission.PermissionReadOnly;
IList<ReferencedEntity> referencedEntities = GetReferenceEntities(connectorName, stringValue);
ConnectorType connectorType = new ConnectorType(jsonElement, compatibility, sqlRelationships, referencedEntities, datasetName, name, connectorName, tableResolver, serviceCapabilities, isTableReadOnly);
SymbolTable symbolTable = new SymbolTable();
ConnectorType connectorType = new ConnectorType(jsonElement, tableName, symbolTable, compatibility, sqlRelationships, referencedEntities, datasetName, name, connectorName, tableResolver, serviceCapabilities, isTableReadOnly);
delegationInfo = ((DataSourceInfo)connectorType.FormulaType._type.AssociatedDataSources.First()).DelegationInfo;
optionSets = symbolTable.OptionSets.Select(kvp => kvp.Value);
return connectorType;
}
@ -1400,7 +1406,7 @@ namespace Microsoft.PowerFx.Connectors
{
// Ex: Api-Version
hiddenRequired = true;
}
}
}
else if (ConnectorSettings.Compatibility.ExcludeInternals())
{
@ -1413,8 +1419,8 @@ namespace Microsoft.PowerFx.Connectors
return null;
}
ConnectorParameter connectorParameter = errorsAndWarnings.AggregateErrorsAndWarnings(new ConnectorParameter(parameter, ConnectorSettings.Compatibility));
ConnectorParameter connectorParameter = errorsAndWarnings.AggregateErrorsAndWarnings(new ConnectorParameter(parameter, ConnectorSettings.Compatibility));
if (connectorParameter.HiddenRecordType != null)
{
errorsAndWarnings.AddError("[Internal error] Unexpected HiddenRecordType non-null value");
@ -1482,11 +1488,11 @@ namespace Microsoft.PowerFx.Connectors
}
OpenApiParameter bodyParameter = new OpenApiParameter() { Name = bodyPropertyName, Schema = bodyPropertySchema, Description = requestBody.Description, Required = bodyPropertyRequired, Extensions = bodyPropertySchema.Extensions };
ConnectorParameter bodyConnectorParameter2 = errorsAndWarnings.AggregateErrorsAndWarnings(new ConnectorParameter(bodyParameter, requestBody, ConnectorSettings.Compatibility));
ConnectorParameter bodyConnectorParameter2 = errorsAndWarnings.AggregateErrorsAndWarnings(new ConnectorParameter(bodyParameter, requestBody, ConnectorSettings.Compatibility));
openApiBodyParameters.Add(bodyConnectorParameter2, OpenApiExtensions.TryGetOpenApiValue(bodyConnectorParameter2.Schema.Default, null, out FormulaValue defaultValue, errorsAndWarnings) ? defaultValue : null);
if (bodyConnectorParameter2.HiddenRecordType != null)
{
{
hiddenRequiredParameters.Add(errorsAndWarnings.AggregateErrorsAndWarnings(new ConnectorParameter(bodyParameter, true, ConnectorSettings.Compatibility)));
}
@ -1506,7 +1512,7 @@ namespace Microsoft.PowerFx.Connectors
}
OpenApiParameter bodyParameter2 = new OpenApiParameter() { Name = bodyName, Schema = bodySchema, Description = requestBody.Description, Required = requestBody.Required, Extensions = bodySchema.Extensions };
ConnectorParameter bodyConnectorParameter3 = errorsAndWarnings.AggregateErrorsAndWarnings(new ConnectorParameter(bodyParameter2, requestBody, ConnectorSettings.Compatibility));
ConnectorParameter bodyConnectorParameter3 = errorsAndWarnings.AggregateErrorsAndWarnings(new ConnectorParameter(bodyParameter2, requestBody, ConnectorSettings.Compatibility));
openApiBodyParameters.Add(bodyConnectorParameter3, OpenApiExtensions.TryGetOpenApiValue(bodyConnectorParameter3.Schema.Default, null, out FormulaValue defaultValue, errorsAndWarnings) ? defaultValue : null);
if (bodyConnectorParameter3.HiddenRecordType != null)
@ -1526,7 +1532,7 @@ namespace Microsoft.PowerFx.Connectors
OpenApiSchema bodyParameterSchema = new OpenApiSchema() { Type = "string" };
OpenApiParameter bodyParameter3 = new OpenApiParameter() { Name = bodyName, Schema = bodyParameterSchema, Description = "Body", Required = requestBody.Required };
ConnectorParameter bodyParameter = errorsAndWarnings.AggregateErrorsAndWarnings(new ConnectorParameter(bodyParameter3, requestBody, ConnectorSettings.Compatibility));
ConnectorParameter bodyParameter = errorsAndWarnings.AggregateErrorsAndWarnings(new ConnectorParameter(bodyParameter3, requestBody, ConnectorSettings.Compatibility));
openApiBodyParameters.Add(bodyParameter, OpenApiExtensions.TryGetOpenApiValue(bodyParameter.Schema.Default, null, out FormulaValue defaultValue, errorsAndWarnings) ? defaultValue : null);
List<ConnectorParameter> parameterList = requestBody.Required ? requiredParameters : optionalParameters;
@ -1569,8 +1575,8 @@ namespace Microsoft.PowerFx.Connectors
_arityMax = _arityMin + (_optionalParameters.Length == 0 ? 0 : 1);
_warnings = new List<ErrorResourceKey>();
_returnType = errorsAndWarnings.AggregateErrorsAndWarnings(Operation.GetConnectorReturnType(ConnectorSettings.Compatibility));
_returnType = errorsAndWarnings.AggregateErrorsAndWarnings(Operation.GetConnectorReturnType(ConnectorSettings.Compatibility));
if (IsDeprecated)
{
_warnings.Add(ConnectorStringResources.WarnDeprecatedFunction);

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

@ -283,6 +283,10 @@ namespace Microsoft.PowerFx.Connectors.Execution
{
await WriteBlobValueAsync(bv).ConfigureAwait(false);
}
else if (fv is OptionSetValue optionSetValue)
{
WriteStringValue(optionSetValue.Option);
}
else
{
throw new PowerFxConnectorException($"Expected StringValue and got {fv?.GetType()?.Name ?? "<null>"} value, for property {propertyName}");

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

@ -10,6 +10,9 @@ using System.Net.Http;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.PowerFx.Core;
using Microsoft.PowerFx.Core.Binding;
using Microsoft.PowerFx.Core.Binding.BindInfo;
using Microsoft.PowerFx.Core.IR;
using Microsoft.PowerFx.Core.Utils;
using Microsoft.PowerFx.Types;
@ -82,45 +85,58 @@ namespace Microsoft.PowerFx.Connectors
}
// Get suggested options values. Returns null if none.
internal static (string[] options, ConnectorErrors errors) GetOptions(this OpenApiParameter openApiParameter)
{
ConnectorErrors errors = new ConnectorErrors();
// x-ms-enum-values is: array of { value :string, displayName:string}.
internal static DisplayNameProvider GetEnumValues(this ISwaggerParameter openApiParameter)
{
// x-ms-enum-values is: array of { value: string, displayName: string}.
if (openApiParameter.Extensions.TryGetValue(XMsEnumValues, out var enumValues))
{
if (enumValues is IList<IOpenApiAny> array)
{
var list = new List<string>(array.Count);
{
SingleSourceDisplayNameProvider displayNameProvider = new SingleSourceDisplayNameProvider();
foreach (var item in array)
{
{
string logical = null;
string display = null;
if (item is IDictionary<string, IOpenApiAny> obj)
{
// has keys, "value", and "displayName"
if (obj.TryGetValue("value", out IOpenApiAny value))
{
if (obj.TryGetValue("value", out IOpenApiAny openApiLogical))
{
if (value is OpenApiString str)
if (openApiLogical is OpenApiString logicalStr)
{
list.Add(str.Value);
continue;
logical = logicalStr.Value;
}
else if (value is OpenApiInteger i)
else if (openApiLogical is OpenApiInteger logicalInt)
{
list.Add(i.Value.ToString(CultureInfo.InvariantCulture));
continue;
logical = logicalInt.Value.ToString(CultureInfo.InvariantCulture);
}
}
if (obj.TryGetValue("displayName", out IOpenApiAny openApiDisplay))
{
if (openApiDisplay is OpenApiString displayStr)
{
display = displayStr.Value;
}
else if (openApiDisplay is OpenApiInteger displayInt)
{
display = displayInt.Value.ToString(CultureInfo.InvariantCulture);
}
}
if (!string.IsNullOrEmpty(logical) && !string.IsNullOrEmpty(display))
{
displayNameProvider = displayNameProvider.AddField(new DName(logical), new DName(display));
}
}
errors.AddError($"Unrecognized {XMsEnumValues} schema ({item.GetType().Name})");
}
return (list.ToArray(), errors);
}
return displayNameProvider.LogicalToDisplayPairs.Any() ? displayNameProvider : null;
}
}
return (null, null);
return null;
}
public static bool IsTrigger(this OpenApiOperation op)
@ -342,11 +358,11 @@ namespace Microsoft.PowerFx.Connectors
internal static string GetVisibility(this ISwaggerExtensions schema) => schema.Extensions.TryGetValue(XMsVisibility, out IOpenApiExtension openApiExt) && openApiExt is OpenApiString openApiStr ? openApiStr.Value : null;
internal static string GetEnumName(this ISwaggerExtensions schema) => schema.Extensions.TryGetValue(XMsEnum, out IOpenApiExtension openApiExt) &&
internal static string GetEnumName(this ISwaggerExtensions schema) => schema.Extensions.TryGetValue(XMsEnum, out IOpenApiExtension openApiExt) &&
openApiExt is SwaggerJsonObject jsonObject &&
jsonObject.TryGetValue("name", out IOpenApiAny enumName) &&
enumName is OpenApiString enumNameStr
? enumNameStr.Value
enumName is OpenApiString enumNameStr
? enumNameStr.Value
: null;
internal static string GetMediaKind(this ISwaggerExtensions schema) => schema.Extensions.TryGetValue(XMsMediaKind, out IOpenApiExtension openApiExt) && openApiExt is OpenApiString openApiStr ? openApiStr.Value : null;
@ -378,11 +394,17 @@ namespace Microsoft.PowerFx.Connectors
internal readonly IList<SqlRelationship> SqlRelationships;
internal Stack<string> Chain = new Stack<string>();
internal int Level = 0;
internal readonly SymbolTable OptionSets;
internal ConnectorTypeGetterSettings(ConnectorCompatibility connectorCompatibility, IList<SqlRelationship> sqlRelationships = null)
private readonly string _tableName;
internal ConnectorTypeGetterSettings(ConnectorCompatibility connectorCompatibility, string tableName, SymbolTable optionSets, IList<SqlRelationship> sqlRelationships = null)
{
Compatibility = connectorCompatibility;
OptionSets = optionSets;
SqlRelationships = sqlRelationships;
_tableName = tableName;
}
internal ConnectorTypeGetterSettings Stack(string identifier)
@ -397,11 +419,33 @@ namespace Microsoft.PowerFx.Connectors
Chain.Pop();
Level--;
}
// by default, optionset names will be 'propertyName (tableName)' in CDP case, where propertyName is replaced by x-ms-enum content, when provided
// in non-CDP case, tableName is null and will only be 'propertyName' (or x-ms-enum content)
internal string GetOptionSetName(string optionSetNameBase)
{
string optionSetName = optionSetNameBase;
if (!string.IsNullOrEmpty(_tableName))
{
optionSetName += $" ({_tableName})";
}
return optionSetName;
}
}
internal static ConnectorType GetConnectorType(this ISwaggerParameter openApiParameter, ConnectorCompatibility compatibility, IList<SqlRelationship> sqlRelationships = null)
{
return openApiParameter.GetConnectorType(new ConnectorTypeGetterSettings(compatibility, sqlRelationships));
return openApiParameter.GetConnectorType(tableName: null, optionSets: null, compatibility, sqlRelationships);
}
internal static ConnectorType GetConnectorType(this ISwaggerParameter openApiParameter, string tableName, SymbolTable optionSets, ConnectorCompatibility compatibility, IList<SqlRelationship> sqlRelationships = null)
{
ConnectorTypeGetterSettings settings = new ConnectorTypeGetterSettings(compatibility, tableName, optionSets, sqlRelationships);
ConnectorType connectorType = openApiParameter.GetConnectorType(settings);
return connectorType;
}
// See https://swagger.io/docs/specification/data-models/data-types/
@ -445,12 +489,25 @@ namespace Microsoft.PowerFx.Connectors
return new ConnectorType(schema, openApiParameter, FormulaType.Blob);
}
// Try getting enum from 'x-ms-enum-values'
DisplayNameProvider optionSetDisplayNameProvider = openApiParameter.GetEnumValues();
if (optionSetDisplayNameProvider != null && (settings.Compatibility.IsCDP() || schema.Format == "enum"))
{
string optionSetName = settings.GetOptionSetName(schema.GetEnumName() ?? openApiParameter.Name);
OptionSet optionSet = new OptionSet(optionSetName, optionSetDisplayNameProvider);
optionSet = settings.OptionSets.TryAddOptionSet(optionSet);
return new ConnectorType(schema, openApiParameter, optionSet.FormulaType);
}
// Try getting enum from 'enum'
if (schema.Enum != null && (settings.Compatibility.IsCDP() || schema.Format == "enum"))
{
if (schema.Enum.All(e => e is OpenApiString))
{
string enumName = schema.GetEnumName() ?? "enum";
OptionSet optionSet = new OptionSet(enumName, schema.Enum.Select(e => new DName((e as OpenApiString).Value)).ToDictionary(k => k, e => e).ToImmutableDictionary());
string optionSetName = settings.GetOptionSetName(schema.GetEnumName() ?? openApiParameter.Name);
OptionSet optionSet = new OptionSet(optionSetName, schema.Enum.Select(e => new DName((e as OpenApiString).Value)).ToDictionary(k => k, e => e).ToImmutableDictionary());
optionSet = settings.OptionSets.TryAddOptionSet(optionSet);
return new ConnectorType(schema, openApiParameter, optionSet.FormulaType);
}
else
@ -478,7 +535,7 @@ namespace Microsoft.PowerFx.Connectors
case null:
case "decimal":
case "currency":
return new ConnectorType(schema, openApiParameter, FormulaType.Decimal);
return new ConnectorType(schema, openApiParameter, FormulaType.Decimal);
default:
return new ConnectorType(error: $"Unsupported type of number: {schema.Format}");
@ -489,7 +546,7 @@ namespace Microsoft.PowerFx.Connectors
return new ConnectorType(schema, openApiParameter, FormulaType.Number);
// Always a boolean (Format not used)
case "boolean":
case "boolean":
return new ConnectorType(schema, openApiParameter, FormulaType.Boolean);
// OpenAPI spec: Format could be <null>, int32, int64
@ -665,6 +722,34 @@ namespace Microsoft.PowerFx.Connectors
}
}
// If an OptionSet doesn't exist, we add it (and return it)
// If an identical OptionSet exists (same name & list of options), we return it
// Otherwise we throw in case of conflict
internal static OptionSet TryAddOptionSet(this SymbolTable symbolTable, OptionSet optionSet)
{
if (optionSet == null)
{
throw new ArgumentNullException("optionSet");
}
string name = optionSet.EntityName;
// No existing symbols with that name
if (!((INameResolver)symbolTable).Lookup(new DName(name), out NameLookupInfo info, NameLookupPreferences.None))
{
symbolTable.AddOptionSet(optionSet);
return optionSet;
}
// Same optionset already present in table
if (info.Kind == BindKind.OptionSet && info.Data is OptionSet existingOptionSet && existingOptionSet.Equals(optionSet))
{
return existingOptionSet;
}
throw new InvalidOperationException($"Optionset name conflict ({name})");
}
internal static RecordType ToRecordType(this List<(string logicalName, string displayName, FormulaType type)> fields)
{
if (fields == null)
@ -733,7 +818,7 @@ namespace Microsoft.PowerFx.Connectors
}
public static FormulaType GetReturnType(this OpenApiOperation openApiOperation, ConnectorCompatibility compatibility)
{
{
ConnectorType connectorType = openApiOperation.GetConnectorReturnType(compatibility);
FormulaType ft = connectorType.HasErrors ? ConnectorType.DefaultType : connectorType?.FormulaType ?? new BlankType();
return ft;
@ -761,7 +846,7 @@ namespace Microsoft.PowerFx.Connectors
if (response == null)
{
// Returns UntypedObject by default, without error
// Returns UntypedObject by default, without error
return new ConnectorType(null);
}

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

@ -21,13 +21,13 @@ namespace Microsoft.PowerFx.Connectors
{
return GetFunctions(@namespace, openApiDocument, null, configurationLogger);
}
public static IEnumerable<ConnectorFunction> GetFunctions(string @namespace, OpenApiDocument openApiDocument, IReadOnlyDictionary<string, FormulaValue> globalValues, ConnectorLogger configurationLogger = null)
{
try
{
configurationLogger?.LogInformation($"Entering in {nameof(OpenApiParser)}.{nameof(GetFunctions)}, with {nameof(ConnectorSettings)} Namespace {@namespace}");
IEnumerable<ConnectorFunction> functions = GetFunctionsInternal(new ConnectorSettings(@namespace), openApiDocument, configurationLogger, globalValues);
configurationLogger?.LogInformation($"Entering in {nameof(OpenApiParser)}.{nameof(GetFunctions)}, with {nameof(ConnectorSettings)} Namespace {@namespace}");
IEnumerable<ConnectorFunction> functions = GetFunctionsInternal(new ConnectorSettings(@namespace), openApiDocument, configurationLogger, globalValues);
configurationLogger?.LogInformation($"Exiting {nameof(OpenApiParser)}.{nameof(GetFunctions)}, with {nameof(ConnectorSettings)} Namespace {@namespace}, returning {functions.Count()} functions");
return functions.Where(f => ShouldIncludeFunction(f));
}
@ -36,19 +36,19 @@ namespace Microsoft.PowerFx.Connectors
configurationLogger?.LogException(ex, $"Exception in {nameof(OpenApiParser)}.{nameof(GetFunctions)}, {nameof(ConnectorSettings)} Namespace {@namespace}, {LogException(ex)}");
throw;
}
}
}
public static IEnumerable<ConnectorFunction> GetFunctions(ConnectorSettings connectorSettings, OpenApiDocument openApiDocument, ConnectorLogger configurationLogger = null)
{
return GetFunctions(connectorSettings, openApiDocument, null, configurationLogger);
}
}
public static IEnumerable<ConnectorFunction> GetFunctions(ConnectorSettings connectorSettings, OpenApiDocument openApiDocument, IReadOnlyDictionary<string, FormulaValue> globalValues, ConnectorLogger configurationLogger = null)
{
try
{
configurationLogger?.LogInformation($"Entering in {nameof(OpenApiParser)}.{nameof(GetFunctions)}, with {nameof(ConnectorSettings)} {LogConnectorSettings(connectorSettings)}");
IEnumerable<ConnectorFunction> functions = GetFunctionsInternal(connectorSettings, openApiDocument, configurationLogger, globalValues);
configurationLogger?.LogInformation($"Entering in {nameof(OpenApiParser)}.{nameof(GetFunctions)}, with {nameof(ConnectorSettings)} {LogConnectorSettings(connectorSettings)}");
IEnumerable<ConnectorFunction> functions = GetFunctionsInternal(connectorSettings, openApiDocument, configurationLogger, globalValues);
configurationLogger?.LogInformation($"Exiting {nameof(OpenApiParser)}.{nameof(GetFunctions)}, with {nameof(ConnectorSettings)} {LogConnectorSettings(connectorSettings)}, returning {functions.Count()} functions");
return functions.Where(f => ShouldIncludeFunction(f, connectorSettings));
}
@ -62,14 +62,14 @@ namespace Microsoft.PowerFx.Connectors
public static IEnumerable<ConnectorFunction> GetTriggers(string @namespace, OpenApiDocument openApiDocument, ConnectorLogger configurationLogger = null)
{
return GetTriggers(@namespace, openApiDocument, null, configurationLogger);
}
}
public static IEnumerable<ConnectorFunction> GetTriggers(string @namespace, OpenApiDocument openApiDocument, IReadOnlyDictionary<string, FormulaValue> globalValues, ConnectorLogger configurationLogger = null)
{
try
{
configurationLogger?.LogInformation($"Entering in {nameof(OpenApiParser)}.{nameof(GetTriggers)}, with {nameof(ConnectorSettings)} Namespace {@namespace}");
IEnumerable<ConnectorFunction> functions = GetFunctionsInternal(new ConnectorSettings(@namespace), openApiDocument, configurationLogger, globalValues, FunctionType.Trigger);
configurationLogger?.LogInformation($"Entering in {nameof(OpenApiParser)}.{nameof(GetTriggers)}, with {nameof(ConnectorSettings)} Namespace {@namespace}");
IEnumerable<ConnectorFunction> functions = GetFunctionsInternal(new ConnectorSettings(@namespace), openApiDocument, configurationLogger, globalValues, FunctionType.Trigger);
configurationLogger?.LogInformation($"Exiting {nameof(OpenApiParser)}.{nameof(GetTriggers)}, with {nameof(ConnectorSettings)} Namespace {@namespace}, returning {functions.Count()} functions");
return functions.Where(f => ShouldIncludeFunction(f));
}
@ -83,14 +83,14 @@ namespace Microsoft.PowerFx.Connectors
public static IEnumerable<ConnectorFunction> GetTriggers(ConnectorSettings connectorSettings, OpenApiDocument openApiDocument, ConnectorLogger configurationLogger = null)
{
return GetTriggers(connectorSettings, openApiDocument, null, configurationLogger);
}
}
public static IEnumerable<ConnectorFunction> GetTriggers(ConnectorSettings connectorSettings, OpenApiDocument openApiDocument, IReadOnlyDictionary<string, FormulaValue> globalValues, ConnectorLogger configurationLogger = null)
{
try
{
configurationLogger?.LogInformation($"Entering in {nameof(OpenApiParser)}.{nameof(GetTriggers)}, with {nameof(ConnectorSettings)} {LogConnectorSettings(connectorSettings)}");
IEnumerable<ConnectorFunction> functions = GetFunctionsInternal(connectorSettings, openApiDocument, configurationLogger, globalValues, FunctionType.Trigger);
configurationLogger?.LogInformation($"Entering in {nameof(OpenApiParser)}.{nameof(GetTriggers)}, with {nameof(ConnectorSettings)} {LogConnectorSettings(connectorSettings)}");
IEnumerable<ConnectorFunction> functions = GetFunctionsInternal(connectorSettings, openApiDocument, configurationLogger, globalValues, FunctionType.Trigger);
configurationLogger?.LogInformation($"Exiting {nameof(OpenApiParser)}.{nameof(GetTriggers)}, with {nameof(ConnectorSettings)} {LogConnectorSettings(connectorSettings)}, returning {functions.Count()} functions");
return functions.Where(f => ShouldIncludeFunction(f, connectorSettings));
}
@ -493,7 +493,7 @@ namespace Microsoft.PowerFx.Connectors
// Parse an OpenApiDocument and return functions.
internal static (List<ConnectorFunction> connectorFunctions, List<ConnectorTexlFunction> texlFunctions) ParseInternal(ConnectorSettings connectorSettings, OpenApiDocument openApiDocument, ConnectorLogger configurationLogger = null, IReadOnlyDictionary<string, FormulaValue> globalValues = null)
{
{
List<ConnectorFunction> cFunctions = GetFunctionsInternal(connectorSettings, openApiDocument, configurationLogger, globalValues).Where(f => ShouldIncludeFunction(f, connectorSettings)).ToList();
List<ConnectorTexlFunction> tFunctions = cFunctions.Select(f => new ConnectorTexlFunction(f)).ToList();

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

@ -46,6 +46,7 @@ namespace Microsoft.PowerFx.Connectors
Location = openApiParameter.In;
}
// Intellisense only
internal ConnectorParameter(ConnectorParameter connectorParameter, ConnectorType connectorType)
: base(connectorParameter, connectorType)
{

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

@ -30,7 +30,7 @@ namespace Microsoft.PowerFx.Connectors
public bool SupportsDynamicIntellisense => ConnectorType.SupportsDynamicIntellisense;
public bool? NotificationUrl => ConnectorType.NotificationUrl;
internal ConnectorSchema(ISwaggerParameter openApiParameter, ISwaggerExtensions bodyExtensions, bool useHiddenTypes, ConnectorCompatibility compatibility)
{
Schema = openApiParameter.Schema;
@ -40,6 +40,7 @@ namespace Microsoft.PowerFx.Connectors
ConnectorExtensions = new ConnectorExtensions(openApiParameter, bodyExtensions);
}
// Intellisense only
internal ConnectorSchema(ConnectorSchema connectorSchema, ConnectorType connectorType)
{
Schema = connectorSchema.Schema;

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

@ -207,15 +207,16 @@ namespace Microsoft.PowerFx.Connectors
{
}
internal ConnectorType(JsonElement schema, ConnectorCompatibility compatibility, IList<SqlRelationship> sqlRelationships, IList<ReferencedEntity> referencedEntities, string datasetName, string name, string connectorName, ICdpTableResolver resolver, ServiceCapabilities serviceCapabilities, bool isTableReadOnly)
: this(SwaggerJsonSchema.New(schema), null, new SwaggerParameter(null, true, SwaggerJsonSchema.New(schema), null).GetConnectorType(compatibility, sqlRelationships))
// Called by ConnectorFunction.GetCdpTableType
internal ConnectorType(JsonElement schema, string tableName, SymbolTable optionSets, ConnectorCompatibility compatibility, IList<SqlRelationship> sqlRelationships, IList<ReferencedEntity> referencedEntities, string datasetName, string name, string connectorName, ICdpTableResolver resolver, ServiceCapabilities serviceCapabilities, bool isTableReadOnly)
: this(SwaggerJsonSchema.New(schema), null, new SwaggerParameter(null, true, SwaggerJsonSchema.New(schema), null).GetConnectorType(tableName, optionSets, compatibility, sqlRelationships))
{
Name = name;
foreach (ConnectorType field in Fields.Where(f => f.Capabilities != null))
{
{
serviceCapabilities.AddColumnCapability(field.Name, field.Capabilities);
}
}
FormulaType = new CdpRecordType(this, resolver, ServiceCapabilities.ToDelegationInfo(serviceCapabilities, name, isTableReadOnly, this, datasetName));
}
@ -295,7 +296,7 @@ namespace Microsoft.PowerFx.Connectors
ExternalTables.Add(relationship.ReferencedTable);
RelationshipName = relationship.RelationshipName;
ForeignKey = relationship.ReferencedColumnName;
}
}
private void AggregateErrors(ConnectorType[] types)
{

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

@ -11,12 +11,16 @@ using System.Threading.Tasks;
using Microsoft.PowerFx.Core.Entities;
using Microsoft.PowerFx.Types;
#pragma warning disable SA1117
namespace Microsoft.PowerFx.Connectors
{
internal class CdpTableResolver : ICdpTableResolver
{
public ConnectorLogger Logger { get; }
public IEnumerable<OptionSet> OptionSets { get; private set; }
private readonly CdpTable _tabularTable;
private readonly HttpClient _httpClient;
@ -87,7 +91,12 @@ namespace Microsoft.PowerFx.Connectors
string connectorName = _uriPrefix.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries)[1];
return ConnectorFunction.GetCdpTableType(this, connectorName, "Schema/Items", FormulaValue.New(text), sqlRelationships, ConnectorCompatibility.CdpCompatibility, _tabularTable.DatasetName, out string name, out string displayName, out TableDelegationInfo delegationInfo);
ConnectorType connectorType = ConnectorFunction.GetCdpTableType(this, connectorName, _tabularTable.TableName, "Schema/Items", FormulaValue.New(text), sqlRelationships, ConnectorCompatibility.CdpCompatibility, _tabularTable.DatasetName,
out string name, out string displayName, out TableDelegationInfo delegationInfo, out IEnumerable<OptionSet> optionSets);
OptionSets = optionSets;
return connectorType;
}
internal static bool IsSql(string uriPrefix) => uriPrefix.Contains("/sql/");

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

@ -27,6 +27,8 @@ namespace Microsoft.PowerFx.Connectors
public override bool IsDelegable => (DelegationInfo?.SortRestriction != null) || (DelegationInfo?.FilterRestriction != null) || (DelegationInfo?.FilterSupportedFunctions != null);
public IEnumerable<OptionSet> OptionSets { get; private set; }
internal TableDelegationInfo DelegationInfo => ((DataSourceInfo)TabularTableDescriptor.FormulaType._type.AssociatedDataSources.First()).DelegationInfo;
internal override IReadOnlyDictionary<string, Relationship> Relationships => _relationships;
@ -80,6 +82,7 @@ namespace Microsoft.PowerFx.Connectors
TabularTableDescriptor = await tableResolver.ResolveTableAsync(TableName, cancellationToken).ConfigureAwait(false);
_relationships = TabularTableDescriptor.Relationships;
OptionSets = tableResolver.OptionSets;
RecordType = (RecordType)TabularTableDescriptor.FormulaType;
}
@ -87,8 +90,7 @@ namespace Microsoft.PowerFx.Connectors
private async Task InitializeDatasetMetadata(HttpClient httpClient, string uriPrefix, ConnectorLogger logger, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
CdpDataSource cds = new CdpDataSource(DatasetName);
DatasetMetadata dm = await CdpDataSource.GetDatasetsMetadataAsync(httpClient, uriPrefix, cancellationToken, logger).ConfigureAwait(false);
DatasetMetadata = dm ?? throw new InvalidOperationException("Dataset metadata is not available");

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

@ -2556,8 +2556,23 @@ namespace Microsoft.PowerFx.Core.Binding
_txb.SetSelfContainedConstant(node, true);
_txb.SetType(node, DType.ObjNull);
}
// Binding TypeLiteralNode from anywhere other than valid type context should be an error.
// This ensures that binding of unintended use of TypeLiteralNode eg: "If(Type(Boolean), 1, 2)" will result in an error.
// VisitType method is used to resolve the type of TypeLiteralNode from valid context.
public override void Visit(TypeLiteralNode node)
{
AssertValid();
Contracts.AssertValue(node);
_txb.SetType(node, DType.Error);
_txb.ErrorContainer.Error(node, TexlStrings.ErrTypeLiteral_UnsupportedUsage);
}
// Method to bind TypeLiteralNode from valid context where a type is expected.
// Binding TypeLiteralNode in an expression where a type is not expected invokes the Visit method
// from normal visitor pattern and results in error.
private void VisitType(TypeLiteralNode node)
{
AssertValid();
Contracts.AssertValue(node);
@ -2570,7 +2585,7 @@ namespace Microsoft.PowerFx.Core.Binding
var type = DTypeVisitor.Run(node.TypeRoot, _nameResolver);
if (type.IsValid)
if (type.IsValid)
{
_txb.SetType(node, type);
}
@ -5103,7 +5118,7 @@ namespace Microsoft.PowerFx.Core.Binding
}
else if (args[1] is TypeLiteralNode typeLiteral)
{
typeLiteral.Accept(this);
VisitType(typeLiteral);
}
else
{

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

@ -131,8 +131,9 @@ namespace Microsoft.PowerFx.Core.Functions
/// <param name="bindingConfig">Configuration for an invocation of the binder.</param>
/// <param name="features">PowerFx features.</param>
/// <param name="rule"></param>
/// <param name="updateDisplayNames">If true, the binder will update the display names of the nodes in the parse tree.</param>
/// <returns>Returns binding for the function body.</returns>
public TexlBinding BindBody(INameResolver nameResolver, IBinderGlue documentBinderGlue, BindingConfig bindingConfig = null, Features features = null, IExternalRule rule = null)
public TexlBinding BindBody(INameResolver nameResolver, IBinderGlue documentBinderGlue, BindingConfig bindingConfig = null, Features features = null, IExternalRule rule = null, bool updateDisplayNames = false)
{
if (nameResolver is null)
{
@ -150,7 +151,7 @@ namespace Microsoft.PowerFx.Core.Functions
}
bindingConfig = bindingConfig ?? new BindingConfig(this._isImperative, userDefinitionsMode: true);
_binding = TexlBinding.Run(documentBinderGlue, UdfBody, UserDefinitionsNameResolver.Create(nameResolver, _args, ParamTypes), bindingConfig, features: features, rule: rule);
_binding = TexlBinding.Run(documentBinderGlue, UdfBody, UserDefinitionsNameResolver.Create(nameResolver, _args, ParamTypes), bindingConfig, features: features, rule: rule, updateDisplayNames: updateDisplayNames);
CheckTypesOnDeclaration(_binding.CheckTypesContext, _binding.ResultType, _binding);
@ -199,7 +200,7 @@ namespace Microsoft.PowerFx.Core.Functions
/// Clones and binds a user defined function.
/// </summary>
/// <returns>Returns a new functions.</returns>
public UserDefinedFunction WithBinding(INameResolver nameResolver, IBinderGlue binderGlue, out TexlBinding binding, BindingConfig bindingConfig = null, Features features = null, IExternalRule rule = null)
public UserDefinedFunction WithBinding(INameResolver nameResolver, IBinderGlue binderGlue, out TexlBinding binding, BindingConfig bindingConfig = null, Features features = null, IExternalRule rule = null, bool updateDisplayNames = false)
{
if (nameResolver is null)
{
@ -212,7 +213,7 @@ namespace Microsoft.PowerFx.Core.Functions
}
var func = new UserDefinedFunction(Name, ReturnType, UdfBody, _isImperative, new HashSet<UDFArg>(_args), ParamTypes);
binding = func.BindBody(nameResolver, binderGlue, bindingConfig, features, rule);
binding = func.BindBody(nameResolver, binderGlue, bindingConfig, features, rule, updateDisplayNames);
return func;
}

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

@ -783,6 +783,7 @@ namespace Microsoft.PowerFx.Core.Localization
public static ErrorResourceKey WrnUDF_ShadowingBuiltInFunction = new ErrorResourceKey("WrnUDF_ShadowingBuiltInFunction");
public static ErrorResourceKey ErrTypeLiteral_InvalidTypeDefinition = new ErrorResourceKey("ErrTypeLiteral_InvalidTypeDefinition");
public static ErrorResourceKey ErrTypeLiteral_UnsupportedUsage = new ErrorResourceKey("ErrTypeLiteral_UnsupportedUsage");
public static ErrorResourceKey ErrNamedType_InvalidCycles = new ErrorResourceKey("ErrNamedType_InvalidCycles");
public static ErrorResourceKey ErrNamedType_InvalidTypeDefinition = new ErrorResourceKey("ErrNamedType_InvalidTypeDefinition");
public static ErrorResourceKey ErrNamedType_InvalidTypeName = new ErrorResourceKey("ErrNamedType_InvalidTypeName");

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

@ -707,7 +707,7 @@ namespace Microsoft.PowerFx
var summary = new CheckContextSummary
{
AllowsSideEffects = allowSideEffects,
IsPreV1Semantics = isV1,
IsPreV1Semantics = !isV1,
ExpectedReturnType = this._expectedReturnTypes,
SuggestedSymbols = symbolEntries
};

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

@ -9,18 +9,19 @@ using Microsoft.PowerFx.Core.Utils;
namespace Microsoft.PowerFx
{
[ThreadSafeImmutable]
public sealed class Features
{
/// <summary>
/// Enable Table syntax to not add "Value:" extra layer.
/// </summary>
internal bool TableSyntaxDoesntWrapRecords { get; set; }
internal bool TableSyntaxDoesntWrapRecords { get; init; }
/// <summary>
/// Enable functions to consistently return one dimension tables with
/// a "Value" column rather than some other name like "Result".
/// </summary>
internal bool ConsistentOneColumnTableResult { get; set; }
internal bool ConsistentOneColumnTableResult { get; init; }
/// <summary>
/// Disables support for row-scope disambiguation syntax.
@ -28,77 +29,72 @@ namespace Microsoft.PowerFx
/// instead of
/// Filter(A, A[@Value] = 2).
/// </summary>
internal bool DisableRowScopeDisambiguationSyntax { get; set; }
internal bool DisableRowScopeDisambiguationSyntax { get; init; }
/// <summary>
/// Enable Identifier support for describing column names.
/// </summary>
internal bool SupportColumnNamesAsIdentifiers { get; set; }
internal bool SupportColumnNamesAsIdentifiers { get; init; }
/// <summary>
/// Enforces strong-typing for builtin enums, rather than treating
/// them as aliases for values of string/number/boolean types.
/// </summary>
internal bool StronglyTypedBuiltinEnums { get; set; }
internal bool StronglyTypedBuiltinEnums { get; init; }
/// <summary>
/// Updates the IsEmpty function to only allow table arguments, since it
/// does not work properly with other types of arguments.
/// </summary>
internal bool RestrictedIsEmptyArguments { get; set; }
internal bool RestrictedIsEmptyArguments { get; init; }
/// <summary>
/// Allow delegation for async calls (delegate using awaited call result).
/// </summary>
internal bool AllowAsyncDelegation { get; set; }
internal bool AllowAsyncDelegation { get; init; }
/// <summary>
/// Allow delegation for impure nodes.
/// </summary>
internal bool AllowImpureNodeDelegation { get; set; }
internal bool AllowImpureNodeDelegation { get; init; }
/// <summary>
/// Updates the FirstN/LastN functions to require a second argument, instead of
/// defaulting to 1.
/// </summary>
internal bool FirstLastNRequiresSecondArguments { get; set; }
internal bool FirstLastNRequiresSecondArguments { get; init; }
internal bool PowerFxV1CompatibilityRules { get; set; }
internal bool PowerFxV1CompatibilityRules { get; init; }
/// <summary>
/// This is required by AsType() in PA delegation analysis.
/// </summary>
internal bool AsTypeLegacyCheck { get; set; }
/// <summary>
/// This is required by AsType() in Legacy Analysis.
/// </summary>
internal bool IsLegacyAnalysis { get; set; }
internal bool AsTypeLegacyCheck { get; init; }
/// <summary>
/// Removes support for coercing a control to it's primary output property.
/// This only impacts PA Client scenarios, but some code still lives in PFx.
/// </summary>
internal bool PrimaryOutputPropertyCoercionDeprecated { get; set; }
internal bool PrimaryOutputPropertyCoercionDeprecated { get; init; }
/// <summary>
/// This is specific for PVA team and it is a temporary feature.
/// </summary>
internal bool JsonFunctionAcceptsLazyTypes { get; set; }
internal bool JsonFunctionAcceptsLazyTypes { get; init; }
/// <summary>
/// Enables more robust lookup reduction delegation.
/// </summary>
internal bool IsLookUpReductionDelegationEnabled { get; set; }
internal bool IsLookUpReductionDelegationEnabled { get; init; }
/// <summary>
/// Enables User-defined types functionality.
/// </summary>
internal bool IsUserDefinedTypesEnabled { get; set; } = false;
internal bool IsUserDefinedTypesEnabled { get; init; } = false;
internal static Features None => new Features();
internal static readonly Features None = new Features();
public static Features PowerFxV1 => new Features
public static readonly Features PowerFxV1 = new Features
{
TableSyntaxDoesntWrapRecords = true,
ConsistentOneColumnTableResult = true,
@ -130,6 +126,11 @@ namespace Microsoft.PowerFx
PowerFxV1CompatibilityRules = other.PowerFxV1CompatibilityRules;
PrimaryOutputPropertyCoercionDeprecated = other.PrimaryOutputPropertyCoercionDeprecated;
IsUserDefinedTypesEnabled = other.IsUserDefinedTypesEnabled;
AllowImpureNodeDelegation = other.AllowImpureNodeDelegation;
AllowAsyncDelegation = other.AllowAsyncDelegation;
AsTypeLegacyCheck = other.AsTypeLegacyCheck;
JsonFunctionAcceptsLazyTypes = other.JsonFunctionAcceptsLazyTypes;
IsLookUpReductionDelegationEnabled = other.IsLookUpReductionDelegationEnabled;
}
}
}

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

@ -51,6 +51,11 @@ namespace Microsoft.PowerFx.Syntax
/// <inheritdoc />
public override void Visit(SelfNode node)
{
}
/// <inheritdoc />
public override void Visit(TypeLiteralNode node)
{
}
/// <inheritdoc />

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

@ -12,9 +12,7 @@ namespace Microsoft.PowerFx.Syntax
/// Visit <see cref="TypeLiteralNode"/> leaf node.
/// </summary>
/// <param name="node">The visited node.</param>
public virtual void Visit(TypeLiteralNode node)
{
}
public abstract void Visit(TypeLiteralNode node);
/// <summary>
/// Visit <see cref="ErrorNode" /> leaf node.

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

@ -13,7 +13,7 @@ namespace Microsoft.PowerFx.Core.Texl
/// This visitor is used to walkthrough the first node of a filter to get the datasource name and
/// whether or not there is any other filter sub expression that uses a view.
/// </summary>
internal sealed class ViewFilterDataSourceVisitor : TexlVisitor
internal sealed class ViewFilterDataSourceVisitor : IdentityTexlVisitor
{
private const string FilterFunctionName = "Filter";
private readonly TexlBinding _txb;
@ -52,73 +52,5 @@ namespace Microsoft.PowerFx.Core.Texl
}
}
}
public override void PostVisit(DottedNameNode node)
{
}
public override void PostVisit(VariadicOpNode node)
{
}
public override void PostVisit(StrInterpNode node)
{
}
public override void PostVisit(RecordNode node)
{
}
public override void PostVisit(ListNode node)
{
}
public override void PostVisit(BinaryOpNode node)
{
}
public override void PostVisit(UnaryOpNode node)
{
}
public override void PostVisit(TableNode node)
{
}
public override void PostVisit(AsNode node)
{
}
public override void Visit(ParentNode node)
{
}
public override void Visit(NumLitNode node)
{
}
public override void Visit(DecLitNode node)
{
}
public override void Visit(StrLitNode node)
{
}
public override void Visit(BoolLitNode node)
{
}
public override void Visit(BlankNode node)
{
}
public override void Visit(ErrorNode node)
{
}
public override void Visit(SelfNode node)
{
}
}
}

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

@ -12,7 +12,7 @@ namespace Microsoft.PowerFx.Core.Texl
/// <summary>
/// This visitor is used to walkthrough the tree to check the existence of a view.
/// </summary>
internal sealed class ViewFinderVisitor : TexlVisitor
internal sealed class ViewFinderVisitor : IdentityTexlVisitor
{
private readonly TexlBinding _txb;
@ -42,73 +42,5 @@ namespace Microsoft.PowerFx.Core.Texl
ContainsView = true;
}
}
public override void PostVisit(CallNode node)
{
}
public override void PostVisit(VariadicOpNode node)
{
}
public override void PostVisit(StrInterpNode node)
{
}
public override void PostVisit(RecordNode node)
{
}
public override void PostVisit(ListNode node)
{
}
public override void PostVisit(BinaryOpNode node)
{
}
public override void PostVisit(UnaryOpNode node)
{
}
public override void PostVisit(TableNode node)
{
}
public override void PostVisit(AsNode node)
{
}
public override void Visit(ParentNode node)
{
}
public override void Visit(NumLitNode node)
{
}
public override void Visit(DecLitNode node)
{
}
public override void Visit(StrLitNode node)
{
}
public override void Visit(BoolLitNode node)
{
}
public override void Visit(BlankNode node)
{
}
public override void Visit(ErrorNode node)
{
}
public override void Visit(SelfNode node)
{
}
}
}

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

@ -5,6 +5,7 @@ using System;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.PowerFx.Core.IR;
using Microsoft.PowerFx.Core.Utils;
@ -697,29 +698,50 @@ namespace Microsoft.PowerFx.Functions
}
}
private static readonly Regex TimeValueRegex = new Regex(@"(?<hours>\d{1,2})\:(?<minutes>\d{2})(\:(?<seconds>\d{2})(\.(?<milliseconds>\d{1,3}))?)?");
public static FormulaValue TimeParse(EvalVisitor runner, EvalVisitorContext context, IRContext irContext, StringValue[] args)
{
var str = args[0].Value;
// culture will have Cultural info in-case one was passed in argument else it will have the default one.
CultureInfo culture = runner.CultureInfo;
if (args.Length > 1)
var dateTimeResult = DateTimeParse(runner, context, IRContext.NotInSource(FormulaType.DateTime), args);
if (dateTimeResult is DateTimeValue dateTimeValue)
{
var languageCode = args[1].Value;
if (!TextFormatUtils.TryGetCulture(languageCode, out culture))
var dt = dateTimeValue.GetConvertedValue(runner.TimeZoneInfo);
var time = new TimeSpan(0, dt.Hour, dt.Minute, dt.Second, dt.Millisecond);
return new TimeValue(irContext, time);
}
if (dateTimeResult is BlankValue)
{
return dateTimeResult;
}
// Error; trying time-only options
var match = TimeValueRegex.Match(args[0].Value);
if (match.Success)
{
var hours = int.Parse(match.Groups["hours"].Value, CultureInfo.InvariantCulture) % 24;
var minutes = int.Parse(match.Groups["minutes"].Value, CultureInfo.InvariantCulture);
var secondsText = match.Groups["seconds"]?.Value;
var seconds = string.IsNullOrEmpty(secondsText) ? 0 : int.Parse(secondsText, CultureInfo.InvariantCulture);
var millisecondsText = match.Groups["milliseconds"]?.Value;
int milliseconds = 0;
if (!string.IsNullOrEmpty(millisecondsText))
{
return CommonErrors.BadLanguageCode(irContext, languageCode);
milliseconds = int.Parse(millisecondsText, CultureInfo.InvariantCulture);
if (millisecondsText.Length == 1)
{
milliseconds *= 100; // 12:34:56.7 === 12:34:56.700
}
else if (millisecondsText.Length == 2)
{
milliseconds *= 10; // 12:34:56.78 === 12:34:56.780
}
}
return new TimeValue(irContext, new TimeSpan(0, hours, minutes, seconds, milliseconds));
}
if (TimeSpan.TryParse(str, runner.CultureInfo, out var result))
{
return new TimeValue(irContext, result);
}
else
{
return CommonErrors.InvalidDateTimeParsingError(irContext);
}
return dateTimeResult;
}
// Returns the number of minutes between UTC and either local or defined time zone

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

@ -4560,6 +4560,10 @@
<value>Type literal declaration is invalid. The expression '{0}' cannot be used in a type definition.</value>
<comment>Error message when validation of type literal fails.</comment>
</data>
<data name="ErrTypeLiteral_UnsupportedUsage" xml:space="preserve">
<value>Unsupported usage: type literals can only be used in type arguments and type definitions.</value>
<comment>Error message shown when a type literal is used in an unsupported expression.</comment>
</data>
<data name="ErrNamedType_InvalidTypeDefinition" xml:space="preserve">
<value>Definition of type {0} is invalid.</value>
<comment>Error message when type binding is invalid.</comment>

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

@ -33,9 +33,9 @@ namespace Microsoft.PowerFx.Tests
string text = (string)LoggingTestServer.GetFileText(@"Responses\Compatibility GetSchema.json");
ConnectorType ctCdp = ConnectorFunction.GetCdpTableType(tableResolver, "name", "schema/items", StringValue.New(text), null, ConnectorCompatibility.CdpCompatibility, "dataset", out _, out _, out _);
ConnectorType ctPa = ConnectorFunction.GetCdpTableType(tableResolver, "name", "schema/items", StringValue.New(text), null, ConnectorCompatibility.PowerAppsCompatibility, "dataset", out _, out _, out _);
ConnectorType ctSw = ConnectorFunction.GetCdpTableType(tableResolver, "name", "schema/items", StringValue.New(text), null, ConnectorCompatibility.SwaggerCompatibility, "dataset", out _, out _, out _);
ConnectorType ctCdp = ConnectorFunction.GetCdpTableType(tableResolver, "name", null, "schema/items", StringValue.New(text), null, ConnectorCompatibility.CdpCompatibility, "dataset", out _, out _, out _, out _);
ConnectorType ctPa = ConnectorFunction.GetCdpTableType(tableResolver, "name", null, "schema/items", StringValue.New(text), null, ConnectorCompatibility.PowerAppsCompatibility, "dataset", out _, out _, out _, out _);
ConnectorType ctSw = ConnectorFunction.GetCdpTableType(tableResolver, "name", null, "schema/items", StringValue.New(text), null, ConnectorCompatibility.SwaggerCompatibility, "dataset", out _, out _, out _, out _);
string cdp = ctCdp.FormulaType.ToStringWithDisplayNames();
string pa = ctPa.FormulaType.ToStringWithDisplayNames();

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

@ -1373,7 +1373,8 @@ namespace Microsoft.PowerFx.Connectors.Tests
public static string GetString(this OpenApiSchema schema)
{
StringBuilder sb = new StringBuilder();
schema.GetStringInternal(new ConnectorTypeGetterSettings(0), sb);
SymbolTable optionSets = new SymbolTable();
schema.GetStringInternal(new ConnectorTypeGetterSettings(0, null, optionSets), sb);
return sb.ToString();
}

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

@ -42,6 +42,7 @@
<Compile Include="$(MSBuildThisFileDirectory)SafeDTypeToStringTests.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Shims462.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SwaggerFailureTests.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SymbolTableTryAddTests.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TestConnectorRuntimeContext.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ThreadingTests.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Helpers\Helpers.cs" />

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

@ -962,8 +962,8 @@ namespace Microsoft.PowerFx.Connectors.Tests
Assert.Equal(
"r![assignee_id:w, brand_id:w, collaborator_ids:s, created_at:d, custom_fields:s, description:s, due_at:d, external_id:s, followup_ids:s, forum_topic_id:w, group_id:w, has_incidents:b, " +
"id:w, organization_id:w, priority:l, problem_id:w, raw_subject:s, recipient:s, requester_id:w, satisfaction_rating:s, sharing_agreement_ids:s, status:s, subject:s, submitter_id:w, " +
"tags:s, ticket_form_id:w, type:s, updated_at:d, url:s, via:s]", ((CdpRecordType)zdTable.RecordType).ToStringWithDisplayNames());
"id:w, organization_id:w, priority:l, problem_id:w, raw_subject:s, recipient:s, requester_id:w, satisfaction_rating:s, sharing_agreement_ids:s, status:l, subject:s, submitter_id:w, " +
"tags:s, ticket_form_id:w, type:l, updated_at:d, url:s, via:s]", ((CdpRecordType)zdTable.RecordType).ToStringWithDisplayNames());
SymbolValues symbolValues = new SymbolValues().Add("Tickets", zdTable);
RuntimeConfig rc = new RuntimeConfig(symbolValues).AddService<ConnectorLogger>(logger);
@ -979,7 +979,10 @@ namespace Microsoft.PowerFx.Connectors.Tests
OptionSetValue priority = Assert.IsType<OptionSetValue>(result);
Assert.Equal("normal", priority.Option);
Assert.Equal("normal", priority.DisplayName);
Assert.Equal("Normal", priority.DisplayName);
Assert.NotNull(connectorTable.OptionSets);
Assert.Equal("priority (tickets), status (tickets), type (tickets)", string.Join(", ", connectorTable.OptionSets.Select(os => os.EntityName.Value).OrderBy(x => x)));
}
}

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

@ -18,254 +18,308 @@
"type": "array",
"items": {
"type": "object",
"required": [],
"properties": {
"id": {
"type": "integer",
"title": "id",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "add", "sub", "mul", "div", "mod", "negate", "sum", "average" ] },
"type": "integer",
"format": "int64",
"minimum": -9223372036854775808,
"maximum": 9223372036854775807,
"minimum": -9223372036854776000,
"maximum": 9223372036854776000,
"x-ms-keyOrder": 1,
"x-ms-keyType": "primary",
"x-ms-permission": "read-only",
"x-ms-sort": "none"
},
"url": {
"type": "string",
"title": "url",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "contains", "startswith", "endswith", "length", "indexof", "replace", "substring", "substringof", "tolower", "toupper", "trim", "concat" ] },
"type": "string",
"x-ms-permission": "read-only",
"x-ms-sort": "none"
},
"external_id": {
"type": "string",
"title": "external_id",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "contains", "startswith", "endswith", "length", "indexof", "replace", "substring", "substringof", "tolower", "toupper", "trim", "concat" ] },
"type": "string",
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"type": {
"type": "string",
"x-ms-enum-values": [
{
"value": "problem",
"displayName": "Problem"
},
{
"value": "incident",
"displayName": "Incident"
},
{
"value": "question",
"displayName": "Question"
},
{
"value": "task",
"displayName": "Task"
}
],
"title": "type",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "contains", "startswith", "endswith", "length", "indexof", "replace", "substring", "substringof", "tolower", "toupper", "trim", "concat" ] },
"type": "string",
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"subject": {
"type": "string",
"title": "subject",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "contains", "startswith", "endswith", "length", "indexof", "replace", "substring", "substringof", "tolower", "toupper", "trim", "concat" ] },
"type": "string",
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"raw_subject": {
"type": "string",
"title": "raw_subject",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "contains", "startswith", "endswith", "length", "indexof", "replace", "substring", "substringof", "tolower", "toupper", "trim", "concat" ] },
"type": "string",
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"description": {
"type": "string",
"title": "description",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "contains", "startswith", "endswith", "length", "indexof", "replace", "substring", "substringof", "tolower", "toupper", "trim", "concat" ] },
"type": "string",
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"priority": {
"type": "string",
"x-ms-enum-values": [
{
"value": "low",
"displayName": "Low"
},
{
"value": "normal",
"displayName": "Normal"
},
{
"value": "high",
"displayName": "High"
},
{
"value": "urgent",
"displayName": "Urgent"
}
],
"title": "priority",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "contains", "startswith", "endswith", "length", "indexof", "replace", "substring", "substringof", "tolower", "toupper", "trim", "concat" ] },
"type": "string",
"enum": [
"low",
"normal",
"high",
"urgent"
],
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"status": {
"type": "string",
"x-ms-enum-values": [
{
"value": "new",
"displayName": "New"
},
{
"value": "open",
"displayName": "Open"
},
{
"value": "pending",
"displayName": "Pending"
},
{
"value": "hold",
"displayName": "Hold"
},
{
"value": "solved",
"displayName": "Solved"
},
{
"value": "closed",
"displayName": "Closed"
}
],
"title": "status",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "contains", "startswith", "endswith", "length", "indexof", "replace", "substring", "substringof", "tolower", "toupper", "trim", "concat" ] },
"type": "string",
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"recipient": {
"type": "string",
"title": "recipient",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "contains", "startswith", "endswith", "length", "indexof", "replace", "substring", "substringof", "tolower", "toupper", "trim", "concat" ] },
"type": "string",
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"requester_id": {
"type": "integer",
"title": "requester_id",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "add", "sub", "mul", "div", "mod", "negate", "sum", "average" ] },
"type": "integer",
"format": "int64",
"minimum": -9223372036854775808,
"maximum": 9223372036854775807,
"minimum": -9223372036854776000,
"maximum": 9223372036854776000,
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"submitter_id": {
"type": "integer",
"title": "submitter_id",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "add", "sub", "mul", "div", "mod", "negate", "sum", "average" ] },
"type": "integer",
"format": "int64",
"minimum": -9223372036854775808,
"maximum": 9223372036854775807,
"minimum": -9223372036854776000,
"maximum": 9223372036854776000,
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"assignee_id": {
"type": "integer",
"title": "assignee_id",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "add", "sub", "mul", "div", "mod", "negate", "sum", "average" ] },
"type": "integer",
"format": "int64",
"minimum": -9223372036854775808,
"maximum": 9223372036854775807,
"minimum": -9223372036854776000,
"maximum": 9223372036854776000,
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"organization_id": {
"type": "integer",
"title": "organization_id",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "add", "sub", "mul", "div", "mod", "negate", "sum", "average" ] },
"type": "integer",
"format": "int64",
"minimum": -9223372036854775808,
"maximum": 9223372036854775807,
"minimum": -9223372036854776000,
"maximum": 9223372036854776000,
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"group_id": {
"type": "integer",
"title": "group_id",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "add", "sub", "mul", "div", "mod", "negate", "sum", "average" ] },
"type": "integer",
"format": "int64",
"minimum": -9223372036854775808,
"maximum": 9223372036854775807,
"minimum": -9223372036854776000,
"maximum": 9223372036854776000,
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"collaborator_ids": {
"title": "collaborator_ids",
"type": "string",
"title": "collaborator_ids",
"x-ms-permission": "read-only",
"x-ms-sort": "none"
},
"forum_topic_id": {
"type": "integer",
"title": "forum_topic_id",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "add", "sub", "mul", "div", "mod", "negate", "sum", "average" ] },
"type": "integer",
"format": "int64",
"minimum": -9223372036854775808,
"maximum": 9223372036854775807,
"minimum": -9223372036854776000,
"maximum": 9223372036854776000,
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"problem_id": {
"type": "integer",
"title": "problem_id",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "add", "sub", "mul", "div", "mod", "negate", "sum", "average" ] },
"type": "integer",
"format": "int64",
"minimum": -9223372036854775808,
"maximum": 9223372036854775807,
"minimum": -9223372036854776000,
"maximum": 9223372036854776000,
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"has_incidents": {
"type": "boolean",
"title": "has_incidents",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "not", "and", "or" ] },
"type": "boolean",
"x-ms-permission": "read-only",
"x-ms-sort": "none"
},
"due_at": {
"type": "string",
"title": "due_at",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "day", "month", "year", "hour", "minute", "second", "date", "time", "totaloffsetminutes" ] },
"type": "string",
"format": "date-time",
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"tags": {
"title": "tags",
"type": "string",
"title": "tags",
"x-ms-permission": "read-only",
"x-ms-sort": "none"
},
"via": {
"title": "via",
"type": "string",
"title": "via",
"x-ms-permission": "read-only",
"x-ms-sort": "none"
},
"custom_fields": {
"title": "custom_fields",
"type": "string",
"title": "custom_fields",
"x-ms-permission": "read-only",
"x-ms-sort": "none"
},
"satisfaction_rating": {
"title": "satisfaction_rating",
"type": "string",
"title": "satisfaction_rating",
"x-ms-permission": "read-only",
"x-ms-sort": "none"
},
"sharing_agreement_ids": {
"title": "sharing_agreement_ids",
"type": "string",
"title": "sharing_agreement_ids",
"x-ms-permission": "read-only",
"x-ms-sort": "none"
},
"followup_ids": {
"title": "followup_ids",
"type": "string",
"title": "followup_ids",
"x-ms-permission": "read-only",
"x-ms-sort": "none"
},
"ticket_form_id": {
"type": "integer",
"title": "ticket_form_id",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "add", "sub", "mul", "div", "mod", "negate", "sum", "average" ] },
"type": "integer",
"format": "int64",
"minimum": -9223372036854775808,
"maximum": 9223372036854775807,
"minimum": -9223372036854776000,
"maximum": 9223372036854776000,
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"brand_id": {
"type": "integer",
"title": "brand_id",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "add", "sub", "mul", "div", "mod", "negate", "sum", "average" ] },
"type": "integer",
"format": "int64",
"minimum": -9223372036854775808,
"maximum": 9223372036854775807,
"minimum": -9223372036854776000,
"maximum": 9223372036854776000,
"x-ms-permission": "read-write",
"x-ms-sort": "none"
},
"created_at": {
"type": "string",
"title": "created_at",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "day", "month", "year", "hour", "minute", "second", "date", "time", "totaloffsetminutes" ] },
"type": "string",
"format": "date-time",
"x-ms-permission": "read-only",
"x-ms-sort": "none"
},
"updated_at": {
"type": "string",
"title": "updated_at",
"x-ms-capabilities": { "filterFunctions": [ "lt", "le", "eq", "ne", "gt", "ge", "min", "max", "countdistinct", "day", "month", "year", "hour", "minute", "second", "date", "time", "totaloffsetminutes" ] },
"type": "string",
"format": "date-time",
"x-ms-permission": "read-only",
"x-ms-sort": "none"
}
}
},
"x-ms-permission": "read-write"
}
}
}
}

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

@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
using System;
using System.Linq;
using Microsoft.PowerFx.Core;
using Microsoft.PowerFx.Core.Utils;
using Xunit;
namespace Microsoft.PowerFx.Connectors.Tests.Shared
{
public class SymbolTableTryAddOptionSetTests
{
[Fact]
public void TryAddOptionSet()
{
SymbolTable symbolTable = new SymbolTable();
SingleSourceDisplayNameProvider dnp = new SingleSourceDisplayNameProvider();
dnp = dnp.AddField(new DName("logical1"), new DName("display1"));
dnp = dnp.AddField(new DName("logical2"), new DName("display2"));
OptionSet os1 = new OptionSet("os1", dnp);
OptionSet os2 = new OptionSet("os1", dnp);
OptionSet os = symbolTable.TryAddOptionSet(os1);
Assert.Same(os1, os);
// twice the same, nothing added
os = symbolTable.TryAddOptionSet(os1);
Assert.Same(os1, os);
Assert.Single(symbolTable.OptionSets);
// still the same, nothing added
os = symbolTable.TryAddOptionSet(os2);
Assert.Same(os1, os);
Assert.Single(symbolTable.OptionSets);
dnp = dnp.AddField(new DName("logical3"), new DName("display3"));
OptionSet os3 = new OptionSet("os3", dnp);
// new optionSet
os = symbolTable.TryAddOptionSet(os3);
Assert.NotSame(os, os1);
Assert.Equal(2, symbolTable.OptionSets.Count());
// try a name conflict now
OptionSet os4 = new OptionSet("os1", dnp);
InvalidOperationException ioe = Assert.Throws<InvalidOperationException>(() => symbolTable.TryAddOptionSet(os4));
Assert.Equal("Optionset name conflict (os1)", ioe.Message);
}
}
}

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

@ -138,6 +138,11 @@ namespace Microsoft.PowerFx.Core.Tests
{
throw new NotImplementedException();
}
public override void Visit(TypeLiteralNode node)
{
throw new NotImplementedException();
}
}
}
}

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

@ -234,3 +234,7 @@ Error({Kind:ErrorKind.InvalidArgument})
// Numeric enums and numbers
>> If(1<0, 1, ErrorKind.Validation)
11
// Type literal should cause error
>> If(Type(Boolean), 1, 2)
Errors: Error 7-8: Unsupported usage: type literals can only be used in type arguments and type definitions.|Error 0-2: The function 'If' has some invalid arguments.

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

@ -0,0 +1,56 @@
>> TimeValue("12:00:00")
Time(12,0,0,0)
>> TimeValue("27:00:00")
Time(3,0,0,0)
// TimeValue only returns values between [00:00:00.000, 23:59:59.999)
>> Value(TimeValue("27:00:00"))
0.125
>> TimeValue("6:00")
Time(6,0,0,0)
>> TimeValue("6:00 PM")
Time(18,0,0,0)
// Date portion is ignored
>> Value(TimeValue("10/12/2024 6:00:00 AM"))
0.25
>> TimeValue("29 Feb 2008 9:21:33 AM")
Time(9,21,33,0)
>> TimeValue("12:34:56.7")
Time(12,34,56,700)
>> TimeValue("12:34:56.78")
Time(12,34,56,780)
>> TimeValue("12:34:56.789")
Time(12,34,56,789)
// Extra digits on milliseconds are truncated
>> TimeValue("11:22:33.4449999")
Time(11,22,33,444)
>> TimeValue("6:01:02")
Time(6,1,2,0)
>> TimeValue("14:18")
Time(14,18,0,0)
>> TimeValue("24:11:11")
Time(0,11,11,0)
>> TimeValue("Not a time")
Error({Kind:ErrorKind.InvalidArgument})
>> TimeValue("One PM")
Error({Kind:ErrorKind.InvalidArgument})
>> TimeValue("1234")
Error({Kind:ErrorKind.InvalidArgument})
>> TimeValue("20241106073000")
Error({Kind:ErrorKind.InvalidArgument})

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

@ -146,4 +146,7 @@ Blank()
Errors: Error 16-23: Invalid argument 'Blank()'. Expected valid type name or type literal.
>> ParseJSON("42", 1/0)
Errors: Error 17-18: Invalid argument '1 / 0'. Expected valid type name or type literal.
Errors: Error 17-18: Invalid argument '1 / 0'. Expected valid type name or type literal.
>> ParseJSON(Type(Text))
Errors: Error 14-15: Unsupported usage: type literals can only be used in type arguments and type definitions.|Error 0-9: The function 'ParseJSON' has some invalid arguments.

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

@ -278,11 +278,11 @@ Date(2000,1,10)
Time(0,0,0,0)
//Time-String
>> Switch("Case1","Case2",Time(6,30,30),"Case1","1")
Time(0,0,0,0)
>> Switch("Case1","Case2",Time(6,30,30),"Case1","12:34:56")
Time(12,34,56,0)
>> Switch("Case1","Case2",Time(6,30,30),"Case1","200")
Time(0,0,0,0)
>> Switch("Case1","Case2",Time(6,30,30),"Case1","6:00")
Time(6,0,0,0)
>> Switch("Case1","Case2",Time(6,30,30),"Case1","AB$%^")
Error({Kind:ErrorKind.InvalidArgument})

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

@ -4411,6 +4411,19 @@ namespace Microsoft.PowerFx.Core.Tests
features: Features.PowerFxV1);
}
[Theory]
[InlineData("Type(Number)", "e")]
[InlineData("Abs(Type(Number))", "n")]
[InlineData("If(Type(Boolean), 1, 2)", "n")]
[InlineData("Concatenate(Type(Text))", "s")]
public void TestTypeLiteralsNegative(string script, string expectedSchema)
{
TestBindingErrors(
script,
TestUtils.DT(expectedSchema),
features: Features.PowerFxV1);
}
private void TestBindingPurity(string script, bool isPure, SymbolTable symbolTable = null)
{
var config = new PowerFxConfig

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

@ -23,9 +23,12 @@ namespace Microsoft.PowerFx.Json.Tests
{
private RecalcEngine SetupEngine(bool udtFeaturedEnabled = true)
{
var config = new PowerFxConfig();
var features = new Features(Features.PowerFxV1)
{
IsUserDefinedTypesEnabled = udtFeaturedEnabled,
};
var config = new PowerFxConfig(features);
config.EnableJsonFunctions();
config.Features.IsUserDefinedTypesEnabled = udtFeaturedEnabled;
return new RecalcEngine(config);
}
@ -118,30 +121,33 @@ namespace Microsoft.PowerFx.Json.Tests
}
[Theory]
[InlineData("\"42\"", "SomeType", true, "ErrInvalidName")]
[InlineData("\"42\"", "Type(5)", true, "ErrTypeLiteral_InvalidTypeDefinition")]
[InlineData("\"42\"", "Text(42)", true, "ErrInvalidArgumentExpectedType")]
[InlineData("\"\"\"Hello\"\"\"", "\"Hello\"", true, "ErrInvalidArgumentExpectedType")]
[InlineData("\"{}\"", "Type([{a: 42}])", true, "ErrTypeLiteral_InvalidTypeDefinition")]
[InlineData("AsType(ParseJSON(\"42\"))", "", false, "ErrBadArity")]
[InlineData("IsType(ParseJSON(\"42\"))", "", false, "ErrBadArity")]
[InlineData("AsType(ParseJSON(\"42\"), Number , Text(5))", "", false, "ErrBadArity")]
[InlineData("IsType(ParseJSON(\"42\"), Number, 5)", "", false, "ErrBadArity")]
[InlineData("AsType(ParseJSON(\"123\"), 1)", "", false, "ErrInvalidArgumentExpectedType")]
public void TestCompileErrors(string expression, string type, bool testAllFunctions, string expectedError)
[InlineData("\"42\"", "SomeType", "ErrInvalidName")]
[InlineData("\"42\"", "Type(5)", "ErrTypeLiteral_InvalidTypeDefinition")]
[InlineData("\"42\"", "Text(42)", "ErrInvalidArgumentExpectedType")]
[InlineData("\"\"\"Hello\"\"\"", "\"Hello\"", "ErrInvalidArgumentExpectedType")]
[InlineData("\"{}\"", "Type([{a: 42}])", "ErrTypeLiteral_InvalidTypeDefinition")]
public void TestCompileErrorsAllStronglyTypedOverloads(string expression, string type, string expectedError)
{
var engine = SetupEngine();
CheckIsTypeAsTypeParseJSONCompileErrors(engine, expression, type, expectedError);
}
[Theory]
[InlineData("AsType(ParseJSON(\"42\"))", "ErrBadArity")]
[InlineData("IsType(ParseJSON(\"42\"))", "ErrBadArity")]
[InlineData("AsType(ParseJSON(\"42\"), Number , Text(5))", "ErrBadArity")]
[InlineData("IsType(ParseJSON(\"42\"), Number, 5)", "ErrBadArity")]
[InlineData("AsType(ParseJSON(\"123\"), 1)", "ErrInvalidArgumentExpectedType")]
[InlineData("AsType(Type(UntypedObject), ParseJSON(\"123\"))", "ErrTypeLiteral_UnsupportedUsage")]
[InlineData("IsType(Type(UntypedObject), Type(Boolean))", "ErrTypeLiteral_UnsupportedUsage")]
[InlineData("ParseJSON(Type(Text), Type(Text))", "ErrTypeLiteral_UnsupportedUsage")]
public void TestCompileErrors(string expression, string expectedError)
{
var engine = SetupEngine();
if (testAllFunctions)
{
CheckIsTypeAsTypeParseJSONCompileErrors(engine, expression, type, expectedError);
}
else
{
var result = engine.Check(expression);
Assert.False(result.IsSuccess);
Assert.Contains(result.Errors, e => e.MessageKey == expectedError);
}
var result = engine.Check(expression);
Assert.False(result.IsSuccess);
Assert.Contains(result.Errors, e => e.MessageKey == expectedError);
}
[Theory]