ckanext-tmforum

June 12, 2026 ยท View on GitHub

Tests

ckanext-tmforum

CKAN harvester extension for TM Forum APIs.

It harvests product offerings/specifications, enriches them with related service specifications, resolves owner organizations from TMF Party data, and creates/updates datasets in CKAN.

Compatibility

  • CKAN: 2.11 (current target)
  • Python: 3.10
  • Requires ckanext-harvest

Harvester Info

Harvester type name:

  • tmforum

This is the value to use when creating a Harvest Source in CKAN.

Harvest Flow

  1. gather_stage
  • Reads launched product offerings from TMF Product Catalog (/productOffering).
  • Creates harvest objects using product specification id as GUID.
  1. fetch_stage
  • Fetches product specification (/productSpecification/{id}).
  • Fetches related service specifications from configured service catalog.
  • Stores normalized content on harvest object.
  1. import_stage
  • Resolves owner party from relatedParty using configured owner_role.
  • Resolves/creates owner organization in CKAN.
  • Builds CKAN dataset and resources.
  • Creates or updates package through harvest base logic.

Configuration

Configuration is read from the Harvest Source JSON config (source.config) for runtime behavior.

Expected keys:

  • party_url (required)
    • Base TMF Party endpoint.
  • service_catalog_url (required)
    • Base TMF Service Catalog endpoint.
  • product_inventory_url (required)
    • Base TMF Product Inventory endpoint.
  • owner_role (optional)
    • Default: owner
    • Used to select owner party from relatedParty.
  • marketplace_url (optional)
    • If set, adds an extra resource access URL: {marketplace_url}/search/{offer_id}.
  • dsp_url (optional)
    • If set, adds an extra resource access URL pointing to DSP.

The harvest source URL (source.url) is used as TMF Product Catalog base.

Example Harvest Source Config

{
  "party_url": "https://tmf.example.com/party",
  "service_catalog_url": "https://tmf.example.com/serviceCatalogManagement/v4",
  "product_inventory_url": "https://tmf.example.com/productInventoryManagement/v4",
  "owner_role": "Seller",
  "marketplace_url": "https://marketplace.example.com",
  "dsp_url": "https://edc.example.com"
}

Resource Mapping

Per service specification, resources are created with:

  • name, description
  • access_url (TMF product offering URL)
  • conforms_to (TM Forum URIs)
  • a UI type badge derived from conforms_to in both the resource list and resource detail page

Known specCharacteristic mappings:

  • URL -> download_url
  • Format -> format
  • Media Type -> mimetype

Additional TMForum identifiers are stored on each resource to support CKAN-side entitlement checks:

  • tmf_offer_id
  • tmf_service_id

Other characteristics are mapped to flattened resource keys:

  • tmf_char_<normalized_name>
  • if multiple values: tmf_char_<normalized_name>_1, ..._2, etc.

Duplicate characteristic names are ignored after first occurrence.

Organization Resolution

Owner organization is resolved from party data using DID in this order:

  1. externalReference where externalReferenceType == "idm_id" and name starts with did
  2. partyCharacteristic where name == "did"
  3. fallback to party_id

Generated CKAN org name format:

  • vc-org-<sha256(did)[:16]>

Persisted TMForum Metadata

During harvest import, the dataset stores the TMForum endpoint information needed to work with the harvested resources later from the CKAN catalog:

  • tmf_id
  • tmf_product_catalog_url
  • tmf_party_url
  • tmf_service_catalog_url
  • tmf_product_inventory_url

Installation

git clone https://github.com/SEAMWARE/ckanext-tmforum.git
cd ckanext-tmforum
pip install -e .
pip install -r requirements.txt

Enable plugin:

  • ckan.plugins = ... harvest tmforum ...

Running Harvest Jobs (ckan-docker)

Examples:

docker compose exec ckan-dev bash -lc "ckan -c /srv/app/ckan.ini harvester sources" docker compose exec ckan-dev bash -lc "ckan -c /srv/app/ckan.ini harvester jobs" docker compose exec ckan-dev bash -lc "ckan -c /srv/app/ckan.ini harvester run-test <source_id_or_name>" docker compose exec ckan-dev bash -lc "ckan -c /srv/app/ckan.ini harvester gather-consumer" docker compose exec ckan-dev bash -lc "ckan -c /srv/app/ckan.ini harvester fetch-consumer"

Tests

Run all tests:

pytest --ckan-ini=test.ini

Run unit tests only:

pytest --ckan-ini=test.ini ckanext/tmforum/tests/unit

Conditional Visualization

The extension can hide CKAN resource previews and visualization widgets while still showing the protected download_url.

Optional configuration:

  • ckanext.tmforum.visualization_access_check_enabled
    • Default: False

When enabled, the helper checks the logged-in user's TMForum organization party id and then calls _check_visualization_access_for_party_id(). That method is currently a mock that returns a boolean value and is the place where the real backend entitlement call should be added later.

With the current mock returning False, the UI renders the locked/no-access state. This is useful while iterating on the frontend before wiring the real backend entitlement check.

Releasing

  1. Update version in pyproject.toml

  2. Build and validate:

    python -m build && twine check dist/*

  3. Upload:

    twine upload dist/*

  4. Commit and tag:

git commit -a -m "Release x.y.z"
git push
git tag x.y.z
git push --tags

License

AGPL