public MyFieldResolver(
     FillDataExtensions fillDataExtensions,
     IHttpContextAccessor httpContextAccessor)
 {
     _httpContextAccessor = httpContextAccessor;
     _fillDataExtensions  = fillDataExtensions;
     _logger = _httpContextAccessor.HttpContext.RequestServices.GetService <ILogger <MyFieldResolver <TUser, TRole, TUserRole> > >();
 }
        public async Task InvokeAsync(HttpContext context, ISchema schema, FillDataExtensions fillDataExtensions)
        {
            if (context.WebSockets.IsWebSocketRequest || !context.Request.Path.StartsWithSegments(_path))
            {
                await _next(context);

                return;
            }
            _fillDataExtensions = fillDataExtensions;
            // Handle requests as per recommendation at http://graphql.org/learn/serving-over-http/
            // Inspiration: https://github.com/graphql/express-graphql/blob/master/src/index.js
            var httpRequest  = context.Request;
            var httpResponse = context.Response;

            //var writer = context.RequestServices.GetRequiredService<IDocumentWriter>();
            var cancellationToken = GetCancellationToken(context);

            // GraphQL HTTP only supports GET and POST methods
            bool isGet  = HttpMethods.IsGet(httpRequest.Method);
            bool isPost = HttpMethods.IsPost(httpRequest.Method);

            if (!isGet && !isPost)
            {
                httpResponse.Headers["Allow"] = "GET, POST";
                await WriteErrorResponseAsync(context,
                                              $"Invalid HTTP method. Only GET and POST are supported. {DOCS_URL}",
                                              httpStatusCode : 405 // Method Not Allowed
                                              );

                return;
            }

            // Parse POST body
            GraphQLRequest bodyGQLRequest = null;

            GraphQLRequest[] bodyGQLBatchRequest = null;
            if (isPost)
            {
                if (!MediaTypeHeaderValue.TryParse(httpRequest.ContentType, out var mediaTypeHeader))
                {
                    await WriteErrorResponseAsync(context, $"Invalid 'Content-Type' header: value '{httpRequest.ContentType}' could not be parsed.");

                    return;
                }

                switch (mediaTypeHeader.MediaType)
                {
                case MediaType.JSON:
                    var deserializationResult = await _deserializer.DeserializeFromJsonBodyAsync(httpRequest, cancellationToken);

                    if (!deserializationResult.IsSuccessful)
                    {
                        await WriteErrorResponseAsync(context, "Body text could not be parsed. Body text should start with '{' for normal graphql query or with '[' for batched query.");

                        return;
                    }

                    bodyGQLRequest      = deserializationResult.Single;
                    bodyGQLBatchRequest = deserializationResult.Batch;
                    break;

                case MediaType.GRAPH_QL:
                    bodyGQLRequest = await DeserializeFromGraphBodyAsync(httpRequest.Body);

                    break;

                case MediaType.FORM:
                    var formCollection = await httpRequest.ReadFormAsync();

                    bodyGQLRequest = DeserializeFromFormBody(formCollection);
                    break;

                default:
                    await WriteErrorResponseAsync(context, $"Invalid 'Content-Type' header: non-supported media type. " +
                                                  $"Must be of '{MediaType.JSON}', '{MediaType.GRAPH_QL}' or '{MediaType.FORM}'. {DOCS_URL}");

                    return;
                }
            }

            // If we don't have a batch request, parse the URL too to determine the actual request to run
            // Querystring params take priority
            GraphQLRequest gqlRequest = null;

            if (bodyGQLBatchRequest == null)
            {
                // Parse URL
                var urlGQLRequest = DeserializeFromQueryString(httpRequest.Query);

                gqlRequest = new GraphQLRequest
                {
                    Query         = urlGQLRequest.Query ?? bodyGQLRequest?.Query,
                    Inputs        = urlGQLRequest.Inputs ?? bodyGQLRequest?.Inputs,
                    OperationName = urlGQLRequest.OperationName ?? bodyGQLRequest?.OperationName
                };
            }

            // Prepare context and execute
            var userContextBuilder = context.RequestServices.GetService <IUserContextBuilder>();
            var userContext        = userContextBuilder == null
                ? new Dictionary <string, object>() // in order to allow resolvers to exchange their state through this object
                : await userContextBuilder.BuildUserContext(context);

            var executer = context.RequestServices.GetRequiredService <IGraphQLExecuter <TSchema> >();

            // var executer = context.RequestServices.GetRequiredService<IDocumentExecuter>();

            // Normal execution with single graphql request
            if (bodyGQLBatchRequest == null)
            {
                var stopwatch = ValueStopwatch.StartNew();
                // var result = await ExecuteRequestAsync(schema, gqlRequest, context, executer, cancellationToken);
                var result = await ExecuteRequestAsync(gqlRequest, userContext, executer, context.RequestServices, cancellationToken);


                await RequestExecutedAsync(new GraphQLRequestExecutionResult(gqlRequest, result, stopwatch.Elapsed));

                if (result.Errors?.Count > 0)
                {
                    await WriteErrorResponseAsync(context, result);
                }
                else
                {
                    await WriteResponseAsync(context, result);
                }
            }
            // Execute multiple graphql requests in one batch
            else
            {
                var             error            = false;
                ExecutionResult executionResult  = null;
                var             executionResults = new ExecutionResult[bodyGQLBatchRequest.Length];
                for (int i = 0; i < bodyGQLBatchRequest.Length; ++i)
                {
                    var gqlRequestInBatch = bodyGQLBatchRequest[i];

                    var stopwatch = ValueStopwatch.StartNew();
                    // var result = await ExecuteRequestAsync(schema, gqlRequestInBatch, context, executer, cancellationToken);
                    var result = await ExecuteRequestAsync(gqlRequestInBatch, userContext, executer, context.RequestServices, cancellationToken);

                    await RequestExecutedAsync(new GraphQLRequestExecutionResult(gqlRequestInBatch, result, stopwatch.Elapsed, i));

                    if (result.Errors?.Count > 0)
                    {
                        error           = true;
                        executionResult = result;
                    }
                    executionResults[i] = result;
                }

                if (error)
                {
                    await WriteErrorResponseAsync(context, executionResult);
                }
                else
                {
                    // await WriteResponseAsync(context, executionResults);
                }
            }
        }
