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)); } }
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); }
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); } }
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); }
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); } } } }
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)); }
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); }
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)); }
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); } }
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); }
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()); }
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); }
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); }