ExScim.Storage.Adapter behaviour (ex_scim v0.2.0)

Copy Markdown View Source

Behaviour defining the contract for all storage backends.

Implementations must handle CRUD operations for both Users and Groups. Each callback receives an optional scope for multi-tenant isolation.

See ExScim.Storage.EtsStorage for a reference implementation, or ExScimEcto.StorageAdapter for a production-ready Ecto-based backend.

Configuration

config :ex_scim, storage_strategy: MyApp.Storage

Summary

Types

A domain-level group struct or map as stored by the backend.

A domain-level user struct or map as stored by the backend.

Parsed SCIM filter AST, or nil for no filter.

Unique identifier for a group resource.

Pagination options, e.g. [start_index: 1, count: 20].

Caller scope for multi-tenant isolation, or nil.

Sort options, e.g. [sort_by: {"userName", :asc}].

Unique identifier for a user resource.

Callbacks

Persists a new group.

Persists a new user.

Deletes a group by ID.

Deletes a user by ID.

Retrieves a single group by ID.

Retrieves a single user by ID.

Returns true if a group with the given ID exists.

Lists groups matching a filter with sorting and pagination. Returns {:ok, groups, total_count}.

Lists users matching a filter with sorting and pagination. Returns {:ok, users, total_count}.

Fully replaces an existing group (PUT).

Fully replaces an existing user (PUT).

Partially updates an existing group (PATCH).

Partially updates an existing user (PATCH).

Returns true if a user with the given ID exists.

Types

domain_group()

@type domain_group() :: struct()

A domain-level group struct or map as stored by the backend.

domain_user()

@type domain_user() :: struct()

A domain-level user struct or map as stored by the backend.

filter_ast()

@type filter_ast() :: term() | nil

Parsed SCIM filter AST, or nil for no filter.

The AST is a nested tuple structure, e.g. {:eq, "userName", "alice"} or {:and, {:eq, "active", "true"}, {:co, "userName", "john"}}.

group_id()

@type group_id() :: binary()

Unique identifier for a group resource.

pagination_opts()

@type pagination_opts() :: keyword()

Pagination options, e.g. [start_index: 1, count: 20].

scope()

@type scope() :: ExScim.Scope.t() | nil

Caller scope for multi-tenant isolation, or nil.

sort_opts()

@type sort_opts() :: keyword()

Sort options, e.g. [sort_by: {"userName", :asc}].

user_id()

@type user_id() :: binary()

Unique identifier for a user resource.

Callbacks

create_group(domain_group, scope)

@callback create_group(domain_group(), scope()) ::
  {:ok, domain_group()} | {:error, term()}

Persists a new group.

create_user(domain_user, scope)

@callback create_user(domain_user(), scope()) :: {:ok, domain_user()} | {:error, term()}

Persists a new user.

delete_group(group_id, scope)

@callback delete_group(group_id(), scope()) :: :ok | {:error, term()}

Deletes a group by ID.

delete_user(user_id, scope)

@callback delete_user(user_id(), scope()) :: :ok | {:error, term()}

Deletes a user by ID.

get_group(group_id, scope)

@callback get_group(group_id(), scope()) :: {:ok, domain_group()} | {:error, :not_found}

Retrieves a single group by ID.

get_user(user_id, scope)

@callback get_user(user_id(), scope()) :: {:ok, domain_user()} | {:error, :not_found}

Retrieves a single user by ID.

group_exists?(group_id, scope)

@callback group_exists?(group_id(), scope()) :: boolean()

Returns true if a group with the given ID exists.

list_groups(filter_ast, sort_opts, pagination_opts, scope)

@callback list_groups(filter_ast(), sort_opts(), pagination_opts(), scope()) ::
  {:ok, [domain_group()], non_neg_integer()}

Lists groups matching a filter with sorting and pagination. Returns {:ok, groups, total_count}.

list_users(filter_ast, sort_opts, pagination_opts, scope)

@callback list_users(filter_ast(), sort_opts(), pagination_opts(), scope()) ::
  {:ok, [domain_user()], non_neg_integer()}

Lists users matching a filter with sorting and pagination. Returns {:ok, users, total_count}.

replace_group(group_id, domain_group, scope)

@callback replace_group(group_id(), domain_group(), scope()) ::
  {:ok, domain_group()} | {:error, term()}

Fully replaces an existing group (PUT).

replace_user(user_id, domain_user, scope)

@callback replace_user(user_id(), domain_user(), scope()) ::
  {:ok, domain_user()} | {:error, term()}

Fully replaces an existing user (PUT).

update_group(group_id, domain_group, scope)

@callback update_group(group_id(), domain_group(), scope()) ::
  {:ok, domain_group()} | {:error, term()}

Partially updates an existing group (PATCH).

update_user(user_id, domain_user, scope)

@callback update_user(user_id(), domain_user(), scope()) ::
  {:ok, domain_user()} | {:error, term()}

Partially updates an existing user (PATCH).

user_exists?(user_id, scope)

@callback user_exists?(user_id(), scope()) :: boolean()

Returns true if a user with the given ID exists.