Description
Current situation
The current way how resource definitions work is that JADNC provides a generic base class, which users can derive from for a specific resource type. JADNC contains scanning logic that picks up such non-generic (resource-bound) classes and calls into them from various places.
Example:
public class OrderLineDefinition : ResourceDefinition<OrderLine>
{
// ...
}
When no resource-bound class is found for a specific resource, then resource callbacks are skipped for that resource type. This is accomplished via a non-generic IResourceDefinition
interface, which is a type intended for internal use only to track which callbacks are available. It put me on the wrong track when I started using JADNC: I expected this was something for me to implement (similar to services and repositories), but it is not.
Proposal
I'd like to make resource definitions a first-class pluggable extensibility point, similar to services and repositories, so that resource definitions are always called into. We would provide an interface along with a default generic implementation (that contains no logic). Users can then implement the interface themselves and register their generic type -and- derive from our default generic implementation and register that -and- declare resource-bound definitions, which may derive from their own generic base class.
Example:
namespace JADNC
{
public interface IResourceDefinition<TResource>
{
// ...
}
public class JsonApiResourceDefinition<TResource> : IResourceDefinition<TResource>
{
// ...
}
}
namespace ApiDeveloper
{
public class CustomResourceDefinition<TResource> : JsonApiResourceDefinition<TResource>
{
// ...
}
public class OrderLineResourceDefinition : CustomResourceDefinition<OrderLine>
{
// ...
}
}
Use case
The use case I have in mind for this is authorization. Assume the system stores whether users have read/write access per resource type. While it is possible to annotate controller methods with attributes, this would not be sufficient. It would only work for primary requests like /articles
and /articles/1
, but not secondary requests like /articles/1/author
. The controller would need to contain custom logic that inspects the relationship
parameter and runs the permission check for that too. It is even debatable whether a permission check on articles should be done in that case, because the request is not targeting an article.
But it gets worse: a request like /articles?include=author
would not get checked for access to the author resource. The current way to deal with that would be to override ResourceDefinition.OnApplyIncludes
for all resource types in the project. A similar override for ResourceDefinition.OnApplyFilter
is needed to properly check a request like /articles?filter=any(revisions)
to ensure the caller has read permission on the revisions table.
If it were possible to plug in a generic resource definition, it could handle the permission check for all resource types in the project. Resource-bound classes would then call into the base class to ensure authorization is always applied.