@DataObject(generateConverter = true)
public class ListConsumerGroupOffsetsOptions {

  private List<TopicPartition> topicPartitions = null;

  /**
   * Constructor
   */
  public ListConsumerGroupOffsetsOptions() {}

  /**
   * Constructor (from JSON representation)
   *
   * @param json  JSON representation
   */
  public ListConsumerGroupOffsetsOptions(JsonObject json) {
    ListConsumerGroupOffsetsOptionsConverter.fromJson(json, this);
  }

  /**
   * Set the topic partitions to list as part of the result.
   * {@code null} includes all topic partitions.
   *
   * @param topicPartitions List of topic partitions to include
   * @return This ListGroupOffsetsOptions
   */
  public ListConsumerGroupOffsetsOptions topicPartitions(List<TopicPartition> topicPartitions) {
    this.topicPartitions = topicPartitions;
    return this;
  }

  /**
   * Returns a list of topic partitions to add as part of the result.
   */
  public List<TopicPartition> topicPartitions() {
    return topicPartitions;
  }

  /**
   * Convert object to JSON representation
   *
   * @return  JSON representation
   */
  public JsonObject toJson() {
    JsonObject json = new JsonObject();
    ListConsumerGroupOffsetsOptionsConverter.toJson(this, json);
    return json;
  }

  @Override
  public String toString() {
    return "ListConsumerGroupOffsetsOptions{" +
      "topicPartitions=" + topicPartitions +
      '}';
  }

} @DataObject(generateConverter = true)
public class NewPartitions {

    private int totalCount;

    private List<List<Integer>> newAssignments;

    /**
     * Constructor
     */
    public NewPartitions() {

    }


    /**
     * Constructor
     *
     * @param totalCount total count of partitions
     * @param newAssignments assignment to the brokers
     */
    public NewPartitions(int totalCount, List<List<Integer>> newAssignments) {
        this.totalCount = totalCount;
        this.newAssignments = newAssignments;
    }

    /**
     * Constructor
     *
     * @param totalCount total count of partitions
     */
    public void NewPartitions(int totalCount) {
        this.totalCount = totalCount;
        this.newAssignments = null;
    }


    /**
     * Constructor (from JSON representation)
     *
     * @param json  JSON representation
     */
    public NewPartitions(JsonObject json) {

        NewPartitionsConverter.fromJson(json, this);
    }


    /**
     * Set the number of partitions for the topic
     * @param totalCount the number of partitions for the topic
     * @return current instance of the class to be fluent
     */
    public NewPartitions setTotalCount(int totalCount) {
        this.totalCount = totalCount;
        return this;
    }

    /**
     * Set the assignment for the new partitions
     * @param assignments assignments of the partitions to the brokers
     * @return current instance of the class to be fluent
     */
    public NewPartitions setNewAssignments(List<List<Integer>> assignments) {
        this.newAssignments = assignments;
        return this;
    }

    /**
     * @return number of total partitions
     */
    public int getTotalCount() {
        return this.totalCount;
    }

    /**
     * @return assignment of partitions to the brokers
     */
    public List<List<Integer>> getNewAssignments() {
        return this.newAssignments;
    }

    /**
     * Convert object to JSON representation
     *
     * @return  JSON representation
     */
    public JsonObject toJson() {

        JsonObject json = new JsonObject();
        NewPartitionsConverter.toJson(this, json);
        return json;
    }

    @Override
    public String toString() {
        return "NewPartitions{" +
                "totalCount=" + this.totalCount +
                ",newAssignments=" + this.newAssignments +
                "}";
    }

} @DataObject
public class TopicPartition {

  private String topic;
  private int partition;

  /**
   * Constructor
   */
  public TopicPartition() {
  }

  /**
   * Constructor
   *
   * @param topic the topic name
   * @param partition the partition number
   */
  public TopicPartition(String topic, int partition) {
    this.topic = topic;
    this.partition = partition;
  }

  /**
   * Constructor (from JSON representation)
   *
   * @param json  JSON representation
   */
  public TopicPartition(JsonObject json) {
    this.topic = json.getString("topic");
    this.partition = json.getInteger("partition");
  }

  /**
   * Constructor (copy)
   *
   * @param that  object to copy
   */
  public TopicPartition(TopicPartition that) {
    this.topic = that.topic;
    this.partition = that.partition;
  }

  /**
   * @return  the topic name
   */
  public String getTopic() {
    return this.topic;
  }

  /**
   * Set the topic name
   *
   * @param topic the topic name
   * @return  current instance of the class to be fluent
   */
  public TopicPartition setTopic(String topic) {
    this.topic = topic;
    return this;
  }

  /**
   * @return  the partition number
   */
  public int getPartition() {
    return this.partition;
  }

  /**
   * Set the partition number
   *
   * @param partition the partition number
   * @return  current instance of the class to be fluent
   */
  public TopicPartition setPartition(int partition) {
    this.partition = partition;
    return this;
  }

  /**
   * Convert object to JSON representation
   *
   * @return  JSON representation
   */
  public JsonObject toJson() {
    return new JsonObject().put("topic", this.topic).put("partition", this.partition);
  }

  @Override
  public String toString() {

    return "TopicPartition{" +
      "topic=" + this.topic +
      ", partition=" + this.partition +
      "}";
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    TopicPartition that = (TopicPartition) o;

    if (partition != that.partition) return false;
    return topic != null ? topic.equals(that.topic) : that.topic == null;
  }

  @Override
  public int hashCode() {
    int result = 1;
    result = 31 * result + partition;
    result = 31 * result + (topic != null ? topic.hashCode() : 0);
    return result;
  }
} @DataObject
public class OffsetAndMetadata {

  private long offset;
  private String metadata;

  /**
   * Constructor
   */
  public OffsetAndMetadata() {
  }

  /**
   * Constructor
   *
   * @param offset  offset to commit
   * @param metadata  additional metadata with the offset committed
   */
  public OffsetAndMetadata(long offset, String metadata) {
    this.offset = offset;
    this.metadata = metadata;
  }

  /**
   * Constructor (from JSON representation)
   *
   * @param json  JSON representation
   */
  public OffsetAndMetadata(JsonObject json) {
    this.offset = json.getLong("offset");
    this.metadata = json.getString("metadata");
  }

  /**
   * Constructor (copy)
   *
   * @param that  object to copy
   */
  public OffsetAndMetadata(OffsetAndMetadata that) {
    this.offset = that.offset;
    this.metadata = that.metadata;
  }

  /**
   * @return  offset to commit
   */
  public long getOffset() {
    return this.offset;
  }

  /**
   * Set the offset to commit
   *
   * @param offset  offset to commit
   * @return  current instance of the class to be fluent
   */
  public OffsetAndMetadata setOffset(long offset) {
    this.offset = offset;
    return this;
  }

  /**
   * @return  additional metadata with the offset committed
   */
  public String getMetadata() {
    return this.metadata;
  }

  /**
   * Set additional metadata for the offset committed
   *
   * @param metadata  additional metadata
   * @return  current instance of the class to be fluent
   */
  public OffsetAndMetadata setMetadata(String metadata) {
    this.metadata = metadata;
    return this;
  }

  /**
   * Convert object to JSON representation
   *
   * @return  JSON representation
   */
  public JsonObject toJson() {
    return new JsonObject().put("offset", this.offset).put("metadata", this.metadata);
  }

  @Override
  public String toString() {

    return "OffsetAndMetadata{" +
      "offset=" + this.offset +
      ", metadata=" + this.metadata +
      "}";
  }
} @DataObject(generateConverter = true)
public class ClusterDescription {
  private String clusterId;
  private Node controller;
  private List<Node> nodes;

  /**
   * Constructor
   */
  public ClusterDescription() {

  }

  /**
   * Constructor
   *
   * @param clusterId The cluster ID.
   * @param controller The controller node.
   * @param nodes A collection of nodes belonging to this cluster.
   */
  public ClusterDescription(String clusterId, Node controller, List<Node> nodes) {
    this.clusterId = clusterId;
    this.controller = controller;
    this.nodes = nodes;
  }

  /**
   * Constructor (from JSON representation)
   *
   * @param json  JSON representation
   */
  public ClusterDescription(JsonObject json) {

    ClusterDescriptionConverter.fromJson(json, this);
  }

  /**
   *
   * @return the nodes belonging to this cluster.
   */
  public List<Node> getNodes() {
    return nodes;
  }

  /**
   * Set the nodes belonging to this cluster
   *
   * @param nodes the nodes
   * @return current instance of the class to be fluent
   */
  public ClusterDescription setNodes(List<Node> nodes) {
    this.nodes = nodes;
    return this;
  }

