Beispiel #1
0
        private object CreateCompatibleValue(PaperContext context, object sourceValue, Type targetType)
        {
            if (sourceValue is PropertyMap map)
            {
                var instance = objectFactory.CreateObject(targetType);

                foreach (var key in map.Keys)
                {
                    var property = targetType.GetProperties().FirstOrDefault(x => x.Name.EqualsIgnoreCase(key));
                    if (property == null)
                    {
                        continue;
                    }

                    var value           = map[key];
                    var compatibleValue = CreateCompatibleValue(context, value, property.PropertyType);
                    property.SetValue(instance, compatibleValue);
                }

                return(instance);
            }
            else
            {
                return(Change.To(sourceValue, targetType));
            }
        }
Beispiel #2
0
 private void RenderEntity(PaperContext context, Entity entity, object[] args, object graph)
 {
     entity.AddClass(ClassNames.Record);
     entity.AddClass(graph.GetType());
     entity.SetTitle(Conventions.MakeTitle(graph.GetType()));
     entity.AddProperties(graph);
     entity.AddHeaders(graph, ClassNames.Record);
 }
Beispiel #3
0
 private void FormatEntity(PaperContext context, Entity entity, object[] args, object graph = null)
 {
     RunFormatters(context, entity, args, graph);
     RunActionBuilders(context, entity, args, graph);
     if (args.Length > 0)
     {
         RunFormatters(context, entity, new object[0], graph);
         RunActionBuilders(context, entity, new object[0], graph);
     }
 }
Beispiel #4
0
        private void LinkEntity(PaperContext context, Entity entity)
        {
            var uri = new UriString(context.Request.RequestUri);

            if (context.RenderContext.Filter is IFilter filter)
            {
                uri = filter.CreateUri(uri);
            }

            if (context.RenderContext.Sort is Sort sort)
            {
                uri = sort.CreateUri(uri);

                var headers = entity.Headers().OfType <HeaderDesign>();
                foreach (var fieldName in sort.FieldNames)
                {
                    var matches = headers.Where(x => x.Name.EqualsIgnoreCase(fieldName));
                    foreach (var header in matches)
                    {
                        header.Order = sort.GetSortOrder(fieldName);

                        var order = header.Order == SortOrder.Ascending ? ":desc" : "";
                        var href  = uri.SetArg("sort", $"{header.Name}{order}");
                        header.HeaderEntity.AddLink(href, opt => opt.AddRel(RelNames.Sort));
                    }
                }
            }

            if (context.RenderContext.Page is Page page)
            {
                uri = page.CreateUri(uri);

                if (page.Offset > 0)
                {
                    var href = page.GetFirstPage().CreateUri(uri);
                    entity.AddLink(href, opt => opt.AddRel(RelNames.First));
                }

                if (page.Offset > page.Limit)
                {
                    var href = page.GetPreviousPage().CreateUri(uri);
                    entity.AddLink(href, opt => opt.AddRel(RelNames.Prev));
                }

                if (context.RenderContext.HasMorePages)
                {
                    var href = page.GetNextPage().CreateUri(uri);
                    entity.AddLink(href, opt => opt.AddRel(RelNames.Next));
                }
            }

            entity.SetSelfLink(uri);
        }
Beispiel #5
0
        private void RunFormatters(PaperContext context, Entity entity, object[] args, object graph = null)
        {
            var path  = context.Path;
            var paper = context.Paper;
            var req   = context.Request;
            var res   = context.Response;

            var allArgs = args.Append(graph).NonNull().ToArray();

            var callers = MatchCallers(paper.Formatters, allArgs);

            foreach (var caller in callers)
            {
                var result = objectFactory.Invoke(paper.Paper, caller.Method, caller.Args);
                if (result == null)
                {
                    continue;
                }

                IEnumerable items;

                if (result is IEnumerable enumerable)
                {
                    items = enumerable;
                }
                else if (result is ICollection collection)
                {
                    items = collection;
                }
                else
                {
                    items = new[] { result };
                }

                foreach (var item in items)
                {
                    if (item is IFormatter formatter)
                    {
                        formatter.Format(context, objectFactory, entity);
                    }
                    else if (item is Format format)
                    {
                        format.Invoke(context, objectFactory, entity);
                    }

                    if (item is Link link)
                    {
                        entity.AddLink(link);
                    }
                }
            }
        }
