# `Permit.Absinthe.Resolvers.LoadAndAuthorize`
[🔗](https://github.com/curiosum-dev/permit_absinthe/blob/v0.3.0/lib/permit_absinthe/resolvers/load_and_authorize.ex#L1)

Absinthe resolver that loads and authorizes a resource or list of resources
by combining Permit's authorization rules with Ecto-based query scoping.

## Usage and mechanism

In the basic scenario, Ecto is used to filter out records matching defined
authorization permissions. The _happy path_ works in the following way:

1. Use the `Permit.Absinthe` mixin to point to the application's authorization
module that configures permissions & action names, as well as points `Permit.Ecto`
to the application's Ecto `Repo`.

```
use Permit.Absinthe, authorization_module: MyApp.Authorization
```

2. When a GraphQL field resolves through `load_and_authorize/2`, the subject
(`resolution.context[:current_user]` by default) is matched with the action
configured via the `permit` macro option.

```
field :notes, list_of(non_null(:note)) do
  permit action: :read
  resolve &PermitAbsinthe.load_and_authorize/2
end
```

The resource module, which is typically an Ecto schema, is configured in the
GraphQL type via the `:schema` option in the `permit` macro.

```
object :note do
  # fields

  permit(schema: Note)
end
```

3. `Permit.Ecto.Resolver` uses the subject and authorization module to ask
for permission to take the `:action` by the current user on objects of the
resource module's struct type. If permission is granted, configured permissions
are converted to Ecto query.

```
# This permission...
def can(user) do
  permit() |> read(Note, user_id: user.id)
end

# ...gets converted to the following query:
SELECT * FROM notes WHERE user_id: $1;
```

For single-object queries (e.g. `note(id: 123)`), the ID param is applied to the
query as well.

4. If the field is configured as a `list_of(...)` node, the list of retrieved
records is returned by the resolver as an `{:ok, [%Note{}, ...]}` tuple. If it's
a single-object node, the resolver function returns `{:ok, %Note{}}`.

## Error handling

An error tuple is returned when:
- `{:error, "Not found"}` - in single-object queries when the SQL query with
authorization conditions returns 0 records and an additional query (with only
the ID condition) indicates that a record indeed does not exist at all
- `{:error, "Unauthorized"}` - when authorization fails at any point, or the
additional query indicated that a record with the given ID does exist (so
the query-based authorization has failed).

## Additional options

See `Permit.Absinthe.permit/1` macro for additional options allowing customization
of queries, error handling, current user (subject) retrieval, and ID parameter
& ID field naming.

## Usage as middleware

For mutations, pair `Permit.Absinthe.Middleware` with a
custom resolver. The middleware loads and authorizes the resource using
the `load_and_authorize/2` resolver function, then places it in the context
as `:loaded_resource` (single) or `:loaded_resources` (list):

### Example

```
mutation do
  field :update_post, :post do
    permit action: :update
    arg :id, non_null(:id)
    arg :title, :string

    middleware Permit.Absinthe.Middleware

    resolve fn _, args, %{context: %{loaded_resource: post}} ->
      MyApp.Blog.update_post(post, args)
    end
  end
end
```
See more in `Permit.Absinthe.Middleware` documentation.

# `load_and_authorize`

Resolves and authorizes a resource or list of resources.

This function can be used as a resolver function directly or called from a custom resolver.

## Parameters

* `args` - The arguments passed to the field
* `resolution` - The Absinthe resolution struct

## Examples

    # As a resolver function
    field :post, :post do
      arg :id, non_null(:id)
      resolve &load_and_authorize/2
    end

    # Resolver for a list of resources
    field :posts, list_of(:post) do
      resolve &load_and_authorize/2
    end

    # From a custom resolver
    def my_custom_resolver(parent, args, resolution) do
      case load_and_authorize(parent, args, resolution, :one) do
        {:ok, resource} ->
          # Do something with the authorized resource
          {:ok, transform_resource(resource)}

        error ->
          error
      end
    end

---

*Consult [api-reference.md](api-reference.md) for complete listing*
