Skip to content

Commit

Permalink
Embedded validation into object building
Browse files Browse the repository at this point in the history
  • Loading branch information
neronotte committed Sep 5, 2024
1 parent 5ab9bba commit 54d7b04
Show file tree
Hide file tree
Showing 9 changed files with 344 additions and 343 deletions.
17 changes: 1 addition & 16 deletions Greg.FetchXmlDom.TestSuite/Model/ColumnExpressionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,11 @@ namespace Greg.FetchXmlDom.Model
[TestFixture]
public class ColumnExpressionTest
{
[Test]
public void CreateAggregate_WithAliasNull_ShouldThrowException()
{
var action = () => ColumnExpression.CreateAggregateColumn("a", null, AggregateFunction.Avg);

action.Should().Throw<ArgumentNullException>();
}

[Test]
public void CreateAggregate_WithAliasEmpty_ShouldThrowException()
{
var action = () => ColumnExpression.CreateAggregateColumn("a", string.Empty, AggregateFunction.Avg);

action.Should().Throw<ArgumentNullException>();
}

[Test]
public void CreateAggregate_WithAliasBlank_ShouldThrowException()
{
var action = () => ColumnExpression.CreateAggregateColumn("a", " ", AggregateFunction.Avg);
var action = () => ColumnExpression.CreateAggregateColumn("a", AggregateFunction.Avg, " ");

action.Should().Throw<ArgumentNullException>();
}
Expand Down
30 changes: 26 additions & 4 deletions Greg.FetchXmlDom.TestSuite/Model/FetchXmlExpressionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,27 @@ public class FetchXmlExpressionTest
[Test]
public void ToString_Empty_ShouldWork()
{
var fetch = new FetchXmlExpression();
var fetch = new FetchXmlExpression("account");
var result = fetch.ToString();

result.Should().Be("<fetch />");
result.Should().Be(@"<fetch>
<entity name='account' />
</fetch>".Replace("'", "\""));
}


[Test]
public void ToString_WithTop_ShouldWork()
{
var fetch = new FetchXmlExpression
var fetch = new FetchXmlExpression("account")
{
Top = 10
};
var result = fetch.ToString();

result.Should().Be("<fetch top=\"10\" />");
result.Should().Be(@"<fetch top='10'>
<entity name='account' />
</fetch>".Replace("'", "\""));
}

[Test]
Expand Down Expand Up @@ -102,5 +106,23 @@ public void ToString_WithMediumComplexQuery02_ShouldWork()
</entity>
</fetch>".Replace("'", "\""));
}


[Test]
public void ToString_WithMediumComplexQuery03_ShouldWork()
{
var fetch = new FetchXmlExpression("contact", aggregate:true);
fetch.AddGroupColumn("lastname");
fetch.AddAggregateColumn("contactid", AggregateFunction.Count, "Count");

var result = fetch.ToString();

result.Should().Be(@"<fetch aggregate='true'>
<entity name='contact'>
<attribute name='lastname' alias='lastname' groupby='true' />
<attribute name='contactid' alias='Count' aggregate='count' />
</entity>
</fetch>".Replace("'", "\""));
}
}
}
62 changes: 28 additions & 34 deletions Greg.FetchXmlDom/Model/ColumnExpression.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Xml;

namespace Greg.FetchXmlDom.Model
{
/// <summary>
/// Represents a column in a FetchXml query.
/// </summary>
public sealed class ColumnExpression : IEquatable<ColumnExpression>, IValidatableObject
/// <summary>
/// Represents a column in a FetchXml query.
/// </summary>
public sealed class ColumnExpression : IEquatable<ColumnExpression>
{
/// <summary>
/// Creates a new ColumnExpression object.
Expand All @@ -29,8 +27,8 @@ public ColumnExpression(string columnName, string alias = null)
/// Creates a new ColumnExpression object representing an aggregate column.
/// </summary>
/// <param name="columnName">The name of the column</param>
/// <param name="alias">An alias for the column</param>
/// <param name="aggregateFunction">The aggregate function to apply</param>
/// <param name="alias">An alias for the column. Alias is required for aggregate queries, If not provided, the column name is used.</param>
/// <param name="rowAggregate">
/// When this value is set to CountChildren a value that includes the total number of child records for the record is included in the results.
/// Learn how to use this attribute <see cref="https://learn.microsoft.com/en-us/power-apps/developer/data-platform/query-hierarchical-data#retrieve-the-number-of-hierarchically-related-child-records"/>.
Expand All @@ -40,12 +38,15 @@ public ColumnExpression(string columnName, string alias = null)
/// Learn more about distinct column values <see cref="https://learn.microsoft.com/en-us/power-apps/developer/data-platform/fetchxml/aggregate-data#distinct-column-values"/>.
/// </param>
/// <exception cref="ArgumentNullException"></exception>
public static ColumnExpression CreateAggregateColumn(string columnName, string alias, AggregateFunction aggregateFunction, bool? rowAggregate = null, bool? distinct = null)
public static ColumnExpression CreateAggregateColumn(string columnName, AggregateFunction aggregateFunction, string alias = null, bool? rowAggregate = null, bool? distinct = null)
{
if (string.IsNullOrWhiteSpace(columnName))
throw new ArgumentNullException(nameof(columnName), "The column name is required");

if (alias == null) alias = columnName;
if (string.IsNullOrWhiteSpace(alias))
throw new ArgumentNullException(nameof(alias), "The alias attribute is required for each attribute element in aggregate queries");
throw new ArgumentNullException(nameof(alias), "The alias is required for aggregate columns");


var column = new ColumnExpression(columnName, alias)
{
Expand All @@ -61,7 +62,7 @@ public static ColumnExpression CreateAggregateColumn(string columnName, string a
/// Creates a new ColumnExpression object acts as a GROUP BY condition for the query
/// </summary>
/// <param name="columnName">The name of the column</param>
/// <param name="alias">An alias for the column</param>
/// <param name="alias">An alias for the column. Alias is required for aggregate queries, If not provided, the column name is used.</param>
/// <param name="dateGrouping">
/// When you group data by a datetime value, this attribute specifies the date part to use.
/// See Date grouping options <see cref="https://learn.microsoft.com/en-us/power-apps/developer/data-platform/fetchxml/reference/attribute#date-grouping-options"/>
Expand All @@ -73,12 +74,14 @@ public static ColumnExpression CreateAggregateColumn(string columnName, string a
/// Use this attribute with a false value to force the grouping to use UTC value.
/// When you don't set this attribute, the default value is true, and the user's time zone is used.
/// </param>
public static ColumnExpression CreateGroupColumn(string columnName, string alias, DateGrouping? dateGrouping = null, bool? userTimeZone = null)
public static ColumnExpression CreateGroupColumn(string columnName, string alias = null, DateGrouping? dateGrouping = null, bool? userTimeZone = null)
{
if (string.IsNullOrWhiteSpace(columnName))
throw new ArgumentNullException(nameof(columnName), "The column name is required");

if (alias == null) alias = columnName;
if (string.IsNullOrWhiteSpace(alias))
throw new ArgumentNullException(nameof(alias), "The alias attribute is required for each attribute element in aggregate queries");
throw new ArgumentNullException(nameof(alias), "The alias is required for aggregate columns");

var column = new ColumnExpression(columnName, alias)
{
Expand All @@ -92,7 +95,6 @@ public static ColumnExpression CreateGroupColumn(string columnName, string alias
/// <summary>
/// The logical name of the column.
/// </summary>
[Required(ErrorMessage = "Attribute name is required")]
public string Name { get; }

/// <summary>
Expand Down Expand Up @@ -144,6 +146,15 @@ public static ColumnExpression CreateGroupColumn(string columnName, string alias



/// <summary>
/// Indicates weather the current column is an aggregate column
/// </summary>
/// <returns>True if the current column is an aggregate column, false otherwise</returns>
public bool IsAggregateColumn()
{
return GroupBy || Aggregate.HasValue;
}


/// <summary>
/// Returns <c>true</c> if the specified object is equal to the current object; otherwise, <c>false</c>.
Expand Down Expand Up @@ -199,23 +210,6 @@ public override int GetHashCode()
}
}

/// <summary>
/// Validates the current object
/// </summary>
/// <param name="validationContext"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var isAggregateQuery = validationContext.Items.ContainsKey("aggregate-query");

if (isAggregateQuery && !string.IsNullOrWhiteSpace(this.Alias))
{
yield return new ValidationResult("The alias attribute is required for each attribute element in aggregate queries", new[] { nameof(Aggregate) });
}
}




public void WriteXml(XmlWriter writer)
Expand All @@ -224,14 +218,14 @@ public void WriteXml(XmlWriter writer)

writer.WriteAttributeString("name", Name);

if (Aggregate.HasValue)
if (!string.IsNullOrWhiteSpace(Alias))
{
writer.WriteAttributeString("aggregate", Aggregate.Value.ToString().ToLowerInvariant());
writer.WriteAttributeString("alias", Alias);
}

if (!string.IsNullOrWhiteSpace(Alias))
if (Aggregate.HasValue)
{
writer.WriteAttributeString("alias", Alias);
writer.WriteAttributeString("aggregate", Aggregate.Value.ToString().ToLowerInvariant());
}

if (DateGrouping.HasValue)
Expand Down
Loading

0 comments on commit 54d7b04

Please sign in to comment.