Beispiel #6
0
        public async Task <Ret <Entity> > RenderAsync(PaperContext context, Ret <Result> result)
        {
            var value = result.Value.Value;

            if (value == null)
            {
                var status = HttpEntity.CreateFromRet(context.Request.RequestUri, result);
                return(await Task.FromResult(status));
            }

            var entity = new Entity();
            var args   = context.Args.Values.ToArray();

            var valueType = result.Value.ValueType;
            var isList    = typeof(IEnumerable).IsAssignableFrom(valueType);

            if (isList)
            {
                var list = ((IEnumerable)value).Cast <object>();

                if (context.RenderContext.Page is Page page)
                {
                    context.RenderContext.HasMorePages = (list.Count() >= page.Limit);
                    page.DecreaseLimit();

                    if (context.RenderContext.HasMorePages)
                    {
                        list = list.SkipLast(1);
                    }
                }

                entity.SetTitle(Conventions.MakeTitle(context.Paper.PaperType));
                entity.AddEntities(list, (item, e) =>
                {
                    RenderEntity(context, e, args, item);
                    FormatEntity(context, e, args, item);
                });

                FormatEntity(context, entity, args, value);
            }
            else
            {
                RenderEntity(context, entity, args, value);
                FormatEntity(context, entity, args, value);
            }

            FormatEntity(context, entity, args);
            LinkEntity(context, entity);

            return(await Task.FromResult(entity));
        }
Beispiel #7
0
        private IFilter CreateCompatibleFilter(PaperContext context, HashMap <Var> args, Type filterType)
        {
            var filter = (IFilter)context.Paper.Create(objectFactory, filterType);

            foreach (var property in filter._GetPropertyNames())
            {
                var value = args[property];
                if (value != null)
                {
                    filter._Set(property, value);
                }
            }
            return(filter);
        }
Beispiel #8
0
        public async Task <Ret <Result> > CallAsync(PaperContext context)
        {
            var descriptor = context.Paper;
            var req        = context.Request;
            var res        = context.Response;

            Entity form = null;

            if (!req.Method.EqualsAnyIgnoreCase(MethodNames.Get, MethodNames.Delete))
            {
                form = req.ReadEntityAsync().RunSync();

                // TODO: Uma entidade diferente de "form" não está sendo suportada, mas poderia,
                // se houvesse um algoritmo de conversão.
                var isValid = form.Class.Has(ClassNames.Form);
                if (!isValid)
                {
                    return(Ret.Fail(HttpStatusCode.BadRequest, "Formato de dados não suportado. Os dados devem ser enviados em uma entidade do tipo \"form\"."));
                }
            }

            MethodInfo   method = descriptor.GetMethod(context.Action);
            Ret <Result> ret    = CallPaperMethod(context, descriptor.Paper, method, context.Args, form);

            var isFailure = (ret.Status.CodeClass != HttpStatusClass.Success);

            if (isFailure)
            {
                return(await Task.FromResult(ret));
            }

            Result result = ret.Value;

            var isUri = typeof(string).IsAssignableFrom(result.ValueType) ||
                        typeof(Uri).IsAssignableFrom(result.ValueType) ||
                        typeof(Href).IsAssignableFrom(result.ValueType) ||
                        typeof(UriString).IsAssignableFrom(result.ValueType);

            if (isUri)
            {
                var href = (result.Value as Href)?.ToString() ?? result.Value?.ToString();
                ret = Ret.Create(HttpStatusCode.Found);
                ret.Status.Headers[HeaderNames.Location] = href;
                return(await Task.FromResult(ret));
            }

            return(await Task.FromResult(ret));
        }
Beispiel #9
0
        private void RunActionBuilders(PaperContext context, Entity entity, object[] args, object graph = null)
        {
            var path  = context.Path;
            var paper = context.Paper;
            var req   = context.Request;
            var res   = context.Response;

            var allArgs = args.Append(graph).NonNull().ToArray();

            var callers = MatchCallers(paper.Actions, allArgs, ignore: new[] { typeof(IForm) });

            foreach (var caller in callers)
            {
                RenderForm(caller, context, entity, args, graph);
            }
        }