  /**
   * Add a node belonging to this cluster to the current node list.
   *
   * @param node the node to add
   * @return current instance of the class to be fluent
   */
  public ClusterDescription addNode(Node node) {
    Objects.requireNonNull(node, "Cannot accept null node");
    if (nodes == null) {
      nodes = new ArrayList<>();
    }
    nodes.add(node);
    return this;
  }

  /**
   *
   * @return the controller node.
   */
  public Node getController() {
    return controller;
  }

  /**
   * Set the controller node.
   *
   * @param controller the controller node
   * @return current instance of the class to be fluent
   */
  public ClusterDescription setController(Node controller) {
    this.controller = controller;
    return this;
  }

  /**
   *
   * @return the cluster ID
   */
  public String getClusterId() {
    return clusterId;
  }

  /**
   * Set the cluster ID
   *
   * @param clusterId
   * @return current instance of the class to be fluent
   */
  public ClusterDescription setClusterId(String clusterId) {
    this.clusterId = clusterId;
    return this;
  }

  /**
   * Convert object to JSON representation
   *
   * @return  JSON representation
   */
  public JsonObject toJson() {

    JsonObject json = new JsonObject();
    ClusterDescriptionConverter.toJson(this, json);
    return json;
  }

  @Override
  public String toString() {

    return "ClusterDescription{" +
      "clusterId=" + this.clusterId +
      ",controller=" + this.controller +
      ",nodes=" + this.nodes +
      "}";
  }
} @DataObject(generateConverter = true)
public class Config {

  private List<ConfigEntry> entries;

  /**
   * Constructor
   */
  public Config() {

  }

  /**
   * Constructor
   *
   * @param entries configuration entries for a resource
   */
  public Config(List<ConfigEntry> entries) {
    this.entries = entries;
  }

  /**
   * Constructor (from JSON representation)
   *
   * @param json  JSON representation
   */
  public Config(JsonObject json) {

    ConfigConverter.fromJson(json, this);
  }

  /**
   * @return configuration entries for a resource
   */
  public List<ConfigEntry> getEntries() {
    return entries;
  }

  /**
   * Set the configuration entries for a resource
   *
   * @param entries configuration entries for a resource
   * @return current instance of the class to be fluent
   */
  public Config setEntries(List<ConfigEntry> entries) {
    this.entries = entries;
    return this;
  }

  /**
   * Convert object to JSON representation
   *
   * @return  JSON representation
   */
  public JsonObject toJson() {

    JsonObject json = new JsonObject();
    ConfigConverter.toJson(this, json);
    return json;
  }

  @Override
  public String toString() {

    return "Config{" +
      "entries=" + this.entries +
      "}";
  }
} @DataObject(generateConverter = true)
public class ConsumerGroupDescription  {

  private String groupId;
  private boolean isSimpleConsumerGroup;
  private Node coordinator;
  private List<MemberDescription> members;
  private String partitionAssignor;
  private ConsumerGroupState state;

  /**
   * Constructor
   */
  public ConsumerGroupDescription() {

  }

  /**
   * Constructor
   *
   * @param groupId the id of the consumer group
   * @param isSimpleConsumerGroup if consumer group is simple or not
   * @param members a list of the members of the consumer group
   * @param partitionAssignor the consumer group partition assignor
   * @param state the consumer group state, or UNKNOWN if the state is too new for us to parse
   * @param coordinator the consumer group coordinator, or null if the coordinator is not known
   */
  public ConsumerGroupDescription(String groupId, boolean isSimpleConsumerGroup, List<MemberDescription> members,
                                  String partitionAssignor, ConsumerGroupState state, Node coordinator) {
    this.groupId = groupId;
    this.isSimpleConsumerGroup = isSimpleConsumerGroup;
    this.members = members;
    this.partitionAssignor = partitionAssignor;
    this.state = state;
    this.coordinator = coordinator;
  }

  /**
   * Constructor (from JSON representation)
   *
   * @param json  JSON representation
   */
  public ConsumerGroupDescription(JsonObject json) {

    ConsumerGroupDescriptionConverter.fromJson(json, this);
  }

  /**
   * @return the id of the consumer group
   */
  public String getGroupId() {
    return groupId;
  }

  /**
   * Set the id of the consumer group
   *
   * @param groupId the id of the consumer group
   * @return current instance of the class to be fluent
   */
  public ConsumerGroupDescription setGroupId(String groupId) {
    this.groupId = groupId;
    return this;
  }

  /**
   * @return if consumer group is simple or not
   */
  public boolean isSimpleConsumerGroup() {
    return isSimpleConsumerGroup;
  }

  /**
   * Set if consumer group is simple or not
   *
   * @param isSimpleConsumerGroup if consumer group is simple or not
   * @return current instance of the class to be fluent
   */
  public ConsumerGroupDescription setSimpleConsumerGroup(boolean isSimpleConsumerGroup) {
    this.isSimpleConsumerGroup = isSimpleConsumerGroup;
    return this;
  }

  /**
   * @return the consumer group coordinator, or null if the coordinator is not known
   */
  public Node getCoordinator() {
    return coordinator;
  }

  /**
   * Set the consumer group coordinator, or null if the coordinator is not known
   *
   * @param coordinator the consumer group coordinator, or null if the coordinator is not known
   * @return current instance of the class to be fluent
   */
  public ConsumerGroupDescription setCoordinator(Node coordinator) {
    this.coordinator = coordinator;
    return this;
  }

  /**
   * @return a list of the members of the consumer group
   */
  public List<MemberDescription> getMembers() {
    return members;
  }

  /**
   * Set a list of the members of the consumer group
   *
   * @param members a list of the members of the consumer group
   * @return current instance of the class to be fluent
   */
  public ConsumerGroupDescription setMembers(List<MemberDescription> members) {
    this.members = members;
    return this;
  }

  /**
   * @return the consumer group partition assignor
   */
  public String getPartitionAssignor() {
    return partitionAssignor;
  }

  /**
   * Set the consumer group partition assignor
   *
   * @param partitionAssignor the consumer group partition assignor
   * @return current instance of the class to be fluent
   */
  public ConsumerGroupDescription setPartitionAssignor(String partitionAssignor) {
    this.partitionAssignor = partitionAssignor;
    return this;
  }

  /**
   * @return the consumer group state, or UNKNOWN if the state is too new for us to parse
   */
  public ConsumerGroupState getState() {
    return state;
  }

  /**
   * Set the consumer group state, or UNKNOWN if the state is too new for us to parse
   *
   * @param state the consumer group state, or UNKNOWN if the state is too new for us to parse
   * @return current instance of the class to be fluent
   */
  public ConsumerGroupDescription setState(ConsumerGroupState state) {
    this.state = state;
    return this;
  }

  /**
   * Convert object to JSON representation
   *
   * @return  JSON representation
   */
  public JsonObject toJson() {

    JsonObject json = new JsonObject();
    ConsumerGroupDescriptionConverter.toJson(this, json);
    return json;
  }

  @Override
  public String toString() {

    return "ConsumerGroupDescription{" +
      "groupId=" + this.groupId +
      ",isSimpleConsumerGroup=" + this.isSimpleConsumerGroup +
      ",coordinator=" + this.coordinator +
      ",members=" + this.members +
      ",partitionAssignor=" + this.partitionAssignor +
      ",state=" + this.state +
      "}";
  }
} @DataObject(generateConverter = true)
public class ConsumerGroupListing {

  private String groupId;
  private boolean isSimpleConsumerGroup;

  /**
   * Constructor
   */
  public ConsumerGroupListing() {

  }

  /**
   * Constructor
   *
   * @param groupId consumer group id
   * @param isSimpleConsumerGroup if consumer group is simple or not
   */
  public ConsumerGroupListing(String groupId, boolean isSimpleConsumerGroup) {
    this.groupId = groupId;
    this.isSimpleConsumerGroup = isSimpleConsumerGroup;
  }

  /**
   * Constructor (from JSON representation)
   *
   * @param json  JSON representation
   */
  public ConsumerGroupListing(JsonObject json) {

    ConsumerGroupListingConverter.fromJson(json, this);
  }

  /**
   * @return consumer group id
   */
  public String getGroupId() {
    return groupId;
  }

  /**
   * Set the consumer group id
   *
   * @param groupId consumer group id
   * @return current instance of the class to be fluent
   */
  public ConsumerGroupListing setGroupId(String groupId) {
    this.groupId = groupId;
    return this;
  }

  /**
   * @return if consumer group is simple or not
   */
  public boolean isSimpleConsumerGroup() {
    return isSimpleConsumerGroup;
  }

  /**
   * Set if consumer group is simple or not
   *
   * @param isSimpleConsumerGroup if consumer group is simple or not
   * @return current instance of the class to be fluent
   */
  public ConsumerGroupListing setSimpleConsumerGroup(boolean isSimpleConsumerGroup) {
    this.isSimpleConsumerGroup = isSimpleConsumerGroup;
    return this;
  }

