Skip to content

Enhancement Request: Read-Only Service interface #535

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
rpatrick00 opened this issue Jun 27, 2019 · 7 comments
Closed

Enhancement Request: Read-Only Service interface #535

rpatrick00 opened this issue Jun 27, 2019 · 7 comments

Comments

@rpatrick00
Copy link

Description

I am trying to use the framework to access read-only data stored in Elasticsearch. Taking the recommended approach, I started customizing the service layer only to realize there were a dozen or so create, update, and delete interfaces I needed to implement that were irrelevant to my use case. I think it would be nice if the service layer interface had a read-only interface that I could extend.

@wisepotato
Copy link
Contributor

you need to disable a few methods but this is certainly possible, can you give an example of these "dozens" of interfaces? We're open to these suggestions, as a readonly service is a great addition. @maurei thoughts?

@wisepotato
Copy link
Contributor

wisepotato commented Jun 27, 2019

Althought this isn't too much work to implement. Will investigate. we're currently working on a major refactor to decouple a singleton in the code, so this can either be solved during that time or after :)

@rpatrick00
Copy link
Author

I’m not sure how to “disable” the methods. Simply throw a not implemented exception?

My object generally has no need for create, update, delete operations on the object or relationships. Also, we do not support GetAll without filters.

@wisepotato
Copy link
Contributor

well a not implemented would server well, or a jsonapiexception would serve nice as you have control voer the status code

@maurei
Copy link
Member

maurei commented Jun 28, 2019

Hi @rpatrick00,

If I'm not mistaken, the interfaces you're looking for can be found here. Looks like you want to use the IResourceQueryService<T, TId> interface.

If you need custom service code, you can just use this to build your own implementation and register it

// implementation
public class ReadOnlyResourceService<T, TId> : IResourceQueryService<T, TId> { ... }

// registering
services.AddScoped<IResourceService<T, TId>, ReadOnlyResourceService<T, TId>>();

If you still want to use the boilerplate code from EntityResourceService<T,TId>: I believe you can simply inherit it and register it

// implementation
public class ReadOnlyResourceService<T, TId> : EntityResourceService<T, TId> { ... }

// registering
services.AddScoped<IResourceService<T, TId>, ReadOnlyResourceService<T, TId>>();

This way your implementation will still contain the the create, update, delete methods, but wherever your implementation is injected these methods will not be exposed, so you could choose not to worry about that.

Then there is a JsonApiQueryController that seems to be designed for read-only resources. I would have expected there to be a constructor that just takes the query service:

public JsonApiQueryController(
    IJsonApiContext jsonApiContext,
    IResourceQueryService<T, TId> resourceQueryService)  // <--- this is actually not here, it is IResourceService in the current implementation
: base(jsonApiContext, resourceService)
{ }

But interestingly enough, there is only the constructor that accept IResourceService, which exposes also the create/update/delete operations. I think we should add the constructor above. For completeness you could add the following workaround-base-controller for your readonly resources

    public class JsonApiQueryControllerFixed<T, TId>
            : BaseJsonApiController<T, TId> where T : class, IIdentifiable<TId>
    {
        public JsonApiReadOnlyController(
            IJsonApiContext jsonApiContext,
            IResourceQueryService<T, TId> resourceService)
        : base(jsonApiContext, resourceService)
        { }

        [HttpGet]
        public override async Task<IActionResult> GetAsync() => await base.GetAsync();

        [HttpGet("{id}")]
        public override async Task<IActionResult> GetAsync(TId id) => await base.GetAsync(id);

        [HttpGet("{id}/relationships/{relationshipName}")]
        public override async Task<IActionResult> GetRelationshipsAsync(TId id, string relationshipName)
            => await base.GetRelationshipsAsync(id, relationshipName);

        [HttpGet("{id}/{relationshipName}")]
        public override async Task<IActionResult> GetRelationshipAsync(TId id, string relationshipName)
            => await base.GetRelationshipAsync(id, relationshipName);
    }

so that you can have your controllers as follows

    public class YourResourceController : JsonApiQueryControllerFixed<YourResource, int>
    {
        public CompaniesController(
            IJsonApiContext jsonApiContext,
            IResourceQueryService<YourResource, int> queryService)
        : base(jsonApiContext, queryService)
        { }
    }

Let me know if this helps!

@rpatrick00
Copy link
Author

@maurei, thanks! That's a huge help. I will give it a try.

@maurei
Copy link
Member

maurei commented Jul 4, 2019

closing as question resolved, created a new issue dedicated to the problem I mentioned in my earlier comment

@maurei maurei closed this as completed Jul 4, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

3 participants