Пример #1
0
        private async Task ProcessPutCommand(RestProcessParameters param, CancellationToken ct)
        {
            var restQuery        = new RestQueryString(param);
            var entityDefinition = param.Model.EntitiesByPluralName.SafeGet(restQuery.Root);

            if (entityDefinition == null)
            {
                throw new BadRequestException($"Unable to find an entity definition named {restQuery.Root}.");
            }

            if (restQuery.AdditionalQualifier.IsNullOrEmpty())
            {
                throw new BadRequestException($"A put request must apply to a specific id.");
            }

            var propertyBag = param.RequestSerializer.Deserialize(param.Context.Request.Body);

            propertyBag[MetaConstants.IdProperty] = restQuery.AdditionalQualifier;
            var entity = await EntityFactory.Hydrate(entityDefinition, propertyBag, ct);

            entity.Etag = param.Headers.IfNoneMatch();
            var result = await EntityService.Update(entity, ct);

            Respond(param, result.ToPropertyBag(), StatusCodes.Status200OK, new Dictionary <string, string>
            {
                { WebConstants.ETagHeader, entity.Etag },
                { WebConstants.LastModifiedHeader, result.LastModified.SelectOrDefault(x => x.ToUtcIso8601()) }
            });
        }
Пример #2
0
        private async Task ProcessDeleteCommand(RestProcessParameters param, CancellationToken ct)
        {
            var restQuery        = new RestQueryString(param);
            var entityDefinition = param.Model.EntitiesByPluralName.SafeGet(restQuery.Root);

            if (entityDefinition == null)
            {
                throw new BadRequestException($"Unable to find an entity definition named {restQuery.Root}.");
            }

            if (restQuery.AdditionalQualifier.IsNullOrEmpty())
            {
                throw new BadRequestException($"A delete request must include an id.");
            }

            var result = await EntityService.Delete(entityDefinition, restQuery.AdditionalQualifier, ct);

            if (!result)
            {
                Respond(param,
                        new MessageResponse($"Unable to find a {entityDefinition.SingleName} with id {restQuery.AdditionalQualifier}."),
                        StatusCodes.Status404NotFound);
                return;
            }
            Respond(param, null, StatusCodes.Status204NoContent);
        }
Пример #3
0
        private async Task ProcessPostCommand(RestProcessParameters param, CancellationToken ct)
        {
            var restQuery        = new RestQueryString(param);
            var entityDefinition = param.Model.EntitiesByPluralName.SafeGet(restQuery.Root);

            if (entityDefinition == null)
            {
                throw new BadRequestException($"Unable to find an entity definition named {restQuery.Root}.");
            }

            if (!restQuery.AdditionalQualifier.IsNullOrEmpty())
            {
                throw new BadRequestException($"A post request should not have an additional qualifier.");
            }

            var propertyBag = param.RequestSerializer.Deserialize(param.Context.Request.Body);
            var entity      = await EntityFactory.Create(entityDefinition, propertyBag, ct);

            var result = await EntityService.Create(entity, ct);

            Respond(param, result.ToPropertyBag(), StatusCodes.Status201Created, new Dictionary <string, string>
            {
                { WebConstants.ETagHeader, result.Etag },
                { WebConstants.LastModifiedHeader, result.LastModified.SelectOrDefault(x => x.ToUtcIso8601()) },
                {
                    "Location",
                    param.Context.Request.AbsoluteUri(
                        $"{_options.Value.MountPoint}/{entityDefinition.Model.Name}/{entityDefinition.PluralName}/{WebUtility.UrlEncode(result.Id.ToString())}")
                }
            });
        }
Пример #4
0
        public RestQueryString(RestProcessParameters param)
        {
            Root = param.PathSegments.Length.IsOdd()
                                ? param.PathSegments.Last()
                                : param.PathSegments.ElementAtOrDefault(param.PathSegments.Length - 2);
            AdditionalQualifier = param.PathSegments.Length.IsEven() ? param.PathSegments.Last() : null;

            var pathValues            = new Dictionary <string, string>();
            var remainingPathSegments = param.PathSegments.Length.IsOdd()
                                ? param.PathSegments.Take(param.PathSegments.Length - 1)
                                : param.PathSegments.Take(param.PathSegments.Length - 2);

            string currentKey = null;

            foreach (var remainingPathSegment in remainingPathSegments)
            {
                if (currentKey == null)
                {
                    currentKey = remainingPathSegment;
                }
                else
                {
                    pathValues.Add(currentKey, remainingPathSegment);
                    currentKey = null;
                }
            }
            PathValues        = pathValues.ToImmutableDictionary();
            QueryStringValues = param.Context.Request.Query.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase);
        }