Beispiel #10
0
        private Ret <Result> CallPaperMethod(PaperContext context, IPaper paper, MethodInfo method, Args args, Entity form)
        {
            object result = null;

            try
            {
                var methodArgs = CreateParameters(context, paper, method, args, form);

                context.RenderContext.Sort   = methodArgs.OfType <Sort>().FirstOrDefault();
                context.RenderContext.Page   = methodArgs.OfType <Page>().FirstOrDefault();
                context.RenderContext.Filter = methodArgs.OfType <IFilter>().FirstOrDefault();

                context.RenderContext.Page?.IncreaseLimit();

                result = objectFactory.Invoke(paper, method, methodArgs);
            }
            catch (Exception ex)
            {
                result = Ret.Fail(ex);
            }

            var resultType = method.ReturnType;

            if (Is.Ret(resultType))
            {
                resultType = TypeOf.Ret(resultType);
            }

            Ret <Result> ret;

            if (result == null)
            {
                // Um método que resulta "void" é considerado OK quando não emite exceção.
                // Um método que resulta nulo é considerado NotFound (Não encontrado).
                var isVoid = method.ReturnType == typeof(void);
                if (isVoid)
                {
                    ret = Ret.OK(new Result
                    {
                        Value     = result,
                        ValueType = resultType
                    });
                }
                else
                {
                    ret = Ret.NotFound(new Result
                    {
                        Value     = result,
                        ValueType = resultType
                    });
                }
            }
            else if (Is.Ret(result))
            {
                ret        = new Ret <Result>();
                ret.Status = (RetStatus)result._Get(nameof(ret.Status));
                ret.Fault  = (RetFault)result._Get(nameof(ret.Fault));
                ret.Value  = new Result
                {
                    Value     = result._Get(nameof(ret.Value)),
                    ValueType = resultType
                };
            }
            else
            {
                ret = Ret.OK(new Result
                {
                    Value     = result,
                    ValueType = resultType
                });
            }

            return(ret);
        }
Beispiel #11
0
        private object[] CreateParameters(PaperContext context, IPaper paper, MethodInfo method, Args args, Entity form)
        {
            var methodArgs = new List <object>();

            var argKeys  = args?.Keys.ToList() ?? new List <string>();
            var formKeys = form?.Properties?.Keys.ToList() ?? new List <string>();

            foreach (var parameter in method.GetParameters())
            {
                var name = parameter.Name;

                if (typeof(Sort).IsAssignableFrom(parameter.ParameterType))
                {
                    var sort = (Sort)context.Paper.Create(objectFactory, parameter.ParameterType);
                    sort.CopyFrom(args);
                    methodArgs.Add(sort);
                    continue;
                }

                if (typeof(Page).IsAssignableFrom(parameter.ParameterType))
                {
                    var page = (Page)context.Paper.Create(objectFactory, parameter.ParameterType);
                    page.CopyFrom(args);
                    methodArgs.Add(page);
                    continue;
                }

                if (typeof(IFilter).IsAssignableFrom(parameter.ParameterType))
                {
                    var filter = CreateCompatibleFilter(context, args, parameter.ParameterType);
                    methodArgs.Add(filter);
                    continue;
                }

                string key = null;

                key = argKeys.FirstOrDefault(x => x.EqualsIgnoreCase(name));
                if (key != null)
                {
                    var value           = args[key];
                    var compatibleValue = CreateCompatibleValue(context, value, parameter.ParameterType);
                    methodArgs.Add(compatibleValue);
                    argKeys.Remove(key);
                    continue;
                }

                key = formKeys.FirstOrDefault(x => x.EqualsIgnoreCase(name));
                if (key != null)
                {
                    var value           = form.Properties[key];
                    var compatibleValue = CreateCompatibleValue(context, value, parameter.ParameterType);
                    methodArgs.Add(compatibleValue);
                    formKeys.Remove(key);
                    continue;
                }

                if (Is.Collection(parameter.ParameterType))
                {
                    var records  = form.Children();
                    var itemType = TypeOf.CollectionElement(parameter.ParameterType);
                    var items    = records.Select(record =>
                                                  CreateCompatibleValue(context, record.Properties, itemType)
                                                  ).ToArray();
                    var compatibleValue = CreateCompatibleValue(context, items, parameter.ParameterType);
                    methodArgs.Add(compatibleValue);
                }
                else
                {
                    var record          = form?.Children().FirstOrDefault();
                    var compatibleValue = CreateCompatibleValue(context, record.Properties, parameter.ParameterType);
                    methodArgs.Add(compatibleValue);
                }
            }

            return(methodArgs.ToArray());
        }