  /**
   * Convert object to JSON representation
   *
   * @return  JSON representation
   */
  public JsonObject toJson() {

    JsonObject json = new JsonObject();
    ConsumerGroupListingConverter.toJson(this, json);
    return json;
  }

  @Override
  public String toString() {

    return "ConsumerGroupListing{" +
      "groupId=" + this.groupId +
      ",isSimpleConsumerGroup=" + this.isSimpleConsumerGroup +
      "}";
  }
} @DataObject(generateConverter = true)
public class ListOffsetsResultInfo {
  private long offset;
  private long timestamp;
  private Integer leaderEpoch;

  /**
   * Constructor
   *
   * @param offset the offset
   * @param timestamp the timestamp
   * @param leaderEpoch the leader epoch
   */
  public ListOffsetsResultInfo(long offset, long timestamp, Integer leaderEpoch) {
    this.offset = offset;
    this.timestamp = timestamp;
    this.leaderEpoch = leaderEpoch;
  }


  /**
   * Constructor (from JSON representation)
   *
   * @param json  JSON representation
   */
  public ListOffsetsResultInfo(JsonObject json) {
    ListOffsetsResultInfoConverter.fromJson(json, this);
  }

  /**
   * @return the offset
   */
  public long getOffset() {
    return offset;
  }

  /**
   * @return the timestamp
   */
  public long getTimestamp() {
    return timestamp;
  }

  /**
   * @return the leader epoch
   */
  public Integer getLeaderEpoch() {
    return leaderEpoch;
  }

  /**
   * Set the offset
   *
   * @param offset the offset
   * @return current instance of the class to be fluent
   */
  public ListOffsetsResultInfo setOffset(long offset) {
    this.offset = offset;
    return this;
  }
  /**
   * Set the timestamp
   *
   * @param timestamp the timestamp
   * @return current instance of the class to be fluent
   */
  public ListOffsetsResultInfo setTimestamp(long timestamp) {
    this.timestamp = timestamp;
    return this;
  }
  /**
   * Set the leader epoch
   *
   * @param leaderEpoch the leader epoch
   * @return current instance of the class to be fluent
   */
  public ListOffsetsResultInfo setLeaderEpoch(Integer leaderEpoch) {
    this.leaderEpoch = leaderEpoch;
    return this;
  }

  /**
   * Convert object to JSON representation
   *
   * @return  JSON representation
   */
  public JsonObject toJson() {

    JsonObject json = new JsonObject();
    ListOffsetsResultInfoConverter.toJson(this, json);
    return json;
  }

  @Override
  public String toString() {
    return "ListOffsetsResultInfo{" +
      "offset=" + this.offset +
      ",timestamp=" + this.timestamp +
      (this.leaderEpoch != null ? ",leaderEpoch=" + this.leaderEpoch : "") +
      "}";
  }
} @DataObject(generateConverter = true)
public class MemberDescription {

  private String consumerId;
  private String clientId;
  private MemberAssignment assignment;
  private String host;

  /**
   * Constructor
   */
  public MemberDescription() {

  }

  /**
   * Constructor
   *
   * @param consumerId the consumer id of the group member
   * @param clientId the client id of the group member
   * @param host the host where the group member is running
   * @param assignment the assignment of the group member
   */
  public MemberDescription(String consumerId, String clientId, String host, MemberAssignment assignment) {
    this.consumerId = consumerId;
    this.clientId = clientId;
    this.host = host;
    this.assignment = assignment;
  }

  /**
   * Constructor (from JSON representation)
   *
   * @param json  JSON representation
   */
  public MemberDescription(JsonObject json) {

    MemberDescriptionConverter.fromJson(json, this);
  }

  /**
   * @return the consumer id of the group member
   */
  public String getConsumerId() {
    return consumerId;
  }

  /**
   * Set the consumer id of the group member
   *
   * @param consumerId the consumer id of the group member
   * @return current instance of the class to be fluent
   */
  public MemberDescription setConsumerId(String consumerId) {
    this.consumerId = consumerId;
    return this;
  }

  /**
   * @return the client id of the group member
   */
  public String getClientId() {
    return clientId;
  }

  /**
   * Set the client id of the group member
   *
   * @param clientId the client id of the group member
   * @return current instance of the class to be fluent
   */
  public MemberDescription setClientId(String clientId) {
    this.clientId = clientId;
    return this;
  }

  /**
   * @return the assignment of the group member
   */
  public MemberAssignment getAssignment() {
    return assignment;
  }

  /**
   * Set the assignment of the group member
   *
   * @param assignment the assignment of the group member
   * @return current instance of the class to be fluent
   */
  public MemberDescription setAssignment(MemberAssignment assignment) {
    this.assignment = assignment;
    return this;
  }

  /**
   * @return the host where the group member is running
   */
  public String getHost() {
    return host;
  }

  /**
   * Set the host where the group member is running
   *
   * @param host the host where the group member is running
   * @return current instance of the class to be fluent
   */
  public MemberDescription setHost(String host) {
    this.host = host;
    return this;
  }

  /**
   * Convert object to JSON representation
   *
   * @return  JSON representation
   */
  public JsonObject toJson() {

    JsonObject json = new JsonObject();
    MemberDescriptionConverter.toJson(this, json);
    return json;
  }

  @Override
  public String toString() {

    return "MemberDescription{" +
      "consumerId=" + this.consumerId +
      ",clientId=" + this.clientId +
      ",assignment=" + this.assignment +
      ",host=" + this.host +
      "}";
  }
} @DataObject(generateConverter = true)
public class NewTopic {

  private String name;
  private int numPartitions;
  private short replicationFactor;
  private Map<Integer, List<Integer>> replicasAssignments;
  private Map<String,String> config;

  /**
   * Constructor
   */
  public NewTopic() {

  }

  /**
   * Constructor
   *
   * @param name the topic name
   * @param numPartitions number of partitions
   * @param replicationFactor replication factor
   */
  public NewTopic(String name, int numPartitions, short replicationFactor) {
    this.name = name;
    this.numPartitions = numPartitions;
    this.replicationFactor = replicationFactor;
  }

  /**
   * Constructor
   *
   * @param name the topic name
   * @param replicasAssignments a map from partition id to replica ids
   */
  public NewTopic(java.lang.String name,
                  java.util.Map<Integer, List<Integer>> replicasAssignments) {
    this.name = name;
    this.replicasAssignments = replicasAssignments;
  }

  /**
   * Constructor (from JSON representation)
   *
   * @param json  JSON representation
   */
  public NewTopic(JsonObject json) {

    NewTopicConverter.fromJson(json, this);
  }

  /**
   * @return the name of the topic to be created
   */
  public String getName() {
    return name;
  }

  /**
   * Set the name of the topic to be created
   *
   * @param name the name of the topic to be created
   * @return current instance of the class to be fluent
   */
  public NewTopic setName(String name) {
    this.name = name;
    return this;
  }

  /**
   * @return the number of partitions for the new topic or -1 if a replica assignment has been specified
   */
  public int getNumPartitions() {
    return numPartitions;
  }

  /**
   * Set the number of partitions for the new topic or -1 if a replica assignment has been specified
   *
   * @param numPartitions the number of partitions for the new topic or -1 if a replica assignment has been specified
   * @return current instance of the class to be fluent
   */
  public NewTopic setNumPartitions(int numPartitions) {
    this.numPartitions = numPartitions;
    return this;
  }

  /**
   * @return the replication factor for the new topic or -1 if a replica assignment has been specified
   */
  public short getReplicationFactor() {
    return replicationFactor;
  }

  /**
   * Set the replication factor for the new topic or -1 if a replica assignment has been specified
   *
   * @param replicationFactor the replication factor for the new topic or -1 if a replica assignment has been specified
   * @return current instance of the class to be fluent
   */
  public NewTopic setReplicationFactor(short replicationFactor) {
    this.replicationFactor = replicationFactor;
    return this;
  }

  /**
   * @return a map from partition id to replica ids
   */
  public Map<Integer, List<Integer>> getReplicasAssignments() {
    return replicasAssignments;
  }

  /**
   * Set a map from partition id to replica ids
   *
   * @param replicasAssignments a map from partition id to replica ids
   * @return current instance of the class to be fluent
   */
  public NewTopic setReplicasAssignments(Map<Integer, List<Integer>> replicasAssignments) {
    this.replicasAssignments = replicasAssignments;
    return this;
  }

  /**
   * @return the configuration for the new topic or null if no configs ever specified
   */
  public Map<String, String> getConfig() {
    return config;
  }

