The package io.github.devbpm.linneo.catalog.openmetadata translates a Taxonomy into
OpenMetadata classifications and tags, and computes the tags of each classified column.
It makes no network calls: send the JSON bodies with the HTTP client you already use to
PUT /api/v1/classifications and PUT /api/v1/tags, then add the tags to the columns of each table. The bodies
follow OpenMetadata’s CreateClassification and CreateTag schemas, including parent. They were checked against
OpenMetadata 2.0.6 and use only fields present since 0.13.2. OpenMetadata accepts associatedTags on a tag but never
stores them, so Linneo does not send them: the protection a category implies goes on each column, see
Tags of a column.
The repository has an integration test, OpenMetadataIT, that sends these bodies to a real server; see
“Integration tests against catalog servers” in CONTRIBUTING.md.
io.github.devbpm.linneo.catalog.openmetadata.OpenMetadataCatalog — public final class, @since 0.1.0
Immutable and thread-safe. There is no public constructor.
| Method | Returns | Description |
|---|---|---|
static OpenMetadataCatalog of(Taxonomy taxonomy) |
catalog | With the default names (OpenMetadataMapping.defaults()). |
static OpenMetadataCatalog of(Taxonomy taxonomy, OpenMetadataMapping mapping) |
catalog | With the given names and sensitivity threshold. NullPointerException if either argument is null. |
List<OmClassification> classifications() |
immutable list | The classifications to create, in creation order. The built-in PersonalData and PII classifications are not included: they are reused. |
List<OmTag> tags() |
list | The tags to create, in creation order: the level tags first, then one tag per data category, every parent before its children. |
String tagOf(DataCategory category) |
String |
The fully qualified name of the category’s tag, such as DataCategory.user.contact.email. |
List<String> tagsOf(DataCategory category) |
list | The category’s own tag followed by the tags it implies (the tags the category implies). |
List<String> tagsOf(FieldClassification field) |
list | The tags of a classified column: its category, whether and how it is personal data, and the field’s effective protection. |
Taxonomy taxonomy = Taxonomy.load();
OpenMetadataCatalog catalog = OpenMetadataCatalog.of(taxonomy);
for (OmClassification c : catalog.classifications()) {
String body = c.toJson(); // PUT /api/v1/classifications
}
for (OmTag t : catalog.tags()) {
String body = t.toJson(); // PUT /api/v1/tags, in this order
}
ClassClassification person = new Classifier(taxonomy).classify(Person.class);
for (FieldClassification field : person.fields()) {
List<String> columnTags = catalog.tagsOf(field); // add to the column mapped to the field
}
With the default mapping, classifications() returns:
name |
mutuallyExclusive |
description |
|---|---|---|
DataCategory |
true |
What a column holds, from the Linneo data category taxonomy (fideslang default taxonomy plus extensions). |
Confidentiality |
true |
Harm that disclosing the data would cause. |
Integrity |
true |
Harm that an unwanted change to the data would cause. |
Identifiability |
true |
How directly a personal data value points to the person. |
DataHandling |
false |
Handling rules that apply to the data. |
tags() returns, in this order (12 + one per category; 137 for the standard taxonomy):
| Tags | Classification | name / displayName |
description |
|---|---|---|---|
Public, Low, Medium, High |
Confidentiality |
the level, capitalized | Confidentiality level <Level>. |
Low, Medium, High |
Integrity |
the level, capitalized | Integrity level <Level>. |
Identified, Pseudonymized, Anonymized, Aggregated |
Identifiability |
the value, capitalized | Personal data that is <value>. |
Secret |
DataHandling |
Secret |
A secret that grants access: it must never be read back, exported or published, only verified. |
| one per data category, in key order | DataCategory |
name: last key segment; displayName: the category’s name |
the category’s description, plus ` Deprecated. or Deprecated: use |
A category tag has parent = DataCategory.<parent key> (none for a root), so the hierarchy becomes nested tags:
user.contact.email is the tag DataCategory.user.contact.email. Level tags have no parent.
| Linneo | OpenMetadata tag (default names) |
|---|---|
Data category <key> |
DataCategory.<key> |
Nature ORDINARY |
PersonalData.Personal (built-in) |
Nature SPECIAL_CATEGORY or CRIMINAL_OFFENCE |
PersonalData.SpecialCategory (built-in; the only OpenMetadata tag for data with reinforced protection) |
Nature NONE |
no PersonalData tag |
Personal, confidentiality ≥ sensitiveFrom (default MEDIUM) |
PII.Sensitive (built-in) |
Personal, confidentiality < sensitiveFrom |
PII.NonSensitive (built-in) |
| Not personal | PII.None (built-in) |
| Confidentiality | Confidentiality.Public / .Low / .Medium / .High |
| Integrity | Integrity.Low / .Medium / .High |
| Identifiability (fields only) | Identifiability.Identified / .Pseudonymized / .Anonymized / .Aggregated |
| Secret | DataHandling.Secret |
The tag names Personal, SpecialCategory, None, NonSensitive, Sensitive and Secret are fixed; the
classification names are configurable with OpenMetadataMapping.
tagsOf(DataCategory) computes the tags a category implies from its facets, in this order:
PersonalData.Personal or PersonalData.SpecialCategory — only if the category is personal;PII.None, PII.NonSensitive or PII.Sensitive;Confidentiality.<level>;Integrity.<level>;DataHandling.Secret — only if the category is secret.tagsOf(DataCategory) prepends the category’s own tag. For example user.authorization.password gives
[DataCategory.user.authorization.password, PersonalData.Personal, PII.Sensitive, Confidentiality.High,
Integrity.High, DataHandling.Secret].
tagsOf(FieldClassification) uses the field’s effective levels and identifiability:
DataCategory.<key>;field.isPersonal() (personal category stored IDENTIFIED or PSEUDONYMIZED): the PersonalData tag of the
nature, then PII.Sensitive or PII.NonSensitive from the field’s confidentiality; otherwise PII.None
(an anonymized or aggregated personal field gets PII.None and no PersonalData tag);Confidentiality.<field level>;Integrity.<field level>;Identifiability.<value> — only if the field has an identifiability (personal category);DataHandling.Secret — only if the category is secret.@Category(value = Categories.USER_UNIQUE_ID_PSEUDONYMOUS, identifiability = Identifiability.PSEUDONYMIZED,
confidentiality = Confidentiality.MEDIUM)
private String reference;
// tagsOf → [DataCategory.user.unique_id.pseudonymous, PersonalData.Personal, PII.Sensitive,
// Confidentiality.Medium, Integrity.Medium, Identifiability.Pseudonymized]
io.github.devbpm.linneo.catalog.openmetadata.OmClassification — public record, @since 0.1.0
public record OmClassification(String name, String description, boolean mutuallyExclusive)
An OpenMetadata classification to create: the body of PUT /api/v1/classifications. mutuallyExclusive means an
entity can carry only one of its tags. The constructor throws NullPointerException if name or description is
null.
| Method | Description |
|---|---|
String toJson() |
The JSON body of a CreateClassification request. |
{"name":"DataCategory","description":"What a column holds, from the Linneo data category taxonomy (fideslang default taxonomy plus extensions).","mutuallyExclusive":true}
io.github.devbpm.linneo.catalog.openmetadata.OmTag — public record, @since 0.1.0
public record OmTag(String classification, String parent, String name, String displayName, String description)
An OpenMetadata tag to create: the body of PUT /api/v1/tags.
| Component | Nullable | Description |
|---|---|---|
classification |
no | Name of the classification it belongs to. |
parent |
yes | Fully qualified name of the parent tag; null at the root of the classification. |
name |
no | The tag name, one segment without dots. |
displayName |
no | The human-readable name. |
description |
no | What it means. |
The constructor throws NullPointerException if classification, name, displayName or description is null.
| Method | Description |
|---|---|
String fullyQualifiedName() |
parent (or classification when there is no parent) + . + name, such as DataCategory.user.contact.email. |
String toJson() |
The JSON body of a CreateTag request. |
{"classification":"DataCategory","parent":"DataCategory.user.contact","name":"email","displayName":"User Contact Email","description":"User's contact email address."}
toJson() writes a compact JSON object (no white space) with the internal writer
io.github.devbpm.linneo.internal.Json, shared with the DataHub and Atlas exporters (the package is not exported), so
the library needs no JSON dependency:
OmClassification: name, description, mutuallyExclusive. OmTag: classification,
parent, name, displayName, description.OmTag omits parent when it is null.mutuallyExclusive)." → \", \ → \\, newline → \n, carriage return → \r, tab → \t, backspace → \b, form
feed → \f, other control characters below U+0020 → \u00XX. All other characters, including non-ASCII ones, are
written as they are (the output is a Java String; encode it as UTF-8 when sending it).See also: Mapping · Results · Protection levels · Data catalogues compared · course: OpenMetadata