Skip to content

Commit 29a3d62

Browse files
authored
[#3460] feat(api): Add API design for Tag system (#3486)
### What changes were proposed in this pull request? This PR adds the API support for Tag system in Gravitino. ### Why are the changes needed? This is the first step to add a tag system. Fix: #3460 ### Does this PR introduce _any_ user-facing change? Yes. ### How was this patch tested? No. Test will be added later when implementing the logic.
1 parent ee6b9db commit 29a3d62

File tree

10 files changed

+674
-104
lines changed

10 files changed

+674
-104
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2024 Datastrato Pvt Ltd.
3+
* This software is licensed under the Apache License version 2.
4+
*/
5+
6+
package com.datastrato.gravitino;
7+
8+
import com.datastrato.gravitino.annotation.Unstable;
9+
import javax.annotation.Nullable;
10+
11+
/**
12+
* The MetadataObject is the basic unit of the Gravitino system. It represents the metadata object
13+
* in the Gravitino system. The object can be a metalake, catalog, schema, table, topic, etc.
14+
*/
15+
@Unstable
16+
public interface MetadataObject {
17+
/**
18+
* The type of object in the Gravitino system. Every type will map one kind of the entity of the
19+
* underlying system.
20+
*/
21+
enum Type {
22+
/**
23+
* A metalake is a concept of tenant. It means an organization. A metalake contains many data
24+
* sources.
25+
*/
26+
METALAKE,
27+
/**
28+
* A catalog is a collection of metadata from a specific metadata source, like Apache Hive
29+
* catalog, Apache Iceberg catalog, JDBC catalog, etc.
30+
*/
31+
CATALOG,
32+
/**
33+
* A schema is a sub collection of the catalog. The schema can contain filesets, tables, topics,
34+
* etc.
35+
*/
36+
SCHEMA,
37+
/** A fileset is mapped to a directory on a file system like HDFS, S3, ADLS, GCS, etc. */
38+
FILESET,
39+
/** A table is mapped the table of relational data sources like Apache Hive, MySQL, etc. */
40+
TABLE,
41+
/**
42+
* A topic is mapped the topic of messaging data sources like Apache Kafka, Apache Pulsar, etc.
43+
*/
44+
TOPIC,
45+
/** A column is a sub-collection of the table that represents a group of same type data. */
46+
COLUMN
47+
}
48+
49+
/**
50+
* The parent full name of the object. If the object doesn't have parent, this method will return
51+
* null.
52+
*
53+
* @return The parent full name of the object.
54+
*/
55+
@Nullable
56+
String parent();
57+
58+
/**
59+
* The name of th object.
60+
*
61+
* @return The name of the object.
62+
*/
63+
String name();
64+
65+
/**
66+
* The full name of th object. Full name will be separated by "." to represent a string identifier
67+
* of the object, like catalog, catalog.table, etc.
68+
*
69+
* @return The name of the object.
70+
*/
71+
String fullName();
72+
73+
/**
74+
* The type of the object.
75+
*
76+
* @return The type of the object.
77+
*/
78+
Type type();
79+
}

api/src/main/java/com/datastrato/gravitino/authorization/SecurableObject.java

+2-63
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
*/
55
package com.datastrato.gravitino.authorization;
66

7+
import com.datastrato.gravitino.MetadataObject;
78
import com.datastrato.gravitino.annotation.Unstable;
89
import java.util.List;
9-
import javax.annotation.Nullable;
1010

1111
/**
1212
* The securable object is the entity which access can be granted. Unless allowed by a grant, access
@@ -41,38 +41,7 @@
4141
* can use add `read table` privilege for `catalog1.schema1` directly
4242
*/
4343
@Unstable
44-
public interface SecurableObject {
45-
46-
/**
47-
* The parent full name of securable object. If the securable object doesn't have parent, this
48-
* method will return null.
49-
*
50-
* @return The parent full name of securable object.
51-
*/
52-
@Nullable
53-
String parent();
54-
55-
/**
56-
* The name of th securable object.
57-
*
58-
* @return The name of the securable object.
59-
*/
60-
String name();
61-
62-
/**
63-
* The full name of th securable object. If the parent isn't null, the full name will join the
64-
* parent full name and the name with `.`, otherwise will return the name.
65-
*
66-
* @return The name of the securable object.
67-
*/
68-
String fullName();
69-
70-
/**
71-
* The type of securable object
72-
*
73-
* @return The type of securable object.
74-
*/
75-
Type type();
44+
public interface SecurableObject extends MetadataObject {
7645

7746
/**
7847
* The privileges of the securable object. For example: If the securable object is a table, the
@@ -82,34 +51,4 @@ public interface SecurableObject {
8251
* @return The privileges of the role.
8352
*/
8453
List<Privilege> privileges();
85-
86-
/**
87-
* The type of securable object in the Gravitino system. Every type will map one kind of the
88-
* entity of the underlying system.
89-
*/
90-
enum Type {
91-
/**
92-
* A catalog is a collection of metadata from a specific metadata source, like Apache Hive
93-
* catalog, Apache Iceberg catalog, JDBC catalog, etc.
94-
*/
95-
CATALOG,
96-
/**
97-
* A schema is a sub collection of the catalog. The schema can contain filesets, tables, topics,
98-
* etc.
99-
*/
100-
SCHEMA,
101-
/** A fileset is mapped to a directory on a file system like HDFS, S3, ADLS, GCS, etc. */
102-
FILESET,
103-
/** A table is mapped the table of relational data sources like Apache Hive, MySQL, etc. */
104-
TABLE,
105-
/**
106-
* A topic is mapped the topic of messaging data sources like Apache Kafka, Apache Pulsar, etc.
107-
*/
108-
TOPIC,
109-
/**
110-
* A metalake is a concept of tenant. It means an organization. A metalake contains many data
111-
* sources.
112-
*/
113-
METALAKE
114-
}
11554
}

api/src/main/java/com/datastrato/gravitino/authorization/SecurableObjects.java

+27-14
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
*/
55
package com.datastrato.gravitino.authorization;
66

7+
import com.datastrato.gravitino.MetadataObject;
78
import com.google.common.base.Splitter;
89
import com.google.common.collect.ImmutableList;
910
import com.google.common.collect.Lists;
1011
import java.util.List;
1112
import java.util.Objects;
13+
import java.util.stream.Collectors;
1214
import org.apache.commons.lang3.StringUtils;
1315

1416
/** The helper class for {@link SecurableObject}. */
@@ -139,7 +141,11 @@ public String name() {
139141

140142
@Override
141143
public String fullName() {
142-
return toString();
144+
if (parent != null) {
145+
return parent + "." + name;
146+
} else {
147+
return name;
148+
}
143149
}
144150

145151
@Override
@@ -159,11 +165,18 @@ public int hashCode() {
159165

160166
@Override
161167
public String toString() {
162-
if (parent != null) {
163-
return parent + "." + name;
164-
} else {
165-
return name;
166-
}
168+
String privilegesStr =
169+
privileges.stream()
170+
.map(p -> "[" + p.simpleString() + "]")
171+
.collect(Collectors.joining(","));
172+
173+
return "SecurableObject: [fullName="
174+
+ fullName()
175+
+ "], [type="
176+
+ type
177+
+ "], [privileges="
178+
+ privilegesStr
179+
+ "]";
167180
}
168181

169182
@Override
@@ -189,9 +202,9 @@ public boolean equals(Object other) {
189202
* @return The created {@link SecurableObject}
190203
*/
191204
public static SecurableObject parse(
192-
String fullName, SecurableObject.Type type, List<Privilege> privileges) {
205+
String fullName, MetadataObject.Type type, List<Privilege> privileges) {
193206
if ("*".equals(fullName)) {
194-
if (type != SecurableObject.Type.METALAKE) {
207+
if (type != MetadataObject.Type.METALAKE) {
195208
throw new IllegalArgumentException("If securable object isn't metalake, it can't be `*`");
196209
}
197210
return SecurableObjects.ofAllMetalakes(privileges);
@@ -215,7 +228,7 @@ public static SecurableObject parse(
215228
* @return The created {@link SecurableObject}
216229
*/
217230
static SecurableObject of(
218-
SecurableObject.Type type, List<String> names, List<Privilege> privileges) {
231+
MetadataObject.Type type, List<String> names, List<Privilege> privileges) {
219232
if (names == null) {
220233
throw new IllegalArgumentException("Cannot create a securable object with null names");
221234
}
@@ -234,8 +247,8 @@ static SecurableObject of(
234247
}
235248

236249
if (names.size() == 1
237-
&& type != SecurableObject.Type.CATALOG
238-
&& type != SecurableObject.Type.METALAKE) {
250+
&& type != MetadataObject.Type.CATALOG
251+
&& type != MetadataObject.Type.METALAKE) {
239252
throw new IllegalArgumentException(
240253
"If the length of names is 1, it must be the CATALOG or METALAKE type");
241254
}
@@ -245,9 +258,9 @@ static SecurableObject of(
245258
}
246259

247260
if (names.size() == 3
248-
&& type != SecurableObject.Type.FILESET
249-
&& type != SecurableObject.Type.TABLE
250-
&& type != SecurableObject.Type.TOPIC) {
261+
&& type != MetadataObject.Type.FILESET
262+
&& type != MetadataObject.Type.TABLE
263+
&& type != MetadataObject.Type.TOPIC) {
251264
throw new IllegalArgumentException(
252265
"If the length of names is 3, it must be FILESET, TABLE or TOPIC");
253266
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2024 Datastrato Pvt Ltd.
3+
* This software is licensed under the Apache License version 2.
4+
*/
5+
6+
package com.datastrato.gravitino.exceptions;
7+
8+
import com.google.errorprone.annotations.FormatMethod;
9+
10+
/** Exception thrown when a tag with specified name is not existed. */
11+
public class NoSuchTagException extends NotFoundException {
12+
13+
/**
14+
* Constructs a new exception with the specified detail message.
15+
*
16+
* @param message the detail message.
17+
* @param args the arguments to the message.
18+
*/
19+
@FormatMethod
20+
public NoSuchTagException(String message, Object... args) {
21+
super(message, args);
22+
}
23+
24+
/**
25+
* Constructs a new exception with the specified detail message and cause.
26+
*
27+
* @param cause the cause.
28+
* @param message the detail message.
29+
* @param args the arguments to the message.
30+
*/
31+
@FormatMethod
32+
public NoSuchTagException(Throwable cause, String message, Object... args) {
33+
super(cause, message, args);
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2024 Datastrato Pvt Ltd.
3+
* This software is licensed under the Apache License version 2.
4+
*/
5+
6+
package com.datastrato.gravitino.exceptions;
7+
8+
import com.google.errorprone.annotations.FormatMethod;
9+
10+
/** Exception thrown when a tag with specified name already exists. */
11+
public class TagAlreadyExistsException extends AlreadyExistsException {
12+
13+
/**
14+
* Constructs a new exception with the specified detail message.
15+
*
16+
* @param message the detail message.
17+
* @param args the arguments to the message.
18+
*/
19+
@FormatMethod
20+
public TagAlreadyExistsException(String message, Object... args) {
21+
super(message, args);
22+
}
23+
24+
/**
25+
* Constructs a new exception with the specified detail message and cause.
26+
*
27+
* @param cause the cause.
28+
* @param message the detail message.
29+
* @param args the arguments to the message.
30+
*/
31+
@FormatMethod
32+
public TagAlreadyExistsException(Throwable cause, String message, Object... args) {
33+
super(cause, message, args);
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2024 Datastrato Pvt Ltd.
3+
* This software is licensed under the Apache License version 2.
4+
*/
5+
6+
package com.datastrato.gravitino.tag;
7+
8+
import com.datastrato.gravitino.annotation.Evolving;
9+
import com.datastrato.gravitino.exceptions.NoSuchTagException;
10+
11+
/**
12+
* Interface for supporting getting or associate tags to objects. This interface will be mixed with
13+
* metadata objects to provide tag operations.
14+
*/
15+
@Evolving
16+
public interface SupportsTags {
17+
18+
/**
19+
* List all the tag names for the specific object.
20+
*
21+
* @return The list of tag names.
22+
*/
23+
String[] listTags();
24+
25+
/**
26+
* List all the tags with details for the specific object.
27+
*
28+
* @return The list of tags.
29+
*/
30+
Tag[] listTagsInfo();
31+
32+
/**
33+
* Get a tag by its name for the specific object.
34+
*
35+
* @param name The name of the tag.
36+
* @return The tag.
37+
*/
38+
Tag getTag(String name) throws NoSuchTagException;
39+
40+
/**
41+
* Associate tags to the specific object. The tagsToAdd will be added to the object, and the
42+
* tagsToRemove will be removed from the object.
43+
*
44+
* @param tagsToAdd The arrays of tag name to be added to the object.
45+
* @param tagsToRemove The array of tag name to be removed from the object.
46+
* @return The array of tag names that are associated with the object.
47+
*/
48+
String[] associateTags(String[] tagsToAdd, String[] tagsToRemove);
49+
}

0 commit comments

Comments
 (0)