Пример #5
0
        private async Task ProcessPatchCommand(RestProcessParameters param, CancellationToken ct)
        {
            var restQuery        = new RestQueryString(param);
            var entityDefinition = param.Model.EntitiesByPluralName.SafeGet(restQuery.Root);

            if (entityDefinition == null)
            {
                throw new BadRequestException($"Unable to find an entity definition named {restQuery.Root}.");
            }

            if (restQuery.AdditionalQualifier.IsNullOrEmpty())
            {
                throw new BadRequestException($"A patch request must apply to a specific id.");
            }

            if (!(param.RequestSerializer is JsonRestSerializer))
            {
                throw new BadRequestException("Only JSON Patch is supported for patching resources.");
            }

            JsonPatchDocument jsonPatchDocument;

            try
            {
                jsonPatchDocument = HiveJsonSerializer.Instance.Deserialize <JsonPatchDocument>(param.Context.Request.Body);
            }
            catch (Exception ex)
            {
                throw new BadRequestException("Malformed JSON Patch.", ex);
            }

            var entity = await EntityService.GetById(entityDefinition, restQuery.AdditionalQualifier, ct);

            if (entity == null)
            {
                Respond(param,
                        new MessageResponse($"Unable to find a {entityDefinition.SingleName} with id {restQuery.AdditionalQualifier}"),
                        StatusCodes.Status404NotFound);
                return;
            }
            jsonPatchDocument.ApplyTo(entity, new EntityPatchObjectAdapter());
            entity.Etag = param.Headers.IfNoneMatch();
            var result = await EntityService.Update(entity, ct);

            Respond(param, result.ToPropertyBag(), StatusCodes.Status200OK, new Dictionary <string, string>
            {
                { WebConstants.ETagHeader, result.Etag },
                { WebConstants.LastModifiedHeader, result.LastModified.SelectOrDefault(x => x.ToUtcIso8601()) }
            });
        }
Пример #6
0
        private void Respond(RestProcessParameters param, object message, int statusCode,
                             IDictionary <string, string> responseHeaders = null)
        {
            param.Context.Response.StatusCode = statusCode;
            foreach (var responseHeader in responseHeaders.Safe())
            {
                param.Context.Response.Headers.Add(responseHeader.Key, new StringValues(responseHeader.Value));
            }

            if (message != null)
            {
                param.Context.Response.Headers["Content-Type"] = param.ResponseSerializer.MediaTypes.First();
                param.ResponseSerializer.Serialize(message, param.Context.Response.Body);
            }
        }
Пример #7
0
        private static void FillOperators(IQuery query, RestQueryString restQuery, RestProcessParameters param)
        {
            if (restQuery.QueryStringValues.ContainsKey(RestConstants.LimitOperator))
            {
                var maxResults = restQuery.QueryStringValues[RestConstants.LimitOperator].First().IntSafeInvariantParse();
                query.SetMaxResults(maxResults);
            }

            if (restQuery.QueryStringValues.ContainsKey(RestConstants.OrderOperator))
            {
                var orderby      = restQuery.QueryStringValues[RestConstants.OrderOperator].FirstOrDefault();
                var orderbyMatch = OrderByRegex.Match(orderby);
                if (orderbyMatch.Success)
                {
                    switch (orderbyMatch.Groups["asc"].Value.IsNullOrEmpty() ? "asc" : orderbyMatch.Groups["asc"].Value)
                    {
                    case "asc":
                        query.AddOrder(Order.Asc(orderbyMatch.Groups["prop"].Value));
                        break;

                    case "desc":
                        query.AddOrder(Order.Desc(orderbyMatch.Groups["prop"].Value));
                        break;
                    }
                }
            }

            if (restQuery.QueryStringValues.ContainsKey(RestConstants.IncludeOperator))
            {
                foreach (var value in restQuery.QueryStringValues[RestConstants.IncludeOperator])
                {
                    query.Include(value);
                }
            }

            if (restQuery.QueryStringValues.ContainsKey(RestConstants.SelectOperator))
            {
                query.SetProjection(Projection.Properties(restQuery.QueryStringValues[RestConstants.SelectOperator]));
            }

            if (param.Context.Request.Headers.ContainsKey(RestConstants.ContinuationTokenHeader))
            {
                var continuationToken = param.Context.Request.Headers[RestConstants.ContinuationTokenHeader].FirstOrDefault();
                query.SetContinuationToken(continuationToken);
            }
        }
