diff --git a/src/modules/crm/CRMCore.Module.GraphQL/CRMCore.Module.GraphQL.csproj b/src/modules/crm/CRMCore.Module.GraphQL/CRMCore.Module.GraphQL.csproj index a220072..1c47059 100644 --- a/src/modules/crm/CRMCore.Module.GraphQL/CRMCore.Module.GraphQL.csproj +++ b/src/modules/crm/CRMCore.Module.GraphQL/CRMCore.Module.GraphQL.csproj @@ -7,6 +7,7 @@ + diff --git a/src/modules/crm/CRMCore.Module.GraphQL/DbContextExtensions.cs b/src/modules/crm/CRMCore.Module.GraphQL/DbContextExtensions.cs new file mode 100644 index 0000000..7d90443 --- /dev/null +++ b/src/modules/crm/CRMCore.Module.GraphQL/DbContextExtensions.cs @@ -0,0 +1,18 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; +using System.Reflection; + +namespace CRMCore.Module.GraphQL +{ + public static class DbContextExtensions + { + public static IQueryable Query(this DbContext context, string entityName) => + context.Query(context.Model.FindEntityType(entityName).ClrType); + + static readonly MethodInfo SetMethod = typeof(DbContext).GetMethod(nameof(DbContext.Set)); + + public static IQueryable Query(this DbContext context, Type entityType) => + (IQueryable)SetMethod.MakeGenericMethod(entityType).Invoke(context, null); + } +} diff --git a/src/modules/crm/CRMCore.Module.GraphQL/GraphQLController.cs b/src/modules/crm/CRMCore.Module.GraphQL/GraphQLController.cs index 8003d5f..2824052 100644 --- a/src/modules/crm/CRMCore.Module.GraphQL/GraphQLController.cs +++ b/src/modules/crm/CRMCore.Module.GraphQL/GraphQLController.cs @@ -17,8 +17,16 @@ public GraphQLController(Schema schema) graphQLSchema = schema; } - [HttpPost("")] - public async Task Get([FromQuery] string query = "{ crm_Tasks_list { id } }") + /// + /// Try as following + /// { crm_Tasks_list(offset:1, first:10) { name } } + /// { crm_Tasks_list { name } } + /// { crm_Tasks(id: "5BEF390D-5B71-4DBA-853A-00E164D4EA93") { name } } + /// + /// + /// + [HttpPost] + public async Task Get([FromQuery] string query = "{ crm_Tasks_list(offset:1, first:10) { id, name } }") { var result = await new DocumentExecuter().ExecuteAsync( new ExecutionOptions() @@ -28,6 +36,11 @@ public async Task Get([FromQuery] string query = "{ crm_Tasks_list { id } ).ConfigureAwait(false); + if (result.Errors?.Count > 0) + { + return result.Errors.ToString(); + } + var json = new DocumentWriter(indent: true).Write(result.Data); return json; } diff --git a/src/modules/crm/CRMCore.Module.GraphQL/GraphQLQuery.cs b/src/modules/crm/CRMCore.Module.GraphQL/GraphQLQuery.cs index 368da9f..852d240 100644 --- a/src/modules/crm/CRMCore.Module.GraphQL/GraphQLQuery.cs +++ b/src/modules/crm/CRMCore.Module.GraphQL/GraphQLQuery.cs @@ -1,19 +1,16 @@ -using CRMCore.Module.Data; -using CRMCore.Module.GraphQL.Models; -using GraphQL.Resolvers; +using CRMCore.Module.GraphQL.Models; +using CRMCore.Module.GraphQL.Resolvers; using GraphQL.Types; using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; namespace CRMCore.Module.GraphQL { public class GraphQLQuery : ObjectGraphType { private IDatabaseMetadata _dbMetadata; - private ApplicationDbContext _dbContext; + private DbContext _dbContext; - public GraphQLQuery(ApplicationDbContext dbContext, IDatabaseMetadata dbMetadata) + public GraphQLQuery(DbContext dbContext, IDatabaseMetadata dbMetadata) { _dbMetadata = dbMetadata; _dbContext = dbContext; @@ -23,7 +20,7 @@ public GraphQLQuery(ApplicationDbContext dbContext, IDatabaseMetadata dbMetadata foreach (var metaTable in _dbMetadata.GetMetadataTables()) { var tableType = new TableType(metaTable); - /*AddField(new FieldType() + AddField(new FieldType { Name = metaTable.TableName, Type = tableType.GetType(), @@ -32,9 +29,9 @@ public GraphQLQuery(ApplicationDbContext dbContext, IDatabaseMetadata dbMetadata Arguments = new QueryArguments( tableType.TableArgs ) - });*/ + }); - //lets add key to get list of current table + // lets add key to get list of current table var listType = new ListGraphType(tableType); AddField(new FieldType { @@ -42,61 +39,11 @@ public GraphQLQuery(ApplicationDbContext dbContext, IDatabaseMetadata dbMetadata Type = listType.GetType(), ResolvedType = listType, Resolver = new MyFieldResolver(metaTable, _dbContext), - /*Arguments = new QueryArguments( - tableType.TableArgs - ) */ + Arguments = new QueryArguments( + tableType.TableArgs + ) }); } } } - - public class MyFieldResolver : IFieldResolver - { - private TableMetadata _tableMetadata; - private ApplicationDbContext _dbContext; - - public MyFieldResolver(TableMetadata tableMetadata, ApplicationDbContext dbContext) - { - _tableMetadata = tableMetadata; - _dbContext = dbContext; - } - - public object Resolve(ResolveFieldContext context) - { - var source = context.Source; - - List> finalResult = new List>(); - if (context.FieldName.Contains("_list")) - { - - using (var command = _dbContext.Database.GetDbConnection().CreateCommand()) - { - command.CommandText = "SELECT * FROM " + _tableMetadata.TableName; - _dbContext.Database.OpenConnection(); - using (var result = command.ExecuteReader()) - { - while (result.Read()) - { - var temp = new Dictionary(); - - for (var index = 0; index < result.FieldCount; index++) - { - var lowerCase = Char.ToLowerInvariant(result.GetName(index)[0]) + result.GetName(index).Substring(1); - temp.Add(lowerCase, result[result.GetName(index)]); - } - - finalResult.Add(temp); - } - } - - if (_dbContext.Database.GetDbConnection().State == System.Data.ConnectionState.Open) - { - _dbContext.Database.CloseConnection(); - } - } - } - - return finalResult; - } - } } diff --git a/src/modules/crm/CRMCore.Module.GraphQL/Models/ColumnMetadata.cs b/src/modules/crm/CRMCore.Module.GraphQL/Models/ColumnMetadata.cs index 9f6a4fe..73497e3 100644 --- a/src/modules/crm/CRMCore.Module.GraphQL/Models/ColumnMetadata.cs +++ b/src/modules/crm/CRMCore.Module.GraphQL/Models/ColumnMetadata.cs @@ -1,25 +1,15 @@ -using System.ComponentModel.DataAnnotations.Schema; - -namespace CRMCore.Module.GraphQL.Models +namespace CRMCore.Module.GraphQL.Models { public class ColumnMetadata { - [Column("name")] public string ColumnName { get; set; } - [Column("type")] public string DataType { get; set; } - - [Column("notnull")] - public string IsNullable - { - get; set; - } } } diff --git a/src/modules/crm/CRMCore.Module.GraphQL/Models/DatabaseMetadata.cs b/src/modules/crm/CRMCore.Module.GraphQL/Models/DatabaseMetadata.cs index 9792742..e95658a 100644 --- a/src/modules/crm/CRMCore.Module.GraphQL/Models/DatabaseMetadata.cs +++ b/src/modules/crm/CRMCore.Module.GraphQL/Models/DatabaseMetadata.cs @@ -1,5 +1,4 @@ -using CRMCore.Module.Data; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; using System.Collections.Generic; using System.Linq; @@ -8,9 +7,9 @@ namespace CRMCore.Module.GraphQL.Models { public class DatabaseMetadata : IDatabaseMetadata { - protected ApplicationDbContext _dbContext; + protected DbContext _dbContext; - public DatabaseMetadata(ApplicationDbContext dbContext) + public DatabaseMetadata(DbContext dbContext) { _dbContext = dbContext; DatabaseName = _dbContext.Database.GetDbConnection().Database; @@ -37,22 +36,9 @@ public IEnumerable GetMetadataTables() private void LoadMetaData() { - // var res = new List(); - /*res.Add( - FetchTableMetaData("Customers") - );*/ - Tables = FetchTableMetaData(); } - /*public List GetMetadataTables() - { - if (Tables == null) - return new List(); - - return Tables; - } */ - private List FetchTableMetaData() { var metaTables = new List(); @@ -63,6 +49,7 @@ private List FetchTableMetaData() var tableName = relational.TableName; metaTable.TableName = tableName; + metaTable.AssemblyFullName = entityType.ClrType.FullName; metaTable.Columns = GetColumnsMetadata(entityType).ToList(); metaTables.Add(metaTable); diff --git a/src/modules/crm/CRMCore.Module.GraphQL/Models/TableMetadata.cs b/src/modules/crm/CRMCore.Module.GraphQL/Models/TableMetadata.cs index bd54df7..4032d72 100644 --- a/src/modules/crm/CRMCore.Module.GraphQL/Models/TableMetadata.cs +++ b/src/modules/crm/CRMCore.Module.GraphQL/Models/TableMetadata.cs @@ -1,14 +1,13 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; namespace CRMCore.Module.GraphQL.Models { public class TableMetadata { - [Column("table_name")] public string TableName { get; set; } + public string AssemblyFullName { get; set; } + public List Columns { get; set; } } } diff --git a/src/modules/crm/CRMCore.Module.GraphQL/Resolvers/MyFieldResolver.cs b/src/modules/crm/CRMCore.Module.GraphQL/Resolvers/MyFieldResolver.cs new file mode 100644 index 0000000..7a97f89 --- /dev/null +++ b/src/modules/crm/CRMCore.Module.GraphQL/Resolvers/MyFieldResolver.cs @@ -0,0 +1,48 @@ +using CRMCore.Module.GraphQL.Models; +using GraphQL.Resolvers; +using GraphQL.Types; +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; +using System.Linq.Dynamic.Core; + +namespace CRMCore.Module.GraphQL.Resolvers +{ + public class MyFieldResolver : IFieldResolver + { + private TableMetadata _tableMetadata; + private DbContext _dbContext; + + public MyFieldResolver(TableMetadata tableMetadata, DbContext dbContext) + { + _tableMetadata = tableMetadata; + _dbContext = dbContext; + } + + public object Resolve(ResolveFieldContext context) + { + var queryable = _dbContext.Query(_tableMetadata.FullQualifiedName); + if (context.FieldName.Contains("_list")) + { + + var first = context.Arguments["first"] != null ? + context.GetArgument("first", int.MaxValue) : + int.MaxValue; + + var offset = context.Arguments["offset"] != null ? + context.GetArgument("offset", 0) : + 0; + + return queryable + .Skip(offset) + .Take(first) + .ToDynamicList(); + } + else + { + var id = context.GetArgument("id"); + return queryable.FirstOrDefault($"Id == @0", id); + } + } + } +} diff --git a/src/modules/crm/CRMCore.Module.GraphQL/Resolvers/NameFieldResolver.cs b/src/modules/crm/CRMCore.Module.GraphQL/Resolvers/NameFieldResolver.cs new file mode 100644 index 0000000..1b346be --- /dev/null +++ b/src/modules/crm/CRMCore.Module.GraphQL/Resolvers/NameFieldResolver.cs @@ -0,0 +1,34 @@ +using GraphQL.Resolvers; +using GraphQL.Types; +using System; + +namespace CRMCore.Module.GraphQL.Resolvers +{ + public class NameFieldResolver : IFieldResolver + { + public object Resolve(ResolveFieldContext context) + { + var source = context.Source; + + if (source == null) + { + return null; + } + + var name = Char.ToUpperInvariant(context.FieldAst.Name[0]) + context.FieldAst.Name.Substring(1); + var value = GetPropValue(source, name); + + if (value == null) + { + throw new InvalidOperationException($"Expected to find property {context.FieldAst.Name} on {context.Source.GetType().Name} but it does not exist."); + } + + return value; + } + + private static object GetPropValue(object src, string propName) + { + return src.GetType().GetProperty(propName).GetValue(src, null); + } + } +} diff --git a/src/modules/crm/CRMCore.Module.GraphQL/TableType.cs b/src/modules/crm/CRMCore.Module.GraphQL/Types/TableType.cs similarity index 54% rename from src/modules/crm/CRMCore.Module.GraphQL/TableType.cs rename to src/modules/crm/CRMCore.Module.GraphQL/Types/TableType.cs index 1ecdb5f..3f2581b 100644 --- a/src/modules/crm/CRMCore.Module.GraphQL/TableType.cs +++ b/src/modules/crm/CRMCore.Module.GraphQL/Types/TableType.cs @@ -1,28 +1,27 @@ using CRMCore.Module.GraphQL.Models; +using CRMCore.Module.GraphQL.Resolvers; using GraphQL; -using GraphQL.Resolvers; using GraphQL.Types; using System; using System.Collections.Generic; -using System.Reflection; -namespace CRMCore.Module.GraphQL +namespace CRMCore.Module.GraphQL.Types { - public class TableType : ObjectGraphType> + public class TableType : ObjectGraphType { public QueryArguments TableArgs { get; set; } - private IDictionary _SqliteTypeToSystemType; - protected IDictionary SqliteTypeToSystemType + private IDictionary _databaseTypeToSystemType; + protected IDictionary DatabaseTypeToSystemType { get { - if (_SqliteTypeToSystemType == null) + if (_databaseTypeToSystemType == null) { - _SqliteTypeToSystemType = new Dictionary { + _databaseTypeToSystemType = new Dictionary { {"uniqueidentifier", typeof(String) }, { "char", typeof(String) }, { "nvarchar", typeof(String) }, @@ -31,7 +30,7 @@ protected IDictionary SqliteTypeToSystemType { "bit", typeof(bool) } }; } - return _SqliteTypeToSystemType; + return _databaseTypeToSystemType; } } @@ -52,11 +51,11 @@ private void InitGraphTableColumn(ColumnMetadata columnMetadata) columnMetadata.ColumnName ); - columnField.Resolver = new DictionaryNameFieldResolver(); - // FillArgs(columnMetadata.ColumnName); + columnField.Resolver = new NameFieldResolver(); + FillArgs(columnMetadata.ColumnName); } - /*private void FillArgs(string columnName) + private void FillArgs(string columnName) { if (TableArgs == null) { @@ -71,38 +70,18 @@ private void InitGraphTableColumn(ColumnMetadata columnMetadata) { TableArgs.Add(new QueryArgument { Name = columnName }); } - } */ - private Type ResolveColumnMetaType(string dbType) - { - if (SqliteTypeToSystemType.ContainsKey(dbType)) - return SqliteTypeToSystemType[dbType]; - - return typeof(String); + TableArgs.Add(new QueryArgument { Name = "id" }); + TableArgs.Add(new QueryArgument { Name = "first" }); + TableArgs.Add(new QueryArgument { Name = "offset" }); } - } - - public class DictionaryNameFieldResolver : IFieldResolver - { - // private BindingFlags _flags = BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance; - public object Resolve(ResolveFieldContext context) + private Type ResolveColumnMetaType(string dbType) { - var source = context.Source; + if (DatabaseTypeToSystemType.ContainsKey(dbType)) + return DatabaseTypeToSystemType[dbType]; - if (source == null) - { - return null; - } - - var value = (source as IDictionary)[context.FieldAst.Name]; - - if (value == null) - { - throw new InvalidOperationException($"Expected to find property {context.FieldAst.Name} on {context.Source.GetType().Name} but it does not exist."); - } - - return value; + return typeof(String); } } } diff --git a/src/targets/CRMCore.Application.Crm.targets/ServiceExtensions.cs b/src/targets/CRMCore.Application.Crm.targets/ServiceExtensions.cs index b0592f0..47a522b 100644 --- a/src/targets/CRMCore.Application.Crm.targets/ServiceExtensions.cs +++ b/src/targets/CRMCore.Application.Crm.targets/ServiceExtensions.cs @@ -12,6 +12,7 @@ using System.Text.Encodings.Web; using CRMCore.Module.Swagger; using CRMCore.Module.GraphQL; +using Microsoft.EntityFrameworkCore; namespace CRMCore.Application.Crm.targets { @@ -35,6 +36,8 @@ public static IServiceCollection AddCrmCore(this IServiceCollection services) extendOptionsBuilder.Extend(optionsBuilder, connectionString, assemblyName); }); + services.AddScoped(resolver => resolver.GetRequiredService()); + services.AddCors(options => { options.AddPolicy("CorsPolicy",