  /**
   * Set the configuration for the new topic or null if no configs ever specified
   *
   * @param config the configuration for the new topic or null if no configs ever specified
   * @return current instance of the class to be fluent
   */
  public NewTopic setConfig(Map<String, String> config) {
    this.config = config;
    return this;
  }

  /**
   * Convert object to JSON representation
   *
   * @return  JSON representation
   */
  public JsonObject toJson() {

    JsonObject json = new JsonObject();
    NewTopicConverter.toJson(this, json);
    return json;
  }

  @Override
  public String toString() {

    return "NewTopic{" +
      "name=" + this.name +
      ",numPartitions=" + this.numPartitions +
      ",replicationFactor=" + this.replicationFactor +
      ",replicasAssignments=" + this.replicasAssignments +
      ",config=" + this.config +
      "}";
  }
} @DataObject(generateConverter = true)
public class OffsetSpec {
  public final static OffsetSpec EARLIEST = new OffsetSpec(-2L);
  public final static OffsetSpec LATEST = new OffsetSpec(-1L);
  public final static OffsetSpec TIMESTAMP(long timestamp) {
    return new OffsetSpec(timestamp);
  }

  private long spec;

  /**
   * Constructor
   *
   * @param spec the offset spec Spec.EARLIEST, Spec.LATEST, or a Spec.TIMESTAMP(long) value
   */
  public OffsetSpec(long spec) {
    this.spec = spec;
  }

  /**
   * Constructor (from JSON representation)
   *
   * @param json  JSON representation
   */
  public OffsetSpec(JsonObject json) {
    OffsetSpecConverter.fromJson(json, this);
  }

  /**
   * @return offset spec
   */
  public long getSpec() {
    return spec;
  }

  /**
   * Set the offset spec
   *
   * @param spec the offset spec
   * @return current instance of the class to be fluent
   */
  public OffsetSpec setSpec(long spec) {
    this.spec = spec;
    return this;
  }

  /**
   * Convert object to JSON representation
   *
   * @return  JSON representation
   */
  public JsonObject toJson() {

    JsonObject json = new JsonObject();
    OffsetSpecConverter.toJson(this, json);
    return json;
  }

  @Override
  public String toString() {
    String value;
    if (EARLIEST == this) {
      value = "EARLIEST";
    } else if (LATEST == this) {
      value = "LATEST";
    } else {
      value = String.format("TIMESTAMP(%d)", this.spec);
    }

    return "OffsetSpec{" +
      "spec=" + value +
      "}";
  }
} @DataObject(generateConverter = true)
public class TopicDescription {

  private boolean isInternal;
  private String name;
  private List<TopicPartitionInfo> partitions;

  /**
   * Constructor
   */
  public TopicDescription() {

  }

  /**
   * Constructor
   *
   * @param isInternal Whether the topic is internal to Kafka.
   * @param name The name of the topic.
   * @param partitions A list of partitions where the index represents the partition id and the element
   *                   contains leadership and replica information for that partition.
   */
  public TopicDescription(boolean isInternal, String name, List<TopicPartitionInfo> partitions) {
    this.isInternal = isInternal;
    this.name = name;
    this.partitions = partitions;
  }

  /**
   * Constructor (from JSON representation)
   *
   * @param json  JSON representation
   */
  public TopicDescription(JsonObject json) {

    TopicDescriptionConverter.fromJson(json, this);
  }

  /**
   * @return Whether the topic is internal to Kafka.
   */
  public boolean isInternal() {
    return isInternal;
  }

  /**
   * Set whether the topic is internal to Kafka.
   *
   * @param internal Whether the topic is internal to Kafka.
   * @return current instance of the class to be fluent
   */
  public TopicDescription setInternal(boolean internal) {
    isInternal = internal;
    return this;
  }

  /**
   * @return The name of the topic.
   */
  public String getName() {
    return name;
  }

  /**
   * Set the name of the topic.
   *
   * @param name The name of the topic.
   * @return current instance of the class to be fluent
   */
  public TopicDescription setName(String name) {
    this.name = name;
    return this;
  }

  /**
   * @return A list of partitions where the index represents the partition id and the element
   * contains leadership and replica information for that partition.
   */
  public List<TopicPartitionInfo> getPartitions() {
    return partitions;
  }

  /**
   * Set A list of partitions where the index represents the partition id and the element
   * contains leadership and replica information for that partition.
   *
   * @param partitions A list of partitions where the index represents the partition id and the element
   *                   contains leadership and replica information for that partition.
   * @return current instance of the class to be fluent
   */
  public TopicDescription setPartitions(List<TopicPartitionInfo> partitions) {
    this.partitions = partitions;
    return this;
  }

  /**
   * Convert object to JSON representation
   *
   * @return  JSON representation
   */
  public JsonObject toJson() {

    JsonObject json = new JsonObject();
    TopicDescriptionConverter.toJson(this, json);
    return json;
  }

  @Override
  public String toString() {

    return "TopicDescription{" +
      "isInternal=" + this.isInternal +
      ",name=" + this.name +
      ",partitions=" + this.partitions +
      "}";
  }
} @DataObject(generateConverter = true)
public class ConfigResource {

  private String name;
  private boolean isDefault;
  private Type type;

  /**
   * Constructor
   */
  public ConfigResource() {
  }

  /**
   * Constructor
   *
   * @param type a non-null resource type
   * @param name a non-null resource name
   */
  public ConfigResource(Type type,
                        java.lang.String name) {
    this.type = type;
    this.name = name;
  }

  /**
   * Constructor (from JSON representation)
   *
   * @param json  JSON representation
   */
  public ConfigResource(JsonObject json) {

    ConfigResourceConverter.fromJson(json, this);
  }

  /**
   * @return the resource name
   */
  public String getName() {
    return name;
  }

  /**
   * Set the resource name
   *
   * @param name the resource name
   * @return current instance of the class to be fluent
   */
  public ConfigResource setName(String name) {
    this.name = name;
    return this;
  }

  /**
   * @return true if this is the default resource of a resource type. Resource name is empty for the default resource.
   */
  public boolean isDefault() {
    return isDefault;
  }

  /**
   * Set if this is the default resource of a resource type. Resource name is empty for the default resource.
   *
   * @param isDefault if this is the default resource of a resource type
   * @return current instance of the class to be fluent
   */
  public ConfigResource setDefault(boolean isDefault) {
    this.isDefault = isDefault;
    return this;
  }

  /**
   * @return the resource type
   */
  public Type getType() {
    return type;
  }

  /**
   * Set the resource type
   *
   * @param type the resource type
   * @return current instance of the class to be fluent
   */
  public ConfigResource setType(Type type) {
    this.type = type;
    return this;
  }

  /**
   * Convert object to JSON representation
   *
   * @return  JSON representation
   */
  public JsonObject toJson() {

    JsonObject json = new JsonObject();
    ConfigResourceConverter.toJson(this, json);
    return json;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    ConfigResource that = (ConfigResource) o;

    if (!name.equals(that.name)) return false;
    if (isDefault != that.isDefault) return false;
    return type == that.type;
  }

  @Override
  public int hashCode() {
    int result = 1;
    result = 31 * result + (name != null ? name.hashCode() : 0);
    result = 31 * result + (isDefault ? 1 : 0);
    result = 31 * result + type.ordinal();
    return result;
  }

  @Override
  public String toString() {

    return "ConfigResource{" +
      "name=" + this.name +
      ",type=" + this.type +
      ",isDefault=" + this.isDefault +
      "}";
  }
} @DataObject
public class Node {

  private boolean hasRack;
  private String host;
  private int id;
  private String idString;
  private boolean isEmpty;
  private int port;
  private String rack;

  /**
   * Constructor
   */
  public Node() {

  }

  /**
   * Constructor
   *
   * @param hasRack true if this node has a defined rack
   * @param host  the host name for this node
   * @param id  the node id of this node
   * @param idString  String representation of the node id
   * @param isEmpty if this node is empty, which may be the case if noNode() is used as a placeholder in a response payload with an error
   * @param port  the port for this node
   * @param rack  the rack for this node
   */
  public Node(boolean hasRack, String host, int id, String idString, boolean isEmpty, int port, String rack) {
    this.hasRack = hasRack;
    this.host = host;
    this.id = id;
    this.idString = idString;
    this.isEmpty = isEmpty;
    this.port = port;
    this.rack = rack;
  }

  /**
   * Constructor (from JSON representation)
   *
   * @param json  JSON representation
   */
  public Node(JsonObject json) {
    this.hasRack = json.getBoolean("hasRack");
    this.host = json.getString("host");
    this.id = json.getInteger("id");
    this.idString = json.getString("idString");
    this.isEmpty = json.getBoolean("isEmpty");
    this.port = json.getInteger("port");
    this.rack = json.getString("rack");
  }

