This is an automated email from the ASF dual-hosted git repository.
mchades pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new fb6f8a2fda [#11002] improvement(view): Add view CRUD event
dispatching, hooks, audit mapping, and tests (#11028)
fb6f8a2fda is described below
commit fb6f8a2fdace3eb07f39c8ef8c3f38fd48e6fde0
Author: Raushan Prabhakar <[email protected]>
AuthorDate: Tue May 26 07:26:37 2026 +0530
[#11002] improvement(view): Add view CRUD event dispatching, hooks, audit
mapping, and tests (#11028)
### What changes were proposed in this pull request?
- Add `ViewInfo` and view listener event types under
`org.apache.gravitino.listener.api.event.view` (pre / success / failure)
for **list**, **load**, **create**, **alter**, and **drop** views,
consistent with existing table-style events.
- Add `ViewEventDispatcher` to publish those events on the `EventBus`
around view operations.
- Wire `GravitinoEnv` so the view stack is **`ViewEventDispatcher` →
`ViewNormalizeDispatcher` → `ViewOperationDispatcher`**.
`ViewHookDispatcher` is **not** included; ownership / privilege hooks
for views are deferred (see TODO referencing #11007).
- Extend `AuditLog.Operation.fromEvent` so view events map to the
existing view audit operations (`CREATE_VIEW`, `ALTER_VIEW`, etc.)
instead of `UNKNOWN_OPERATION`.
- Add `TestViewEvent` and extend `TestOperation` for event emission and
audit mapping.
**Out of scope (removed or never merged here, per review):**
`AuthorizationUtils.getMetadataObjectLocation` handling for `VIEW`,
Ranger fileset IT changes, and view hook dispatching—those can land in
separate PRs if still desired.
### Why are the changes needed?
Relational view CRUD did not emit the same listener events as tables, so
`EventListenerPlugin` implementations could not observe view operations
and audit could not classify them. This closes the listener / audit gap
for the view operations listed above (aligned with #11002).
Fix: #11002
### Does this PR introduce _any_ user-facing change?
**Yes, for operators and integrations:** event listeners and audit
pipelines now see view list/load/create/alter/drop (and failures) with
concrete audit operation types. **No** REST or Java client API changes.
**`viewExists`** still has no dedicated events (same idea as
`tableExists` for tables).
### How was this patch tested?
- `./gradlew :core:spotlessApply :core:test --tests
org.apache.gravitino.listener.api.event.TestViewEvent --tests
org.apache.gravitino.audit.TestOperation -PskipITs`
---------
Co-authored-by: Raushan Prabhakar <[email protected]>
Co-authored-by: Copilot Autofix powered by AI
<[email protected]>
---
.../java/org/apache/gravitino/GravitinoEnv.java | 11 +-
.../java/org/apache/gravitino/audit/AuditLog.java | 20 ++
.../gravitino/listener/ViewEventDispatcher.java | 174 +++++++++++
.../listener/api/event/view/AlterViewEvent.java | 53 ++++
.../api/event/view/AlterViewFailureEvent.java | 46 +++
.../listener/api/event/view/AlterViewPreEvent.java | 45 +++
.../listener/api/event/view/CreateViewEvent.java | 45 +++
.../api/event/view/CreateViewFailureEvent.java | 46 +++
.../api/event/view/CreateViewPreEvent.java | 45 +++
.../listener/api/event/view/DropViewEvent.java | 44 +++
.../api/event/view/DropViewFailureEvent.java | 37 +++
.../listener/api/event/view/DropViewPreEvent.java | 37 +++
.../listener/api/event/view/ListViewEvent.java | 48 +++
.../api/event/view/ListViewFailureEvent.java | 45 +++
.../listener/api/event/view/ListViewPreEvent.java | 45 +++
.../listener/api/event/view/LoadViewEvent.java | 50 ++++
.../api/event/view/LoadViewFailureEvent.java | 37 +++
.../listener/api/event/view/LoadViewPreEvent.java | 37 +++
.../listener/api/event/view/ViewEvent.java | 39 +++
.../listener/api/event/view/ViewFailureEvent.java | 33 +++
.../listener/api/event/view/ViewPreEvent.java | 32 ++
.../gravitino/listener/api/info/ViewInfo.java | 134 +++++++++
.../org/apache/gravitino/audit/TestOperation.java | 77 +++++
.../listener/api/event/TestViewEvent.java | 326 +++++++++++++++++++++
24 files changed, 1503 insertions(+), 3 deletions(-)
diff --git a/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
b/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
index fd00c2a0e0..7514d2b930 100644
--- a/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
+++ b/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
@@ -88,6 +88,7 @@ import org.apache.gravitino.listener.StatisticEventDispatcher;
import org.apache.gravitino.listener.TableEventDispatcher;
import org.apache.gravitino.listener.TagEventDispatcher;
import org.apache.gravitino.listener.TopicEventDispatcher;
+import org.apache.gravitino.listener.ViewEventDispatcher;
import org.apache.gravitino.lock.LockManager;
import org.apache.gravitino.metalake.MetalakeDispatcher;
import org.apache.gravitino.metalake.MetalakeManager;
@@ -633,13 +634,17 @@ public class GravitinoEnv {
new FunctionEventDispatcher(eventBus, functionNormalizeDispatcher);
this.functionDispatcher = new
FunctionHookDispatcher(functionEventDispatcher);
- // TODO: Add ViewHookDispatcher and ViewEventDispatcher when needed for
view-specific hooks
- // and event handling.
+ // View operation chain: ViewEventDispatcher -> ViewNormalizeDispatcher ->
+ // ViewOperationDispatcher.
+ // TODO(#11007): Add ViewHookDispatcher for view ownership and privilege
hooks when view
+ // privilege support is finalized.
ViewOperationDispatcher viewOperationDispatcher =
new ViewOperationDispatcher(catalogManager, entityStore, idGenerator);
ViewNormalizeDispatcher viewNormalizeDispatcher =
new ViewNormalizeDispatcher(viewOperationDispatcher, catalogManager);
- this.viewDispatcher = viewNormalizeDispatcher;
+ ViewEventDispatcher viewEventDispatcher =
+ new ViewEventDispatcher(eventBus, viewNormalizeDispatcher);
+ this.viewDispatcher = viewEventDispatcher;
this.statisticDispatcher =
new StatisticEventDispatcher(
diff --git a/core/src/main/java/org/apache/gravitino/audit/AuditLog.java
b/core/src/main/java/org/apache/gravitino/audit/AuditLog.java
index 1d03be7d3e..bfd76e84cc 100644
--- a/core/src/main/java/org/apache/gravitino/audit/AuditLog.java
+++ b/core/src/main/java/org/apache/gravitino/audit/AuditLog.java
@@ -105,6 +105,16 @@ import
org.apache.gravitino.listener.api.event.PurgePartitionEvent;
import org.apache.gravitino.listener.api.event.PurgePartitionFailureEvent;
import org.apache.gravitino.listener.api.event.PurgeTableEvent;
import org.apache.gravitino.listener.api.event.PurgeTableFailureEvent;
+import org.apache.gravitino.listener.api.event.view.AlterViewEvent;
+import org.apache.gravitino.listener.api.event.view.AlterViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.CreateViewEvent;
+import org.apache.gravitino.listener.api.event.view.CreateViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.DropViewEvent;
+import org.apache.gravitino.listener.api.event.view.DropViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.ListViewEvent;
+import org.apache.gravitino.listener.api.event.view.ListViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.LoadViewEvent;
+import org.apache.gravitino.listener.api.event.view.LoadViewFailureEvent;
/** The interface define unified audit log schema. */
public interface AuditLog {
@@ -551,6 +561,16 @@ public interface AuditLog {
return LOAD_TOPIC;
} else if (event instanceof ListTopicEvent || event instanceof
ListTopicFailureEvent) {
return LIST_TOPIC;
+ } else if (event instanceof CreateViewEvent || event instanceof
CreateViewFailureEvent) {
+ return CREATE_VIEW;
+ } else if (event instanceof AlterViewEvent || event instanceof
AlterViewFailureEvent) {
+ return ALTER_VIEW;
+ } else if (event instanceof DropViewEvent || event instanceof
DropViewFailureEvent) {
+ return DROP_VIEW;
+ } else if (event instanceof LoadViewEvent || event instanceof
LoadViewFailureEvent) {
+ return LOAD_VIEW;
+ } else if (event instanceof ListViewEvent || event instanceof
ListViewFailureEvent) {
+ return LIST_VIEW;
} else if (event instanceof CreateFilesetEvent
|| event instanceof CreateFilesetFailureEvent) {
return CREATE_FILESET;
diff --git
a/core/src/main/java/org/apache/gravitino/listener/ViewEventDispatcher.java
b/core/src/main/java/org/apache/gravitino/listener/ViewEventDispatcher.java
new file mode 100644
index 0000000000..764e989c63
--- /dev/null
+++ b/core/src/main/java/org/apache/gravitino/listener/ViewEventDispatcher.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener;
+
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.catalog.ViewDispatcher;
+import org.apache.gravitino.catalog.ViewOperationDispatcher;
+import org.apache.gravitino.exceptions.NoSuchSchemaException;
+import org.apache.gravitino.exceptions.NoSuchViewException;
+import org.apache.gravitino.exceptions.ViewAlreadyExistsException;
+import org.apache.gravitino.listener.api.event.view.AlterViewEvent;
+import org.apache.gravitino.listener.api.event.view.AlterViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.AlterViewPreEvent;
+import org.apache.gravitino.listener.api.event.view.CreateViewEvent;
+import org.apache.gravitino.listener.api.event.view.CreateViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.CreateViewPreEvent;
+import org.apache.gravitino.listener.api.event.view.DropViewEvent;
+import org.apache.gravitino.listener.api.event.view.DropViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.DropViewPreEvent;
+import org.apache.gravitino.listener.api.event.view.ListViewEvent;
+import org.apache.gravitino.listener.api.event.view.ListViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.ListViewPreEvent;
+import org.apache.gravitino.listener.api.event.view.LoadViewEvent;
+import org.apache.gravitino.listener.api.event.view.LoadViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.LoadViewPreEvent;
+import org.apache.gravitino.listener.api.info.ViewInfo;
+import org.apache.gravitino.rel.Column;
+import org.apache.gravitino.rel.Representation;
+import org.apache.gravitino.rel.View;
+import org.apache.gravitino.rel.ViewChange;
+import org.apache.gravitino.utils.PrincipalUtils;
+
+/**
+ * Decorates a {@link ViewDispatcher} to dispatch pre/post/failure view events
to an {@link
+ * EventBus}, mirroring {@link TableEventDispatcher}.
+ */
+public class ViewEventDispatcher implements ViewDispatcher {
+
+ private final EventBus eventBus;
+ private final ViewDispatcher dispatcher;
+
+ /**
+ * @param eventBus event bus for listener plugins
+ * @param dispatcher underlying dispatcher (for example {@link
ViewOperationDispatcher} behind a
+ * normalize layer)
+ */
+ public ViewEventDispatcher(EventBus eventBus, ViewDispatcher dispatcher) {
+ this.eventBus = eventBus;
+ this.dispatcher = dispatcher;
+ }
+
+ @Override
+ public NameIdentifier[] listViews(Namespace namespace) throws
NoSuchSchemaException {
+ eventBus.dispatchEvent(new
ListViewPreEvent(PrincipalUtils.getCurrentUserName(), namespace));
+ try {
+ NameIdentifier[] identifiers = dispatcher.listViews(namespace);
+ eventBus.dispatchEvent(new
ListViewEvent(PrincipalUtils.getCurrentUserName(), namespace));
+ return identifiers;
+ } catch (Exception e) {
+ eventBus.dispatchEvent(
+ new ListViewFailureEvent(PrincipalUtils.getCurrentUserName(),
namespace, e));
+ throw e;
+ }
+ }
+
+ @Override
+ public View loadView(NameIdentifier ident) throws NoSuchViewException {
+ eventBus.dispatchEvent(new
LoadViewPreEvent(PrincipalUtils.getCurrentUserName(), ident));
+ try {
+ View view = dispatcher.loadView(ident);
+ eventBus.dispatchEvent(
+ new LoadViewEvent(PrincipalUtils.getCurrentUserName(), ident, new
ViewInfo(view)));
+ return view;
+ } catch (Exception e) {
+ eventBus.dispatchEvent(
+ new LoadViewFailureEvent(PrincipalUtils.getCurrentUserName(), ident,
e));
+ throw e;
+ }
+ }
+
+ @Override
+ public boolean viewExists(NameIdentifier ident) {
+ return dispatcher.viewExists(ident);
+ }
+
+ @Override
+ public View createView(
+ NameIdentifier ident,
+ @Nullable String comment,
+ Column[] columns,
+ Representation[] representations,
+ @Nullable String defaultCatalog,
+ @Nullable String defaultSchema,
+ Map<String, String> properties)
+ throws NoSuchSchemaException, ViewAlreadyExistsException {
+ ViewInfo createRequest =
+ new ViewInfo(
+ ident.name(),
+ columns,
+ comment,
+ representations,
+ defaultCatalog,
+ defaultSchema,
+ properties,
+ null);
+ eventBus.dispatchEvent(
+ new CreateViewPreEvent(PrincipalUtils.getCurrentUserName(), ident,
createRequest));
+ try {
+ View view =
+ dispatcher.createView(
+ ident, comment, columns, representations, defaultCatalog,
defaultSchema, properties);
+ eventBus.dispatchEvent(
+ new CreateViewEvent(PrincipalUtils.getCurrentUserName(), ident, new
ViewInfo(view)));
+ return view;
+ } catch (Exception e) {
+ eventBus.dispatchEvent(
+ new CreateViewFailureEvent(PrincipalUtils.getCurrentUserName(),
ident, e, createRequest));
+ throw e;
+ }
+ }
+
+ @Override
+ public View alterView(NameIdentifier ident, ViewChange... changes)
+ throws NoSuchViewException, IllegalArgumentException {
+ eventBus.dispatchEvent(
+ new AlterViewPreEvent(PrincipalUtils.getCurrentUserName(), ident,
changes));
+ try {
+ View view = dispatcher.alterView(ident, changes);
+ eventBus.dispatchEvent(
+ new AlterViewEvent(
+ PrincipalUtils.getCurrentUserName(), ident, changes, new
ViewInfo(view)));
+ return view;
+ } catch (Exception e) {
+ eventBus.dispatchEvent(
+ new AlterViewFailureEvent(PrincipalUtils.getCurrentUserName(),
ident, e, changes));
+ throw e;
+ }
+ }
+
+ @Override
+ public boolean dropView(NameIdentifier ident) {
+ eventBus.dispatchEvent(new
DropViewPreEvent(PrincipalUtils.getCurrentUserName(), ident));
+ try {
+ boolean existed = dispatcher.dropView(ident);
+ eventBus.dispatchEvent(
+ new DropViewEvent(PrincipalUtils.getCurrentUserName(), ident,
existed));
+ return existed;
+ } catch (Exception e) {
+ eventBus.dispatchEvent(
+ new DropViewFailureEvent(PrincipalUtils.getCurrentUserName(), ident,
e));
+ throw e;
+ }
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/AlterViewEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/AlterViewEvent.java
new file mode 100644
index 0000000000..d9c73ca331
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/AlterViewEvent.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.listener.api.info.ViewInfo;
+import org.apache.gravitino.rel.ViewChange;
+
+/** Successful alter-view event. */
+@DeveloperApi
+public final class AlterViewEvent extends ViewEvent {
+ private final ViewInfo updatedViewInfo;
+ private final ViewChange[] viewChanges;
+
+ public AlterViewEvent(
+ String user, NameIdentifier identifier, ViewChange[] viewChanges,
ViewInfo updatedViewInfo) {
+ super(user, identifier);
+ this.viewChanges = viewChanges.clone();
+ this.updatedViewInfo = updatedViewInfo;
+ }
+
+ public ViewInfo updatedViewInfo() {
+ return updatedViewInfo;
+ }
+
+ public ViewChange[] viewChanges() {
+ return viewChanges;
+ }
+
+ @Override
+ public OperationType operationType() {
+ return OperationType.ALTER_VIEW;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/AlterViewFailureEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/AlterViewFailureEvent.java
new file mode 100644
index 0000000000..0a8a6d9e74
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/AlterViewFailureEvent.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.rel.ViewChange;
+
+/** Failure event for altering a view. */
+@DeveloperApi
+public final class AlterViewFailureEvent extends ViewFailureEvent {
+ private final ViewChange[] viewChanges;
+
+ public AlterViewFailureEvent(
+ String user, NameIdentifier identifier, Exception exception,
ViewChange[] viewChanges) {
+ super(user, identifier, exception);
+ this.viewChanges = viewChanges.clone();
+ }
+
+ public ViewChange[] viewChanges() {
+ return viewChanges;
+ }
+
+ @Override
+ public OperationType operationType() {
+ return OperationType.ALTER_VIEW;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/AlterViewPreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/AlterViewPreEvent.java
new file mode 100644
index 0000000000..022ad33026
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/AlterViewPreEvent.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.rel.ViewChange;
+
+/** Pre-event before altering a view. */
+@DeveloperApi
+public class AlterViewPreEvent extends ViewPreEvent {
+ private final ViewChange[] viewChanges;
+
+ public AlterViewPreEvent(String user, NameIdentifier identifier,
ViewChange[] viewChanges) {
+ super(user, identifier);
+ this.viewChanges = viewChanges;
+ }
+
+ public ViewChange[] viewChanges() {
+ return viewChanges;
+ }
+
+ @Override
+ public OperationType operationType() {
+ return OperationType.ALTER_VIEW;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/CreateViewEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/CreateViewEvent.java
new file mode 100644
index 0000000000..df6f490db4
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/CreateViewEvent.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.listener.api.info.ViewInfo;
+
+/** Successful create-view event. */
+@DeveloperApi
+public final class CreateViewEvent extends ViewEvent {
+ private final ViewInfo createdViewInfo;
+
+ public CreateViewEvent(String user, NameIdentifier identifier, ViewInfo
createdViewInfo) {
+ super(user, identifier);
+ this.createdViewInfo = createdViewInfo;
+ }
+
+ public ViewInfo createdViewInfo() {
+ return createdViewInfo;
+ }
+
+ @Override
+ public OperationType operationType() {
+ return OperationType.CREATE_VIEW;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/CreateViewFailureEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/CreateViewFailureEvent.java
new file mode 100644
index 0000000000..e40f0fa325
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/CreateViewFailureEvent.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.listener.api.info.ViewInfo;
+
+/** Failure event for creating a view. */
+@DeveloperApi
+public final class CreateViewFailureEvent extends ViewFailureEvent {
+ private final ViewInfo createViewRequest;
+
+ public CreateViewFailureEvent(
+ String user, NameIdentifier identifier, Exception exception, ViewInfo
createViewRequest) {
+ super(user, identifier, exception);
+ this.createViewRequest = createViewRequest;
+ }
+
+ public ViewInfo createViewRequest() {
+ return createViewRequest;
+ }
+
+ @Override
+ public OperationType operationType() {
+ return OperationType.CREATE_VIEW;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/CreateViewPreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/CreateViewPreEvent.java
new file mode 100644
index 0000000000..16599a57a8
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/CreateViewPreEvent.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.listener.api.info.ViewInfo;
+
+/** Pre-event before creating a view. */
+@DeveloperApi
+public class CreateViewPreEvent extends ViewPreEvent {
+ private final ViewInfo createViewRequest;
+
+ public CreateViewPreEvent(String user, NameIdentifier identifier, ViewInfo
createViewRequest) {
+ super(user, identifier);
+ this.createViewRequest = createViewRequest;
+ }
+
+ public ViewInfo createViewRequest() {
+ return createViewRequest;
+ }
+
+ @Override
+ public OperationType operationType() {
+ return OperationType.CREATE_VIEW;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/DropViewEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/DropViewEvent.java
new file mode 100644
index 0000000000..0772f4323a
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/DropViewEvent.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+
+/** Successful drop-view event. */
+@DeveloperApi
+public final class DropViewEvent extends ViewEvent {
+ private final boolean isExists;
+
+ public DropViewEvent(String user, NameIdentifier identifier, boolean
isExists) {
+ super(user, identifier);
+ this.isExists = isExists;
+ }
+
+ public boolean isExists() {
+ return isExists;
+ }
+
+ @Override
+ public OperationType operationType() {
+ return OperationType.DROP_VIEW;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/DropViewFailureEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/DropViewFailureEvent.java
new file mode 100644
index 0000000000..07ec06f029
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/DropViewFailureEvent.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+
+/** Failure event for dropping a view. */
+@DeveloperApi
+public final class DropViewFailureEvent extends ViewFailureEvent {
+ public DropViewFailureEvent(String user, NameIdentifier identifier,
Exception exception) {
+ super(user, identifier, exception);
+ }
+
+ @Override
+ public OperationType operationType() {
+ return OperationType.DROP_VIEW;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/DropViewPreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/DropViewPreEvent.java
new file mode 100644
index 0000000000..3acb2fe433
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/DropViewPreEvent.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+
+/** Pre-event before dropping a view. */
+@DeveloperApi
+public class DropViewPreEvent extends ViewPreEvent {
+ public DropViewPreEvent(String user, NameIdentifier identifier) {
+ super(user, identifier);
+ }
+
+ @Override
+ public OperationType operationType() {
+ return OperationType.DROP_VIEW;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/ListViewEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/ListViewEvent.java
new file mode 100644
index 0000000000..a4afbb59eb
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/ListViewEvent.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+
+/**
+ * Successful list-views event. Like {@link
org.apache.gravitino.listener.api.event.ListTableEvent},
+ * listed identifiers are not stored on the event.
+ */
+@DeveloperApi
+public final class ListViewEvent extends ViewEvent {
+ private final Namespace namespace;
+
+ public ListViewEvent(String user, Namespace namespace) {
+ super(user, NameIdentifier.of(namespace.levels()));
+ this.namespace = namespace;
+ }
+
+ public Namespace namespace() {
+ return namespace;
+ }
+
+ @Override
+ public OperationType operationType() {
+ return OperationType.LIST_VIEW;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/ListViewFailureEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/ListViewFailureEvent.java
new file mode 100644
index 0000000000..bf4b482dd6
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/ListViewFailureEvent.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+
+/** Failure event for listing views. */
+@DeveloperApi
+public final class ListViewFailureEvent extends ViewFailureEvent {
+ private final Namespace namespace;
+
+ public ListViewFailureEvent(String user, Namespace namespace, Exception
exception) {
+ super(user, NameIdentifier.of(namespace.levels()), exception);
+ this.namespace = namespace;
+ }
+
+ public Namespace namespace() {
+ return namespace;
+ }
+
+ @Override
+ public OperationType operationType() {
+ return OperationType.LIST_VIEW;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/ListViewPreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/ListViewPreEvent.java
new file mode 100644
index 0000000000..3bd2acdf91
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/ListViewPreEvent.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+
+/** Pre-event before listing views in a namespace. */
+@DeveloperApi
+public class ListViewPreEvent extends ViewPreEvent {
+ private final Namespace namespace;
+
+ public ListViewPreEvent(String user, Namespace namespace) {
+ super(user, NameIdentifier.of(namespace.levels()));
+ this.namespace = namespace;
+ }
+
+ public Namespace namespace() {
+ return namespace;
+ }
+
+ @Override
+ public OperationType operationType() {
+ return OperationType.LIST_VIEW;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/LoadViewEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/LoadViewEvent.java
new file mode 100644
index 0000000000..a8120131c9
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/LoadViewEvent.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.listener.api.info.ViewInfo;
+
+/** Successful load-view event. */
+@DeveloperApi
+public final class LoadViewEvent extends ViewEvent {
+ private final ViewInfo loadedViewInfo;
+
+ public LoadViewEvent(String user, NameIdentifier identifier, ViewInfo
loadedViewInfo) {
+ super(user, identifier);
+ this.loadedViewInfo = loadedViewInfo;
+ }
+
+ /**
+ * Returns the loaded view information.
+ *
+ * @return the loaded view information
+ */
+ public ViewInfo loadedViewInfo() {
+ return loadedViewInfo;
+ }
+
+ @Override
+ public OperationType operationType() {
+ return OperationType.LOAD_VIEW;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/LoadViewFailureEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/LoadViewFailureEvent.java
new file mode 100644
index 0000000000..100a978dce
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/LoadViewFailureEvent.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+
+/** Failure event for loading a view. */
+@DeveloperApi
+public final class LoadViewFailureEvent extends ViewFailureEvent {
+ public LoadViewFailureEvent(String user, NameIdentifier identifier,
Exception exception) {
+ super(user, identifier, exception);
+ }
+
+ @Override
+ public OperationType operationType() {
+ return OperationType.LOAD_VIEW;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/LoadViewPreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/LoadViewPreEvent.java
new file mode 100644
index 0000000000..3f4ca91746
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/LoadViewPreEvent.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+
+/** Pre-event before loading a view. */
+@DeveloperApi
+public class LoadViewPreEvent extends ViewPreEvent {
+ public LoadViewPreEvent(String user, NameIdentifier identifier) {
+ super(user, identifier);
+ }
+
+ @Override
+ public OperationType operationType() {
+ return OperationType.LOAD_VIEW;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/ViewEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/ViewEvent.java
new file mode 100644
index 0000000000..8fcdc1e279
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/ViewEvent.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.Event;
+import org.apache.gravitino.listener.api.event.OperationStatus;
+
+/** Base class for successful view operation events. */
+@DeveloperApi
+public abstract class ViewEvent extends Event {
+
+ protected ViewEvent(String user, NameIdentifier identifier) {
+ super(user, identifier);
+ }
+
+ @Override
+ public OperationStatus operationStatus() {
+ return OperationStatus.SUCCESS;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/ViewFailureEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/ViewFailureEvent.java
new file mode 100644
index 0000000000..b14ce130e8
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/ViewFailureEvent.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.FailureEvent;
+
+/** Base class for view operation failure events. */
+@DeveloperApi
+public abstract class ViewFailureEvent extends FailureEvent {
+
+ protected ViewFailureEvent(String user, NameIdentifier identifier, Exception
exception) {
+ super(user, identifier, exception);
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/view/ViewPreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/ViewPreEvent.java
new file mode 100644
index 0000000000..0d0a64da45
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/view/ViewPreEvent.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event.view;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.PreEvent;
+
+/** Pre-event base class for view operations. */
+@DeveloperApi
+public abstract class ViewPreEvent extends PreEvent {
+ protected ViewPreEvent(String user, NameIdentifier identifier) {
+ super(user, identifier);
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/info/ViewInfo.java
b/core/src/main/java/org/apache/gravitino/listener/api/info/ViewInfo.java
new file mode 100644
index 0000000000..e9b31e843f
--- /dev/null
+++ b/core/src/main/java/org/apache/gravitino/listener/api/info/ViewInfo.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.info;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.apache.gravitino.Audit;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.rel.Column;
+import org.apache.gravitino.rel.Representation;
+import org.apache.gravitino.rel.View;
+
+/**
+ * ViewInfo exposes view information for event listeners; it is read-only.
Most fields are shallow
+ * copies internally, not deep copies, for performance.
+ */
+@DeveloperApi
+public final class ViewInfo {
+ private final String name;
+ private final Column[] columns;
+ @Nullable private final String comment;
+ private final Representation[] representations;
+ @Nullable private final String defaultCatalog;
+ @Nullable private final String defaultSchema;
+ private final Map<String, String> properties;
+ @Nullable private final Audit auditInfo;
+
+ /** Constructs a ViewInfo from a {@link View}. */
+ public ViewInfo(View view) {
+ this(
+ view.name(),
+ view.columns(),
+ view.comment(),
+ view.representations(),
+ view.defaultCatalog(),
+ view.defaultSchema(),
+ view.properties(),
+ view.auditInfo());
+ }
+
+ /**
+ * Constructs ViewInfo with the given fields.
+ *
+ * @param name View name
+ * @param columns Output columns
+ * @param comment Optional comment
+ * @param representations View representations (at least one in a valid
create request)
+ * @param defaultCatalog Optional default catalog for the definition
+ * @param defaultSchema Optional default schema for the definition
+ * @param properties View properties; copied defensively
+ * @param auditInfo Optional audit information
+ */
+ public ViewInfo(
+ String name,
+ Column[] columns,
+ @Nullable String comment,
+ Representation[] representations,
+ @Nullable String defaultCatalog,
+ @Nullable String defaultSchema,
+ Map<String, String> properties,
+ @Nullable Audit auditInfo) {
+ this.name = name;
+ this.columns = columns == null ? new Column[0] : columns.clone();
+ this.comment = comment;
+ this.representations =
+ representations == null ? new Representation[0] :
representations.clone();
+ this.defaultCatalog = defaultCatalog;
+ this.defaultSchema = defaultSchema;
+ this.properties = properties == null ? ImmutableMap.of() :
ImmutableMap.copyOf(properties);
+ this.auditInfo = auditInfo;
+ }
+
+ /** Returns the view name. */
+ public String name() {
+ return name;
+ }
+
+ /** Returns the output columns of the view. */
+ public Column[] columns() {
+ return columns;
+ }
+
+ /** Returns the optional comment for the view. */
+ @Nullable
+ public String comment() {
+ return comment;
+ }
+
+ /** Returns the view representations. */
+ public Representation[] representations() {
+ return representations;
+ }
+
+ /** Returns the optional default catalog used by the view definition. */
+ @Nullable
+ public String defaultCatalog() {
+ return defaultCatalog;
+ }
+
+ /** Returns the optional default schema used by the view definition. */
+ @Nullable
+ public String defaultSchema() {
+ return defaultSchema;
+ }
+
+ /** Returns the view properties. */
+ public Map<String, String> properties() {
+ return properties;
+ }
+
+ /** Returns the optional audit information for the view. */
+ @Nullable
+ public Audit auditInfo() {
+ return auditInfo;
+ }
+}
diff --git a/core/src/test/java/org/apache/gravitino/audit/TestOperation.java
b/core/src/test/java/org/apache/gravitino/audit/TestOperation.java
index 3a9bedb983..2a7032e45e 100644
--- a/core/src/test/java/org/apache/gravitino/audit/TestOperation.java
+++ b/core/src/test/java/org/apache/gravitino/audit/TestOperation.java
@@ -107,17 +107,31 @@ import
org.apache.gravitino.listener.api.event.PartitionExistsEvent;
import org.apache.gravitino.listener.api.event.PurgePartitionEvent;
import org.apache.gravitino.listener.api.event.PurgePartitionFailureEvent;
import org.apache.gravitino.listener.api.event.PurgeTableEvent;
+import org.apache.gravitino.listener.api.event.view.AlterViewEvent;
+import org.apache.gravitino.listener.api.event.view.AlterViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.CreateViewEvent;
+import org.apache.gravitino.listener.api.event.view.CreateViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.DropViewEvent;
+import org.apache.gravitino.listener.api.event.view.DropViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.ListViewEvent;
+import org.apache.gravitino.listener.api.event.view.ListViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.LoadViewEvent;
+import org.apache.gravitino.listener.api.event.view.LoadViewFailureEvent;
import org.apache.gravitino.listener.api.info.CatalogInfo;
import org.apache.gravitino.listener.api.info.FilesetInfo;
import org.apache.gravitino.listener.api.info.MetalakeInfo;
import org.apache.gravitino.listener.api.info.SchemaInfo;
import org.apache.gravitino.listener.api.info.TableInfo;
import org.apache.gravitino.listener.api.info.TopicInfo;
+import org.apache.gravitino.listener.api.info.ViewInfo;
import org.apache.gravitino.listener.api.info.partitions.IdentityPartitionInfo;
import org.apache.gravitino.listener.api.info.partitions.PartitionInfo;
import org.apache.gravitino.messaging.TopicChange;
import org.apache.gravitino.rel.Column;
+import org.apache.gravitino.rel.Representation;
+import org.apache.gravitino.rel.SQLRepresentation;
import org.apache.gravitino.rel.TableChange;
+import org.apache.gravitino.rel.ViewChange;
import org.apache.gravitino.rel.expressions.NamedReference;
import org.apache.gravitino.rel.expressions.distributions.Distributions;
import org.apache.gravitino.rel.expressions.distributions.Strategy;
@@ -162,6 +176,10 @@ public class TestOperation {
private TopicInfo topicInfo;
+ private NameIdentifier viewIdentifier;
+
+ private ViewInfo viewInfo;
+
private NameIdentifier partitionIdentifier;
private PartitionInfo partitionInfo;
@@ -187,6 +205,9 @@ public class TestOperation {
this.topicIdentifier = mockTopicIdentifier();
this.topicInfo = mockTopicInfo();
+ this.viewIdentifier = mockViewIdentifier();
+ this.viewInfo = mockViewInfo();
+
this.filesetIdentifier = mockFilesetIdentifier();
this.filesetInfo = mockFilesetInfo();
@@ -243,6 +264,14 @@ public class TestOperation {
new CreateTopicFailureEvent(USER, topicIdentifier, new Exception(),
topicInfo);
Assertions.assertEquals(
AuditLog.Operation.fromEvent(createTopicFailureEvent),
AuditLog.Operation.CREATE_TOPIC);
+
+ Event createViewEvent = new CreateViewEvent(USER, viewIdentifier,
viewInfo);
+ Assertions.assertEquals(
+ AuditLog.Operation.fromEvent(createViewEvent),
AuditLog.Operation.CREATE_VIEW);
+ Event createViewFailureEvent =
+ new CreateViewFailureEvent(USER, viewIdentifier, new Exception(),
viewInfo);
+ Assertions.assertEquals(
+ AuditLog.Operation.fromEvent(createViewFailureEvent),
AuditLog.Operation.CREATE_VIEW);
}
@Test
@@ -303,6 +332,14 @@ public class TestOperation {
new AlterTopicFailureEvent(USER, topicIdentifier, new Exception(), new
TopicChange[] {});
Assertions.assertEquals(
AuditLog.Operation.fromEvent(alterTopicFailureEvent),
AuditLog.Operation.ALTER_TOPIC);
+
+ Event alterViewEvent = new AlterViewEvent(USER, viewIdentifier, new
ViewChange[] {}, viewInfo);
+ Assertions.assertEquals(
+ AuditLog.Operation.fromEvent(alterViewEvent),
AuditLog.Operation.ALTER_VIEW);
+ Event alterViewFailureEvent =
+ new AlterViewFailureEvent(USER, viewIdentifier, new Exception(), new
ViewChange[] {});
+ Assertions.assertEquals(
+ AuditLog.Operation.fromEvent(alterViewFailureEvent),
AuditLog.Operation.ALTER_VIEW);
}
@Test
@@ -351,6 +388,13 @@ public class TestOperation {
Event dropTopicFailureEvent = new DropTopicFailureEvent(USER,
topicIdentifier, new Exception());
Assertions.assertEquals(
AuditLog.Operation.fromEvent(dropTopicFailureEvent),
AuditLog.Operation.DROP_TOPIC);
+
+ Event dropViewEvent = new DropViewEvent(USER, viewIdentifier, true);
+ Assertions.assertEquals(
+ AuditLog.Operation.fromEvent(dropViewEvent),
AuditLog.Operation.DROP_VIEW);
+ Event dropViewFailureEvent = new DropViewFailureEvent(USER,
viewIdentifier, new Exception());
+ Assertions.assertEquals(
+ AuditLog.Operation.fromEvent(dropViewFailureEvent),
AuditLog.Operation.DROP_VIEW);
}
@Test
@@ -426,6 +470,14 @@ public class TestOperation {
Assertions.assertEquals(
AuditLog.Operation.fromEvent(listTopicFailureEvent),
AuditLog.Operation.LIST_TOPIC);
+ Namespace viewNamespace = Namespace.of("metalake", "catalog", "schema");
+ Event listViewEvent = new ListViewEvent(USER, viewNamespace);
+ Assertions.assertEquals(
+ AuditLog.Operation.fromEvent(listViewEvent),
AuditLog.Operation.LIST_VIEW);
+ Event listViewFailureEvent = new ListViewFailureEvent(USER, viewNamespace,
new Exception());
+ Assertions.assertEquals(
+ AuditLog.Operation.fromEvent(listViewFailureEvent),
AuditLog.Operation.LIST_VIEW);
+
Event listFilesetEvent = new ListFilesetEvent(USER, namespace);
Assertions.assertEquals(
AuditLog.Operation.fromEvent(listFilesetEvent),
AuditLog.Operation.LIST_FILESET);
@@ -489,6 +541,13 @@ public class TestOperation {
Event loadTopicFailureEvent = new LoadTopicFailureEvent(USER,
topicIdentifier, new Exception());
Assertions.assertEquals(
AuditLog.Operation.fromEvent(loadTopicFailureEvent),
AuditLog.Operation.LOAD_TOPIC);
+
+ Event loadViewEvent = new LoadViewEvent(USER, viewIdentifier, viewInfo);
+ Assertions.assertEquals(
+ AuditLog.Operation.fromEvent(loadViewEvent),
AuditLog.Operation.LOAD_VIEW);
+ Event loadViewFailureEvent = new LoadViewFailureEvent(USER,
viewIdentifier, new Exception());
+ Assertions.assertEquals(
+ AuditLog.Operation.fromEvent(loadViewFailureEvent),
AuditLog.Operation.LOAD_VIEW);
}
@Test
@@ -602,6 +661,24 @@ public class TestOperation {
return new TopicInfo("topic", "comment", ImmutableMap.of("a", "b"), null);
}
+ private NameIdentifier mockViewIdentifier() {
+ return NameIdentifier.of("metalake", "catalog", "schema", "view");
+ }
+
+ private ViewInfo mockViewInfo() {
+ return new ViewInfo(
+ "view",
+ new Column[] {Column.of("a", Types.IntegerType.get())},
+ "comment",
+ new Representation[] {
+ SQLRepresentation.builder().withDialect("trino").withSql("SELECT
1").build()
+ },
+ "dc",
+ "ds",
+ ImmutableMap.of("a", "b"),
+ null);
+ }
+
private NameIdentifier mockPartitionIdentifier() {
return NameIdentifier.of("metalake", "catalog", "schema", "table",
PARTITION_NAME);
}
diff --git
a/core/src/test/java/org/apache/gravitino/listener/api/event/TestViewEvent.java
b/core/src/test/java/org/apache/gravitino/listener/api/event/TestViewEvent.java
new file mode 100644
index 0000000000..1d9529ce22
--- /dev/null
+++
b/core/src/test/java/org/apache/gravitino/listener/api/event/TestViewEvent.java
@@ -0,0 +1,326 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.listener.api.event;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Arrays;
+import java.util.Map;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.catalog.ViewDispatcher;
+import org.apache.gravitino.exceptions.GravitinoRuntimeException;
+import org.apache.gravitino.listener.DummyEventListener;
+import org.apache.gravitino.listener.EventBus;
+import org.apache.gravitino.listener.ViewEventDispatcher;
+import org.apache.gravitino.listener.api.event.view.AlterViewEvent;
+import org.apache.gravitino.listener.api.event.view.AlterViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.AlterViewPreEvent;
+import org.apache.gravitino.listener.api.event.view.CreateViewEvent;
+import org.apache.gravitino.listener.api.event.view.CreateViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.CreateViewPreEvent;
+import org.apache.gravitino.listener.api.event.view.DropViewEvent;
+import org.apache.gravitino.listener.api.event.view.DropViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.DropViewPreEvent;
+import org.apache.gravitino.listener.api.event.view.ListViewEvent;
+import org.apache.gravitino.listener.api.event.view.ListViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.ListViewPreEvent;
+import org.apache.gravitino.listener.api.event.view.LoadViewEvent;
+import org.apache.gravitino.listener.api.event.view.LoadViewFailureEvent;
+import org.apache.gravitino.listener.api.event.view.LoadViewPreEvent;
+import org.apache.gravitino.listener.api.info.ViewInfo;
+import org.apache.gravitino.rel.Column;
+import org.apache.gravitino.rel.Representation;
+import org.apache.gravitino.rel.SQLRepresentation;
+import org.apache.gravitino.rel.View;
+import org.apache.gravitino.rel.ViewChange;
+import org.apache.gravitino.rel.types.Types;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestInstance.Lifecycle;
+
+@TestInstance(Lifecycle.PER_CLASS)
+public class TestViewEvent {
+ private ViewEventDispatcher dispatcher;
+ private ViewEventDispatcher failureDispatcher;
+ private DummyEventListener dummyEventListener;
+ private View view;
+
+ @BeforeAll
+ void init() {
+ this.view = mockView();
+ this.dummyEventListener = new DummyEventListener();
+ EventBus eventBus = new EventBus(Arrays.asList(dummyEventListener));
+ ViewDispatcher viewDispatcher = mockViewDispatcher();
+ this.dispatcher = new ViewEventDispatcher(eventBus, viewDispatcher);
+ ViewDispatcher exceptionDispatcher = mockExceptionViewDispatcher();
+ this.failureDispatcher = new ViewEventDispatcher(eventBus,
exceptionDispatcher);
+ }
+
+ @Test
+ void testCreateViewEvent() {
+ NameIdentifier identifier = NameIdentifier.of("metalake", "catalog",
"schema", view.name());
+ dispatcher.createView(
+ identifier,
+ view.comment(),
+ view.columns(),
+ view.representations(),
+ view.defaultCatalog(),
+ view.defaultSchema(),
+ view.properties());
+
+ Event event = dummyEventListener.popPostEvent();
+ Assertions.assertEquals(identifier, event.identifier());
+ Assertions.assertEquals(CreateViewEvent.class, event.getClass());
+ checkViewInfo(((CreateViewEvent) event).createdViewInfo(), view);
+ Assertions.assertEquals(OperationType.CREATE_VIEW, event.operationType());
+ Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus());
+
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(identifier, preEvent.identifier());
+ Assertions.assertEquals(CreateViewPreEvent.class, preEvent.getClass());
+ checkViewInfo(((CreateViewPreEvent) preEvent).createViewRequest(), view);
+ Assertions.assertEquals(OperationType.CREATE_VIEW,
preEvent.operationType());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+ }
+
+ @Test
+ void testLoadViewEvent() {
+ NameIdentifier identifier = NameIdentifier.of("metalake", "catalog",
"schema", view.name());
+ dispatcher.loadView(identifier);
+
+ Event event = dummyEventListener.popPostEvent();
+ Assertions.assertEquals(identifier, event.identifier());
+ Assertions.assertEquals(LoadViewEvent.class, event.getClass());
+ checkViewInfo(((LoadViewEvent) event).loadedViewInfo(), view);
+ Assertions.assertEquals(OperationType.LOAD_VIEW, event.operationType());
+ Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus());
+
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(identifier, preEvent.identifier());
+ Assertions.assertEquals(LoadViewPreEvent.class, preEvent.getClass());
+ Assertions.assertEquals(OperationType.LOAD_VIEW, preEvent.operationType());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+ }
+
+ @Test
+ void testAlterViewEvent() {
+ NameIdentifier identifier = NameIdentifier.of("metalake", "catalog",
"schema", view.name());
+ ViewChange change = ViewChange.setProperty("a", "b");
+ dispatcher.alterView(identifier, change);
+
+ Event event = dummyEventListener.popPostEvent();
+ Assertions.assertEquals(identifier, event.identifier());
+ Assertions.assertEquals(AlterViewEvent.class, event.getClass());
+ checkViewInfo(((AlterViewEvent) event).updatedViewInfo(), view);
+ Assertions.assertEquals(1, ((AlterViewEvent) event).viewChanges().length);
+ Assertions.assertEquals(change, ((AlterViewEvent) event).viewChanges()[0]);
+ Assertions.assertEquals(OperationType.ALTER_VIEW, event.operationType());
+ Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus());
+
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(identifier, preEvent.identifier());
+ Assertions.assertEquals(AlterViewPreEvent.class, preEvent.getClass());
+ Assertions.assertEquals(1, ((AlterViewPreEvent)
preEvent).viewChanges().length);
+ Assertions.assertEquals(change, ((AlterViewPreEvent)
preEvent).viewChanges()[0]);
+ Assertions.assertEquals(OperationType.ALTER_VIEW,
preEvent.operationType());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+ }
+
+ @Test
+ void testDropViewEvent() {
+ NameIdentifier identifier = NameIdentifier.of("metalake", "catalog",
"schema", view.name());
+ dispatcher.dropView(identifier);
+
+ Event event = dummyEventListener.popPostEvent();
+ Assertions.assertEquals(identifier, event.identifier());
+ Assertions.assertEquals(DropViewEvent.class, event.getClass());
+ Assertions.assertTrue(((DropViewEvent) event).isExists());
+ Assertions.assertEquals(OperationType.DROP_VIEW, event.operationType());
+ Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus());
+
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(identifier, preEvent.identifier());
+ Assertions.assertEquals(DropViewPreEvent.class, preEvent.getClass());
+ Assertions.assertEquals(OperationType.DROP_VIEW, preEvent.operationType());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+ }
+
+ @Test
+ void testListViewEvent() {
+ Namespace namespace = Namespace.of("metalake", "catalog", "schema");
+ dispatcher.listViews(namespace);
+
+ Event event = dummyEventListener.popPostEvent();
+ Assertions.assertEquals(namespace.toString(),
event.identifier().toString());
+ Assertions.assertEquals(ListViewEvent.class, event.getClass());
+ Assertions.assertEquals(namespace, ((ListViewEvent) event).namespace());
+ Assertions.assertEquals(OperationType.LIST_VIEW, event.operationType());
+ Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus());
+
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(namespace.toString(),
preEvent.identifier().toString());
+ Assertions.assertEquals(ListViewPreEvent.class, preEvent.getClass());
+ Assertions.assertEquals(namespace, ((ListViewPreEvent)
preEvent).namespace());
+ Assertions.assertEquals(OperationType.LIST_VIEW, preEvent.operationType());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+ }
+
+ @Test
+ void testCreateViewFailureEvent() {
+ NameIdentifier identifier = NameIdentifier.of("metalake", "catalog",
"schema", view.name());
+ Assertions.assertThrowsExactly(
+ GravitinoRuntimeException.class,
+ () ->
+ failureDispatcher.createView(
+ identifier,
+ view.comment(),
+ view.columns(),
+ view.representations(),
+ view.defaultCatalog(),
+ view.defaultSchema(),
+ view.properties()));
+ Event event = dummyEventListener.popPostEvent();
+ Assertions.assertEquals(identifier, event.identifier());
+ Assertions.assertEquals(CreateViewFailureEvent.class, event.getClass());
+ Assertions.assertEquals(
+ GravitinoRuntimeException.class, ((CreateViewFailureEvent)
event).exception().getClass());
+ checkViewInfo(((CreateViewFailureEvent) event).createViewRequest(), view);
+ Assertions.assertEquals(OperationType.CREATE_VIEW, event.operationType());
+ Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+ }
+
+ @Test
+ void testLoadViewFailureEvent() {
+ NameIdentifier identifier = NameIdentifier.of("metalake", "catalog",
"schema", view.name());
+ Assertions.assertThrowsExactly(
+ GravitinoRuntimeException.class, () ->
failureDispatcher.loadView(identifier));
+ Event event = dummyEventListener.popPostEvent();
+ Assertions.assertEquals(identifier, event.identifier());
+ Assertions.assertEquals(LoadViewFailureEvent.class, event.getClass());
+ Assertions.assertEquals(
+ GravitinoRuntimeException.class, ((LoadViewFailureEvent)
event).exception().getClass());
+ Assertions.assertEquals(OperationType.LOAD_VIEW, event.operationType());
+ Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+ }
+
+ @Test
+ void testAlterViewFailureEvent() {
+ NameIdentifier identifier = NameIdentifier.of("metalake", "catalog",
"schema", view.name());
+ ViewChange change = ViewChange.setProperty("a", "b");
+ Assertions.assertThrowsExactly(
+ GravitinoRuntimeException.class, () ->
failureDispatcher.alterView(identifier, change));
+ Event event = dummyEventListener.popPostEvent();
+ Assertions.assertEquals(identifier, event.identifier());
+ Assertions.assertEquals(AlterViewFailureEvent.class, event.getClass());
+ Assertions.assertEquals(
+ GravitinoRuntimeException.class, ((AlterViewFailureEvent)
event).exception().getClass());
+ Assertions.assertEquals(1, ((AlterViewFailureEvent)
event).viewChanges().length);
+ Assertions.assertEquals(change, ((AlterViewFailureEvent)
event).viewChanges()[0]);
+ Assertions.assertEquals(OperationType.ALTER_VIEW, event.operationType());
+ Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+ }
+
+ @Test
+ void testDropViewFailureEvent() {
+ NameIdentifier identifier = NameIdentifier.of("metalake", "catalog",
"schema", view.name());
+ Assertions.assertThrowsExactly(
+ GravitinoRuntimeException.class, () ->
failureDispatcher.dropView(identifier));
+ Event event = dummyEventListener.popPostEvent();
+ Assertions.assertEquals(identifier, event.identifier());
+ Assertions.assertEquals(DropViewFailureEvent.class, event.getClass());
+ Assertions.assertEquals(
+ GravitinoRuntimeException.class, ((DropViewFailureEvent)
event).exception().getClass());
+ Assertions.assertEquals(OperationType.DROP_VIEW, event.operationType());
+ Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+ }
+
+ @Test
+ void testListViewFailureEvent() {
+ Namespace namespace = Namespace.of("metalake", "catalog", "schema");
+ Assertions.assertThrowsExactly(
+ GravitinoRuntimeException.class, () ->
failureDispatcher.listViews(namespace));
+ Event event = dummyEventListener.popPostEvent();
+ Assertions.assertEquals(namespace.toString(),
event.identifier().toString());
+ Assertions.assertEquals(ListViewFailureEvent.class, event.getClass());
+ Assertions.assertEquals(
+ GravitinoRuntimeException.class, ((ListViewFailureEvent)
event).exception().getClass());
+ Assertions.assertEquals(namespace, ((ListViewFailureEvent)
event).namespace());
+ Assertions.assertEquals(OperationType.LIST_VIEW, event.operationType());
+ Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+ }
+
+ private void checkViewInfo(ViewInfo viewInfo, View view) {
+ Assertions.assertEquals(view.name(), viewInfo.name());
+ Assertions.assertEquals(view.comment(), viewInfo.comment());
+ Assertions.assertEquals(view.defaultCatalog(), viewInfo.defaultCatalog());
+ Assertions.assertEquals(view.defaultSchema(), viewInfo.defaultSchema());
+ Assertions.assertEquals(view.properties(), viewInfo.properties());
+ Assertions.assertArrayEquals(view.columns(), viewInfo.columns());
+ Assertions.assertArrayEquals(view.representations(),
viewInfo.representations());
+ Assertions.assertEquals(view.auditInfo(), viewInfo.auditInfo());
+ }
+
+ private View mockView() {
+ View v = mock(View.class);
+ Representation rep =
+ SQLRepresentation.builder().withDialect("trino").withSql("SELECT
1").build();
+ when(v.name()).thenReturn("view");
+ when(v.comment()).thenReturn("comment");
+ when(v.properties()).thenReturn(ImmutableMap.of("a", "b"));
+ when(v.columns()).thenReturn(new Column[] {Column.of("a",
Types.IntegerType.get())});
+ when(v.representations()).thenReturn(new Representation[] {rep});
+ when(v.defaultCatalog()).thenReturn("dc");
+ when(v.defaultSchema()).thenReturn("ds");
+ when(v.auditInfo()).thenReturn(null);
+ return v;
+ }
+
+ private ViewDispatcher mockViewDispatcher() {
+ ViewDispatcher d = mock(ViewDispatcher.class);
+ when(d.createView(
+ any(NameIdentifier.class),
+ any(),
+ any(Column[].class),
+ any(Representation[].class),
+ any(),
+ any(),
+ any(Map.class)))
+ .thenReturn(view);
+ when(d.loadView(any(NameIdentifier.class))).thenReturn(view);
+ when(d.dropView(any(NameIdentifier.class))).thenReturn(true);
+ when(d.listViews(any(Namespace.class))).thenReturn(null);
+ when(d.alterView(any(NameIdentifier.class),
any(ViewChange.class))).thenReturn(view);
+ return d;
+ }
+
+ private ViewDispatcher mockExceptionViewDispatcher() {
+ return mock(
+ ViewDispatcher.class,
+ invocation -> {
+ throw new GravitinoRuntimeException("Exception for all methods");
+ });
+ }
+}