DCATR.Service.Type behaviour (DCAT-R.ex v0.1.0)

Copy Markdown View Source

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:

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

Inherits from DCATR.GraphResolver:

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.

Summary

Callbacks

Returns the default graph if one is designated.

Returns a graph by its local name.

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

Loads a service from a dataset.

Loads graph name mappings from a service manifest graph.

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

Returns the effective value of use_primary_as_default for this service.

Functions

Adds a graph name mapping to the service.

Default implementation of default_graph/1.

Default implementation of graph/2.

Default implementation of graph_by_id/2.

Default implementation for graph_name_mapping/1.

Default implementation of graphs/1.

Default implementation of primary_graph/1.

Returns the repository type module for a given service type.

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

Types

coercible_graph_name()

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

schema()

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

t()

@type t() :: module()

Callbacks

default_graph(service)

@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(service, coercible_graph_name)

@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(service, selector_or_graph_or_id, opts)

@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(dataset, service_id, opts)

@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(service, graph)

@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 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(service)

@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(service)

@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).

Functions

add_graph_name(service, graph_name, graph_id)

@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(service)

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

Default implementation of default_graph/1.

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

graph(service, name_or_selector_or_id)

Default implementation of graph/2.

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

graph_by_id(map, id)

@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(service, graph_name)

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

Default implementation of graph_by_name/2.

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

graph_name(service, selector_or_graph_or_id, opts \\ [])

Default implementation of 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(map)

@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(map)

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

Default implementation of graphs/1.

Aggregates all graphs from Repository and ServiceData catalogs.

load_from_dataset(service_type, dataset, service_id, opts \\ [])

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

Default implementation of 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 load_graph_names/2.

load_graph_names(service, graph)

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

Default implementation of 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(map)

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

Default implementation of primary_graph/1.

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

repository_type(service_type)

@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(service, selector)

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

Default implementation of DCATR.GraphResolver.resolve_graph_selector/2.

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

service_data_type(service_type)

@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(map)

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

Default implementation of 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