  /**
   * @return  true if this node has a defined rack
   */
  public boolean hasRack() {
    return this.hasRack;
  }

  /**
   * Set if this node has a defined rack
   *
   * @param hasRack if this node has a defined rack
   * @return  current instance of the class to be fluent
   */
  public Node setHasRack(boolean hasRack) {
    this.hasRack = hasRack;
    return this;
  }

  /**
   * @return  the host name for this node
   */
  public String getHost() {
    return this.host;
  }

  /**
   * Set the host name for this node
   *
   * @param host  the host name for this node
   * @return  current instance of the class to be fluent
   */
  public Node setHost(String host) {
    this.host = host;
    return this;
  }

  /**
   * @return  the node id of this node
   */
  public int getId() {
    return this.id;
  }

  /**
   * Set the node id of this node
   *
   * @param id  the node id of this node
   * @return  current instance of the class to be fluent
   */
  public Node setId(int id) {
    this.id = id;
    return this;
  }

  /**
   * @return  String representation of the node id
   */
  public String getIdString() {
    return this.idString;
  }

  /**
   * Set the string representation of the node id
   *
   * @param idString  String representation of the node id
   * @return  current instance of the class to be fluent
   */
  public Node setIdString(String idString) {
    this.idString = idString;
    return this;
  }

  /**
   * @return  if this node is empty, which may be the case if noNode() is used as a placeholder in a response payload with an error
   */
  public boolean isEmpty() {
    return this.isEmpty;
  }

  /**
   * Set if this node is empty
   *
   * @param isEmpty if this node is empty
   * @return  current instance of the class to be fluent
   */
  public Node setIsEmpty(boolean isEmpty) {
    this.isEmpty = isEmpty;
    return this;
  }

  /**
   * @return  the port for this node
   */
  public int getPort() {
    return this.port;
  }

  /**
   * Set the port for this node
   *
   * @param port  the port for this node
   * @return  current instance of the class to be fluent
   */
  public Node setPort(int port) {
    this.port = port;
    return this;
  }

  /**
   * @return  the rack for this node
   */
  public String rack() {
    return this.rack;
  }

  /**
   * Set the rack for this node
   *
   * @param rack  the rack for this node
   * @return  current instance of the class to be fluent
   */
  public Node setRack(String rack) {
    this.rack = rack;
    return this;
  }

  /**
   * Convert object to JSON representation
   *
   * @return  JSON representation
   */
  public JsonObject toJson() {
    JsonObject jsonObject = new JsonObject();

    jsonObject
      .put("hasRack", this.hasRack)
      .put("host", this.host)
      .put("id", this.id)
      .put("idString", this.idString)
      .put("isEmpty", this.isEmpty)
      .put("port", this.port)
      .put("rack", this.rack);

    return jsonObject;
  }

  @Override
  public String toString() {

    return "Node{" +
            "id=" + this.id +
            ", host=" + this.host +
            ", hasRack=" + this.hasRack +
            ", idString=" + this.idString +
            ", isEmpty=" + this.isEmpty +
            ", port=" + this.port +
            ", rack=" + this.rack +
            "}";
  }
} @DataObject(generateConverter = true)
public class TopicPartitionInfo {

  private List<Node> isr;
  private Node leader;
  private int partition;
  private List<Node> replicas;

  /**
   * Constructor
   */
  public TopicPartitionInfo() {

  }

  /**
   * Constructor
   *
   * @param isr  the subset of the replicas that are in sync
   * @param leader  the node id of the node currently acting as a leader
   * @param partition the partition id
   * @param replicas  the complete set of replicas for this partition
   */
  public TopicPartitionInfo(List<Node> isr, Node leader, int partition, List<Node> replicas) {
    this.isr = isr;
    this.leader = leader;
    this.partition = partition;
    this.replicas = replicas;
  }

  /**
   * Constructor (from JSON representation)
   *
   * @param json  JSON representation
   */
  public TopicPartitionInfo(JsonObject json) {

    TopicPartitionInfoConverter.fromJson(json, this);
  }

  /**
   * @return  the subset of the replicas that are in sync, that is caught-up to the leader and ready to take over as leader if the leader should fail
   */
  public List<Node> getIsr() {
    return this.isr;
  }

  /**
   * Set the subset of the replicas that are in sync
   *
   * @param isr  the subset of the replicas that are in sync
   * @return  current instance of the class to be fluent
   */
  public TopicPartitionInfo setIsr(List<Node> isr) {
    this.isr = isr;
    return this;
  }

  /**
   * @return  the node id of the node currently acting as a leader for this partition or null if there is no leader
   */
  public Node getLeader() {
    return this.leader;
  }

  /**
   * Set the node id of the node currently acting as a leader
   *
   * @param leader  the node id of the node currently acting as a leader
   * @return  current instance of the class to be fluent
   */
  public TopicPartitionInfo setLeader(Node leader) {
    this.leader = leader;
    return this;
  }

  /**
   * @return  the partition id
   */
  public int getPartition() {
    return this.partition;
  }

  /**
   * Set the partition id
   *
   * @param partition the partition id
   * @return  current instance of the class to be fluent
   */
  public TopicPartitionInfo setPartition(int partition) {
    this.partition = partition;
    return this;
  }

  /**
   * @return  the complete set of replicas for this partition regardless of whether they are alive or up-to-date
   */
  public List<Node> getReplicas() {
    return this.replicas;
  }

  /**
   * Set the complete set of replicas for this partition
   *
   * @param replicas  the complete set of replicas for this partition
   * @return  current instance of the class to be fluent
   */
  public TopicPartitionInfo setReplicas(List<Node> replicas) {
    this.replicas = replicas;
    return this;
  }

  /**
   * Convert object to JSON representation
   *
   * @return  JSON representation
   */
  public JsonObject toJson() {

    JsonObject json = new JsonObject();
    TopicPartitionInfoConverter.toJson(this, json);
    return json;
  }

