class NodeEvent {

    private final String graph;
    private final long nodeId;
    private final EventType eventType;

    public NodeEvent(String graph, long nodeId, EventType eventType) {
        this.graph = graph;
        this.nodeId = nodeId;
        this.eventType = eventType;
    }

    public String getGraph() {
        return graph;
    }

    public long getNodeId() {
        return nodeId;
    }

    public EventType getEventType() {
        return eventType;
    }

    @Override
    public boolean equals(Object o) {<FILL_FUNCTION_BODY>}

    @Override
    public int hashCode() {
        return Objects.hash(graph, nodeId, eventType);
    }

    @Override
    public String toString() {
        return "NodeEvent{" +
               "graph='" + graph + '\'' +
               ", nodeId=" + nodeId +
               ", eventType=" + eventType +
               '}';
    }

    public enum EventType {
        UNKNOWN,
        NODE_ONLINE,
        NODE_OFFLINE,
        NODE_RAFT_CHANGE,
        NODE_PD_LEADER_CHANGE;

        public static EventType grpcTypeOf(NodeEventType grpcType) {
            switch (grpcType) {
                case NODE_EVENT_TYPE_NODE_ONLINE:
                    return NODE_ONLINE;
                case NODE_EVENT_TYPE_NODE_OFFLINE:
                    return NODE_OFFLINE;
                case NODE_EVENT_TYPE_NODE_RAFT_CHANGE:
                    return NODE_RAFT_CHANGE;
                case NODE_EVENT_TYPE_PD_LEADER_CHANGE:
                    return NODE_PD_LEADER_CHANGE;
                default:
                    return UNKNOWN;
            }

        }

    }
}

        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        NodeEvent nodeEvent = (NodeEvent) o;
        return nodeId == nodeEvent.nodeId && Objects.equals(graph,
                                                            nodeEvent.graph) &&
               eventType == nodeEvent.eventType;
    
<no_super_class>