# `Ltix.MembershipsService`
[🔗](https://github.com/DecoyLex/ltix/blob/main/lib/ltix/memberships_service.ex#L1)

Query context membership (roster) from a platform using the
[Names and Roles Provisioning Service (NRPS) v2.0](https://www.imsglobal.org/spec/lti-nrps/v2p0/).

Given a successful LTI 1.3 launch that includes the memberships endpoint
claim, retrieve the list of users enrolled in the course or resource link.

## From a launch context

    {:ok, client} = Ltix.MembershipsService.authenticate(launch_context)
    {:ok, roster} = Ltix.MembershipsService.get_members(client)

    Enum.each(roster, fn member ->
      IO.puts("#{member.name}: #{inspect(member.roles)}")
    end)

## From a registration

    alias Ltix.LaunchClaims.MembershipsEndpoint

    {:ok, client} = Ltix.MembershipsService.authenticate(registration,
      endpoint: MembershipsEndpoint.new("https://lms.example.com/memberships")
    )

    {:ok, roster} = Ltix.MembershipsService.get_members(client, role: :learner)

## Streaming large rosters

    {:ok, stream} = Ltix.MembershipsService.stream_members(client)

    stream
    |> Stream.filter(&(&1.status == :active))
    |> Enum.each(&process_member/1)

# `authenticate`

```elixir
@spec authenticate(
  Ltix.LaunchContext.t() | Ltix.Registration.t(),
  keyword()
) :: {:ok, Ltix.OAuth.Client.t()} | {:error, Exception.t()}
```

Acquire an OAuth token for the memberships service.

Accepts a `%LaunchContext{}` or a `%Registration{}`. With a launch context,
the endpoint is extracted from the launch claims. With a registration,
pass the endpoint via the `:endpoint` option.

## From a launch context

    {:ok, client} = Ltix.MembershipsService.authenticate(launch_context)

## From a registration

    {:ok, client} = Ltix.MembershipsService.authenticate(registration,
      endpoint: MembershipsEndpoint.new("https://lms.example.com/memberships")
    )

## Options (launch context)

* `:req_options` (`t:keyword/0`) - Options passed through to `Req.request/2`. The default value is `[]`.

## Options (registration)

* `:endpoint` (struct of type `Ltix.LaunchClaims.MembershipsEndpoint`) - Required. MembershipsEndpoint struct for the service endpoint.

* `:req_options` (`t:keyword/0`) - Options passed through to `Req.request/2`. The default value is `[]`.

# `authenticate!`

```elixir
@spec authenticate!(
  Ltix.LaunchContext.t() | Ltix.Registration.t(),
  keyword()
) :: Ltix.OAuth.Client.t()
```

Same as `authenticate/2` but raises on error.

# `get_members`

```elixir
@spec get_members(
  Ltix.OAuth.Client.t(),
  keyword()
) ::
  {:ok, Ltix.MembershipsService.MembershipContainer.t()}
  | {:error, Exception.t()}
```

Fetch all members from the memberships endpoint.

Follows all `rel="next"` pagination links and returns a complete
`%MembershipContainer{}`. The container implements `Enumerable`,
so you can pipe it directly into `Enum` or `Stream` functions.

## Options

* `:endpoint` (struct of type `Ltix.LaunchClaims.MembershipsEndpoint`) - Override the endpoint stored on the client.

* `:role` (`t:atom/0` | `t:String.t/0` | struct of type `Ltix.LaunchClaims.Role`) - Filter by role. Accepts a role atom (e.g., `:learner`), URI string, `%Role{}` struct, or short name string (e.g., `"Learner"`).

* `:resource_link_id` (`t:String.t/0`) - Query resource link membership.

* `:per_page` (`t:integer/0`) - Page size hint. The platform may return more or fewer than requested.

* `:max_members` (`t:integer/0` | `:infinity`) - Safety limit for eager fetch. Returns a `RosterTooLarge` error if exceeded. Set to `:infinity` to disable. The default value is `10000`.

# `get_members!`

```elixir
@spec get_members!(
  Ltix.OAuth.Client.t(),
  keyword()
) :: Ltix.MembershipsService.MembershipContainer.t()
```

Same as `get_members/2` but raises on error.

# `stream_members`

```elixir
@spec stream_members(
  Ltix.OAuth.Client.t(),
  keyword()
) :: {:ok, Enumerable.t()} | {:error, Exception.t()}
```

Fetch members as a lazy stream.

Returns `{:ok, stream}` where each element is a `%Member{}`.
Use this instead of `get_members/2` for large rosters where you
want to process members incrementally or stop early.

## Options

* `:endpoint` (struct of type `Ltix.LaunchClaims.MembershipsEndpoint`) - Override the endpoint stored on the client.

* `:role` (`t:atom/0` | `t:String.t/0` | struct of type `Ltix.LaunchClaims.Role`) - Filter by role. Accepts a role atom (e.g., `:learner`), URI string, `%Role{}` struct, or short name string (e.g., `"Learner"`).

* `:resource_link_id` (`t:String.t/0`) - Query resource link membership.

* `:per_page` (`t:integer/0`) - Page size hint. The platform may return more or fewer than requested.

# `stream_members!`

```elixir
@spec stream_members!(
  Ltix.OAuth.Client.t(),
  keyword()
) :: Enumerable.t()
```

Same as `stream_members/2` but raises on error.

---

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