Skip to main content

Management & Support

Two sections of the standard are about operating and reaching the data product rather than about its data: managementPorts are access points for managing the product itself — a REST admin endpoint, a Kafka control topic — and support lists the channels consumers use to get help.

Management ports​

A ManagementPort requires name and content. Its $type defaults to "rest", but the upstream example's single management port is a Kafka topic, so we set it explicitly. This is management/port.k:

import enkinex_odps.management

DictionaryUpdatesPort = management.ManagementPort {
content = "dictionary"
$type = "topic"
name = "tpc-dict-update"
description = "Kafka topic for dictionary updates"
tags = ["kafka"]
customProperties = [
{property = "kafkaTopic", value = "true"}
]
authoritativeDefinitions = [
{$type = "kafka_topic", url = "https://mykafka/topic"}
]
}

The type field is written $type in KCL: the $-prefix lets a schema use type as an attribute name without colliding with the language keyword. It maps back to a plain type: key when rendered — you will see it in the exported document on the last page.

The customProperties and authoritativeDefinitions entries are written as bare { ... } dict literals inside a typed list. KCL infers the element schema from ManagementPort's own attribute types, so neither an import nor a schema name is needed at each call site.

Support channels​

A Support entry requires channel and url. Unlike ManagementPort.url, which is optional, Support.url is always validated against the library's URL pattern, which accepts mailto: links alongside https: — both appear here. This is support/channels.k:

import enkinex_odps.support

DataTeamSlack = support.Support {
channel = "Data Team Slack"
url = "https://retailcorp.slack.com/archives/C1234567890"
description = "Primary support channel for data product questions"
tool = "slack"
scope = "interactive"
}

EmailSupport = support.Support {
channel = "Email Support"
url = "mailto:data-support@retailcorp.com"
description = "Email support for urgent issues"
tool = "email"
scope = "issues"
}

Writing each channel as its own named value is what makes it reusable: a shared incident channel or announcement feed is declared once and referenced from every data product that uses it, instead of being copied between YAML files.

Composing the root product.k​

Two more local imports join the root file, and the managementPorts and support fields join the ones we already had:

import enkinex_odps.odps

import .metadata.description as product_description
import .management.port as management_port
import .support.channels as support_channels
import .team.team as product_team

odps.DataProduct {
name = "Customer Data Product"
id = "fbe8d147-28db-4f1d-bedf-a3fe9f458427"
domain = "seller"
status = "draft"
tenant = "RetailCorp"
tags = ["customer"]
description = product_description.ProductDescription
managementPorts = [
management_port.DictionaryUpdatesPort
]
support = [
support_channels.DataTeamSlack
support_channels.EmailSupport
]
team = product_team.DataTeam
productCreatedTs = "2023-01-15T10:30:00Z"
}

The data product now describes itself, its owners, how it is operated, and where to get help. What remains is the largest piece: the ports — what the product actually consumes and produces — which we cover next.