  @Override
  public String toString() {

    return "PartitionInfo{" +
      "partition=" + this.partition +
      ", isr=" + this.isr +
      ", leader=" + this.leader +
      ", replicas=" + this.replicas +
      "}";
  }
} public class Helper {

  private Helper() {
  }

  public static <T> Set<T> toSet(Collection<T> collection) {
    if (collection instanceof Set) {
      return (Set<T>) collection;
    } else {
      return new HashSet<>(collection);
    }
  }

  public static org.apache.kafka.common.TopicPartition to(TopicPartition topicPartition) {
    return new org.apache.kafka.common.TopicPartition(topicPartition.getTopic(), topicPartition.getPartition());
  }

  public static Set<org.apache.kafka.common.TopicPartition> to(Set<TopicPartition> topicPartitions) {
    return topicPartitions.stream().map(Helper::to).collect(Collectors.toSet());
  }

  public static Map<org.apache.kafka.common.TopicPartition, org.apache.kafka.clients.consumer.OffsetAndMetadata> to(Map<TopicPartition, OffsetAndMetadata> offsets) {
    return offsets.entrySet().stream().collect(Collectors.toMap(
      e -> new org.apache.kafka.common.TopicPartition(e.getKey().getTopic(), e.getKey().getPartition()),
      e -> new org.apache.kafka.clients.consumer.OffsetAndMetadata(e.getValue().getOffset(), e.getValue().getMetadata()))
    );
  }

  public static Map<String, org.apache.kafka.clients.admin.NewPartitions> toPartitions(Map<String, NewPartitions> newPartitions) {
    return newPartitions.entrySet().stream().collect(Collectors.toMap(
            e -> e.getKey(),
            e -> org.apache.kafka.clients.admin.NewPartitions.increaseTo(e.getValue().getTotalCount(), e.getValue().getNewAssignments()))
    );
  }

  public static Map<TopicPartition, OffsetAndMetadata> from(Map<org.apache.kafka.common.TopicPartition, org.apache.kafka.clients.consumer.OffsetAndMetadata> offsets) {
    return offsets.entrySet().stream().collect(Collectors.toMap(
      e -> new TopicPartition(e.getKey().topic(), e.getKey().partition()),
      e -> new OffsetAndMetadata(e.getValue().offset(), e.getValue().metadata()))
    );
  }

  public static TopicPartition from(org.apache.kafka.common.TopicPartition topicPartition) {
    return new TopicPartition(topicPartition.topic(), topicPartition.partition());
  }

  public static Set<TopicPartition> from(Collection<org.apache.kafka.common.TopicPartition> topicPartitions) {
    return topicPartitions.stream().map(Helper::from).collect(Collectors.toSet());
  }

  public static Handler<Set<org.apache.kafka.common.TopicPartition>> adaptHandler(Handler<Set<TopicPartition>> handler) {
    if (handler != null) {
      return topicPartitions -> handler.handle(Helper.from(topicPartitions));
    } else {
      return null;
    }
  }

  public static Node from(org.apache.kafka.common.Node node) {
    return new Node(node.hasRack(), node.host(), node.id(), node.idString(),
      node.isEmpty(), node.port(), node.rack());
  }

  public static RecordMetadata from(org.apache.kafka.clients.producer.RecordMetadata metadata) {
    return new RecordMetadata(metadata.checksum(), metadata.offset(),
      metadata.partition(), metadata.timestamp(), metadata.topic());
  }

  public static OffsetAndMetadata from(org.apache.kafka.clients.consumer.OffsetAndMetadata offsetAndMetadata) {
    if (offsetAndMetadata != null) {
      return new OffsetAndMetadata(offsetAndMetadata.offset(), offsetAndMetadata.metadata());
    } else {
      return null;
    }
  }

  public static org.apache.kafka.clients.consumer.OffsetAndMetadata to(OffsetAndMetadata offsetAndMetadata) {
    return new org.apache.kafka.clients.consumer.OffsetAndMetadata(offsetAndMetadata.getOffset(), offsetAndMetadata.getMetadata());
  }

  public static Map<TopicPartition, Long> fromTopicPartitionOffsets(Map<org.apache.kafka.common.TopicPartition, Long> offsets) {
    return offsets.entrySet().stream().collect(Collectors.toMap(
      e -> new TopicPartition(e.getKey().topic(), e.getKey().partition()),
      Map.Entry::getValue)
    );
  }

  public static Map<org.apache.kafka.common.TopicPartition, Long> toTopicPartitionTimes(Map<TopicPartition, Long> topicPartitionTimes) {
    return topicPartitionTimes.entrySet().stream().collect(Collectors.toMap(
      e -> new org.apache.kafka.common.TopicPartition(e.getKey().getTopic(), e.getKey().getPartition()),
      Map.Entry::getValue)
    );
  }

  public static Map<TopicPartition, OffsetAndTimestamp> fromTopicPartitionOffsetAndTimestamp(Map<org.apache.kafka.common.TopicPartition, org.apache.kafka.clients.consumer.OffsetAndTimestamp> topicPartitionOffsetAndTimestamps) {
    return topicPartitionOffsetAndTimestamps.entrySet().stream()
      .filter(e-> e.getValue() != null)
      .collect(Collectors.toMap(
        e -> new TopicPartition(e.getKey().topic(), e.getKey().partition()),
        e ->new OffsetAndTimestamp(e.getValue().offset(), e.getValue().timestamp()))
      );
  }

  public static org.apache.kafka.clients.admin.NewTopic to(NewTopic topic) {
    org.apache.kafka.clients.admin.NewTopic newTopic = null;
    if (topic.getNumPartitions() != -1 && topic.getReplicationFactor() != -1) {
      newTopic = new org.apache.kafka.clients.admin.NewTopic(topic.getName(), topic.getNumPartitions(), topic.getReplicationFactor());
    } else {
      newTopic = new org.apache.kafka.clients.admin.NewTopic(topic.getName(), topic.getReplicasAssignments());
    }
    if (topic.getConfig() != null && !topic.getConfig().isEmpty()) {
      newTopic.configs(topic.getConfig());
    }
    return newTopic;
  }

  public static org.apache.kafka.common.config.ConfigResource to(ConfigResource configResource) {
    return new org.apache.kafka.common.config.ConfigResource(configResource.getType(), configResource.getName());
  }

  public static ConfigResource from(org.apache.kafka.common.config.ConfigResource configResource) {
    return new ConfigResource(configResource.type(), configResource.name());
  }

  public static Config from(org.apache.kafka.clients.admin.Config config) {
    return new Config(Helper.fromConfigEntries(config.entries()));
  }

  public static List<org.apache.kafka.clients.admin.NewTopic> toNewTopicList(List<NewTopic> topics) {
    return topics.stream().map(Helper::to).collect(Collectors.toList());
  }

  public static List<org.apache.kafka.common.config.ConfigResource> toConfigResourceList(List<ConfigResource> configResources) {
    return configResources.stream().map(Helper::to).collect(Collectors.toList());
  }

  public static org.apache.kafka.clients.admin.ConfigEntry to(ConfigEntry configEntry) {
    return new org.apache.kafka.clients.admin.ConfigEntry(configEntry.getName(), configEntry.getValue());
  }

  public static Map<org.apache.kafka.common.config.ConfigResource, Collection<AlterConfigOp>> toConfigMaps(Map<ConfigResource, Config> configs) {

    return configs.entrySet().stream().collect(Collectors.toMap(
            e -> new org.apache.kafka.common.config.ConfigResource(e.getKey().getType(), e.getKey().getName()),
            e -> e.getValue().getEntries().stream().map(
                    v -> new AlterConfigOp(to(v), AlterConfigOp.OpType.SET)).collect(Collectors.toList())));

  }

  public static ConfigEntry from(org.apache.kafka.clients.admin.ConfigEntry configEntry) {
    return new ConfigEntry(configEntry.name(), configEntry.value());
  }

  public static List<ConfigEntry> fromConfigEntries(Collection<org.apache.kafka.clients.admin.ConfigEntry> configEntries) {
    return configEntries.stream().map(Helper::from).collect(Collectors.toList());
  }

  public static ConsumerGroupListing from(org.apache.kafka.clients.admin.ConsumerGroupListing consumerGroupListing) {
    return new ConsumerGroupListing(consumerGroupListing.groupId(), consumerGroupListing.isSimpleConsumerGroup());
  }

  public static List<ConsumerGroupListing> fromConsumerGroupListings(Collection<org.apache.kafka.clients.admin.ConsumerGroupListing> consumerGroupListings) {
    return consumerGroupListings.stream().map(Helper::from).collect(Collectors.toList());
  }

  public static MemberAssignment from(org.apache.kafka.clients.admin.MemberAssignment memberAssignment) {
    return new MemberAssignment(Helper.from(memberAssignment.topicPartitions()));
  }

  public static org.apache.kafka.clients.admin.ListConsumerGroupOffsetsOptions to(ListConsumerGroupOffsetsOptions listConsumerGroupOffsetsOptions) {

    org.apache.kafka.clients.admin.ListConsumerGroupOffsetsOptions newListConsumerGroupOffsetsOptions = new org.apache.kafka.clients.admin.ListConsumerGroupOffsetsOptions();

    if (listConsumerGroupOffsetsOptions.topicPartitions() != null) {
      List<org.apache.kafka.common.TopicPartition> topicPartitions = listConsumerGroupOffsetsOptions.topicPartitions().stream()
        .map(tp -> new org.apache.kafka.common.TopicPartition(tp.getTopic(), tp.getPartition()))
        .collect(Collectors.toList());

      newListConsumerGroupOffsetsOptions.topicPartitions(topicPartitions);
    }

    return newListConsumerGroupOffsetsOptions;
  }

  public static Set<org.apache.kafka.common.TopicPartition> toTopicPartitionSet(Set<TopicPartition> partitions) {
    return partitions.stream().map(Helper::to).collect(Collectors.toSet());
  }

  public static org.apache.kafka.clients.admin.OffsetSpec to(OffsetSpec os) {
    if (os.EARLIEST == os) {
      return org.apache.kafka.clients.admin.OffsetSpec.earliest();
    } else if (os.LATEST == os) {
      return org.apache.kafka.clients.admin.OffsetSpec.latest();
    } else {
      return org.apache.kafka.clients.admin.OffsetSpec.forTimestamp(os.getSpec());
    }
  }

  public static Map<org.apache.kafka.common.TopicPartition, org.apache.kafka.clients.admin.OffsetSpec> toTopicPartitionOffsets(Map<TopicPartition, OffsetSpec> topicPartitionOffsets) {
    return topicPartitionOffsets.entrySet().stream().collect(Collectors.toMap(
      e -> new org.apache.kafka.common.TopicPartition(e.getKey().getTopic(), e.getKey().getPartition()),
      e -> to(e.getValue())
    ));
  }

  public static ListOffsetsResultInfo from(org.apache.kafka.clients.admin.ListOffsetsResult.ListOffsetsResultInfo lori) {
    return new ListOffsetsResultInfo(lori.offset(), lori.timestamp(), lori.leaderEpoch().orElse(null));
  }
} @VertxGen
public interface KafkaAdminClient {

  /**
   * Create a new KafkaAdminClient instance
   *
   * @param vertx Vert.x instance to use
   * @param adminClient Kafka native Admin client instance
   * @return an instance of the KafkaAdminClient
   */
  @GenIgnore
  static KafkaAdminClient create(Vertx vertx, AdminClient adminClient) {
    return new KafkaAdminClientImpl(vertx, adminClient);
  }

