# `DCATR.Service.Type`
[🔗](https://github.com/dcat-r/dcatr-ex/blob/v0.1.0/lib/dcatr/model/service/type.ex#L1)

Behaviour for service types that provide operations for repository access and processing.

This behaviour enables applications to create specialized `DCATR.Service` types with custom operations,
APIs, and data processing workflows. See `DCATR.Service` for the foundational service model.

Service types define the infrastructure for implementing operations that access repository data.
The callbacks provided by this behaviour handle loading, graph management, and name resolution -
all in service of enabling operation implementations.

## Service Structure

Each service aggregates data from two catalogs:

- **repository** - Distributable repository catalog (Repository)
- **local_data** - Service-local catalog (ServiceData)
- **graph_names** - Bidirectional graph ID↔name mapping for triple-store operations

## Callback Hierarchy

**Loading callbacks**:

- `c:load_from_dataset/3` - Initialize service from RDF manifest data
- `c:load_graph_names/2` - Extract graph name mappings from manifest

**Graph name callbacks** (infrastructure for triple-store operations):

- `c:graph_name/3` - Translate graph references to local storage names
- `c:graph_by_name/2` - Retrieve graphs via local names
- `c:default_graph/1` - Access the designated default graph
- `c:primary_graph/1` - Access the primary graph from the repository

Inherits from `DCATR.GraphResolver`:

- `c:DCATR.GraphResolver.resolve_graph_selector/2` — delegates across `repository` and `local_data`

## Graph Name Management

Graph names enable triple-store operations by mapping global graph IDs to service-local
storage names. Names can be:

- Graph ID as name (automatic fallback)
- Explicitly mapped (via `dcatr:localGraphName`)
- Designated as `:default` (via `dcatr:DefaultGraph`)

## Usage

Custom service types should `use DCATR.Service.Type` and define a Grax schema
extending `DCATR.Service`:

    defmodule MyApp.Service do
      use DCATR.Service.Type

      schema MyApp.NS.Service < DCATR.Service do
      end

      # Define service-specific operations
      def query(service, sparql) do
        # Use graph/2, graph_name/3 to implement operations
        ...
      end
    end

The module automatically provides delegating implementations of all callbacks, convenience
functions for type introspection, and helper functions for graph name management.
All generated functions are overridable.

# `coercible_graph_name`

```elixir
@type coercible_graph_name() :: DCATR.Service.graph_name() | RDF.IRI.coercible()
```

# `schema`

```elixir
@type schema() :: Grax.Schema.t()
```

# `t`

```elixir
@type t() :: module()
```

# `default_graph`

```elixir
@callback default_graph(service :: schema()) :: DCATR.Graph.t() | nil
```

Returns the default graph if one is designated.

Override to customize default graph selection.

# `graph_by_name`

```elixir
@callback graph_by_name(service :: schema(), coercible_graph_name()) ::
  DCATR.Graph.t() | nil
```

Returns a graph by its local name.

This callback enables lookups via service-specific local names.

Override to customize name resolution logic.

# `graph_name`

```elixir
@callback graph_name(
  service :: schema(),
  selector_or_graph_or_id :: atom() | DCATR.Graph.t() | RDF.IRI.coercible(),
  opts :: keyword()
) :: DCATR.Service.graph_name() | nil
```

Returns the graph name for a given graph, selector, or ID.

This callback provides the critical graph-name translation API for triple-store access
(e.g., Gno), where operations require the local graph name under which a graph is stored.

Called by client code that needs to translate graph references into their local storage
names for triple-store operations, SPARQL queries, or graph management commands.

Override to customize graph name resolution (e.g., dynamic name generation, store-specific
naming conventions).

## Options

- `:strict` - When `true` (default), verifies graph existence before returning the ID;
  when `false`, returns the ID without existence check

# `load_from_dataset`

```elixir
@callback load_from_dataset(
  dataset :: RDF.Dataset.t(),
  service_id :: RDF.IRI.coercible(),
  opts :: keyword()
) :: {:ok, schema()} | {:error, any()}
```

Loads a service from a dataset.

This callback enables service initialization from RDF manifest data, supporting
environment-specific configuration and manifest-based bootstrapping.

Called by `DCATR.Manifest.Loader.load_manifest/2` during manifest loading to
construct the complete service hierarchy (Service → Repository → Dataset).

Override to customize service loading logic (e.g., loading additional linked resources,
applying environment-specific transformations).

# `load_graph_names`

```elixir
@callback load_graph_names(service :: schema(), graph :: RDF.Graph.t()) ::
  {:ok, schema()} | {:error, Exception.t()}
```

Loads graph name mappings from a service manifest graph.

This callback extracts local graph name assignments from RDF manifest data, populating
the service's `graph_names` mapping.

Usually called by `c:load_from_dataset/3` after the service and repository are loaded.

Override to customize graph name extraction (e.g., additional naming schemes, validation).

# `primary_graph`

```elixir
@callback primary_graph(service :: schema()) :: DCATR.DataGraph.t() | nil
```

Returns the primary graph from the repository if one is designated.

Override to customize primary graph selection.

# `use_primary_as_default`

```elixir
@callback use_primary_as_default(service :: schema()) :: boolean() | nil
```

Returns the effective value of `use_primary_as_default` for this service.

Resolves the three-value semantics:

- When set in manifest: returns the manifest value (`true`, `false`, or `nil`)
- When not set in manifest: returns application config value (defaults to `nil`)

Override to customize the resolution logic (e.g., different configuration source).

# `add_graph_name`

```elixir
@spec add_graph_name(schema(), coercible_graph_name(), coercible_graph_name()) ::
  {:ok, schema()} | {:error, Exception.t()}
```

Adds a graph name mapping to the service.

Validates graph existence and ensures name uniqueness before updating both
`graph_names` (name→ID) and `graph_names_by_id` (ID→name) mappings of `DCATR.Service`.

# `default_graph`

```elixir
@spec default_graph(schema()) :: DCATR.Graph.t() | nil
```

Default implementation of `c:default_graph/1`.

Returns the graph mapped to the `:default` local name via `c:graph_by_name/2`.

# `graph`

```elixir
@spec graph(schema(), DCATR.GraphResolver.id_or_selector()) :: DCATR.Graph.t() | nil
```

Default implementation of `graph/2`.

Tries selector resolution, then local name lookup, then ID lookup across Repository
and ServiceData catalogs.

# `graph_by_id`

```elixir
@spec graph_by_id(schema(), RDF.IRI.coercible()) :: DCATR.Graph.t() | nil
```

Default implementation of `graph_by_id/2`.

Searches in Repository and ServiceData catalogs.

# `graph_by_name`

```elixir
@spec graph_by_name(schema(), coercible_graph_name()) :: DCATR.Graph.t() | nil
```

Default implementation of `c:graph_by_name/2`.

Looks up the graph ID in `graph_names` mapping, then delegates to `graph_by_id/2`.

# `graph_name`

```elixir
@spec graph_name(
  schema(),
  DCATR.GraphResolver.selector() | DCATR.Graph.t() | RDF.IRI.coercible(),
  keyword()
) :: DCATR.Service.graph_name() | nil
```

Default implementation of `c:graph_name/3`.

Resolves selectors via `graph/2` and looks up local name mappings in `graph_names_by_id`.
Returns `nil` if the graph does not exist (when `strict: true`).

## Options

- `:strict` - When `true` (default), checks graph existence before returning
  the ID as a fallback. When `false`, returns the provided graph ID directly
  without existence check.

# `graph_name_mapping`

```elixir
@spec graph_name_mapping(schema()) :: DCATR.Service.graph_names()
```

Default implementation for `graph_name_mapping/1`.

Returns the `graph_names` map from the `DCATR.Service` struct.

# `graphs`

```elixir
@spec graphs(schema()) :: [DCATR.Graph.t()]
```

Default implementation of `graphs/1`.

Aggregates all graphs from Repository and ServiceData catalogs.

# `load_from_dataset`

```elixir
@spec load_from_dataset(t(), RDF.Dataset.t(), RDF.IRI.coercible(), keyword()) ::
  {:ok, schema()} | {:error, any()}
```

Default implementation of `c:load_from_dataset/3`.

Loads service and repository from their manifest graphs in two stages from the
different manifest graphs, then enriches the service with graph name mappings
from the service manifest via `c:load_graph_names/2`.

# `load_graph_names`

```elixir
@spec load_graph_names(schema(), RDF.Graph.t()) :: {:ok, schema()} | {:error, any()}
```

Default implementation of `c:load_graph_names/2`.

Extracts `dcatr:localGraphName` statements and `dcatr:DefaultGraph` type assertions
from the given graph, populating `graph_names` and `graph_names_by_id` maps of `DCATR.Service`.

After extracting explicit graph name mappings, applies automatic primary-as-default designation
based on the service's `use_primary_as_default` setting.

# `primary_graph`

```elixir
@spec primary_graph(schema()) :: DCATR.DataGraph.t() | nil
```

Default implementation of `c:primary_graph/1`.

Delegates to the repository's `c:DCATR.Repository.Type.primary_graph/1`.

# `repository_type`

```elixir
@spec repository_type(module()) :: module()
```

Returns the repository type module for a given service type.

Extracts the `DCATR.Repository.Type` from the service's `:repository` property schema definition.

## Examples

    iex> DCATR.Service.Type.repository_type(DCATR.Service)
    DCATR.Repository

# `resolve_graph_selector`

```elixir
@spec resolve_graph_selector(schema(), DCATR.GraphResolver.selector()) ::
  DCATR.Graph.t() | nil | :undefined
```

Default implementation of `c:DCATR.GraphResolver.resolve_graph_selector/2`.

Resolves `:default` selector at service level, then delegates to Repository and ServiceData catalogs.

# `service_data_type`

```elixir
@spec service_data_type(module()) :: module()
```

Returns the service data type module for a given service type.

Extracts the `DCATR.ServiceData.Type` from the service's `:local_data` property schema definition.

## Examples

    iex> DCATR.Service.Type.service_data_type(DCATR.Service)
    DCATR.ServiceData

# `use_primary_as_default`

```elixir
@spec use_primary_as_default(schema()) :: boolean() | nil
```

Default implementation of `c:use_primary_as_default/1`.

Resolves the three-value semantics:

- When set in manifest: returns the manifest value (`true`, `false`, or `nil`)
- When not set in manifest: returns application config value (defaults to `nil`), which can
  be configured like this

    config :dcatr, :use_primary_as_default, true # Enforce mode

---

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