Example #1
0
        public async Task Invoke(HttpContext context)
        {
            // POST application/graphql body is query
            // POST application/json and { "query": "..." }
            // TODO: POST /graphql?query={users{name}}
            // TODO: GET /graphql?query={users{name}}

            if (!context.Request.Path.Value.StartsWith($"/{Config.GraphQLRoute}"))
            {
                await _next(context).ConfigureAwait(false);

                return;
            }

            if (_authenticationEnabled && !context.User.Identity.IsAuthenticated)
            {
                context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                return;
            }

            if (context.Request.Method != "POST" || !_allowedTypes.Any(context.Request.ContentType.Contains))
            {
                context.Response.StatusCode = (int)HttpStatusCode.NotImplemented;
                await context.Response.WriteAsync(JsonConvert.SerializeObject(new { errors = new[] { "Not implemented" } }));

                return;
            }

            GraphQLResult result = null;

            var(success, query, error) = await ParseQuery(context);

            if (!success)
            {
                result = new GraphQLResult {
                    Errors = new List <string> {
                        error
                    }
                };
            }
            else
            {
                var toReplace = new[] { "\r\n", "\\r\\n", "\\n", "\n" };

                query = toReplace.Aggregate(query, (acc, curr) => acc.Replace(curr, ""));

                result = await GraphQL.HandleQuery(query, _datastore);
            }

            var json = result.Errors?.Any() == true
                            ? JsonConvert.SerializeObject(new { data = result.Data, errors = result.Errors })
                            : JsonConvert.SerializeObject(new { data = result.Data });

            result.Notifications?.ForEach(msg => _bus.Publish("updated", msg));

            context.Response.ContentType = "application/json";
            context.Response.StatusCode  = result.Errors?.Any() == true ? (int)HttpStatusCode.BadRequest : (int)HttpStatusCode.OK;

            await context.Response.WriteAsync(json);
        }
        private static Dictionary <string, dynamic> ExecuteMutationFields(dynamic source, dynamic target, Dictionary <string, Fields> fields, string idFieldName, bool isRoot = false)
        {
            return(fields.ToDictionary(
                       pair => GetResponseName(pair),
                       pair =>
            {
                var allowedActions = new[] { "add", "update", "replace", "delete" };

                var fieldsToUse = CollectFields(pair.Value.First().SelectionSet);
                var collectionName = fieldsToUse.Any()
                                            ? fieldsToUse.FirstOrDefault().Key
                                            : allowedActions.Aggregate(pair.Value.First().Name, (e, c) => e.Replace(c, "")).ToLower();
                var collection = ((IDataStore)source).GetCollection(collectionName);

                if (pair.Value.First().Name.StartsWith("add"))
                {
                    var newItem = ResolveMutationField(fieldsToUse.First().Key, target, pair.Value, isRoot);

                    var success = collection.InsertOne(newItem);
                    var itemId = ((dynamic)newItem).id;

                    dynamic updateData = success ? new { Method = "POST", Path = $"{collectionName}/{itemId}", Collection = collectionName, ItemId = itemId } : null;

                    ExecutionResult item = GetMutationReturnItem(source, fieldsToUse, itemId, idFieldName);
                    return Tuple.Create(item.Data as object, updateData);
                    //return item.Value as object;
                }
                else if (pair.Value.First().Name.StartsWith("update"))
                {
                    dynamic id = GraphQL.GetInputId(pair, idFieldName);
                    ExpandoObject newItem = ResolveMutationField("patch", target, pair.Value, isRoot);

                    var success = collection.UpdateOne(id, newItem);

                    dynamic updateData = success ? new { Method = "PATCH", Path = $"{collectionName}/{id}", Collection = collectionName, ItemId = id } : null;

                    ExecutionResult item = GetMutationReturnItem(source, fieldsToUse, id, idFieldName);
                    return Tuple.Create(item.Data as object, updateData);
                    //return item.Value as object;
                }
                else if (pair.Value.First().Name.StartsWith("replace"))
                {
                    dynamic id = GraphQL.GetInputId(pair, idFieldName);
                    dynamic newItem = ResolveMutationField(collectionName, target, pair.Value, isRoot);

                    // Make sure that new data has id field correctly
                    ObjectHelper.SetFieldValue(newItem, idFieldName, id);
                    //newItem.id = id;

                    var success = collection.ReplaceOne(id, newItem as ExpandoObject);

                    dynamic updateData = success ? new { Method = "PUT", Path = $"{collectionName}/{id}", Collection = collectionName, ItemId = id } : null;

                    ExecutionResult item = GetMutationReturnItem(source, fieldsToUse, id, idFieldName);
                    return Tuple.Create(item.Data as object, updateData);
                    //return item.Value as object;
                }
                else if (pair.Value.First().Name.StartsWith("delete"))
                {
                    dynamic id = GraphQL.GetInputId(pair, idFieldName);

                    var success = collection.DeleteOne(id);

                    dynamic updateData = success ? new { Method = "DELETE", Path = $"{collectionName}/{id}", Collection = collectionName, ItemId = id } : null;
                    var item = new ExecutionResult {
                        Data = success
                    };

                    return Tuple.Create(success, updateData);
                    //return success;
                }
                else
                {
                    return Tuple.Create <dynamic, dynamic>(null, null);
                }
            }));
        }
        private static Task <Dictionary <string, object> > ExecuteMutationFieldsAsync(dynamic source, dynamic target, Dictionary <string, Fields> fields, bool isRoot = false)
        {
            return(fields.ToDictionaryAsync <KeyValuePair <string, Fields>, string, ResolveFieldResult <object>, object>(
                       pair => GetResponseName(pair),
                       pair =>
            {
                var allowedActions = new[] { "add", "update", "replace", "delete" };

                var fieldsToUse = CollectFields(pair.Value.First().SelectionSet);
                var collectionName = fieldsToUse.Any()
                                            ? fieldsToUse.FirstOrDefault().Key
                                            : allowedActions.Aggregate(pair.Value.First().Name, (e, c) => e.Replace(c, "")).ToLower();
                var collection = ((IDataStore)source).GetCollection(collectionName);

                if (pair.Value.First().Name.StartsWith("add"))
                {
                    var newItem = ResolveMutationFieldAsync(fieldsToUse.First().Key, target, pair.Value, isRoot);

                    var success = collection.InsertOne(newItem);
                    var itemId = ((dynamic)newItem).id;

                    dynamic updateData = success ? new { Method = "POST", Path = $"{collection}/{itemId}", Collection = collection, ItemId = itemId } : null;

                    ResolveFieldResult <object> item = GetMutationReturnItem(source, fieldsToUse, itemId);
                    //return Tuple.Create(item, updateData);
                    //return item;
                    return Task.FromResult(item);
                }
                else if (pair.Value.First().Name.StartsWith("update"))
                {
                    dynamic id = GraphQL.GetInputId(pair);
                    ExpandoObject newItem = ResolveMutationFieldAsync("patch", target, pair.Value, isRoot);

                    var success = collection.UpdateOne(e => e.id == id, newItem);

                    dynamic updateData = success ? new { Method = "PATCH", Path = $"{collection}/{id}", Collection = collection, ItemId = id } : null;

                    ResolveFieldResult <object> item = GetMutationReturnItem(source, fieldsToUse, id);
                    //return Tuple.Create(item, updateData);
                    //return item;

                    return Task.FromResult(item);
                }
                else if (pair.Value.First().Name.StartsWith("replace"))
                {
                    dynamic id = GraphQL.GetInputId(pair);
                    dynamic newItem = ResolveMutationFieldAsync(collectionName, target, pair.Value, isRoot);

                    // Make sure that new data has id field correctly
                    newItem.id = id;

                    var success = collection.ReplaceOne(e => e.id == id, newItem as ExpandoObject);

                    dynamic updateData = success ? new { Method = "PUT", Path = $"{collection}/{id}", Collection = collection, ItemId = id } : null;

                    ResolveFieldResult <object> item = GetMutationReturnItem(source, fieldsToUse, id);
                    //return Tuple.Create(item, updateData);
                    //return item;

                    return Task.FromResult(item);
                }
                else if (pair.Value.First().Name.StartsWith("delete"))
                {
                    dynamic id = GraphQL.GetInputId(pair);

                    var success = collection.DeleteOne(e => e.id == id);

                    dynamic updateData = success ? new { Method = "DELETE", Path = $"{collection}/{id}", Collection = collection, ItemId = id } : null;
                    var item = new ResolveFieldResult <object> {
                        Value = success
                    };

                    //return Tuple.Create(item, updateData);
                    //return item;

                    return Task.FromResult(item);
                }
                else
                {
                    dynamic r = null;
                    return Tuple.Create(new ResolveFieldResult <object>(), r);
                }
            }));
        }