Skip to content

Commit

Permalink
Update ComponentDefinition OM (#736)
Browse files Browse the repository at this point in the history
- replaced parameter `IsRequired` keyword with `IsOptional`. To ensure smaller serialized yaml.
- `PaYamlPropertyDataType` consolidated to replace `PFxDataType` and `PFxFunctionReturnType` to move validation to compiler which will simplify compiler implementation.
- change serialized order of `Parameters` to occur last. `NamedObjectCollectionExtensions.EmptyToNull`  extension methods.
- updated schema documentation
- Add additional examples of custom properties
  • Loading branch information
joem-msft authored Feb 10, 2025
1 parent 153809e commit 68a1f10
Show file tree
Hide file tree
Showing 13 changed files with 225 additions and 90 deletions.
33 changes: 16 additions & 17 deletions schemas/pa-yaml/v3.0/pa.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ definitions:
Control-Group-name:
description: |-
The name of the group of controls to associate this control with.
Groups do not impact the behavior of an app, but are used in the Studio to organize controls when editing.
allOf:
- { $ref: "#/definitions/Control-instance-name" }
Expand Down Expand Up @@ -333,22 +333,26 @@ definitions:
type: string
oneOf:
- const: Input
description: An input data property.
description: This kind of property can send or receive values between the app and the component. The formula for this property is defined in the app where the component is used.
- const: Output
description: An output data property.
description: This kind of property can send or receive values between the app and the component. The formula for this property is defined once in the component.
- const: InputFunction
description: An input function property.
description: This kind of property can be called as a function with parameters. The formula for this property is defined in the app where the component is used.
- const: OutputFunction
description: An output function property.
description: This kind of property can be called as a function with parameters. The formula for this property is defined once in the component.
- const: Event
description: A property that represents an event.
description: This kind of property allows you to create an event that the component can trigger, and then be handled by the app.
- const: Action
description: A property that represents an action.
description: This kind of property can be called as a function with parameters, and can contain logic that changes state (side effects).
DisplayName:
description: DEPRECATED. This is not used anywhere and will be removed.
type: string
Description:
type: string
Default:
description: The default formula to use for this property when an instance does not explicitly set it.
allOf:
- $ref: "#/definitions/pfx-formula"
allOf:
- if:
properties:
Expand All @@ -364,10 +368,7 @@ definitions:
RaiseOnReset:
description: If turned on, the component's OnReset behavior will run when the input property's value changes.
type: boolean
Default:
allOf:
- description: The default formula to use for this property when an instance does not explicitly set it.
- $ref: "#/definitions/pfx-formula"
Default: true
- if:
properties:
PropertyKind: { const: "Output" }
Expand All @@ -390,11 +391,8 @@ definitions:
DisplayName: true
Description: true
ReturnType: { $ref: "#/definitions/pfx-function-return-type" }
Default: true
Parameters: { $ref: "#/definitions/pfx-function-parameters" }
Default:
description: The default formula to use for this property when an instance does not explicitly set it.
allOf:
- $ref: "#/definitions/pfx-formula"
- if:
properties:
PropertyKind: { const: "OutputFunction" }
Expand All @@ -418,6 +416,7 @@ definitions:
DisplayName: true
Description: true
ReturnType: { $ref: "#/definitions/pfx-function-return-type" }
Default: true
Parameters: { $ref: "#/definitions/pfx-function-parameters" }
- if:
properties:
Expand Down Expand Up @@ -470,7 +469,7 @@ definitions:
Parameters:
type: object
additionalProperties:
type: string
type: string
oneOf:
- required: [Type]
additionalProperties: false
Expand Down Expand Up @@ -524,7 +523,7 @@ definitions:
additionalProperties: false
properties:
Description: { type: string }
IsRequired: { type: boolean }
IsOptional: { type: boolean }
DataType: { $ref: "#/definitions/pfx-data-type" }
Default:
description: The default formula to use for this parameter when not explicitly specified.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using Microsoft.PowerPlatform.PowerApps.Persistence;
using Microsoft.PowerPlatform.PowerApps.Persistence.PaYaml.Models;
using Microsoft.PowerPlatform.PowerApps.Persistence.PaYaml.Models.PowerFx;
using Microsoft.PowerPlatform.PowerApps.Persistence.PaYaml.Models.SchemaV3;
using Microsoft.PowerPlatform.PowerApps.Persistence.PaYaml.Serialization;

Expand Down Expand Up @@ -123,7 +122,7 @@ static int GetGroupCount(IPaControlInstanceContainer container)
}