Пример #8
0
        protected override async Task <bool> ProcessInternal(HttpContext context, CancellationToken ct)
        {
            var request = context.Request;

            PathString partialPath;

            if (!request.Path.StartsWithSegments(_options.Value.MountPoint, out partialPath))
            {
                return(false);
            }

            var pathSegments = partialPath.Value?.Split('/').Where(x => !x.Trim().IsNullOrEmpty()).ToArray();

            if (pathSegments == null || pathSegments.Length < 2)
            {
                return(false);
            }

            var model = await MetaService.GetModel(pathSegments[0], ct);

            pathSegments = pathSegments.Skip(1).ToArray();

            var headers            = context.Request.GetTypedHeaders();
            var requestSerializer  = _serializerFactory.GetByMediaType(headers.ContentType?.MediaType);
            var responseSerializer = headers.Accept.IsNullOrEmpty()
                                ? requestSerializer
                                : _serializerFactory.GetByMediaType(headers.Accept.Safe().Select(x => x.MediaType));
            var processParams = new RestProcessParameters(context, headers, pathSegments, model, requestSerializer,
                                                          responseSerializer);

            if (HttpMethods.IsGet(request.Method))
            {
                await ProcessQuery(processParams, ct);

                return(true);
            }

            if (HttpMethods.IsPost(request.Method))
            {
                await ProcessPostCommand(processParams, ct);

                return(true);
            }

            if (HttpMethods.IsPut(request.Method))
            {
                await ProcessPutCommand(processParams, ct);

                return(true);
            }

            if (HttpMethods.IsPatch(request.Method))
            {
                await ProcessPatchCommand(processParams, ct);

                return(true);
            }

            if (HttpMethods.IsDelete(request.Method))
            {
                await ProcessDeleteCommand(processParams, ct);

                return(true);
            }

            throw new NotSupportedException();
        }
Пример #9
0
        private IQuery CreateQuery(IEntityDefinition entityDefinition, RestQueryString restQuery, RestProcessParameters param)
        {
            var query = EntityService.CreateQuery(entityDefinition);

            foreach (
                var queryStringValue in
                restQuery.QueryStringValues.Where(x => !x.Key.StartsWith(RestConstants.ReservedOperatorsPrefix)))
            {
                RecursiveFillQuery(query, null, queryStringValue.Key, queryStringValue.Value);
            }

            FillOperators(query, restQuery, param);

            if (restQuery.PathValues.Count == 0)
            {
                return(query);
            }

            if (restQuery.PathValues.Count > 1)
            {
                throw new BadRequestException("Multi-level query expressions in path is not supported.");
            }

            var pathValue = restQuery.PathValues.First();

            var targetEntityDef = entityDefinition.Model.EntitiesByPluralName.SafeGet(pathValue.Key);

            if (targetEntityDef == null)
            {
                throw new NotFoundException($"Unable to find an entity definition named {pathValue.Key}");
            }

            var targetPropertyDefinition = entityDefinition.Properties.SafeGet(targetEntityDef.SingleName);

            if (targetPropertyDefinition != null)
            {
                var subQuery = query.GetOrCreateSubQuery(targetPropertyDefinition.Name);
                RecursiveFillQuery(subQuery, null, MetaConstants.IdProperty, pathValue.Value);
            }

            return(query);
        }
Пример #10
0
        private async Task ProcessQuery(RestProcessParameters param, CancellationToken ct)
        {
            var restQuery        = new RestQueryString(param);
            var entityDefinition = param.Model.EntitiesByPluralName.SafeGet(restQuery.Root);

            if (entityDefinition == null)
            {
                throw new NotFoundException($"Unable to find an entity definition named {restQuery.Root}.");
            }

            if (!restQuery.AdditionalQualifier.IsNullOrEmpty())
            {
                var result = await EntityService.GetById(entityDefinition, restQuery.AdditionalQualifier, ct);

                if (result == null)
                {
                    Respond(param,
                            new MessageResponse($"Unable to find a {entityDefinition.SingleName} with id {restQuery.AdditionalQualifier}"),
                            StatusCodes.Status404NotFound);
                    return;
                }

                var ifNoneMatch = param.Headers.IfNoneMatch();
                if (string.Equals(ifNoneMatch, result.Etag, StringComparison.Ordinal))
                {
                    Respond(param, null, StatusCodes.Status304NotModified, new Dictionary <string, string>
                    {
                        { WebConstants.ETagHeader, result.Etag },
                        { WebConstants.LastModifiedHeader, result.LastModified.SelectOrDefault(x => x.ToUtcIso8601()) }
                    });
                }
                else
                {
                    Respond(param, result.ToPropertyBag(), StatusCodes.Status200OK, new Dictionary <string, string>
                    {
                        { WebConstants.ETagHeader, result.Etag },
                        { WebConstants.LastModifiedHeader, result.LastModified.SelectOrDefault(x => x.ToUtcIso8601()) }
                    });
                }
            }
            else
            {
                var query = CreateQuery(entityDefinition, restQuery, param);
                if (query.MaxResults.HasValue)
                {
                    var result = await query.ToContinuationEnumerable <IEntity>(ct);

                    Respond(param, result.Select(x => x.ToPropertyBag()).ToArray(), StatusCodes.Status200OK,
                            new Dictionary <string, string>
                    {
                        { RestConstants.ContinuationTokenHeader, result.ContinuationToken }
                    });
                }
                else
                {
                    var result = await query.ToEnumerable <IEntity>(ct);

                    Respond(param, result.Select(x => x.ToPropertyBag()).ToArray(), StatusCodes.Status200OK);
                }
            }
        }