Razor Pages get class Attributes in Unit Tests - .net-core

currently I'm trying to check if there is a way to get the class names of all attributes on razor page classes.
I unit tests all my controllers to look for an AuthorizeFilter so that it is impossible to forget it
Currently this is how I do it via MVC (does not work in Razor Pages):
var values = actionDescriptorCollectionProvider
.ActionDescriptors
.Items
.OfType<ControllerActionDescriptor>()
.Select(a => new
{
a.DisplayName,
a.ControllerName,
a.ActionName,
AttributeRouteTemplate = a.AttributeRouteInfo?.Template,
HttpMethods = string.Join(", ", a.ActionConstraints?.OfType<HttpMethodActionConstraint>().SingleOrDefault()?.HttpMethods ?? new string[] { "any" }),
Parameters = a.Parameters?.Select(p => new
{
Type = p.ParameterType.Name,
p.Name
}),
ControllerClassName = a.ControllerTypeInfo.FullName,
ActionMethodName = a.MethodInfo.Name,
Filters = a.FilterDescriptors?.Select(f => new
{
ClassName = f.Filter.GetType().FullName,
f.Scope //10 = Global, 20 = Controller, 30 = Action
}),
Constraints = a.ActionConstraints?.Select(c => new
{
Type = c.GetType().Name
}),
RouteValues = a.RouteValues.Select(r => new
{
r.Key,
r.Value
}),
});
The problem is, that this code won't work with Razor Pages, i.e. FilterDescriptors is empty for PageActionDescriptor.

You need to use PageActionDescriptor for RazorPages instead of ControllerActionDescriptor:
var values = actionDescriptorCollectionProvider
.ActionDescriptors
.Items
.OfType<PageActionDescriptor>()
.Select(descriptor => new
{
// descriptor...,
// ...
});

Related

GetItemLinqQueryable doesn't return any items

I have two sets of code (below) that fetches items from a container that match conditions specified in "where" statement. One with the sql query works as expected whereas the other with the Linq expressions doesn't. The Linq one would return items if I remove all "Where" statements but what I want is to fetch items matching the conditions in those "Where" statements. Can you help me understand why the Linq one doesn't work?
QueryRequestOptions queryRequestOptions = new QueryRequestOptions()
{
MaxItemCount = DefaultPageSize, // 100
};
// THIS CODE WORKS
string query = $"SELECT * FROM root r WHERE r.documentType = #documentType and r._ts > #timestamp";
QueryDefinition queryDefinition = new QueryDefinition(query);
queryDefinition.WithParameter("#documentType", docType);
queryDefinition.WithParameter("#timestamp", TimestampConverter.ToTimestamp(fromTime));
using (var feedIterator = container.GetItemQueryIterator<T>(
queryDefinition,
null,
queryRequestOptions))
{
while (feedIterator.HasMoreResults)
{
FeedResponse<T> feedResponse = await feedIterator.ReadNextAsync(cancellationToken);
// THIS CODE DOESN"T WORK
using (var feedIterator = container.GetItemLinqQueryable<T>(false, null, queryRequestOptions)
.Where(d => d.DocumentType == docType)
.Where(d => d.Timestamp > fromTime)
.ToFeedIterator())
{
while (feedIterator.HasMoreResults)
{
FeedResponse<T> feedResponse = await feedIterator.ReadNextAsync(cancellationToken);
}
you can try like this
select many is used wherever i have an array in document
container.GetItemLinqQueryable<>()
.Where(ar => ar.FiscalYear == fiscalYear)
.SelectMany(ar => ar.ResourcePrivileges.Where(arp => arp.ResourceName == accessRequest.PendingUserRequest.ResourcePrivileges.ResourceName)
.SelectMany(rp => rp.Filters.Where(f => (int)f.Role > 2 && subRegionLst.Contains(f.SubRegion) && reqProduct.Contains(f.ProductName)))
.Select(i => new
{
Alias = ar.UserAlias,
Filter = ar.ResourcePrivileges.Where(arp => arp.ResourceName == accessRequest.PendingUserRequest.ResourcePrivileges.ResourceName).SelectMany(x => x.Filters),
})).Distinct();
I believe your issue has to do with how the linq query in serialized. By default GetItemLinqQueryable doesn't use camel case.
You can control the serialization by passing serializing options:
container.GetItemLinqQueryable<T>(
linqSerializerOptions: new CosmosLinqSerializerOptions
{
PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase
});

Error UseHealthChecksUI Unexpected character encountered

I'm trying to implement the ASP.NET Core 2.2 health check feature. Setting up the health check itself isn't the problem, but I also want to be able to use the UI feature in other project to monitoring all my apis. Right now I get the exception message
Unexpected character encountered while parsing value: <.
What I'm doing bad?
API Project:
var healthCheckOptions = new HealthCheckOptions
{
Predicate = _ => true,
ResponseWriter = async (c, r) =>
{
c.Response.ContentType = MediaTypeNames.Application.Json;
var result = JsonConvert.SerializeObject(
new
{
Checks = r.Entries.Select(e =>
new
{
Description = e.Key,
Status = e.Value.Status.ToString(),
ResponseTime = e.Value.Duration.TotalMilliseconds
}),
TotalResponseTime = r.TotalDuration.TotalMilliseconds
});
await c.Response.WriteAsync(result);
}
};
app.UseHealthChecks("/live", new HealthCheckOptions
{
Predicate = _ => true
});
app.UseHealthChecks("/hc", healthCheckOptions);
app.UseHealthChecksUI(options => options.UIPath = "/healtcheck");
// Registers required services for health checks
services
.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy())
.AddCheck("ComunContext Database", new SqlServerHealthCheck(configuration["ConnectionStrings:ComunContext"]));
Web project:
services.AddHealthChecksUI();
app.UseHealthChecksUI(config =>
{
config.UIPath = "/healthcheck";
});
appsettings.json
{
"HealthChecks-UI": {
"HealthChecks": [
{
"Name": "Local",
"Uri": "http://localhost:27365/hc"
}
],
"EvaluationTimeOnSeconds": 10,
"MinimumSecondsBetweenFailureNotifications": 60
}
}
Try adding a ResponseWriter:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseHealthChecks("/healthchecks", new HealthCheckOptions
{
ResponseWriter = async (context, report) =>
{
context.Response.ContentType = "application/json; charset=utf-8";
var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(report));
await context.Response.Body.WriteAsync(bytes);
}
});
app.UseHealthChecksUI();
}
After a few days struggling with this parser error, I've figured out that there are 2 problems:
1 - If you have an Exception, Health UI tries to convert Exception object field, resulting on error;
2 - If you try to pass your own anonymous object, Health UI fails to convert Entries collection, because it need to be an specific anonymous Dictionary.
Try this:
var healthCheckOptions = new HealthCheckOptions
{
Predicate = _ => true,
ResponseWriter = async (c, r) =>
{
c.Response.ContentType = MediaTypeNames.Application.Json;
var result = JsonConvert.SerializeObject(
new
{
Checks = r.Entries.ToDictionary(
e => e.Key,
e =>
new
{
Description = e.Key,
Status = e.Value.Status.ToString(),
ResponseTime = e.Value.Duration.TotalMilliseconds
}),
TotalResponseTime = r.TotalDuration.TotalMilliseconds
});
await c.Response.WriteAsync(result);
}
};