[TestMethod]
[DataRow(@"_TestData/SchemaV3_0/Examples/Src/Components/MyHeaderComponent.pa.yaml", 9, 6, 1)]
[DataRow(@"_TestData/SchemaV3_0/Examples/Src/Components/MyHeaderComponent.pa.yaml", 9, 5, 1)]
public void DeserializeExamplePaYamlComponentDefinition(string path, int expectedCustomPropertiesCount, int expectedPropertiesCount, int expectedChildrenCount)
{
var paFileRoot = PaYamlSerializer.Deserialize<PaModule>(File.ReadAllText(path));
Expand Down Expand Up @@ -182,6 +181,9 @@ public void DeserializeDuplicateControlNamesShouldFail()
[DataRow(@"_TestData/SchemaV3_0/Examples/Src/Screens/ComponentsScreen4.pa.yaml")]
[DataRow(@"_TestData/SchemaV3_0/Examples/Src/Components/MyHeaderComponent.pa.yaml")]
[DataRow(@"_TestData/SchemaV3_0/Examples/Src/Components/MyComponentLibrary.pa.yaml")]
[DataRow(@"_TestData/SchemaV3_0/Examples/Src/Components/InputProperties-Default.pa.yaml")]
[DataRow(@"_TestData/SchemaV3_0/Examples/Src/Components/OutputProperties.pa.yaml")]
[DataRow(@"_TestData/SchemaV3_0/Examples/Src/Components/Parameters-Default.pa.yaml")]
[DataRow(@"_TestData/SchemaV3_0/Examples/Single-File-App.pa.yaml")]
[DataRow(@"_TestData/SchemaV3_0/Examples/AmbiguousComponentNames.pa.yaml")]
[DataRow(@"_TestData/SchemaV3_0/FullSchemaUses/App.pa.yaml")]
Expand Down Expand Up @@ -274,7 +276,7 @@ public void SerializeComponentCustomPropertyUnionPropertyOrder()
PropertyKind = ComponentPropertyKind.Input,
DisplayName = "My Property",
Description = "My Property Description",
DataType = PFxDataType.Boolean,
DataType = PaYamlPropertyDataType.Boolean,
Default = new("true"),
});
actualYaml.TrimEnd().Should().Be("""
Expand Down
19 changes: 19 additions & 0 deletions src/Persistence/PaYaml/Models/NamedObjectCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.PowerPlatform.PowerApps.Persistence.PaYaml.Models;

public static class NamedObjectCollectionExtensions
{
public static NamedObjectMapping<TValue>? EmptyToNull<TValue>(this NamedObjectMapping<TValue>? collection)
where TValue : notnull
{
return collection?.Count == 0 ? null : collection;
}

public static NamedObjectSequence<TValue>? EmptyToNull<TValue>(this NamedObjectSequence<TValue>? collection)
where TValue : notnull
{
return collection?.Count == 0 ? null : collection;
}
}
18 changes: 0 additions & 18 deletions src/Persistence/PaYaml/Models/PowerFx/PFxFunctionParameter.cs

This file was deleted.

20 changes: 0 additions & 20 deletions src/Persistence/PaYaml/Models/PowerFx/PFxFunctionReturnType.cs

This file was deleted.

22 changes: 18 additions & 4 deletions src/Persistence/PaYaml/Models/SchemaV3/ComponentDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,28 @@ public record ComponentCustomPropertyUnion : ComponentCustomPropertyBase
{
public bool? RaiseOnReset { get; init; }

public PFxDataType? DataType { get; init; }
public PaYamlPropertyDataType? DataType { get; init; }

public PFxFunctionReturnType? ReturnType { get; init; }

public NamedObjectSequence<PFxFunctionParameter> Parameters { get; init; } = new();
public PaYamlPropertyDataType? ReturnType { get; init; }

/// <summary>
/// The default script for this custom input property.
/// </summary>
public PFxExpressionYaml? Default { get; init; }

public NamedObjectSequence<ComponentCustomPropertyParameter>? Parameters { get; init; }
}

public record ComponentCustomPropertyParameter()
{
public string? Description { get; init; }

public bool IsOptional { get; init; }

public PaYamlPropertyDataType? DataType { get; init; }

/// <summary>
/// The default script for this optional parameter.
/// </summary>
public PFxExpressionYaml? Default { get; init; }
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.PowerPlatform.PowerApps.Persistence.PaYaml.Models.PowerFx;
namespace Microsoft.PowerPlatform.PowerApps.Persistence.PaYaml.Models.SchemaV3;

public enum PFxDataType
public enum PaYamlPropertyDataType
{
/// <summary>
/// aka Void. Only valid for function return types which are allowed to have side effects.
/// </summary>
None,
Text,
Number,
Boolean,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.PowerPlatform.PowerApps.Persistence.PaYaml.Models.PowerFx;
using Microsoft.PowerPlatform.PowerApps.Persistence.PaYaml.Models.SchemaV3;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
Expand Down Expand Up @@ -81,7 +80,7 @@ private void AddTypeConverters<TBuilder>(BuilderSkeleton<TBuilder> builder)
{
builder.WithTypeConverter(new PFxExpressionYamlConverter(Options.PFxExpressionYamlFormatting));
builder.WithTypeConverter(new NamedObjectYamlConverter<ControlInstance>(this));
builder.WithTypeConverter(new NamedObjectYamlConverter<PFxFunctionParameter>(this));
builder.WithTypeConverter(new NamedObjectYamlConverter<ComponentCustomPropertyParameter>(this));
}

// BUG 27469059: Internal classes not accessible to test project. InternalsVisibleTo attribute added to csproj doesn't get emitted because GenerateAssemblyInfo is false.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
ComponentDefinitions:
MyHeaderComponent:
DefinitionType: CanvasComponent
Description: A component with input properties.
CustomProperties:
MyTextProp1:
PropertyKind: Input
DisplayName: App Title
Description: The title of the App
RaiseOnReset: true
DataType: Text
Default: ="Text"

MyNumberProp1:
PropertyKind: Input
DataType: Number
Default: =100

MyInputFunc1:
PropertyKind: InputFunction
ReturnType: Number
Default: =lhs + rhs

OnMyEvent1:
PropertyKind: Event
ReturnType: None
# default for void is no formula
Default: =

Properties:
Fill: =Color.Azure
# Note: Custom input properties do not get any rules stored here.
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,10 @@ ComponentDefinitions:
PropertyKind: InputFunction
Description: An input function property
ReturnType: Number
Default: =parameter1 + 1
Parameters:
- parameter1:
DataType: Number
IsRequired: true
Default: =parameter1 + 1

MyOutputFunc2:
PropertyKind: OutputFunction
Expand All @@ -55,16 +54,15 @@ ComponentDefinitions:
Parameters:
- parameter1:
DataType: Number
IsRequired: true

MyEvent1None:
PropertyKind: Event
Description: An event property
ReturnType: Number
Default: =1 // comment
Parameters:
- parameter1:
DataType: Number
IsRequired: true

MyAction1:
PropertyKind: Action
Expand All @@ -73,20 +71,20 @@ ComponentDefinitions:
Parameters:
- parameter1:
DataType: Number
IsRequired: true
- parameter3:
IsOptional: true
DataType: Text
Default: ="Hello"
- param4:
IsOptional: true
DataType: Number

Properties:
Fill: =RGBA(141, 198, 63, 1)
Height: =50
HeaderTitle: =Concatenate(MyHeaderComponent.AppTitle, " - ", MyHeaderComponent.ScreenTitle)
Width: =640
X: =0
Y: =0
MyAction1: =Set(myvar, parameter1)

Children:
- Label4:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
ComponentDefinitions:
MyHeaderComponent:
DefinitionType: CanvasComponent
Description: A component with input properties.
CustomProperties:
MyTextProp1:
PropertyKind: Output
DisplayName: App Title
Description: The title of the App
DataType: Text

MyNumberProp1:
PropertyKind: Output
DataType: Number

MyOutputFunc1:
PropertyKind: OutputFunction
ReturnType: Number
Parameters:
- lhs:
DataType: Number
Default: =100
- rhs:
IsOptional: true
DataType: Number
Default: =1

DoMyAction1:
PropertyKind: Action
ReturnType: None
Parameters:
- newValue:
DataType: Number
Default: =100
- reason:
IsOptional: true
DataType: Text
Default: ="Text"

# Note: Custom output properties have their rules stored here
# because they are internal implementation details of each instance.
Properties:
DoMyAction1: =
Fill: =Color.Azure
MyNumberProp1: =100
MyOutputFunc1: =lhs + rhs
MyTextProp1: ="Text"
Loading

0 comments on commit 68a1f10

Please sign in to comment.