  /**
   * Create a new KafkaAdminClient instance
   *
   * @param vertx Vert.x instance to use
   * @param config Kafka admin client configuration
   * @return an instance of the KafkaAdminClient
   */
  static KafkaAdminClient create(Vertx vertx, Map<String, String> config) {
    return create(vertx, AdminClient.create(new HashMap<>(config)));
  }

  /**
   * Create a new KafkaAdminClient instance
   *
   * @param vertx Vert.x instance to use
   * @param config Kafka admin client configuration
   * @return an instance of the KafkaAdminClient
   */
  @GenIgnore
  static KafkaAdminClient create(Vertx vertx, Properties config) {
    return create(vertx, AdminClient.create(config));
  }

  /**
   * List the topics available in the cluster with the default options.
   *
   * @param completionHandler handler called on operation completed with the topics set
   */
  void listTopics(Handler<AsyncResult<Set<String>>> completionHandler);

  /**
   * Like {@link #listTopics(Handler)} but returns a {@code Future} of the asynchronous result
   */
  Future<Set<String>> listTopics();

  /**
   * Describe some topics in the cluster, with the default options.
   *
   * @param topicNames the names of the topics to describe
   * @param completionHandler handler called on operation completed with the topics descriptions
   */
  void describeTopics(List<String> topicNames, Handler<AsyncResult<Map<String, TopicDescription>>> completionHandler);

  /**
   * Like {@link #describeTopics(List, Handler)} but returns a {@code Future} of the asynchronous result
   */
  Future<Map<String, TopicDescription>> describeTopics(List<String> topicNames);

  /**
   * Creates a batch of new Kafka topics
   *
   * @param topics topics to create
   * @param completionHandler handler called on operation completed
   */
  void createTopics(List<NewTopic> topics, Handler<AsyncResult<Void>> completionHandler);

  /**
   * Like {@link #createTopics(List, Handler)} but returns a {@code Future} of the asynchronous result
   */
  Future<Void> createTopics(List<NewTopic> topics);

  /**
   * Deletes a batch of Kafka topics
   *
   * @param topicNames the names of the topics to delete
   * @param completionHandler handler called on operation completed
   */
  void deleteTopics(List<String> topicNames, Handler<AsyncResult<Void>> completionHandler);

  /**
   * Like {@link #deleteTopics(List, Handler)} but returns a {@code Future} of the asynchronous result
   */
  Future<Void> deleteTopics(List<String> topicNames);

  /**
   * Creates a batch of new partitions in the Kafka topic
   *
   * @param partitions partitions to create
   * @param completionHandler handler called on operation completed
   */
  void createPartitions(Map<String, io.vertx.kafka.admin.NewPartitions> partitions, Handler<AsyncResult<Void>> completionHandler);

  /**
   * Like {@link #createPartitions(Map, Handler)} but returns a {@code Future} of the asynchronous result
   * @param partitions
   */
  Future<Void> createPartitions(Map<String, io.vertx.kafka.admin.NewPartitions> partitions);


  /**
   * Get the configuration for the specified resources with the default options
   *
   * @param configResources the resources (topic and broker resource types are currently supported)
   * @param completionHandler handler called on operation completed with the configurations
   */
  @GenIgnore
  void describeConfigs(List<ConfigResource> configResources, Handler<AsyncResult<Map<ConfigResource, Config>>> completionHandler);

  /**
   * Like {@link #describeConfigs(List, Handler)} but returns a {@code Future} of the asynchronous result
   */
  @GenIgnore
  Future<Map<ConfigResource, Config>> describeConfigs(List<ConfigResource> configResources);

  /**
   * Update the configuration for the specified resources with the default options
   *
   * @param configs The resources with their configs (topic is the only resource type with configs that can be updated currently)
   * @param completionHandler handler called on operation completed
   */
  @GenIgnore
  void alterConfigs(Map<ConfigResource,Config> configs, Handler<AsyncResult<Void>> completionHandler);

  /**
   * Like {@link #alterConfigs(Map, Handler)} but returns a {@code Future} of the asynchronous result
   */
  @GenIgnore
  Future<Void> alterConfigs(Map<ConfigResource,Config> configs);

  /**
   * Get the the consumer groups available in the cluster with the default options
   *
   * @param completionHandler handler called on operation completed with the consumer groups ids
   */
  void listConsumerGroups(Handler<AsyncResult<List<ConsumerGroupListing>>> completionHandler);

  /**
   * Like {@link #listConsumerGroups(Handler)} but returns a {@code Future} of the asynchronous result
   */
  Future<List<ConsumerGroupListing>> listConsumerGroups();

  /**
   * Describe some group ids in the cluster, with the default options
   *
   * @param groupIds the ids of the groups to describe
   * @param completionHandler handler called on operation completed with the consumer groups descriptions
   */
  void describeConsumerGroups(List<String> groupIds, Handler<AsyncResult<Map<String, ConsumerGroupDescription>>> completionHandler);

  /**
   * Like {@link #describeConsumerGroups(List, Handler)} but returns a {@code Future} of the asynchronous result
   */
  Future<Map<String, ConsumerGroupDescription>> describeConsumerGroups(List<String> groupIds);

  /**
   * Describe the nodes in the cluster with the default options
   *
   * @param completionHandler handler called on operation completed with the cluster description
   */
  void describeCluster(Handler<AsyncResult<ClusterDescription>> completionHandler);

  /**
   * Like {@link #describeCluster(Handler)} but returns a {@code Future} of the asynchronous result
   */
  Future<ClusterDescription> describeCluster();

  /**
   * Delete consumer groups from the cluster.
   *
   * @param groupIds the ids of the groups to delete
   * @param completionHandler handler called on operation completed
   */
  void deleteConsumerGroups(List<String> groupIds, Handler<AsyncResult<Void>> completionHandler);

  /**
   * Like {@link #deleteConsumerGroups(List, Handler)} but returns a {@code Future} of the asynchronous result
   */
  Future<Void> deleteConsumerGroups(List<String> groupIds);

  /**
   * List the consumer group offsets available in the cluster.
   *
   * @param groupId The group id of the group whose offsets will be listed
   * @param options The options to use when listing the consumer group offsets.
   * @param completionHandler handler called on operation completed with the consumer groups offsets
   */
  @GenIgnore
  void listConsumerGroupOffsets(String groupId, ListConsumerGroupOffsetsOptions options, Handler<AsyncResult<Map<TopicPartition, OffsetAndMetadata>>> completionHandler);

  /**
   * Like {@link #listConsumerGroupOffsets(String, ListConsumerGroupOffsetsOptions, Handler)} but returns a {@code Future} of the asynchronous result
   */
  @GenIgnore
  Future<Map<TopicPartition, OffsetAndMetadata>> listConsumerGroupOffsets(String groupId, ListConsumerGroupOffsetsOptions options);

  /**
   * List the consumer group offsets available in the cluster.
   *
   * @param groupId The group id of the group whose offsets will be listed
   * @param completionHandler handler called on operation completed with the consumer groups offsets
   */
  @GenIgnore
  default void listConsumerGroupOffsets(String groupId, Handler<AsyncResult<Map<TopicPartition, OffsetAndMetadata>>> completionHandler) {
    listConsumerGroupOffsets(groupId, new ListConsumerGroupOffsetsOptions(), completionHandler);
  }

  /**
   * Like {@link #listConsumerGroupOffsets(String, Handler)} but returns a {@code Future} of the asynchronous result
   */
  @GenIgnore
  default Future<Map<TopicPartition, OffsetAndMetadata>> listConsumerGroupOffsets(String groupId) {
    return listConsumerGroupOffsets(groupId, new ListConsumerGroupOffsetsOptions());
  }

  /**
   * Delete committed offsets for a set of partitions in a consumer group. This will
   * succeed at the partition level only if the group is not actively subscribed
   * to the corresponding topic.
   *
   * @param groupId The group id of the group whose offsets will be deleted
   * @param partitions The set of partitions in the consumer group whose offsets will be deleted
   */
  void deleteConsumerGroupOffsets(String groupId, Set<TopicPartition> partitions, Handler<AsyncResult<Void>> completionHandler);

  /**
   * Like {@link #deleteConsumerGroupOffsets(String, Set, Handler)} but returns a {@code Future} of the asynchronous result
   */
  Future<Void> deleteConsumerGroupOffsets(String groupId, Set<TopicPartition> partitions);

  /**
   * Alter committed offsets for a set of partitions in a consumer group.
   *
   * @param groupId The group id of the group whose offsets will be altered
   * @param offsets The map of offsets in the consumer group which will be altered
   */
  @GenIgnore
  void alterConsumerGroupOffsets(String groupId, Map<TopicPartition, OffsetAndMetadata> offsets, Handler<AsyncResult<Void>> completionHandler);

