diff --git a/Demo/Demo.csproj b/Demo/Demo.csproj
index 9450b4e..dadea88 100644
--- a/Demo/Demo.csproj
+++ b/Demo/Demo.csproj
@@ -19,6 +19,7 @@
+
diff --git a/Demo/Startup.cs b/Demo/Startup.cs
index c26d25b..a7a97ff 100644
--- a/Demo/Startup.cs
+++ b/Demo/Startup.cs
@@ -4,15 +4,11 @@ using Demo.DynamicQueries;
using Demo.Queries;
using FluentValidation;
using FluentValidation.AspNetCore;
-using HotChocolate.Types;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.HttpsPolicy;
-using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
using PoweredSoft.CQRS;
using PoweredSoft.CQRS.Abstractions;
using PoweredSoft.CQRS.AspNetCore.Mvc;
@@ -24,13 +20,11 @@ using PoweredSoft.CQRS.GraphQL.HotChocolate;
using PoweredSoft.Data;
using PoweredSoft.Data.Core;
using PoweredSoft.DynamicQuery;
-using System;
-using System.Collections.Generic;
using System.Linq;
-using System.Threading.Tasks;
+using PoweredSoft.CQRS.GraphQL.HotChocolate.DynamicQuery;
namespace Demo
-{
+{
public class Startup
{
public Startup(IConfiguration configuration)
@@ -65,6 +59,7 @@ namespace Demo
.AddGraphQLServer()
.AddQueryType(d => d.Name("Query"))
.AddPoweredSoftQueries()
+ .AddPoweredSoftDynamicQueries()
.AddMutationType(d => d.Name("Mutation"))
.AddPoweredSoftMutations();
@@ -114,14 +109,14 @@ namespace Demo
app.UseAuthorization();
- //app.UseSwagger();
+ app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
- //app.UseSwaggerUI(c =>
- //{
- // c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
- //});
+ app.UseSwaggerUI(c =>
+ {
+ c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
+ });
app.UseEndpoints(endpoints =>
{
diff --git a/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLAdvanceQueryAggregate.cs b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLAdvanceQueryAggregate.cs
new file mode 100644
index 0000000..a3d353c
--- /dev/null
+++ b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLAdvanceQueryAggregate.cs
@@ -0,0 +1,21 @@
+using PoweredSoft.DynamicQuery;
+using PoweredSoft.DynamicQuery.Core;
+using System;
+
+namespace PoweredSoft.CQRS.GraphQL
+{
+ public class GraphQLAdvanceQueryAggregate
+ {
+ public string Path { get; set; }
+ public AggregateType Type { get; set; }
+
+ internal IAggregate ToAggregate()
+ {
+ return new Aggregate
+ {
+ Path = Path,
+ Type = Type
+ };
+ }
+ }
+}
diff --git a/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLAdvanceQueryFilter.cs b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLAdvanceQueryFilter.cs
new file mode 100644
index 0000000..20464f0
--- /dev/null
+++ b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLAdvanceQueryFilter.cs
@@ -0,0 +1,49 @@
+using PoweredSoft.DynamicQuery;
+using PoweredSoft.DynamicQuery.Core;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace PoweredSoft.CQRS.GraphQL.DynamicQuery
+{
+ public class GraphQLAdvanceQueryFilter
+ {
+ public bool? And { get; set; }
+ public FilterType Type { get; set; }
+ public string Path { get; set; }
+ public GraphQLVariantInput Value { get; set; }
+ public bool? Not { get; set; }
+
+ public List Filters { get; set; }
+
+ internal IFilter ToFilter()
+ {
+ if (Type == FilterType.Composite)
+ {
+ var ret = new CompositeFilter
+ {
+ And = And,
+ Type = FilterType.Composite
+ };
+
+ if (Filters == null)
+ ret.Filters = new List();
+ else
+ ret.Filters = Filters.Select(t => t.ToFilter()).ToList();
+
+ return ret;
+ }
+ else
+ {
+ return new SimpleFilter
+ {
+ And = And,
+ Type = Type,
+ Not = Not,
+ Path = Path,
+ Value = Value.GetRawObjectValue()
+ };
+ }
+ }
+ }
+}
diff --git a/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLAdvanceQueryGroup.cs b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLAdvanceQueryGroup.cs
new file mode 100644
index 0000000..b0b70cd
--- /dev/null
+++ b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLAdvanceQueryGroup.cs
@@ -0,0 +1,21 @@
+using PoweredSoft.DynamicQuery;
+using PoweredSoft.DynamicQuery.Core;
+using System;
+
+namespace PoweredSoft.CQRS.GraphQL
+{
+ public class GraphQLAdvanceQueryGroup
+ {
+ public string Path { get; set; }
+ public bool? Ascending { get; set; }
+
+ internal IGroup ToGroup()
+ {
+ return new Group
+ {
+ Path = Path,
+ Ascending = Ascending
+ };
+ }
+ }
+}
diff --git a/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLAggregateResult.cs b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLAggregateResult.cs
new file mode 100644
index 0000000..7348070
--- /dev/null
+++ b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLAggregateResult.cs
@@ -0,0 +1,11 @@
+using PoweredSoft.DynamicQuery.Core;
+
+namespace PoweredSoft.CQRS.GraphQL.DynamicQuery
+{
+ public class GraphQLAggregateResult
+ {
+ public string Path { get; set; }
+ public AggregateType Type { get; set; }
+ public GraphQLVariantResult Value { get; set; }
+ }
+}
diff --git a/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLDynamicQuery.cs b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLDynamicQuery.cs
new file mode 100644
index 0000000..f3cf550
--- /dev/null
+++ b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLDynamicQuery.cs
@@ -0,0 +1,73 @@
+using PoweredSoft.CQRS.DynamicQuery.Abstractions;
+using PoweredSoft.CQRS.GraphQL.DynamicQuery;
+using PoweredSoft.DynamicQuery.Core;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace PoweredSoft.CQRS.GraphQL.DynamicQuery
+{
+ public class GraphQLDynamicQuery : GraphQLDynamicQuery, IDynamicQuery
+ where TSource : class
+ where TDestination : class
+ {
+
+ }
+
+ public class GraphQLDynamicQuery : GraphQLDynamicQuery,
+ IDynamicQuery
+ where TSource : class
+ where TDestination : class
+ where TParams : class
+ {
+ public TParams Params { get; set; }
+
+ public TParams GetParams() => Params;
+ }
+
+ public class GraphQLDynamicQuery : IDynamicQuery
+ {
+ public int? Page { get; set; }
+ public int? PageSize { get; set; }
+
+ public List Sorts { get; set; }
+ public List Filters { get; set; }
+ public List Groups { get; set; }
+ public List Aggregates { get; set; }
+
+ public List GetAggregates()
+ {
+ if (Aggregates == null)
+ return new List();
+
+ return Aggregates.Select(a => a.ToAggregate()).ToList();
+ }
+
+ public List GetFilters()
+ {
+ if (Filters == null)
+ return new List();
+
+ return Filters.Select(t => t.ToFilter()).ToList();
+ }
+
+ public List GetGroups()
+ {
+ if (Groups == null)
+ return new List();
+
+ return Groups.Select(t => t.ToGroup()).ToList();
+ }
+
+ public int? GetPage() => Page;
+
+ public int? GetPageSize() => PageSize;
+
+ public List GetSorts()
+ {
+ if (Sorts == null)
+ return new List();
+
+ return Sorts.Select(t => t.ToSort()).ToList();
+ }
+ }
+}
diff --git a/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLDynamicQueryExecutionResult.cs b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLDynamicQueryExecutionResult.cs
new file mode 100644
index 0000000..4ce2aae
--- /dev/null
+++ b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLDynamicQueryExecutionResult.cs
@@ -0,0 +1,59 @@
+using PoweredSoft.DynamicQuery.Core;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace PoweredSoft.CQRS.GraphQL.DynamicQuery
+{
+ public class GraphQLDynamicQueryExecutionResult : GraphQLDynamicQueryResult
+ {
+ public List> Groups { get; set; }
+ public long TotalRecords { get; set; }
+ public long? NumberOfPages { get; set; }
+
+ public void FromResult(IQueryExecutionResult queryResult)
+ {
+ TotalRecords = queryResult.TotalRecords;
+ NumberOfPages = queryResult.NumberOfPages;
+
+
+ if (queryResult.Aggregates != null)
+ Aggregates = queryResult.Aggregates.Select(ConvertAggregateResult).ToList();
+
+ if (queryResult.Data != null)
+ Data = queryResult.Data;
+
+ if (queryResult is IQueryExecutionGroupResult groupedResult)
+ Groups = groupedResult.Groups.Select(ConvertGroupResult).ToList();
+ }
+
+ protected virtual GraphQLDynamicQueryGroupResult ConvertGroupResult(IGroupQueryResult arg)
+ {
+ var group = new GraphQLDynamicQueryGroupResult();
+
+ group.GroupPath = arg.GroupPath;
+ group.GroupValue = new GraphQLVariantResult(arg.GroupValue);
+
+ if (arg.Data != null)
+ group.Data = arg.Data;
+
+ if (arg.Aggregates != null)
+ group.Aggregates = arg.Aggregates.Select(ConvertAggregateResult).ToList();
+
+ if (arg.HasSubGroups)
+ group.SubGroups = arg.SubGroups.Select(ConvertGroupResult).ToList();
+
+ return group;
+ }
+
+ protected virtual GraphQLAggregateResult ConvertAggregateResult(IAggregateResult arg)
+ {
+ return new GraphQLAggregateResult
+ {
+ Path = arg.Path,
+ Type = arg.Type,
+ Value = new GraphQLVariantResult(arg.Value)
+ };
+ }
+
+ }
+}
diff --git a/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLDynamicQueryGroupResult.cs b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLDynamicQueryGroupResult.cs
new file mode 100644
index 0000000..4e3b703
--- /dev/null
+++ b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLDynamicQueryGroupResult.cs
@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace PoweredSoft.CQRS.GraphQL.DynamicQuery
+{
+ public class GraphQLDynamicQueryGroupResult : GraphQLDynamicQueryResult
+ {
+ public string GroupPath { get; set; }
+ public GraphQLVariantResult GroupValue { get; set; }
+ public bool HasSubGroups => SubGroups?.Any() == true;
+ public List> SubGroups { get; set; }
+ }
+}
diff --git a/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLDynamicQueryResult.cs b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLDynamicQueryResult.cs
new file mode 100644
index 0000000..fe4973b
--- /dev/null
+++ b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLDynamicQueryResult.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+
+namespace PoweredSoft.CQRS.GraphQL.DynamicQuery
+{
+ public class GraphQLDynamicQueryResult
+ {
+ public List Data { get; set; }
+ public List Aggregates { get; set; }
+ }
+}
diff --git a/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLSort.cs b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLSort.cs
new file mode 100644
index 0000000..0e0b6c5
--- /dev/null
+++ b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLSort.cs
@@ -0,0 +1,17 @@
+using PoweredSoft.DynamicQuery;
+using PoweredSoft.DynamicQuery.Core;
+using System;
+
+namespace PoweredSoft.CQRS.GraphQL.DynamicQuery
+{
+ public class GraphQLSort
+ {
+ public string Path { get; set; }
+ public bool? Ascending { get; set; }
+
+ internal ISort ToSort()
+ {
+ return new Sort(Path, Ascending);
+ }
+ }
+}
\ No newline at end of file
diff --git a/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLVariant.cs b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLVariant.cs
new file mode 100644
index 0000000..4a6401a
--- /dev/null
+++ b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLVariant.cs
@@ -0,0 +1,90 @@
+using System;
+
+namespace PoweredSoft.CQRS.GraphQL.DynamicQuery
+{
+ public abstract class GraphQLVariant
+ {
+
+ protected virtual string ResolveTypeName(object value)
+ {
+ if (value != null)
+ {
+ if (value is int)
+ return "int";
+ if (value is long)
+ return "long";
+ if (value is string)
+ return "string";
+ if (value is bool)
+ return "boolean";
+ if (value is decimal)
+ return "decimal";
+ if (value is DateTime)
+ return "datetime";
+ }
+
+ return null;
+ }
+
+ public string GetTypeName()
+ {
+ var value = GetRawObjectValue();
+ return ResolveTypeName(value);
+ }
+
+ public virtual void SetVariant(object raw)
+ {
+ ClearVariant();
+ if (raw != null)
+ {
+ if (raw is int rawInt)
+ IntValue = rawInt;
+ if (raw is long rawLong)
+ LongValue = rawLong;
+ if (raw is string rawStr)
+ StringValue = rawStr;
+ if (raw is bool rawBool)
+ BooleanValue = rawBool;
+ if (raw is decimal rawDec)
+ DecimalValue = rawDec;
+ if (raw is DateTime rawDt)
+ DateTimeValue = rawDt;
+ }
+ }
+
+ public virtual object GetRawObjectValue()
+ {
+ if (IntValue != null && IntValue is int)
+ return IntValue;
+ if (LongValue != null && LongValue is long)
+ return LongValue;
+ if (StringValue != null && StringValue is string)
+ return StringValue;
+ if (BooleanValue != null && BooleanValue is bool)
+ return BooleanValue;
+ if (DecimalValue != null && DecimalValue is decimal)
+ return DecimalValue;
+ if (DateTimeValue != null && DateTimeValue is DateTime)
+ return DateTimeValue;
+
+ return null;
+ }
+
+ public int? IntValue { get; set; }
+ public long? LongValue { get; set; }
+ public string StringValue { get; set; }
+ public decimal? DecimalValue { get; set; }
+ public DateTime? DateTimeValue { get; set; }
+ public bool? BooleanValue { get; set; }
+
+ public virtual void ClearVariant()
+ {
+ this.IntValue = null;
+ this.LongValue = null;
+ this.StringValue = null;
+ this.DecimalValue = null;
+ this.DateTimeValue = null;
+ this.BooleanValue = null;
+ }
+ }
+}
diff --git a/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLVariantInput.cs b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLVariantInput.cs
new file mode 100644
index 0000000..ded90f3
--- /dev/null
+++ b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLVariantInput.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace PoweredSoft.CQRS.GraphQL.DynamicQuery
+{
+ public class GraphQLVariantInput : GraphQLVariant
+ {
+ }
+}
diff --git a/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLVariantResult.cs b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLVariantResult.cs
new file mode 100644
index 0000000..5ce0475
--- /dev/null
+++ b/PoweredSoft.CQRS.GraphQL.DynamicQuery/GraphQLVariantResult.cs
@@ -0,0 +1,60 @@
+using Newtonsoft.Json;
+
+namespace PoweredSoft.CQRS.GraphQL.DynamicQuery
+{
+ public class GraphQLVariantResult : GraphQLVariant
+ {
+ public GraphQLVariantResult()
+ {
+
+ }
+
+ public GraphQLVariantResult(object raw)
+ {
+ SetVariant(raw);
+ }
+
+ protected override string ResolveTypeName(object value)
+ {
+ var valueType = base.ResolveTypeName(value);
+ if (value != null && valueType == null)
+ return "json";
+
+ return valueType;
+ }
+
+ public override object GetRawObjectValue()
+ {
+ if (jsonValue != null)
+ return jsonValue;
+
+ return base.GetRawObjectValue();
+ }
+
+ private object jsonValue = null;
+
+ public string Json
+ {
+ get
+ {
+ if (jsonValue != null)
+ return JsonConvert.SerializeObject(jsonValue, new JsonSerializerSettings
+ {
+ ReferenceLoopHandling = ReferenceLoopHandling.Ignore
+ });
+
+ return null;
+ }
+ set
+ {
+ jsonValue = JsonConvert.DeserializeObject(value);
+ }
+ }
+
+ public override void ClearVariant()
+ {
+ base.ClearVariant();
+ this.jsonValue = null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PoweredSoft.CQRS.GraphQL.DynamicQuery/PoweredSoft.CQRS.GraphQL.DynamicQuery.csproj b/PoweredSoft.CQRS.GraphQL.DynamicQuery/PoweredSoft.CQRS.GraphQL.DynamicQuery.csproj
new file mode 100644
index 0000000..995173c
--- /dev/null
+++ b/PoweredSoft.CQRS.GraphQL.DynamicQuery/PoweredSoft.CQRS.GraphQL.DynamicQuery.csproj
@@ -0,0 +1,17 @@
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PoweredSoft.CQRS.GraphQL.HotChocolate.DynamicQuery/DynamicQueryObjectType.cs b/PoweredSoft.CQRS.GraphQL.HotChocolate.DynamicQuery/DynamicQueryObjectType.cs
new file mode 100644
index 0000000..564f955
--- /dev/null
+++ b/PoweredSoft.CQRS.GraphQL.HotChocolate.DynamicQuery/DynamicQueryObjectType.cs
@@ -0,0 +1,84 @@
+using HotChocolate.Types;
+using PoweredSoft.CQRS.Abstractions;
+using PoweredSoft.CQRS.Abstractions.Discovery;
+using PoweredSoft.CQRS.DynamicQuery.Discover;
+using System;
+
+namespace PoweredSoft.CQRS.GraphQL.HotChocolate.DynamicQuery
+{
+
+ internal class DynamicQueryObjectType : ObjectTypeExtension
+ {
+ private readonly IQueryDiscovery queryDiscovery;
+
+ public DynamicQueryObjectType(IQueryDiscovery queryDiscovery) : base()
+ {
+ this.queryDiscovery = queryDiscovery;
+ }
+
+ protected override void Configure(IObjectTypeDescriptor descriptor)
+ {
+ base.Configure(descriptor);
+ descriptor.Name("Query");
+
+ foreach(var q in queryDiscovery.GetQueries())
+ {
+ if (q.Category == "DynamicQuery" && q is DynamicQueryMeta dq)
+ {
+ var f = descriptor.Field(q.LowerCamelCaseName);
+
+ // service to execute with.
+ var queryHandlerServiceType = typeof(IQueryHandler<,>).MakeGenericType(dq.QueryType, dq.QueryResultType);
+
+ // destermine argument type.
+ Type argumentType;
+ Type runnerType;
+ if (dq.ParamsType != null)
+ {
+ argumentType = typeof(GraphQL.DynamicQuery.GraphQLDynamicQuery<,,>).MakeGenericType(
+ dq.SourceType, dq.DestinationType, dq.ParamsType);
+
+ runnerType = typeof(DynamicQueryRunnerWithParams<,,>)
+ .MakeGenericType(dq.SourceType, dq.DestinationType, dq.ParamsType);
+ }
+ else
+ {
+ argumentType = typeof(GraphQL.DynamicQuery.GraphQLDynamicQuery<,>).MakeGenericType(
+ dq.SourceType, dq.DestinationType);
+
+ runnerType = typeof(DynamicQueryRunner<,>)
+ .MakeGenericType(dq.SourceType, dq.DestinationType);
+ }
+
+ f.Argument("params", a => a
+ .Type(argumentType)
+ .DefaultValue(Activator.CreateInstance(argumentType))
+ );
+
+ // make generic type of outgoing type.
+ var resultType = typeof(GraphQL.DynamicQuery.GraphQLDynamicQueryExecutionResult<>)
+ .MakeGenericType(dq.DestinationType);
+
+ f.Type(resultType);
+
+ // resolver
+ f.Resolve(async r =>
+ {
+ dynamic argument = r.ArgumentValue