dataproduct-cli 0.0.1__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/CHANGELOG.md +8 -2
- {dataproduct_cli-0.0.1/dataproduct_cli.egg-info → dataproduct_cli-0.2.0}/PKG-INFO +6 -4
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/README.md +5 -3
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/data_product.py +8 -5
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/init/init_template.py +1 -1
- dataproduct_cli-0.2.0/dataproduct/lint/schema.py +64 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/lint/validate.py +16 -7
- dataproduct_cli-0.0.1/dataproduct/schemas/odps-1.0.0.init.yaml → dataproduct_cli-0.2.0/dataproduct/schemas/odps-1.1.0.init.yaml +2 -1
- dataproduct_cli-0.2.0/dataproduct/schemas/odps-1.1.0.schema.json +717 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0/dataproduct_cli.egg-info}/PKG-INFO +6 -4
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct_cli.egg-info/SOURCES.txt +2 -1
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/pyproject.toml +1 -1
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/tests/test_init.py +1 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/tests/test_lint.py +55 -0
- dataproduct_cli-0.0.1/dataproduct/lint/schema.py +0 -44
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/LICENSE +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/MANIFEST.in +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/__init__.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/cli.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/command_init.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/command_lint.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/command_publish.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/config/__init__.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/init/__init__.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/integration/__init__.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/integration/entropy_data.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/lint/__init__.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/lint/files.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/lint/resolve.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/model/__init__.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/model/exceptions.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/model/run.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/output/__init__.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/output/output_format.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/output/result_writer.py +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/py.typed +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct/schemas/odps-1.0.0.schema.json +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct_cli.egg-info/dependency_links.txt +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct_cli.egg-info/entry_points.txt +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct_cli.egg-info/requires.txt +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct_cli.egg-info/top_level.txt +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/setup.cfg +0 -0
- {dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/tests/test_publish.py +0 -0
|
@@ -5,12 +5,18 @@ what changed (user-facing).
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
-
## [0.0
|
|
8
|
+
## [0.2.0]
|
|
9
9
|
|
|
10
|
-
-
|
|
10
|
+
- Support ODPS v1.1.0: `lint` validates against the bundled v1.1.0 JSON Schema (`type`, `context`, `synonyms`, `deprecated`, `customProperties[].vendor`, element `id`s, optional port `version`/`contractId`)
|
|
11
|
+
- `lint` validates against the bundled JSON Schema for the `apiVersion` the document declares (`v1.1.0` → v1.1.0 schema; `v1.0.0`/`v0.9.0` → v1.0.0 schema; unknown → newest), and check names state which schema ran
|
|
12
|
+
- `init` template now uses `apiVersion: v1.1.0`
|
|
11
13
|
|
|
12
14
|
## [0.1.0]
|
|
13
15
|
|
|
14
16
|
- `init` command: create a valid `dataproduct.odps.yaml` from a bundled ODPS v1.0.0 template
|
|
15
17
|
- `lint` command: validate a data product against the ODPS v1.0.0 JSON Schema (json/junit output)
|
|
16
18
|
- `publish` command: publish a data product to Entropy Data (`PUT /api/dataproducts/{id}`)
|
|
19
|
+
|
|
20
|
+
## [0.0.1]
|
|
21
|
+
|
|
22
|
+
- Test release to validate the PyPI publishing pipeline
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dataproduct-cli
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: The dataproduct CLI is an open source command-line tool for working with data products. It uses data product YAML files following the Open Data Product Standard (ODPS) to lint the definition against the standard and publish it to Entropy Data. The tool is written in Python. It can be used as a standalone CLI tool, in a CI/CD pipeline, or directly as a Python library.
|
|
5
5
|
Author-email: Michael Kutz <michael.kutz@entropy-data.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -44,7 +44,7 @@ Dynamic: license-file
|
|
|
44
44
|
|
|
45
45
|
The `dataproduct` CLI is an open-source command-line tool for working with
|
|
46
46
|
**data products** defined with the
|
|
47
|
-
[Open Data Product Standard (ODPS)](https://bitol-io.github.io/open-data-product-standard/v1.
|
|
47
|
+
[Open Data Product Standard (ODPS)](https://bitol-io.github.io/open-data-product-standard/v1.1.0/).
|
|
48
48
|
|
|
49
49
|
It is the data-product sibling of
|
|
50
50
|
[`datacontract-cli`](https://github.com/datacontract/datacontract-cli) (which
|
|
@@ -83,8 +83,10 @@ dataproduct lint --output-format junit --output TEST-dataproduct.xml
|
|
|
83
83
|
dataproduct lint --json-schema ./odps.schema.json # validate against a custom schema
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
Validation is schema-only
|
|
87
|
-
|
|
86
|
+
Validation is schema-only: the data product is checked against the bundled
|
|
87
|
+
ODPS JSON Schema matching its `apiVersion` (`v1.1.0`, `v1.0.0`, or `v0.9.0`;
|
|
88
|
+
unknown versions are validated against the latest). Exit code is `0` when
|
|
89
|
+
valid, `1` otherwise.
|
|
88
90
|
|
|
89
91
|
### `publish` — publish to Entropy Data
|
|
90
92
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The `dataproduct` CLI is an open-source command-line tool for working with
|
|
4
4
|
**data products** defined with the
|
|
5
|
-
[Open Data Product Standard (ODPS)](https://bitol-io.github.io/open-data-product-standard/v1.
|
|
5
|
+
[Open Data Product Standard (ODPS)](https://bitol-io.github.io/open-data-product-standard/v1.1.0/).
|
|
6
6
|
|
|
7
7
|
It is the data-product sibling of
|
|
8
8
|
[`datacontract-cli`](https://github.com/datacontract/datacontract-cli) (which
|
|
@@ -41,8 +41,10 @@ dataproduct lint --output-format junit --output TEST-dataproduct.xml
|
|
|
41
41
|
dataproduct lint --json-schema ./odps.schema.json # validate against a custom schema
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
Validation is schema-only
|
|
45
|
-
|
|
44
|
+
Validation is schema-only: the data product is checked against the bundled
|
|
45
|
+
ODPS JSON Schema matching its `apiVersion` (`v1.1.0`, `v1.0.0`, or `v0.9.0`;
|
|
46
|
+
unknown versions are validated against the latest). Exit code is `0` when
|
|
47
|
+
valid, `1` otherwise.
|
|
46
48
|
|
|
47
49
|
### `publish` — publish to Entropy Data
|
|
48
50
|
|
|
@@ -11,7 +11,7 @@ from typing import Optional, Union
|
|
|
11
11
|
from dataproduct.config import Config
|
|
12
12
|
from dataproduct.integration.entropy_data import publish_data_product_to_entropy_data
|
|
13
13
|
from dataproduct.lint.files import read_resource
|
|
14
|
-
from dataproduct.lint.schema import fetch_schema
|
|
14
|
+
from dataproduct.lint.schema import fetch_schema, schema_version_for
|
|
15
15
|
from dataproduct.lint.validate import parse_yaml, validate_against_schema
|
|
16
16
|
from dataproduct.model.exceptions import DataProductException
|
|
17
17
|
from dataproduct.model.run import Check, ResultEnum, Run
|
|
@@ -46,15 +46,16 @@ class DataProduct:
|
|
|
46
46
|
return parse_yaml(content)
|
|
47
47
|
|
|
48
48
|
def lint(self) -> Run:
|
|
49
|
-
"""Validate the data product against the ODPS JSON Schema (schema-only)."""
|
|
49
|
+
"""Validate the data product against the ODPS JSON Schema matching its ``apiVersion`` (schema-only)."""
|
|
50
50
|
run = Run.create_run()
|
|
51
51
|
run.log_info("Linting data product")
|
|
52
52
|
try:
|
|
53
53
|
data = self._load_dict()
|
|
54
54
|
run.dataProductId = data.get("id")
|
|
55
55
|
run.dataProductVersion = data.get("version")
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
schema_version = None if self._schema_location else schema_version_for(data.get("apiVersion"))
|
|
57
|
+
schema = fetch_schema(self._schema_location, schema_version)
|
|
58
|
+
checks = validate_against_schema(data, schema, self._all_errors, schema_version)
|
|
58
59
|
if checks:
|
|
59
60
|
run.checks.extend(checks)
|
|
60
61
|
for check in checks:
|
|
@@ -64,7 +65,9 @@ class DataProduct:
|
|
|
64
65
|
Check(
|
|
65
66
|
type="lint",
|
|
66
67
|
result=ResultEnum.passed,
|
|
67
|
-
name="Data product is syntactically valid"
|
|
68
|
+
name="Data product is syntactically valid"
|
|
69
|
+
if schema_version is None
|
|
70
|
+
else f"Data product is valid against ODPS v{schema_version}",
|
|
68
71
|
)
|
|
69
72
|
)
|
|
70
73
|
except DataProductException as e:
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import importlib.resources as resources
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, Dict, Optional, Union
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
from dataproduct.model.exceptions import DataProductException
|
|
11
|
+
from dataproduct.model.run import ResultEnum
|
|
12
|
+
|
|
13
|
+
# ODPS v1.1.0 relaxed several required fields (e.g. `status`, port `version`/`contractId`)
|
|
14
|
+
# and added new ones, so older documents must be validated against their own schema.
|
|
15
|
+
# v0.9.0 has no dedicated bundled schema; the v1.0.0 schema accepts it.
|
|
16
|
+
ODPS_SCHEMA_VERSIONS = {
|
|
17
|
+
"v1.1.0": "1.1.0",
|
|
18
|
+
"v1.0.0": "1.0.0",
|
|
19
|
+
"v0.9.0": "1.0.0",
|
|
20
|
+
}
|
|
21
|
+
DEFAULT_ODPS_SCHEMA_VERSION = "1.1.0"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def schema_version_for(api_version: Any = None) -> str:
|
|
25
|
+
"""Return the bundled ODPS schema version for a document's ``apiVersion``.
|
|
26
|
+
|
|
27
|
+
Unknown or missing versions fall back to the newest bundled schema, which
|
|
28
|
+
then reports the invalid ``apiVersion`` as a schema violation.
|
|
29
|
+
"""
|
|
30
|
+
if isinstance(api_version, str):
|
|
31
|
+
return ODPS_SCHEMA_VERSIONS.get(api_version, DEFAULT_ODPS_SCHEMA_VERSION)
|
|
32
|
+
return DEFAULT_ODPS_SCHEMA_VERSION
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def fetch_schema(location: Union[str, Path] = None, schema_version: Optional[str] = None) -> Dict[str, Any]:
|
|
36
|
+
"""Fetch the ODPS JSON Schema to validate against.
|
|
37
|
+
|
|
38
|
+
``None`` uses the bundled schema for ``schema_version`` (newest when
|
|
39
|
+
omitted); otherwise ``location`` is a URL or local path.
|
|
40
|
+
"""
|
|
41
|
+
if location is None:
|
|
42
|
+
schema_name = f"odps-{schema_version or DEFAULT_ODPS_SCHEMA_VERSION}.schema.json"
|
|
43
|
+
logging.info("Use default bundled schema " + schema_name)
|
|
44
|
+
schemas = resources.files("dataproduct")
|
|
45
|
+
schema_file = schemas.joinpath("schemas", schema_name)
|
|
46
|
+
with schema_file.open("r") as file:
|
|
47
|
+
return json.load(file)
|
|
48
|
+
|
|
49
|
+
location_str = str(location)
|
|
50
|
+
if location_str.startswith("http://") or location_str.startswith("https://"):
|
|
51
|
+
logging.debug(f"Downloading schema from {location_str}")
|
|
52
|
+
response = requests.get(location_str)
|
|
53
|
+
return response.json()
|
|
54
|
+
|
|
55
|
+
if not os.path.exists(location_str):
|
|
56
|
+
raise DataProductException(
|
|
57
|
+
type="lint",
|
|
58
|
+
name=f"Reading schema from {location_str}",
|
|
59
|
+
reason=f"The file '{location_str}' does not exist.",
|
|
60
|
+
result=ResultEnum.error,
|
|
61
|
+
)
|
|
62
|
+
logging.debug(f"Loading JSON schema locally at {location_str}")
|
|
63
|
+
with open(location_str, "r") as file:
|
|
64
|
+
return json.load(file)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, Dict, List
|
|
1
|
+
from typing import Any, Dict, List, Optional
|
|
2
2
|
|
|
3
3
|
import yaml
|
|
4
4
|
from jsonschema.validators import validator_for
|
|
@@ -32,11 +32,15 @@ def parse_yaml(content: str) -> Dict[str, Any]:
|
|
|
32
32
|
return data
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
def validate_against_schema(
|
|
35
|
+
def validate_against_schema(
|
|
36
|
+
data: Dict[str, Any], schema: Dict[str, Any], all_errors: bool = False, schema_version: Optional[str] = None
|
|
37
|
+
) -> List[Check]:
|
|
36
38
|
"""Validate ``data`` against the ODPS JSON Schema.
|
|
37
39
|
|
|
38
40
|
Returns a list of ``error`` checks — empty when the document is valid. With
|
|
39
|
-
``all_errors=False`` (default) only the first violation is reported.
|
|
41
|
+
``all_errors=False`` (default) only the first violation is reported. Check
|
|
42
|
+
names state the bundled ``schema_version`` that ran; ``None`` means a custom
|
|
43
|
+
schema was supplied and no version is named.
|
|
40
44
|
"""
|
|
41
45
|
validator_cls = validator_for(schema)
|
|
42
46
|
validator_cls.check_schema(schema)
|
|
@@ -46,16 +50,21 @@ def validate_against_schema(data: Dict[str, Any], schema: Dict[str, Any], all_er
|
|
|
46
50
|
if not all_errors:
|
|
47
51
|
errors = errors[:1]
|
|
48
52
|
|
|
53
|
+
name = (
|
|
54
|
+
"Check that data product YAML is valid"
|
|
55
|
+
if schema_version is None
|
|
56
|
+
else f"Check that data product is valid against ODPS v{schema_version}"
|
|
57
|
+
)
|
|
49
58
|
checks: List[Check] = []
|
|
50
59
|
for error in errors:
|
|
51
|
-
path = "/".join(str(p) for p in error.absolute_path)
|
|
60
|
+
path = "/".join(str(p) for p in error.absolute_path)
|
|
52
61
|
checks.append(
|
|
53
62
|
Check(
|
|
54
63
|
type="lint",
|
|
55
64
|
result=ResultEnum.error,
|
|
56
|
-
name=
|
|
57
|
-
reason=error.message,
|
|
58
|
-
field=path,
|
|
65
|
+
name=name,
|
|
66
|
+
reason=f"{path}: {error.message}" if path else error.message,
|
|
67
|
+
field=path or "(root)",
|
|
59
68
|
)
|
|
60
69
|
)
|
|
61
70
|
return checks
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
apiVersion: v1.
|
|
1
|
+
apiVersion: v1.1.0
|
|
2
2
|
kind: DataProduct
|
|
3
3
|
id: my-data-product-id
|
|
4
4
|
name: My Data Product
|
|
5
5
|
version: v1.0.0
|
|
6
6
|
status: draft
|
|
7
|
+
# type: consumerAligned # sourceAligned | aggregate | consumerAligned
|
|
7
8
|
|
|
8
9
|
description:
|
|
9
10
|
purpose: Purpose of the data product.
|
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2019-09/schema",
|
|
3
|
+
"title": "Open Data Product Standard (ODPS)",
|
|
4
|
+
"description": "An open data product standard descriptor to enable defining data products.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["apiVersion", "kind", "id"],
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"apiVersion": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"default": "v1.1.0",
|
|
12
|
+
"description": "Version of the standard used to build data product. Default value is v1.1.0.",
|
|
13
|
+
"enum": ["v1.1.0", "v1.0.0", "v0.9.0"]
|
|
14
|
+
},
|
|
15
|
+
"kind": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"default": "DataProduct",
|
|
18
|
+
"description": "The kind of file this is. Valid value is `DataProduct`.",
|
|
19
|
+
"enum": ["DataProduct"]
|
|
20
|
+
},
|
|
21
|
+
"id": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"description": "A unique identifier used to reduce the risk of dataset name collisions, such as a UUID."
|
|
24
|
+
},
|
|
25
|
+
"name": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"description": "Name of the data product."
|
|
28
|
+
},
|
|
29
|
+
"deprecated": {
|
|
30
|
+
"type": "boolean",
|
|
31
|
+
"description": "Indicates this data product is deprecated and should not be used in new implementations. Defaults to false.",
|
|
32
|
+
"default": false
|
|
33
|
+
},
|
|
34
|
+
"synonyms": {
|
|
35
|
+
"$ref": "#/$defs/Synonyms"
|
|
36
|
+
},
|
|
37
|
+
"version": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"description": "Current version of the data product. Not required, but highly recommended."
|
|
40
|
+
},
|
|
41
|
+
"status": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"description": "Current status of the data product.",
|
|
44
|
+
"examples": ["proposed", "draft", "active", "deprecated", "retired"]
|
|
45
|
+
},
|
|
46
|
+
"domain": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "Business domain"
|
|
49
|
+
},
|
|
50
|
+
"type": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"description": "Architectural type of the data product. Common values: `sourceAligned`, `aggregate`, `consumerAligned`. Organizations may define custom types."
|
|
53
|
+
},
|
|
54
|
+
"context": {
|
|
55
|
+
"$ref": "#/$defs/Context"
|
|
56
|
+
},
|
|
57
|
+
"tenant": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"description": "Organization identifier"
|
|
60
|
+
},
|
|
61
|
+
"authoritativeDefinitions": {
|
|
62
|
+
"type": "array",
|
|
63
|
+
"description": "List of links to sources that provide more details on the data contract.",
|
|
64
|
+
"items": {
|
|
65
|
+
"$ref": "#/$defs/AuthoritativeDefinition"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"description": {
|
|
69
|
+
"$ref": "#/$defs/Description"
|
|
70
|
+
},
|
|
71
|
+
"customProperties": {
|
|
72
|
+
"type": "array",
|
|
73
|
+
"description": "A list of key/value pairs for custom properties.",
|
|
74
|
+
"items": {
|
|
75
|
+
"$ref": "#/$defs/CustomProperty"
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"tags": {
|
|
79
|
+
"$ref": "#/$defs/Tags"
|
|
80
|
+
},
|
|
81
|
+
"inputPorts": {
|
|
82
|
+
"type": "array",
|
|
83
|
+
"description": "List of objects describing an input port. You need at least one as a data product needs to get data somewhere.",
|
|
84
|
+
"items": {
|
|
85
|
+
"$ref": "#/$defs/InputPort"
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
"outputPorts": {
|
|
89
|
+
"type": "array",
|
|
90
|
+
"description": "List of objects describing an output port. You need at least one, as a data product without output is useless.",
|
|
91
|
+
"items": {
|
|
92
|
+
"$ref": "#/$defs/OutputPort"
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"managementPorts": {
|
|
96
|
+
"type": "array",
|
|
97
|
+
"description": "Management ports define access points for managing the data product.",
|
|
98
|
+
"items": {
|
|
99
|
+
"$ref": "#/$defs/ManagementPort"
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"support": {
|
|
103
|
+
"type": "array",
|
|
104
|
+
"description": "Support and communication channels.",
|
|
105
|
+
"items": {
|
|
106
|
+
"$ref": "#/$defs/Support"
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"team": {
|
|
110
|
+
"$ref": "#/$defs/Team"
|
|
111
|
+
},
|
|
112
|
+
"productCreatedTs": {
|
|
113
|
+
"type": "string",
|
|
114
|
+
"format": "date-time",
|
|
115
|
+
"description": "Timestamp in UTC of when the data product was created, using ISO 8601."
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"$defs": {
|
|
119
|
+
"Tags": {
|
|
120
|
+
"type": "array",
|
|
121
|
+
"description": "A list of tags that may be assigned to the elements (object or property); the tags keyword may appear at any level. Tags may be used to better categorize an element. For example, `finance`, `sensitive`, `employee_record`.",
|
|
122
|
+
"examples": ["finance", "sensitive", "employee_record"],
|
|
123
|
+
"items": {
|
|
124
|
+
"type": "string"
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"Description": {
|
|
128
|
+
"type": "object",
|
|
129
|
+
"description": "Object containing the descriptions.",
|
|
130
|
+
"additionalProperties": false,
|
|
131
|
+
"properties": {
|
|
132
|
+
"purpose": {
|
|
133
|
+
"type": "string",
|
|
134
|
+
"description": "Intended purpose for the provided data."
|
|
135
|
+
},
|
|
136
|
+
"limitations": {
|
|
137
|
+
"type": "string",
|
|
138
|
+
"description": "Technical, compliance, and legal limitations for data use."
|
|
139
|
+
},
|
|
140
|
+
"usage": {
|
|
141
|
+
"type": "string",
|
|
142
|
+
"description": "Recommended usage of the data."
|
|
143
|
+
},
|
|
144
|
+
"authoritativeDefinitions": {
|
|
145
|
+
"type": "array",
|
|
146
|
+
"description": "List of links to sources that provide more details on the data contract.",
|
|
147
|
+
"items": {
|
|
148
|
+
"$ref": "#/$defs/AuthoritativeDefinition"
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
"customProperties": {
|
|
152
|
+
"type": "array",
|
|
153
|
+
"description": "A list of key/value pairs for custom properties.",
|
|
154
|
+
"items": {
|
|
155
|
+
"$ref": "#/$defs/CustomProperty"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
"CustomProperty": {
|
|
161
|
+
"type": "object",
|
|
162
|
+
"description": "A key/value pair for custom properties.",
|
|
163
|
+
"additionalProperties": false,
|
|
164
|
+
"properties": {
|
|
165
|
+
"id": {
|
|
166
|
+
"type": "string",
|
|
167
|
+
"description": "A unique identifier for the element used to create stable, refactor-safe references. Recommended for elements that will be referenced."
|
|
168
|
+
},
|
|
169
|
+
"property": {
|
|
170
|
+
"type": "string",
|
|
171
|
+
"description": "The name of the key. Names should be in camel case, the same as if they were permanent properties in the contract."
|
|
172
|
+
},
|
|
173
|
+
"value": {
|
|
174
|
+
"description": "The value of the key."
|
|
175
|
+
},
|
|
176
|
+
"description": {
|
|
177
|
+
"type": "string",
|
|
178
|
+
"description": "Optional description."
|
|
179
|
+
},
|
|
180
|
+
"vendor": {
|
|
181
|
+
"type": "string",
|
|
182
|
+
"description": "Identifies the vendor, provider, or external system associated with this custom property. SHOULD be a stable, lowercase identifier matching ^[a-z0-9][a-z0-9-]*$ (e.g. confluent, zeenea, atlan, soda). Tools MUST preserve unknown vendor values.",
|
|
183
|
+
"examples": ["confluent", "zeenea", "atlan", "soda"]
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
"required": ["property", "value"]
|
|
187
|
+
},
|
|
188
|
+
"Synonyms": {
|
|
189
|
+
"type": "array",
|
|
190
|
+
"description": "A list of alternative names for the object. See RFC 0041.",
|
|
191
|
+
"items": {
|
|
192
|
+
"$ref": "#/$defs/Synonym"
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
"Synonym": {
|
|
196
|
+
"type": "object",
|
|
197
|
+
"description": "An alternative name for a named object, helping catalogs, AI/LLM tools, and natural language interfaces resolve business vocabulary to the underlying object. See RFC 0041.",
|
|
198
|
+
"additionalProperties": false,
|
|
199
|
+
"properties": {
|
|
200
|
+
"id": {
|
|
201
|
+
"type": "string",
|
|
202
|
+
"description": "A unique identifier for the synonym, useful when referencing or deduplicating synonyms across tools. Recommended for elements that will be referenced."
|
|
203
|
+
},
|
|
204
|
+
"synonym": {
|
|
205
|
+
"type": "string",
|
|
206
|
+
"description": "The synonymous term."
|
|
207
|
+
},
|
|
208
|
+
"description": {
|
|
209
|
+
"type": "string",
|
|
210
|
+
"description": "Short human-readable note about when or why this synonym is used."
|
|
211
|
+
},
|
|
212
|
+
"locale": {
|
|
213
|
+
"type": "string",
|
|
214
|
+
"description": "BCP 47 language tag (e.g., `en-US`, `fr-FR`) when the synonym is language-specific."
|
|
215
|
+
},
|
|
216
|
+
"source": {
|
|
217
|
+
"type": "string",
|
|
218
|
+
"description": "Origin of the synonym (e.g., `glossary`, `finance-team`, `legacy-system`)."
|
|
219
|
+
},
|
|
220
|
+
"status": {
|
|
221
|
+
"type": "string",
|
|
222
|
+
"description": "Lifecycle status of the synonym (e.g., `active`, `deprecated`)."
|
|
223
|
+
},
|
|
224
|
+
"customProperties": {
|
|
225
|
+
"type": "array",
|
|
226
|
+
"description": "Custom properties block.",
|
|
227
|
+
"items": {
|
|
228
|
+
"$ref": "#/$defs/CustomProperty"
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
"required": ["synonym"]
|
|
233
|
+
},
|
|
234
|
+
"AuthoritativeDefinition": {
|
|
235
|
+
"type": "object",
|
|
236
|
+
"description": "A type/link pair for authoritative definitions.",
|
|
237
|
+
"additionalProperties": false,
|
|
238
|
+
"properties": {
|
|
239
|
+
"id": {
|
|
240
|
+
"type": "string",
|
|
241
|
+
"description": "A unique identifier for the element used to create stable, refactor-safe references. Recommended for elements that will be referenced."
|
|
242
|
+
},
|
|
243
|
+
"type": {
|
|
244
|
+
"type": "string",
|
|
245
|
+
"description": "Type of definition for authority.",
|
|
246
|
+
"examples": ["businessDefinition", "canonicalUrl", "glossary", "implementation", "ontology", "taxonomy", "transformationImplementation", "tutorial", "videoTutorial"]
|
|
247
|
+
},
|
|
248
|
+
"url": {
|
|
249
|
+
"type": "string",
|
|
250
|
+
"format": "uri",
|
|
251
|
+
"description": "URL to the authority."
|
|
252
|
+
},
|
|
253
|
+
"description": {
|
|
254
|
+
"type": "string",
|
|
255
|
+
"description": "Optional description."
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
"required": ["type", "url"]
|
|
259
|
+
},
|
|
260
|
+
"InputPort": {
|
|
261
|
+
"type": "object",
|
|
262
|
+
"description": "An input port describing expectations.",
|
|
263
|
+
"additionalProperties": false,
|
|
264
|
+
"properties": {
|
|
265
|
+
"deprecated": {
|
|
266
|
+
"type": "boolean",
|
|
267
|
+
"description": "Indicates this input port is deprecated and should not be used in new implementations. Defaults to false.",
|
|
268
|
+
"default": false
|
|
269
|
+
},
|
|
270
|
+
"id": {
|
|
271
|
+
"type": "string",
|
|
272
|
+
"description": "A unique identifier for the element used to create stable, refactor-safe references. Recommended for elements that will be referenced."
|
|
273
|
+
},
|
|
274
|
+
"name": {
|
|
275
|
+
"type": "string",
|
|
276
|
+
"description": "Name of the input port."
|
|
277
|
+
},
|
|
278
|
+
"version": {
|
|
279
|
+
"type": "string",
|
|
280
|
+
"description": "Version of the input port."
|
|
281
|
+
},
|
|
282
|
+
"contractId": {
|
|
283
|
+
"type": "string",
|
|
284
|
+
"description": "Contract ID for the input port."
|
|
285
|
+
},
|
|
286
|
+
"tags": {
|
|
287
|
+
"$ref": "#/$defs/Tags"
|
|
288
|
+
},
|
|
289
|
+
"customProperties": {
|
|
290
|
+
"type": "array",
|
|
291
|
+
"description": "Custom properties block.",
|
|
292
|
+
"items": {
|
|
293
|
+
"$ref": "#/$defs/CustomProperty"
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
"authoritativeDefinitions": {
|
|
297
|
+
"type": "array",
|
|
298
|
+
"description": "Authoritative definitions block.",
|
|
299
|
+
"items": {
|
|
300
|
+
"$ref": "#/$defs/AuthoritativeDefinition"
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
"required": ["name"]
|
|
305
|
+
},
|
|
306
|
+
"OutputPort": {
|
|
307
|
+
"type": "object",
|
|
308
|
+
"description": "An output port describing promises.",
|
|
309
|
+
"additionalProperties": false,
|
|
310
|
+
"properties": {
|
|
311
|
+
"deprecated": {
|
|
312
|
+
"type": "boolean",
|
|
313
|
+
"description": "Indicates this output port is deprecated and should not be used in new implementations. Defaults to false.",
|
|
314
|
+
"default": false
|
|
315
|
+
},
|
|
316
|
+
"id": {
|
|
317
|
+
"type": "string",
|
|
318
|
+
"description": "A unique identifier for the element used to create stable, refactor-safe references. Recommended for elements that will be referenced."
|
|
319
|
+
},
|
|
320
|
+
"name": {
|
|
321
|
+
"type": "string",
|
|
322
|
+
"description": "Name of the output port."
|
|
323
|
+
},
|
|
324
|
+
"description": {
|
|
325
|
+
"type": "string",
|
|
326
|
+
"description": "Human readable short description of the output port."
|
|
327
|
+
},
|
|
328
|
+
"type": {
|
|
329
|
+
"type": "string",
|
|
330
|
+
"description": "There can be different types of output ports, each automated and handled differently. Here you can indicate the type."
|
|
331
|
+
},
|
|
332
|
+
"version": {
|
|
333
|
+
"type": "string",
|
|
334
|
+
"description": "For each version, a different instance of the output port is listed. The combination of the name and version is the key."
|
|
335
|
+
},
|
|
336
|
+
"contractId": {
|
|
337
|
+
"type": "string",
|
|
338
|
+
"description": "Contract ID for the output port."
|
|
339
|
+
},
|
|
340
|
+
"sbom": {
|
|
341
|
+
"type": "array",
|
|
342
|
+
"description": "The SBOM can/should be at the version level.",
|
|
343
|
+
"items": {
|
|
344
|
+
"$ref": "#/$defs/SBOM"
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
"inputContracts": {
|
|
348
|
+
"type": "array",
|
|
349
|
+
"description": "Dependencies or input contracts.",
|
|
350
|
+
"items": {
|
|
351
|
+
"$ref": "#/$defs/InputContract"
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
"synonyms": {
|
|
355
|
+
"$ref": "#/$defs/Synonyms"
|
|
356
|
+
},
|
|
357
|
+
"tags": {
|
|
358
|
+
"$ref": "#/$defs/Tags"
|
|
359
|
+
},
|
|
360
|
+
"customProperties": {
|
|
361
|
+
"type": "array",
|
|
362
|
+
"description": "Custom properties block.",
|
|
363
|
+
"items": {
|
|
364
|
+
"$ref": "#/$defs/CustomProperty"
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
"authoritativeDefinitions": {
|
|
368
|
+
"type": "array",
|
|
369
|
+
"description": "Authoritative definitions block.",
|
|
370
|
+
"items": {
|
|
371
|
+
"$ref": "#/$defs/AuthoritativeDefinition"
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
"context": {
|
|
375
|
+
"$ref": "#/$defs/Context"
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
"required": ["name"]
|
|
379
|
+
},
|
|
380
|
+
"SBOM": {
|
|
381
|
+
"type": "object",
|
|
382
|
+
"description": "Software Bill of Materials.",
|
|
383
|
+
"additionalProperties": false,
|
|
384
|
+
"properties": {
|
|
385
|
+
"id": {
|
|
386
|
+
"type": "string",
|
|
387
|
+
"description": "A unique identifier for the element used to create stable, refactor-safe references. Recommended for elements that will be referenced."
|
|
388
|
+
},
|
|
389
|
+
"type": {
|
|
390
|
+
"type": "string",
|
|
391
|
+
"default": "external",
|
|
392
|
+
"description": "Type of SBOM."
|
|
393
|
+
},
|
|
394
|
+
"url": {
|
|
395
|
+
"type": "string",
|
|
396
|
+
"format": "uri",
|
|
397
|
+
"description": "URL to the SBOM."
|
|
398
|
+
},
|
|
399
|
+
"tags": { "$ref": "#/$defs/Tags" },
|
|
400
|
+
"customProperties": {
|
|
401
|
+
"type": "array",
|
|
402
|
+
"description": "Custom properties block.",
|
|
403
|
+
"items": { "$ref": "#/$defs/CustomProperty" }
|
|
404
|
+
},
|
|
405
|
+
"authoritativeDefinitions": {
|
|
406
|
+
"type": "array",
|
|
407
|
+
"description": "Authoritative definitions block.",
|
|
408
|
+
"items": { "$ref": "#/$defs/AuthoritativeDefinition" }
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
"InputContract": {
|
|
413
|
+
"type": "object",
|
|
414
|
+
"description": "Input contract dependency.",
|
|
415
|
+
"additionalProperties": false,
|
|
416
|
+
"properties": {
|
|
417
|
+
"id": {
|
|
418
|
+
"type": "string",
|
|
419
|
+
"description": "Contract ID or contractId."
|
|
420
|
+
},
|
|
421
|
+
"version": {
|
|
422
|
+
"type": "string",
|
|
423
|
+
"description": "Version of the input contract."
|
|
424
|
+
}
|
|
425
|
+
},
|
|
426
|
+
"required": ["id", "version"]
|
|
427
|
+
},
|
|
428
|
+
"ManagementPort": {
|
|
429
|
+
"type": "object",
|
|
430
|
+
"description": "Management port for managing the data product.",
|
|
431
|
+
"additionalProperties": false,
|
|
432
|
+
"properties": {
|
|
433
|
+
"deprecated": {
|
|
434
|
+
"type": "boolean",
|
|
435
|
+
"description": "Indicates this management port is deprecated and should not be used in new implementations. Defaults to false.",
|
|
436
|
+
"default": false
|
|
437
|
+
},
|
|
438
|
+
"id": {
|
|
439
|
+
"type": "string",
|
|
440
|
+
"description": "A unique identifier for the element used to create stable, refactor-safe references. Recommended for elements that will be referenced."
|
|
441
|
+
},
|
|
442
|
+
"name": {
|
|
443
|
+
"type": "string",
|
|
444
|
+
"description": "Endpoint identifier or unique name."
|
|
445
|
+
},
|
|
446
|
+
"content": {
|
|
447
|
+
"type": "string",
|
|
448
|
+
"description": "Content type.",
|
|
449
|
+
"examples": ["discoverability", "observability", "control", "dictionary"]
|
|
450
|
+
},
|
|
451
|
+
"type": {
|
|
452
|
+
"type": "string",
|
|
453
|
+
"default": "rest",
|
|
454
|
+
"description": "Type: can be `rest` or `topic`. Default is `rest`.",
|
|
455
|
+
"examples": ["rest", "topic"]
|
|
456
|
+
},
|
|
457
|
+
"url": {
|
|
458
|
+
"type": "string",
|
|
459
|
+
"format": "uri",
|
|
460
|
+
"description": "URL to access the endpoint."
|
|
461
|
+
},
|
|
462
|
+
"channel": {
|
|
463
|
+
"type": "string",
|
|
464
|
+
"description": "Channel to communicate with the data product."
|
|
465
|
+
},
|
|
466
|
+
"description": {
|
|
467
|
+
"type": "string",
|
|
468
|
+
"description": "Purpose and usage."
|
|
469
|
+
},
|
|
470
|
+
"tags": {
|
|
471
|
+
"$ref": "#/$defs/Tags"
|
|
472
|
+
},
|
|
473
|
+
"customProperties": {
|
|
474
|
+
"type": "array",
|
|
475
|
+
"description": "Custom properties block.",
|
|
476
|
+
"items": {
|
|
477
|
+
"$ref": "#/$defs/CustomProperty"
|
|
478
|
+
}
|
|
479
|
+
},
|
|
480
|
+
"authoritativeDefinitions": {
|
|
481
|
+
"type": "array",
|
|
482
|
+
"description": "Authoritative definitions block.",
|
|
483
|
+
"items": {
|
|
484
|
+
"$ref": "#/$defs/AuthoritativeDefinition"
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
},
|
|
488
|
+
"required": ["name", "content"]
|
|
489
|
+
},
|
|
490
|
+
"Support": {
|
|
491
|
+
"type": "object",
|
|
492
|
+
"description": "Support channel.",
|
|
493
|
+
"additionalProperties": false,
|
|
494
|
+
"properties": {
|
|
495
|
+
"id": {
|
|
496
|
+
"type": "string",
|
|
497
|
+
"description": "A unique identifier for the element used to create stable, refactor-safe references. Recommended for elements that will be referenced."
|
|
498
|
+
},
|
|
499
|
+
"channel": {
|
|
500
|
+
"type": "string",
|
|
501
|
+
"description": "Channel name or identifier."
|
|
502
|
+
},
|
|
503
|
+
"url": {
|
|
504
|
+
"type": "string",
|
|
505
|
+
"format": "uri",
|
|
506
|
+
"description": "Access URL using normal URL scheme (https, mailto, etc.)."
|
|
507
|
+
},
|
|
508
|
+
"description": {
|
|
509
|
+
"type": "string",
|
|
510
|
+
"description": "Description of the channel, free text."
|
|
511
|
+
},
|
|
512
|
+
"tool": {
|
|
513
|
+
"type": "string",
|
|
514
|
+
"description": "Name of the tool.",
|
|
515
|
+
"examples": ["email", "slack", "teams", "discord", "ticket", "other"]
|
|
516
|
+
},
|
|
517
|
+
"scope": {
|
|
518
|
+
"type": "string",
|
|
519
|
+
"description": "Scope can be: `interactive`, `announcements`, `issues`.",
|
|
520
|
+
"examples": ["interactive", "announcements", "issues"]
|
|
521
|
+
},
|
|
522
|
+
"invitationUrl": {
|
|
523
|
+
"type": "string",
|
|
524
|
+
"format": "uri",
|
|
525
|
+
"description": "Some tools uses invitation URL for requesting or subscribing. Follows the URL scheme."
|
|
526
|
+
},
|
|
527
|
+
"tags": {
|
|
528
|
+
"$ref": "#/$defs/Tags"
|
|
529
|
+
},
|
|
530
|
+
"customProperties": {
|
|
531
|
+
"type": "array",
|
|
532
|
+
"description": "Custom properties block.",
|
|
533
|
+
"items": {
|
|
534
|
+
"$ref": "#/$defs/CustomProperty"
|
|
535
|
+
}
|
|
536
|
+
},
|
|
537
|
+
"authoritativeDefinitions": {
|
|
538
|
+
"type": "array",
|
|
539
|
+
"description": "Authoritative definitions block.",
|
|
540
|
+
"items": {
|
|
541
|
+
"$ref": "#/$defs/AuthoritativeDefinition"
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
"required": ["channel", "url"]
|
|
546
|
+
},
|
|
547
|
+
"TeamMember": {
|
|
548
|
+
"type": "object",
|
|
549
|
+
"description": "Team member information.",
|
|
550
|
+
"additionalProperties": false,
|
|
551
|
+
"properties": {
|
|
552
|
+
"id": {
|
|
553
|
+
"type": "string",
|
|
554
|
+
"description": "A unique identifier for the element used to create stable, refactor-safe references. Recommended for elements that will be referenced."
|
|
555
|
+
},
|
|
556
|
+
"username": {
|
|
557
|
+
"type": "string",
|
|
558
|
+
"description": "The user's username or email."
|
|
559
|
+
},
|
|
560
|
+
"name": {
|
|
561
|
+
"type": "string",
|
|
562
|
+
"description": "The user's name."
|
|
563
|
+
},
|
|
564
|
+
"description": {
|
|
565
|
+
"type": "string",
|
|
566
|
+
"description": "The user's description."
|
|
567
|
+
},
|
|
568
|
+
"role": {
|
|
569
|
+
"type": "string",
|
|
570
|
+
"description": "The user's job role; Examples might be owner, data steward. There is no limit on the role."
|
|
571
|
+
},
|
|
572
|
+
"dateIn": {
|
|
573
|
+
"type": "string",
|
|
574
|
+
"format": "date",
|
|
575
|
+
"description": "The date when the user joined the team."
|
|
576
|
+
},
|
|
577
|
+
"dateOut": {
|
|
578
|
+
"type": "string",
|
|
579
|
+
"format": "date",
|
|
580
|
+
"description": "The date when the user ceased to be part of the team."
|
|
581
|
+
},
|
|
582
|
+
"replacedByUsername": {
|
|
583
|
+
"type": "string",
|
|
584
|
+
"description": "The username of the user who replaced the previous user."
|
|
585
|
+
},
|
|
586
|
+
"tags": {
|
|
587
|
+
"$ref": "#/$defs/Tags"
|
|
588
|
+
},
|
|
589
|
+
"customProperties": {
|
|
590
|
+
"type": "array",
|
|
591
|
+
"description": "Custom properties block.",
|
|
592
|
+
"items": {
|
|
593
|
+
"$ref": "#/$defs/CustomProperty"
|
|
594
|
+
}
|
|
595
|
+
},
|
|
596
|
+
"authoritativeDefinitions": {
|
|
597
|
+
"type": "array",
|
|
598
|
+
"description": "Authoritative definitions block.",
|
|
599
|
+
"items": {
|
|
600
|
+
"$ref": "#/$defs/AuthoritativeDefinition"
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
"required": ["username"]
|
|
605
|
+
},
|
|
606
|
+
"Context": {
|
|
607
|
+
"type": "object",
|
|
608
|
+
"description": "AI and semantic context block (RFC-0038). Provides structured guidance for AI agents, LLMs, BI tools, and semantic layer platforms. Optional and additive. Applicable at the data product (top level) and output port levels. Input ports refer to the linked ODCS contract's context instead.",
|
|
609
|
+
"additionalProperties": false,
|
|
610
|
+
"properties": {
|
|
611
|
+
"instructions": {
|
|
612
|
+
"type": "string",
|
|
613
|
+
"description": "Natural language guidance for AI agents and tools on how to use this entity. Equivalent to a system prompt scoped to this level."
|
|
614
|
+
},
|
|
615
|
+
"verifiedStatements": {
|
|
616
|
+
"type": "array",
|
|
617
|
+
"description": "Canonical business questions, each with an optional curated answer. Entries with `answer` should be returned verbatim by AI agents when a query is semantically close; entries without `answer` serve as sample questions for text-to-SQL priming and disambiguation.",
|
|
618
|
+
"items": {
|
|
619
|
+
"type": "object",
|
|
620
|
+
"additionalProperties": false,
|
|
621
|
+
"properties": {
|
|
622
|
+
"id": {
|
|
623
|
+
"type": "string",
|
|
624
|
+
"description": "Stable identifier for the entry, useful when referencing or deduplicating verified statements across tools."
|
|
625
|
+
},
|
|
626
|
+
"question": {
|
|
627
|
+
"type": "string",
|
|
628
|
+
"description": "The canonical question."
|
|
629
|
+
},
|
|
630
|
+
"answer": {
|
|
631
|
+
"type": "string",
|
|
632
|
+
"description": "The expected response or result description. Optional — omit to signal an unanswered sample question."
|
|
633
|
+
},
|
|
634
|
+
"authoritativeDefinitions": {
|
|
635
|
+
"type": "array",
|
|
636
|
+
"items": { "$ref": "#/$defs/AuthoritativeDefinition" }
|
|
637
|
+
},
|
|
638
|
+
"tags": { "$ref": "#/$defs/Tags" },
|
|
639
|
+
"customProperties": {
|
|
640
|
+
"type": "array",
|
|
641
|
+
"items": { "$ref": "#/$defs/CustomProperty" }
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
"required": ["question"]
|
|
645
|
+
}
|
|
646
|
+
},
|
|
647
|
+
"constraints": {
|
|
648
|
+
"type": "array",
|
|
649
|
+
"description": "Negative guidance: what AI agents must NOT do with this entity.",
|
|
650
|
+
"items": {
|
|
651
|
+
"type": "object",
|
|
652
|
+
"additionalProperties": false,
|
|
653
|
+
"properties": {
|
|
654
|
+
"id": {
|
|
655
|
+
"type": "string",
|
|
656
|
+
"description": "Stable identifier for the constraint."
|
|
657
|
+
},
|
|
658
|
+
"constraint": {
|
|
659
|
+
"type": "string",
|
|
660
|
+
"description": "The constraint text (negative guidance for AI agents)."
|
|
661
|
+
},
|
|
662
|
+
"authoritativeDefinitions": {
|
|
663
|
+
"type": "array",
|
|
664
|
+
"items": { "$ref": "#/$defs/AuthoritativeDefinition" }
|
|
665
|
+
},
|
|
666
|
+
"tags": { "$ref": "#/$defs/Tags" },
|
|
667
|
+
"customProperties": {
|
|
668
|
+
"type": "array",
|
|
669
|
+
"items": { "$ref": "#/$defs/CustomProperty" }
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
"required": ["constraint"]
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
},
|
|
677
|
+
"Team": {
|
|
678
|
+
"type": "object",
|
|
679
|
+
"description": "Team information.",
|
|
680
|
+
"additionalProperties": false,
|
|
681
|
+
"properties": {
|
|
682
|
+
"name": {
|
|
683
|
+
"type": "string",
|
|
684
|
+
"description": "Team name."
|
|
685
|
+
},
|
|
686
|
+
"description": {
|
|
687
|
+
"type": "string",
|
|
688
|
+
"description": "Team description."
|
|
689
|
+
},
|
|
690
|
+
"members": {
|
|
691
|
+
"type": "array",
|
|
692
|
+
"description": "List of members.",
|
|
693
|
+
"items": {
|
|
694
|
+
"$ref": "#/$defs/TeamMember"
|
|
695
|
+
}
|
|
696
|
+
},
|
|
697
|
+
"tags": {
|
|
698
|
+
"$ref": "#/$defs/Tags"
|
|
699
|
+
},
|
|
700
|
+
"customProperties": {
|
|
701
|
+
"type": "array",
|
|
702
|
+
"description": "Custom properties block.",
|
|
703
|
+
"items": {
|
|
704
|
+
"$ref": "#/$defs/CustomProperty"
|
|
705
|
+
}
|
|
706
|
+
},
|
|
707
|
+
"authoritativeDefinitions": {
|
|
708
|
+
"type": "array",
|
|
709
|
+
"description": "Authoritative definitions block.",
|
|
710
|
+
"items": {
|
|
711
|
+
"$ref": "#/$defs/AuthoritativeDefinition"
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dataproduct-cli
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: The dataproduct CLI is an open source command-line tool for working with data products. It uses data product YAML files following the Open Data Product Standard (ODPS) to lint the definition against the standard and publish it to Entropy Data. The tool is written in Python. It can be used as a standalone CLI tool, in a CI/CD pipeline, or directly as a Python library.
|
|
5
5
|
Author-email: Michael Kutz <michael.kutz@entropy-data.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -44,7 +44,7 @@ Dynamic: license-file
|
|
|
44
44
|
|
|
45
45
|
The `dataproduct` CLI is an open-source command-line tool for working with
|
|
46
46
|
**data products** defined with the
|
|
47
|
-
[Open Data Product Standard (ODPS)](https://bitol-io.github.io/open-data-product-standard/v1.
|
|
47
|
+
[Open Data Product Standard (ODPS)](https://bitol-io.github.io/open-data-product-standard/v1.1.0/).
|
|
48
48
|
|
|
49
49
|
It is the data-product sibling of
|
|
50
50
|
[`datacontract-cli`](https://github.com/datacontract/datacontract-cli) (which
|
|
@@ -83,8 +83,10 @@ dataproduct lint --output-format junit --output TEST-dataproduct.xml
|
|
|
83
83
|
dataproduct lint --json-schema ./odps.schema.json # validate against a custom schema
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
Validation is schema-only
|
|
87
|
-
|
|
86
|
+
Validation is schema-only: the data product is checked against the bundled
|
|
87
|
+
ODPS JSON Schema matching its `apiVersion` (`v1.1.0`, `v1.0.0`, or `v0.9.0`;
|
|
88
|
+
unknown versions are validated against the latest). Exit code is `0` when
|
|
89
|
+
valid, `1` otherwise.
|
|
88
90
|
|
|
89
91
|
### `publish` — publish to Entropy Data
|
|
90
92
|
|
|
@@ -26,8 +26,9 @@ dataproduct/model/run.py
|
|
|
26
26
|
dataproduct/output/__init__.py
|
|
27
27
|
dataproduct/output/output_format.py
|
|
28
28
|
dataproduct/output/result_writer.py
|
|
29
|
-
dataproduct/schemas/odps-1.0.0.init.yaml
|
|
30
29
|
dataproduct/schemas/odps-1.0.0.schema.json
|
|
30
|
+
dataproduct/schemas/odps-1.1.0.init.yaml
|
|
31
|
+
dataproduct/schemas/odps-1.1.0.schema.json
|
|
31
32
|
dataproduct_cli.egg-info/PKG-INFO
|
|
32
33
|
dataproduct_cli.egg-info/SOURCES.txt
|
|
33
34
|
dataproduct_cli.egg-info/dependency_links.txt
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dataproduct-cli"
|
|
3
|
-
version = "0.0
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "The dataproduct CLI is an open source command-line tool for working with data products. It uses data product YAML files following the Open Data Product Standard (ODPS) to lint the definition against the standard and publish it to Entropy Data. The tool is written in Python. It can be used as a standalone CLI tool, in a CI/CD pipeline, or directly as a Python library."
|
|
5
5
|
license = "MIT"
|
|
6
6
|
readme = "README.md"
|
|
@@ -12,6 +12,7 @@ def test_init_default_creates_valid_file(tmp_path, monkeypatch):
|
|
|
12
12
|
assert result.exit_code == 0, result.output
|
|
13
13
|
created = tmp_path / "dataproduct.odps.yaml"
|
|
14
14
|
assert created.exists()
|
|
15
|
+
assert "apiVersion: v1.1.0" in created.read_text()
|
|
15
16
|
|
|
16
17
|
# The generated file must pass lint with zero errors.
|
|
17
18
|
run = DataProduct(data_product_file=str(created)).lint()
|
|
@@ -4,6 +4,7 @@ from typer.testing import CliRunner
|
|
|
4
4
|
|
|
5
5
|
from dataproduct.cli import app
|
|
6
6
|
from dataproduct.data_product import DataProduct
|
|
7
|
+
from dataproduct.lint.schema import fetch_schema, schema_version_for
|
|
7
8
|
|
|
8
9
|
runner = CliRunner()
|
|
9
10
|
|
|
@@ -22,6 +23,60 @@ def test_lint_valid():
|
|
|
22
23
|
assert run.dataProductId == "064c4630-8aad-4dc0-ba95-0f69940e6b18"
|
|
23
24
|
|
|
24
25
|
|
|
26
|
+
def test_lint_valid_v1_1_0():
|
|
27
|
+
run = _lint("valid-dataproduct-v1.1.0.odps.yaml", all_errors=True)
|
|
28
|
+
assert run.result == "passed", run.checks
|
|
29
|
+
assert run.checks[0].name == "Data product is valid against ODPS v1.1.0"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_lint_names_the_schema_that_ran():
|
|
33
|
+
run = _lint("valid-dataproduct.odps.yaml")
|
|
34
|
+
assert run.result == "passed", run.checks
|
|
35
|
+
assert run.checks[0].name == "Data product is valid against ODPS v1.0.0"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_lint_names_no_version_for_custom_schema():
|
|
39
|
+
run = DataProduct(
|
|
40
|
+
data_product_file=str(FIXTURES / "valid-dataproduct.odps.yaml"),
|
|
41
|
+
schema_location=str(BUNDLED_SCHEMA),
|
|
42
|
+
).lint()
|
|
43
|
+
assert run.checks[0].name == "Data product is syntactically valid"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_lint_v1_1_0_status_is_optional():
|
|
47
|
+
run = _lint("missing-status-v1.1.0.odps.yaml")
|
|
48
|
+
assert run.result == "passed", run.checks
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_lint_v1_1_0_fields_rejected_under_v1_0_0():
|
|
52
|
+
run = _lint("v1.1.0-fields-in-v1.0.0.odps.yaml")
|
|
53
|
+
assert run.result == "failed"
|
|
54
|
+
assert run.checks[0].name == "Check that data product is valid against ODPS v1.0.0"
|
|
55
|
+
assert "type" in (run.checks[0].reason or "")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_lint_unknown_api_version_reported():
|
|
59
|
+
run = _lint("multiple-errors.odps.yaml", all_errors=True)
|
|
60
|
+
assert run.result == "failed"
|
|
61
|
+
assert all(c.name == "Check that data product is valid against ODPS v1.1.0" for c in run.checks)
|
|
62
|
+
assert "apiVersion" in [c.field for c in run.checks]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_schema_version_selected_by_api_version():
|
|
66
|
+
assert schema_version_for("v1.1.0") == "1.1.0"
|
|
67
|
+
assert schema_version_for("v1.0.0") == "1.0.0"
|
|
68
|
+
assert schema_version_for("v0.9.0") == "1.0.0"
|
|
69
|
+
assert schema_version_for(None) == "1.1.0"
|
|
70
|
+
assert schema_version_for("v9.9.9") == "1.1.0"
|
|
71
|
+
assert schema_version_for(["not", "a", "string"]) == "1.1.0"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_fetch_bundled_schema_per_version():
|
|
75
|
+
assert "status" in fetch_schema(schema_version="1.0.0")["required"]
|
|
76
|
+
assert "status" not in fetch_schema(schema_version="1.1.0")["required"]
|
|
77
|
+
assert fetch_schema()["properties"]["apiVersion"]["default"] == "v1.1.0"
|
|
78
|
+
|
|
79
|
+
|
|
25
80
|
def test_lint_valid_cli_exit_zero():
|
|
26
81
|
result = runner.invoke(app, ["lint", str(FIXTURES / "valid-dataproduct.odps.yaml")])
|
|
27
82
|
assert result.exit_code == 0, result.output
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import importlib.resources as resources
|
|
2
|
-
import json
|
|
3
|
-
import logging
|
|
4
|
-
import os
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Any, Dict, Union
|
|
7
|
-
|
|
8
|
-
import requests
|
|
9
|
-
|
|
10
|
-
from dataproduct.model.exceptions import DataProductException
|
|
11
|
-
from dataproduct.model.run import ResultEnum
|
|
12
|
-
|
|
13
|
-
DEFAULT_DATA_PRODUCT_SCHEMA = "odps-1.0.0.schema.json"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def fetch_schema(location: Union[str, Path] = None) -> Dict[str, Any]:
|
|
17
|
-
"""Fetch the ODPS JSON Schema to validate against.
|
|
18
|
-
|
|
19
|
-
``None`` uses the bundled ODPS v1.0.0 schema; otherwise ``location`` is a URL
|
|
20
|
-
or local path.
|
|
21
|
-
"""
|
|
22
|
-
if location is None:
|
|
23
|
-
logging.info("Use default bundled schema " + DEFAULT_DATA_PRODUCT_SCHEMA)
|
|
24
|
-
schemas = resources.files("dataproduct")
|
|
25
|
-
schema_file = schemas.joinpath("schemas", DEFAULT_DATA_PRODUCT_SCHEMA)
|
|
26
|
-
with schema_file.open("r") as file:
|
|
27
|
-
return json.load(file)
|
|
28
|
-
|
|
29
|
-
location_str = str(location)
|
|
30
|
-
if location_str.startswith("http://") or location_str.startswith("https://"):
|
|
31
|
-
logging.debug(f"Downloading schema from {location_str}")
|
|
32
|
-
response = requests.get(location_str)
|
|
33
|
-
return response.json()
|
|
34
|
-
|
|
35
|
-
if not os.path.exists(location_str):
|
|
36
|
-
raise DataProductException(
|
|
37
|
-
type="lint",
|
|
38
|
-
name=f"Reading schema from {location_str}",
|
|
39
|
-
reason=f"The file '{location_str}' does not exist.",
|
|
40
|
-
result=ResultEnum.error,
|
|
41
|
-
)
|
|
42
|
-
logging.debug(f"Loading JSON schema locally at {location_str}")
|
|
43
|
-
with open(location_str, "r") as file:
|
|
44
|
-
return json.load(file)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dataproduct_cli-0.0.1 → dataproduct_cli-0.2.0}/dataproduct_cli.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|