  /**
   * Like {@link #alterConsumerGroupOffsets(String, Map, Handler)} but returns a {@code Future} of the asynchronous result
   */
  @GenIgnore
  Future<Void> alterConsumerGroupOffsets(String groupId, Map<TopicPartition, OffsetAndMetadata> offsets);

  /**
   * List the offsets available for a set of partitions.
   *
   * @param topicPartitionOffsets The options to use when listing the partition offsets.
   * @param completionHandler handler called on operation completed with the partition offsets
   */
  @GenIgnore
  void listOffsets(Map<TopicPartition, OffsetSpec> topicPartitionOffsets, Handler<AsyncResult<Map<TopicPartition, ListOffsetsResultInfo>>> completionHandler);

  /**
   * Like {@link #listOffsets(Map<TopicPartition, OffsetSpec>, Handler)} but returns a {@code Future} of the asynchronous result
   */
  @GenIgnore
  Future<Map<TopicPartition, ListOffsetsResultInfo>> listOffsets(Map<TopicPartition, OffsetSpec> topicPartitionOffsets);

  /**
   * Close the admin client
   *
   * @param handler a {@code Handler} completed with the operation result
   */
  void close(Handler<AsyncResult<Void>> handler);

  /**
   * Like {@link #close(Handler)} but returns a {@code Future} of the asynchronous result
   */
  Future<Void> close();

  /**
   * Close the admin client
   *
   * @param timeout timeout to wait for closing
   * @param handler a {@code Handler} completed with the operation result
   */
  void close(long timeout, Handler<AsyncResult<Void>> handler);

  /**
   * Like {@link #close(long, Handler)} but returns a {@code Future} of the asynchronous result
   */
  Future<Void> close(long timeout);
}
import io.vertx.kafka.admin.ListConsumerGroupOffsetsOptions;
import io.vertx.kafka.admin.NewPartitions;
import io.vertx.kafka.client.common.TopicPartition;
import io.vertx.kafka.client.consumer.OffsetAndMetadata;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import io.vertx.core.Promise;
import io.vertx.core.impl.ContextInternal;
import io.vertx.kafka.admin.ClusterDescription;
import io.vertx.kafka.admin.Config;
import io.vertx.kafka.admin.ConsumerGroupDescription;
import io.vertx.kafka.admin.ConsumerGroupListing;
import io.vertx.kafka.admin.ListOffsetsResultInfo;
import io.vertx.kafka.admin.MemberDescription;
import io.vertx.kafka.admin.NewTopic;
import io.vertx.kafka.admin.OffsetSpec;
import io.vertx.kafka.admin.TopicDescription;
import io.vertx.kafka.client.common.ConfigResource;
import io.vertx.kafka.client.common.Node;
import io.vertx.kafka.client.common.TopicPartitionInfo;
import io.vertx.kafka.client.common.impl.Helper;
import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.AlterConfigsResult;
import org.apache.kafka.clients.admin.AlterConsumerGroupOffsetsResult;
import org.apache.kafka.clients.admin.CreatePartitionsResult;
import org.apache.kafka.clients.admin.CreateTopicsResult;
import org.apache.kafka.clients.admin.DeleteConsumerGroupOffsetsResult;
import org.apache.kafka.clients.admin.DeleteConsumerGroupsResult;
import org.apache.kafka.clients.admin.DeleteTopicsResult;
import org.apache.kafka.clients.admin.DescribeClusterResult;
import org.apache.kafka.clients.admin.DescribeConfigsResult;
import org.apache.kafka.clients.admin.DescribeConsumerGroupsResult;
import org.apache.kafka.clients.admin.DescribeTopicsResult;
import org.apache.kafka.clients.admin.ListConsumerGroupOffsetsResult;
import org.apache.kafka.clients.admin.ListConsumerGroupsResult;
import org.apache.kafka.clients.admin.ListOffsetsResult;
import org.apache.kafka.clients.admin.ListTopicsResult;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.kafka.admin.KafkaAdminClient;
import org.apache.kafka.common.KafkaFuture;
      }
    });
    return promise.future();
  }

  @Override
  public void createPartitions(Map<String, NewPartitions> partitions, Handler<AsyncResult<Void>> completionHandler) {
    createPartitions(partitions).onComplete(completionHandler);
  }

  @Override
  public Future<Void> createPartitions(Map<String, NewPartitions> partitions) {
    ContextInternal ctx = (ContextInternal) vertx.getOrCreateContext();
    Promise<Void> promise = ctx.promise();
    CreatePartitionsResult createPartitionsResult = this.adminClient.createPartitions(Helper.toPartitions(partitions));
    createPartitionsResult.all().whenComplete((v, ex) -> {

      if (ex == null) {
        promise.handle(Future.succeededFuture());
      } else {
        promise.handle(Future.failedFuture(ex));
      }
    });
    return promise.future();
  }


  @Override
  public void describeConfigs(List<ConfigResource> configResources, Handler<AsyncResult<Map<ConfigResource, Config>>> completionHandler) {
    describeConfigs(configResources).onComplete(completionHandler);
  }

  @Override
  public Future<Map<ConfigResource, Config>> describeConfigs(List<ConfigResource> configResources) {
    ContextInternal ctx = (ContextInternal) vertx.getOrCreateContext();
    Promise<Map<ConfigResource, Config>> promise = ctx.promise();

    DescribeConfigsResult describeConfigsResult = this.adminClient.describeConfigs(Helper.toConfigResourceList(configResources));
    describeConfigsResult.all().whenComplete((m, ex) -> {

      if (ex == null) {

        Map<ConfigResource, Config> configs = new HashMap<>();

        for (Map.Entry<org.apache.kafka.common.config.ConfigResource, org.apache.kafka.clients.admin.Config> configEntry : m.entrySet()) {

          ConfigResource configResource = Helper.from(configEntry.getKey());
          Config config = Helper.from(configEntry.getValue());

          configs.put(configResource, config);
        }

        promise.complete(configs);
      } else {
        promise.fail(ex);
      }
    });
    return promise.future();
  }

  @Override
  public void alterConfigs(Map<ConfigResource,Config> configs, Handler<AsyncResult<Void>> completionHandler) {
    alterConfigs(configs).onComplete(completionHandler);
  }

  @Override
  public Future<Void> alterConfigs(Map<ConfigResource, Config> configs) {
    ContextInternal ctx = (ContextInternal) vertx.getOrCreateContext();
    Promise<Void> promise = ctx.promise();

    AlterConfigsResult alterConfigsResult = this.adminClient.incrementalAlterConfigs(Helper.toConfigMaps(configs));
    alterConfigsResult.all().whenComplete((v, ex) -> {

      if (ex == null) {
        promise.complete();
      } else {
        promise.fail(ex);
      }
    });
    return promise.future();
  }

  @Override
  public void listConsumerGroups(Handler<AsyncResult<List<ConsumerGroupListing>>> completionHandler) {
    listConsumerGroups().onComplete(completionHandler);
  }

  @Override
  public Future<List<ConsumerGroupListing>> listConsumerGroups() {
    ContextInternal ctx = (ContextInternal) vertx.getOrCreateContext();
    Promise<List<ConsumerGroupListing>> promise = ctx.promise();

    ListConsumerGroupsResult listConsumerGroupsResult = this.adminClient.listConsumerGroups();
    listConsumerGroupsResult.all().whenComplete((groupIds, ex) -> {

      if (ex == null) {
        promise.complete(Helper.fromConsumerGroupListings(groupIds));
      } else {
        promise.fail(ex);
      }
    });
    return promise.future();
  }

  @Override
  public void describeConsumerGroups(List<java.lang.String> groupIds, Handler<AsyncResult<Map<String, ConsumerGroupDescription>>> completionHandler) {
    describeConsumerGroups(groupIds).onComplete(completionHandler);
  }

  @Override
  public Future<Map<String, ConsumerGroupDescription>> describeConsumerGroups(List<String> groupIds) {
    ContextInternal ctx = (ContextInternal) vertx.getOrCreateContext();
    Promise<Map<String, ConsumerGroupDescription>> promise = ctx.promise();

    DescribeConsumerGroupsResult describeConsumerGroupsResult = this.adminClient.describeConsumerGroups(groupIds);
    describeConsumerGroupsResult.all().whenComplete((cg, ex) -> {
      if (ex == null) {
        Map<String, ConsumerGroupDescription> consumerGroups = new HashMap<>();

        for (Map.Entry<String, org.apache.kafka.clients.admin.ConsumerGroupDescription> cgDescriptionEntry: cg.entrySet()) {
          List<MemberDescription> members = new ArrayList<>();