NSubstitute - setting a property of the calling object

I have a in issue where I would like to swap out Moq for NSubstitute entirely. In most cases, this is extremely straightforward, however I have run across a fairly specialized issue.
Here's the Moq code.
_registrationCommandHandler.Setup(c => c.Execute
(It.Is<CheckUniqueUserCommand>(r => r.Request.UserName == "fred"))).
Callback((CheckUniqueUserCommand c) =>
{
c.Response = new CheckUserNameIsUniqueResponse()
{
IsUnique = true,
Success = true
};
c.Success = true;
});
The closest I seem to be able to get with NSubstitute is
_registrationCommandHandler.When(c => c.Execute
(Arg.Any<CheckUniqueUserCommand>())).Do
((CheckUniqueUserCommand c) =>
{
c.Response = new __Internal.CheckUserNameIsUniqueResponse()
{
IsUnique = true,
Success = true
};
c.Success = true;
});
which won't even compile. This leaves me a bit stuck. Does anyone have any suggestions?
I'm guessing a bit here, but try:
_registrationCommandHandler
.When(c => c.Execute(Arg.Is<CheckUniqueUserCommand>(r => r.Request.UserName == "fred")))
.Do(call => {
var c = call.Arg<CheckUniqueUserCommand>();
c.Response = new __Internal.CheckUserNameIsUniqueResponse()
{
IsUnique = true,
Success = true
};
c.Success = true;
});
NSubstitute does not do the same argument passing as Moq does for callbacks. Instead it passes a parameter with information about the call, and you can access the arguments using call.Arg<T> or call[i].
In addition to changing the .Do block, I also switched from using Arg.Any(..) to Arg.Is(..) to closer match the Moq sample.
Hope this helps.

How to display enum description or name in a grid row?

