json-api-dotnet / JsonApiDotNetCore

A framework for building JSON:API compliant REST APIs using ASP.NET and Entity Framework Core.
https://www.jsonapi.net
MIT License
679 stars 159 forks source link

Use IResourceService outside of the path of a request #1529

Closed verdie-g closed 6 months ago

verdie-g commented 6 months ago

I would need to create a resource outside of the path of a JSON:API request. My exact use-case is in the redirection endpoint of an OpenID authentication, I need to get or create a user so I wrote something like that

var userService = ctx.HttpContext.RequestServices.GetRequiredService<IResourceService<User, long>>();
User? user = null;
try
{
     user = await userService.GetAsync(userId, CancellationToken.None);
}
catch (ResourceNotFoundException)
{
}

if (user == null)
{
    user = new User
    {
        Id = userId,
    };
    await userService.CreateAsync(user, CancellationToken.None);
}

unfortunately it throws InvalidOperationException: Expected IJsonApiRequest.PrimaryResourceType not to be null at this point.

Is this use-case supported?

bkoelman commented 6 months ago

In a regular request, IJsonApiRequest is populated by the middleware, which the rest of the pipeline depends on. It's one of those ambient interfaces, listed at https://www.jsonapi.net/getting-started/faq.html#what-if-i-want-to-use-something-other-than-entity-framework-core. In your case, try something like the following:

var request = (JsonApiRequest)ctx.HttpContext.RequestServices.GetRequiredService<IJsonApiRequest>();
var resourceGraph = ctx.HttpContext.RequestServices.GetRequiredService<IResourceGraph>();

request.Kind = EndpointKind.Primary;
request.PrimaryResourceType = resourceGraph.GetResourceType<User>();
bkoelman commented 6 months ago

For the CreateAsync call, you'll need to set up ITargetedFields likewise.

verdie-g commented 6 months ago

Thanks! Would it make sense to make that use-case smoother (e g. No downcast, no manually building an object). It looks like I would be relying on implementation details here and the use-case is not that exotic. Another similar use-case would be a IHostedService to periodically make some db operations.

bkoelman commented 6 months ago

What do you have in mind? It's quite similar to trying to unittest a method that depends on HttpContext: you'll need to set one up yourself because there's none available. I consider this an advanced use case and am fine with it being not too straightforward, because it's easy to mess things up. It can be done, but it requires to know what's going on. Similar to https://github.com/json-api-dotnet/JsonApiDotNetCore/pull/1144.

bkoelman commented 6 months ago

You can call CopyFrom instead of the cast. And it's not really relying on implementation details, these types are public for usages like this.

verdie-g commented 6 months ago

What do you have in mind?

In my latest large project I had 3 workers (https://github.com/verdie-g/crpg/tree/master/src/WebApi/Workers). I suppose I could get away with your code snippet but I feel like I always have these kinds of workers in my project.

bkoelman commented 6 months ago

I meant to ask: in what way do you think JsonApiDotNetCore should be changed to smoothen your experience? What would that look like?

What I don't understand is why the JADNC pipeline is needed when the background job is just doing database updates. It would be more efficient to directly execute cleanup via EF Core.

verdie-g commented 6 months ago

What I don't understand is why the JADNC pipeline is needed when the background job is just doing database updates. It would be more efficient to directly execute cleanup via EF Core.

Yes on this point I was thinking that I didn't want to bypass the IResourceService which could contain the business logic but I believe directly using EF Core would perfectly work in my use-cases. I think you would know better than me if in the general case it's acceptable to bypass IResourceService.

bkoelman commented 6 months ago

Yeah, sounds like you don't need things like consuming query string parameters, detecting side-effects during update, model state validation, counting resources, render links, etc.

verdie-g commented 6 months ago

Alright, thanks your answers! I'll close the issue for now and I may re-open later in my project if I find out that it's a real issue.