Beispiel #3
0
        public static async Task <IEnumerable <T> > GetPagedAsync <T>(this IQueryable <T> source, IHttpContextAccessor _httpContextAccessor,
                                                                      FillDataExtensions _fillDataExtensions) where T : class
        {
            bool allowCache = true;

            // Filter By
            if (_httpContextAccessor.HttpContext.Request.Query.Any(x => x.Key.Equals("filter_by")))
            {
                var properties = new Dictionary <string, Type>();

                foreach (var propertyInfo in typeof(T).GetProperties())
                {
                    //Console.WriteLine($"_________________TRACEEEEEEEEEEEEEEEEE____________: key: {propertyInfo.Name} value: {propertyInfo.PropertyType.Name}");
                    if (!propertyInfo.GetCustomAttributes(true).Any(x => x.GetType() == typeof(JsonIgnoreAttribute)) &&
                        !propertyInfo.GetCustomAttributes(true).Any(x => x.GetType() == typeof(NotMappedAttribute)) &&
                        !(propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList <>)) &&
                        !(propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable <>)) &&
                        !(propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(ICollection <>)) &&
                        !(typeof(ICollection).IsAssignableFrom(propertyInfo.PropertyType))
                        )
                    {
                        properties.Add(propertyInfo.Name, propertyInfo.PropertyType);
                    }
                }

                var      columnStr = _httpContextAccessor.HttpContext.Request.Query.FirstOrDefault(x => x.Key.Equals("filter_by")).Value.ToString();
                string   pattern   = @"\/|\|";
                string[] columns   = Regex.Split(columnStr, pattern);
                var      divider   = new List <string>();
                Match    match     = Regex.Match(columnStr, pattern);

                // query filtro por AND o OR
                foreach (var(value, index) in columns.Select((v, i) => (v, i)))
                {
                    if (index > 0)
                    {
                        match = match.NextMatch();
                        if (match.Success)
                        {
                            if (match.Value == "/")
                            {
                                divider.Add(" AND ");
                            }
                            else
                            {
                                divider.Add(" OR ");
                            }
                        }
                    }
                    else
                    {
                        if (match.Success)
                        {
                            if (match.Value == "/")
                            {
                                divider.Add(" AND ");
                            }
                            else
                            {
                                divider.Add(" OR ");
                            }
                        }
                    }
                }

                var           expresion = new StringBuilder();
                List <object> values    = new List <object>();
                //Procesamiento query
                string dividerOld = "";
                foreach (var(column, index) in columns.Select((v, i) => (v, i)))
                {
                    allowCache = false;
                    if (index > 0)
                    {
                        if (index > 1 && dividerOld != divider[index - 1])
                        {
                            expresion.Append(")");
                            expresion.Append(divider[index - 1]);
                            expresion.Append("(");
                        }
                        else
                        {
                            expresion.Append(divider[index - 1]);
                        }
                        dividerOld = divider[index - 1];
                    }
                    else
                    {
                        expresion.Append("(");
                    }

                    var      patternStr = @"\=|¬";
                    string[] value      = Regex.Split(column, patternStr);
                    if (string.IsNullOrEmpty(value[1]))
                    {
                        break;
                    }

                    if (value[0] == "$")
                    {
                        try
                        {
                            foreach (var(field, i) in properties.Select((v, i) => (v, i)))
                            {
                                SqlCommandExtension.ConcatFilter(values, expresion, string.Format("@{0}", i + index), field.Key, value[1], column,
                                                                 typeProperty: field.Value, index: i);
                            }
                            break;
                        }
                        catch (Exception)
                        {
                            throw;
                        }
                    }
                    var paramName = string.Format("@{0}", index);
                    SqlCommandExtension.ConcatFilter(values, expresion, paramName, value[0], value[1], column);
                }
                expresion.Append(")");
                Console.WriteLine(expresion.ToString());
                source = source.Where(expresion.ToString().ToLower(), values.ToArray());
            }

            // Order By
            if (_httpContextAccessor.HttpContext.Request.Query.Any(x => x.Key.Equals("order_by")))
            {
                source = source.OrderBy(_httpContextAccessor.HttpContext.Request.Query.FirstOrDefault(x => x.Key.Equals("order_by")).Value.ToString());
            }

            // pagination
            if (_httpContextAccessor.HttpContext.Request.Query.Any(x => x.Key.Equals("enable_pagination")) &&
                bool.TryParse(_httpContextAccessor.HttpContext.Request.Query.FirstOrDefault(x => x.Key.Equals("enable_pagination")).Value.ToString(),
                              out bool enablePagination))
            {
                var pageSizeRequest    = _httpContextAccessor.HttpContext.Request.Query.FirstOrDefault(x => x.Key.Equals("page_size")).Value;
                var currentPageRequest = _httpContextAccessor.HttpContext.Request.Query.FirstOrDefault(x => x.Key.Equals("current_page")).Value;
                int pageSize           = string.IsNullOrEmpty(pageSizeRequest) ? 20 : int.Parse(pageSizeRequest);
                int pageNumber         = string.IsNullOrEmpty(currentPageRequest) ? 1 : int.Parse(currentPageRequest);

                var result = new PagedResultBase();

                result.current_page = pageNumber;
                result.page_size    = pageSize;

                int?rowCount = null;
                if (allowCache)
                {
                    rowCount = source.CacheGetOrCreate(_httpContextAccessor);
                }

                result.row_count = rowCount ?? source.CountAsync().Result;

                var pageCount = (double)result.row_count / pageSize;
                result.page_count = (int)Math.Ceiling(pageCount);

                _fillDataExtensions.Add($"{typeof(T).Name.ToLower()}_list", result);

                //foreach (var propertyInfo in typeof(PagedResultBase).GetProperties())
                //{
                //    var currentValue = propertyInfo.GetValue(result);
                //    _fillDataExtensions.Add(propertyInfo.Name, currentValue);
                //}

                return(await source.Skip((pageNumber - 1) *pageSize).Take(pageSize).AsNoTracking().ToListAsync());
            }
            return(await source.AsNoTracking().ToListAsync());
        }
        public GraphQLQuery(
            IDatabaseMetadata dbMetadata,
            ITableNameLookup tableNameLookup,
            IHttpContextAccessor httpContextAccessor,
            FillDataExtensions fillDataExtensions,
            IDataLoaderContextAccessor accessor,
            ISERFieldResolver <TUser, TRole, TUserRole> fieldResolver,
            IOptionsMonitor <SERGraphQlOptions> optionsDelegate
            )
        {
            _dbMetadata          = dbMetadata;
            _tableNameLookup     = tableNameLookup;
            _httpContextAccessor = httpContextAccessor;
            _fillDataExtensions  = fillDataExtensions;
            _accessor            = accessor;
            _optionsDelegate     = optionsDelegate;
            _fieldResolver       = fieldResolver;

            Name = "Query";
            var tables = _dbMetadata.GetTableMetadatas();

            foreach (var metaTable in tables)
            {
                var friendlyTableName = metaTable.Type.Name.ToSnakeCase().ToLower();

                dynamic objectGraphType = null;
                if (!_tableNameLookup.ExistGraphType(metaTable.Type.Name))
                {
                    var inherateType = typeof(TableType <>).MakeGenericType(new Type[] { metaTable.Type });
                    objectGraphType = Activator.CreateInstance(inherateType, new object[] { metaTable,
                                                                                            _dbMetadata, _tableNameLookup, _httpContextAccessor, _accessor, _optionsDelegate });
                }

                var tableType = _tableNameLookup.GetOrInsertGraphType(metaTable.Type.Name, objectGraphType);

                dynamic objectCountGraphType = null;
                if (!_tableNameLookup.ExistGraphType($"{metaTable.Type.Name}_count"))
                {
                    var inherateType = typeof(CountTableType <>).MakeGenericType(new Type[] { metaTable.Type });
                    objectCountGraphType = Activator.CreateInstance(inherateType, new object[] { _dbMetadata, metaTable, _tableNameLookup, _optionsDelegate });
                }

                var countTableType = _tableNameLookup.GetOrInsertGraphType($"{metaTable.Type.Name}_count", objectCountGraphType);

                dynamic objectSumGraphType = null;
                if (!_tableNameLookup.ExistGraphType($"{metaTable.Type.Name}_sum_plus"))
                {
                    var inherateType = typeof(SumTableType <>).MakeGenericType(new Type[] { metaTable.Type });
                    objectSumGraphType = Activator.CreateInstance(inherateType, new object[] { _dbMetadata, metaTable, _tableNameLookup, _optionsDelegate });
                }
                var sumTableType = _tableNameLookup.GetOrInsertGraphType($"{metaTable.Type.Name}_sum_plus", objectSumGraphType);
                var ttype        = typeof(TableType <>).MakeGenericType(new Type[] { metaTable.Type });

                AddField(new FieldType
                {
                    Name         = friendlyTableName,
                    Type         = tableType.GetType(),
                    ResolvedType = tableType,
                    Resolver     = _fieldResolver, // new MyFieldResolver<TUser, TRole, TUserRole>(_fillDataExtensions, _httpContextAccessor),
                    Arguments    = new QueryArguments(tableType.TableArgs)
                });

                var     inherateListType = typeof(ListGraphType <>).MakeGenericType(new Type[] { tableType.GetType() });
                dynamic listType         = Activator.CreateInstance(inherateListType);
                listType.ResolvedType = tableType;

                AddField(new FieldType
                {
                    Name         = $"{friendlyTableName}_list",
                    Type         = listType.GetType(),
                    ResolvedType = listType,
                    Resolver     = _fieldResolver,
                    Arguments    = new QueryArguments(tableType.TableArgs)
                });

                AddField(new FieldType
                {
                    Name         = $"{friendlyTableName}_count",
                    Type         = countTableType.GetType(),
                    ResolvedType = countTableType,
                    Resolver     = _fieldResolver,
                    Arguments    = new QueryArguments(countTableType.TableArgs)
                });

                AddField(new FieldType
                {
                    Name         = $"{friendlyTableName}_sum",
                    Type         = ttype,
                    ResolvedType = sumTableType,
                    Resolver     = _fieldResolver,
                    Arguments    = new QueryArguments(sumTableType.TableArgs)
                });
            }
        }