Beispiel #12
0
        public async Task RenderAsync(Request req, Response res, NextAsync next)
        {
            var path = req.Path.Substring(Route.Length);

            // Destacando a ação de uma URI, como em /My/Path/-MyAction
            var tokens      = path.Split("/-");
            var paperPath   = tokens.First();
            var paperAction = tokens.Skip(1).FirstOrDefault() ?? "Index";

            var paper = paperCatalog.FindExact(paperPath).FirstOrDefault();

            if (paper == null)
            {
                await next.Invoke();

                return;
            }

            var hasAction = paper.GetMethod(paperAction) != null;

            if (!hasAction)
            {
                await next.Invoke();

                return;
            }

            var args = new Args();

            args.AddMany(req.QueryArgs);
            args.AddMany(Args.ParsePathArgs(path, paper.PathTemplate));

            var context = new PaperContext();

            context.Paper    = paper;
            context.Path     = paperPath;
            context.Action   = paperAction;
            context.Args     = args;
            context.Request  = req;
            context.Response = res;

            var caller   = objectFactory.CreateObject <PaperCaller>();
            var renderer = objectFactory.CreateObject <PaperRenderer>();

            Ret <Result> result = await caller.CallAsync(context);

            if (result.Status.CodeClass != HttpStatusClass.Success)
            {
                var entity = HttpEntity.CreateFromRet(req.RequestUri, result);
                await SendAsync(res, result, entity);

                return;
            }

            Ret <Entity> media = await renderer.RenderAsync(context, result);

            if (!media.Ok)
            {
                var entity = HttpEntity.CreateFromRet(req.RequestUri, result);
                await SendAsync(res, media, entity);

                return;
            }

            await SendAsync(res, media, media.Value);
        }
Beispiel #13
0
        private void RenderForm(Caller caller, PaperContext context, Entity entity, object[] args, object graph = null)
        {
            var href = new UriString(context.Path.Substring(1)).Append($"-{caller.Method.Name}");

            var action = new EntityAction();

            action.Name   = caller.Method.Name;
            action.Title  = caller.Method.Name.ChangeCase(TextCase.ProperCase);
            action.Href   = href;
            action.Method = MethodNames.Post;

            var parameters = caller.Method.GetParameters();

            for (var i = 0; i < parameters.Length; i++)
            {
                var parameter      = parameters[i];
                var parameterValue = caller.Args[i];

                var name = Conventions.MakeName(parameter.Name);

                var isValue = IsValue(parameter.ParameterType);
                var isArray = !isValue && typeof(IEnumerable).IsAssignableFrom(parameter.ParameterType);
                var isForm  = !isValue && !isArray && typeof(IForm).IsAssignableFrom(parameter.ParameterType);

                if (isValue)
                {
                    action.AddField($"Form.{name}", opt =>
                    {
                        opt.SetDefaults(parameter);
                        opt.SetHidden(true);
                        if (parameterValue != null)
                        {
                            opt.SetValue(parameterValue);
                        }
                    });
                }
                else if (isForm)
                {
                    var properties = parameter.ParameterType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                    foreach (var property in properties)
                    {
                        var fieldName  = Conventions.MakeName(property.Name);
                        var fieldValue = (parameterValue != null) ? property.GetValue(parameterValue) : null;
                        action.AddField($"Form.{name}.{fieldName}", opt =>
                        {
                            opt.SetDefaults(property);
                            if (fieldValue != null)
                            {
                                opt.SetValue(fieldValue);
                            }
                        });
                    }
                }
                else if (isArray)
                {
                    var elementType = TypeOf.CollectionElement(parameter.ParameterType);
                    var properties  = elementType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                    var keys        = (
                        from property in properties
                        select Conventions.MakeName(property.Name)
                        ).ToArray();

                    action.AddField("Records", opt => opt

                                    // XXX: FIXME: Esta propriedade agora deve pertencer a uma entidade.
                                    //.SetTitle("Registros Afetados")

                                    .SetPlaceholder("Selecione os registros afetados")
                                    .SetType(FieldTypeNames.SelectRecord)
                                    .SetDataType(DataTypeNames.Record)
                                    .SetMultiline(true)
                                    .SetProvider(provider => provider
                                                 .AddRel(RelNames.Self)
                                                 .SetKeys(keys)
                                                 )
                                    .SetAllowMany()
                                    .SetRequired()
                                    );
                }
                else
                {
                    foreach (var propertyName in parameterValue._GetPropertyNames())
                    {
                        var property   = parameterValue._GetPropertyInfo(propertyName);
                        var fieldName  = Conventions.MakeName(propertyName);
                        var fieldValue = parameterValue._Get(propertyName);
                        action.AddField($"Record.{fieldName}", opt =>
                        {
                            opt.SetDefaults(property);
                            opt.SetHidden(true);
                            if (fieldValue != null)
                            {
                                opt.SetValue(fieldValue);
                            }
                        });
                    }
                }
            }

            entity.AddAction(action);
        }