I am using the Kendo grid in my ASP.Net MVC application. If I have the following grid definition,
#(Html.Kendo().Grid(Model) //Bind the grid to ViewBag.Products
.Name("grid")
.Columns(columns =>
{
columns.Bound(p => p.FullName);
columns.Bound(p => p.MyEnum)
})
.Groupable()
.Pageable()
.Sortable()
.Scrollable(scr => scr.Height(600))
.Filterable()
)
where one of the column is an Enum. My enum definition is:
public enum MyEnum
{
[Display(AutoGenerateField = false, Name = "My enum 1", Description = "My Enum 1")]
EnumOne,
[Display(Name = "My Enum 2")]
EnumTwo
}
How do I make it display "My Enum 1" or "My Enum 2" for each row?
Thanks in advance!
I recently ran into this problem and solved it by using
var someArrayOfValueAndText = new[] {new {
Id = 0, Description = "Foo"
}, new {
Id = 1, Description = "Bar"
}
.Columns(c.ForeignKey(m=> m.MyEnum, someArrayOfValueAndText, "Id","Description"))
instead of the .Bound method
I created an helper class containing some extension methods a while back:
public static class EnumExtensions
{
public static string GetDisplayName(this Enum enu)
{
var attr = GetDisplayAttribute(enu);
return attr != null ? attr.Name : enu.ToString();
}
public static string GetDescription(this Enum enu)
{
var attr = GetDisplayAttribute(enu);
return attr != null ? attr.Description : enu.ToString();
}
private static DisplayAttribute GetDisplayAttribute(object value)
{
Type type = value.GetType();
if (!type.IsEnum)
{
throw new ArgumentException(string.Format("Type {0} is not an enum", type));
}
// Get the enum field.
var field = type.GetField(value.ToString());
return field == null ? null : field.GetCustomAttribute<DisplayAttribute>();
}
}
It contains two methods for extracting the Name and Description of a Display attribute. The display name:
columns.Bound(p => p.MyEnum.GetDisplayName())
And for a description:
columns.Bound(p => p.MyEnum.GetDescription())
You have to add a using statement in your Web.config or in your view.
Update
What if you create a property for it in your model:
public string MyEnumName
{
get { return MyEnum.GetDisplayName(); }
}
Now you should be able to use:
columns.Bound(p => p.MyEnumName);
Henk's solution is good. But you can use filtering capability if you use ClientTemplate:
col.Bound(m => m.MyEnum) // this provides you filtering
.ClientTemplate("#: MyEnumName #") // this shows a name of enum
For more information about kendo ui templates see: http://docs.telerik.com/kendo-ui/framework/templates/overview
I use #user1967246 method and would like to explain more how to i do.
i created array in top of my kendo grid
var statusLikeEntityStatus = new[]
{
new {Id = 0, Status = EntityStatus.Passive},
new {Id = 1, Status = EntityStatus.Active},
new {Id = 2, Status = EntityStatus.Draft},
new {Id = 3, Status = EntityStatus.ReadyToLive},
new {Id = -1, Status = EntityStatus.Freezed},
new {Id = -2, Status = EntityStatus.Blocked}
};
Then i use ForeignKey property instead of Bound.
columns.ForeignKey(m => m.Status, statusLikeEntityStatus, "Id", "Status").Title(Resources.General.Status);
Here is my columns attribute
.Columns(columns =>
{
columns.Bound(m => m.InventoryID).Title("Id");
columns.Bound(m => m.ERPCode).Title(Resources.Products.ProductCode);
columns.Bound(m => m.Price).Title(Resources.Products.ListPrice);
columns.Bound(m => m.Discount).Title(Resources.Products.
columns.Bound(m => m.Stock).Title(Resources.Products.TotalStock); // todo: Resources
columns.ForeignKey(m => m.Status, statusLikeEntityStatus, "Id", "Status").Title(Resources.General.Status);
columns.Command(commandConf =>
{
commandConf.Edit();
commandConf.Destroy();
});
})
Hope it will help to you.

Get child structure groups given a structure group TCM URI with Core Service

How can I get all child structure groups given a structure group TCM URI with Core Service?
I tried using this code:
ItemsFilterData sgFilter = new RepositoryItemsFilterData
{ ItemTypes = new[] { ItemType.StructureGroup },
Recursive = true,
BaseColumns = ListBaseColumns.Id };
XElement listXml;
using (CoreServiceClient client = _coreServiceProvider.GetCoreServiceClient())
{
listXml = XElement.Parse(
client.ProxyClient.GetListXml(structureGroupUri, sgFilter)
.OuterXml);
}
But I got an error saying "Unexpected item type: StructureGroup".
Starting from the Publication's URI, this works:
client.GetListXml("tcm:0-10-1", new RepositoryItemsFilterData {
ItemTypes = new[] { ItemType.StructureGroup },
Recursive = true,
BaseColumns = ListBaseColumns.Id
})
The trick is always to find the correct filter type, which in this case is RepositoryItemsFilterData.
Non-working sample
Starting from the Structure Group's URI, this returns the direct children Structure Groups. Note that that Recursive = true seems to be ignored here.
client.GetListXml("tcm:10-328-4", new OrganizationalItemItemsFilterData {
ItemTypes = new[] { ItemType.StructureGroup },
Recursive = true,
BaseColumns = ListBaseColumns.Id
})

Resources