Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

REST endpoints #6

Draft
wants to merge 48 commits into
base: main
Choose a base branch
from
Draft

REST endpoints #6

wants to merge 48 commits into from

Conversation

sknot-rh
Copy link
Member

@sknot-rh sknot-rh commented Dec 3, 2020

Adding functionality to listTopics, describe particular topic, create topic, update, and delete topic.

Full docs can be found here

For now quay registry set to sknot.
It is possible to deploy app by posting docker/deployment.yaml file to k8s server.

gmcrobert and others added 7 commits December 3, 2020 15:49
that is extensible through the Java SPI.

Signed-off-by: Graeme McRobert <[email protected]>
Contains an initial implementation of the Strimzi Admin server.

The main application is in the `http-server` module. It creates
a vertx verticle which loads routes from modules on the classpath
using the Java service loader and then opens port 8080 to listen
for requests.

There are currently 2 modules containing routes. They are the
`health` module and the `graphql` module. The `health` module
contains a `liveness` and a `status` resource which return a
static `status:ok` json response. The `graphql` module implements
the graphql server. The graphql server creates a base server
with no queries or mutations. Currently, only sample queries are
implemented. The queries are defined in business log modules of
which the `kafka-admin` module is defined as an example. The
graphql server uses the Java service loader as well to register
graphql schema and implementation details to the graphql server.
If the `VERTXWEB_ENVIRONMENT` envar is set to `dev`, the graphql
server will also serve `GraphiQL` as per the `vertx-web-graphql`
documentation.

To server can be built using `mvn clean package` and can then
be run using the following:

java -cp kafka-admin/target/kafka-admin-1.0-SNAPSHOT-fat.jar:health/target/health-1.0-SNAPSHOT-fat.jar:grapql/target/graphql-s.0-SNAPSHOT-far.jar:http-server/target/http-server-1.0-SNAPSHOT-fat.jar io.strimzi.admin.Main

Tests are to follow.

Signed-off-by: Graeme McRobert <[email protected]>
* Added the maven-dependency-plugin `Analyze` rules and tidied the pom files.
* Added try/catch blocks for i/o operations in the executeBlocking blocks.
* Added Javadoc to main classes.
* Removed the duplicated sample data and made the data publicly visible so that
both the TopicHandler and TopicListHandler could access it.
* Uppercased the name of the sample data as it is a constant

Signed-off-by: Graeme McRobert <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
@sknot-rh sknot-rh requested a review from ppatierno December 3, 2020 15:00
@sknot-rh sknot-rh marked this pull request as draft December 3, 2020 15:00
#id: ID
name: String

"""@transient"""
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""@transient"""

extend type Mutation {
createTopic(input: CreateTopicInput!): TopicDescription
updateTopic(input: MutateTopicInput!): TopicDescription
deleteTopic(name: String!): String
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rule of thumb is to never ever return Scalar from the API. It will make our API breaking with each change - no ability to build non breaking API

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. Good to know! Thanks.

}

type Topic {
type TopicOnlyName {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No strong opinion but this might be redundant due to the capabilities of the client to make query with selection set containing only name. Making type for it makes things much more complex and also will put constraints on growing API.

This is not comment to fix it etc. I'm just mentioning it from the point of schema good practices as even if someone will request extra fields in topic query they will get null data.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. This is still a WIP and I was expecting (hoping for) a comment like this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally agree we don't need this.

}

type Topic {
type TopicOnlyName {
#id: ID
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In GraphQL ID's are sacred due to client side caching. If we get list of the topics and then operate on specific topics to get extra data client side will usually normalize those etc.

Frameworks like Apollo etc. tend to have extra configuration to handle objects without id's etc. If we apply suggestion from above this would not be issue

config: TopicConfig
}

type Partitions {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trivial/Opiniated
There are two class of types in GraphQL;

  • Embeeded types
  • Root (Query and Mutation return types)

I tend to organize schema to add extra comment that this type is not used in queries/mutations.

""" embedded """


extend type Query {
topicList(filter: String): [TopicOnlyName!]
topicDescription(name: String!): TopicDescription!
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trivial/Style

This seems to be leaking server side implementation details.
If I want to have some information on topic I should be able to get it with the fields that I have request it and if backend needs to do 6-7 calls to do so then so be it.
Making client doing multiple calls is more restful approach

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you would rather see topicDescription returning just [isInternal, name, partitions] and another query (for example) topicConfiguration to get all these configs? https://kafka.apache.org/documentation/#topicconfigs

Copy link

@wtrocki wtrocki Dec 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is coming from GrapHQL ideology that there is query that returns an entire object and it's relationships and the client can have the freedom to pick what he wants.

Splitting schema objects towards what client might want (list of just names or list of the topic or the configs etc.) seems to be more aligned with the way REST works.

Then client can pick query by selecting what they want:
https://i0.wp.com/css-tricks.com/wp-content/uploads/2020/01/Screen-Shot-2020-01-27-at-11.32.20.png?resize=3104%2C1978&ssl=1

Obviously, this is your project and there might be different reasons why we want to split topic into 3 different objects so do not feel that I'm really suggesting something that should be done.
More like trying to help with schema and avoiding some pitfalls early on.

@wtrocki
Copy link

wtrocki commented Dec 3, 2020

Amazing work @sknot-rh . Really good work on querying and mutations. My comments are opiniated take on the how GraphQL schema might work. My intention was to share some points based on the schemas I have written in the past. Do not feel that you need to apply all of suggestion etc.

@wtrocki
Copy link

wtrocki commented Dec 3, 2020

I'm curious - do we have any ability to generate id for the topics?

@sknot-rh
Copy link
Member Author

sknot-rh commented Dec 3, 2020

Currently, topic identity is determined from its name. There is a KIP for adding a topic ID (@tombentley knows more). I think as an ID we can use for example (namespaceName?+separator+)clusterName+separator+topicName. Do you have any suggestions?

@wtrocki
Copy link

wtrocki commented Dec 3, 2020

It will be good to actually have ID for the topics. If that id will be identified by name.
Sounds like to early optimalization and it will require circling thru arrays to build it but it might be worth to create issue for it in the future. Not having ID will not cause any major problems unless we want to use client side cache - which will make no sense for project like this.

- name: KAFKA_ADMIN_BOOTSTRAP_SERVERS
value: "my-cluster-kafka-bootstrap:9092"
- name: VERTXWEB_ENVIRONMENT
value: "dev"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should be not here for a production-ready environment, it should be removed to be the default one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree.

)
.build();
AdminClientProvider acp = new AdminClientProvider(vertx, config);
acp.open();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the Kafka cluster is not ready, it's running on the Vert.x main thread blocking it. We should avoid that doing asynchronously.

.build();
AdminClientProvider acp = new AdminClientProvider(vertx, config);
acp.open();
// todo close acp
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about this todo? are working on it? why should we close the acp here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure whether we need to close AdminClient at some point.

createOrMutateTopicConfigInput.setReplicationFactor(Long.parseLong(val));
newTopic.setReplicationFactor(Short.parseShort(val));
}
if (configObject.get("pairs") != null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this the topic configuration options, why not calling "config" instead of "pairs"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The query would look like this (nested config). I am not sure we like that.

query {
  topicDescription(name: "topicX") {
    name
    config {
      partitionCount
      replicationFactor
      config {
        key
        value
      }
    }
  }
}

}

type Topic {
type TopicOnlyName {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally agree we don't need this.

Signed-off-by: Stanislav Knot <[email protected]>
@tombentley
Copy link
Member

Currently, topic identity is determined from its name. There is a KIP for adding a topic ID (@tombentley knows more). I think as an ID we can use for example (namespaceName?+separator+)clusterName+separator+topicName. Do you have any suggestions?

https://cwiki.apache.org/confluence/display/KAFKA/KIP-516%3A+Topic+Identifiers it will help in avoiding the current ambiguity when a topic is deleted and a new topic with the same name is created. It's worth understanding the proposed changes for the AdminClient, and also the future work envisaged.

Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
docker/deployment.yaml Outdated Show resolved Hide resolved
Signed-off-by: Stanislav Knot <[email protected]>
@@ -0,0 +1,119 @@
/*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I am not sure what the value of this wrapper is. It seems to just duplicate functionality in the KafkaAdminClient which itself is a wrapper for the Kafka AdminClient. If you are having the wrapper, I would have thought it would have been more useful turning the async code into promise based futures so the methods return a Vertx Future to allow you to use the .onSuccess, .onFailure, .onComplete methods on the Future. Vertx 4 does this in the Vertx KafkaClient but it is not GA yet. From experience, I am also nervous throwing exceptions in a Vertx environment because Vertx has a tendency to swallow exceptions and it is not obvious where things end up. It is worse than a goto in my view.


import java.util.Map;

public class CommonHandler {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an observation, I would have dealt with the security differently. The routingContext is provided to keep a context for the request and be available from any handlers. We are also making the routingContext available in any DataFetcher. I would have created security handlers that deal with the different authentication mechanisms as we are going to support both SASL and OAuth2 and put them at the top of the handler list so that they get called first in the pipeline. The security handlers can create a "User" object on the routingContext allowing any other handler or DataFetcher to access it and use it to decorate the properties.

Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
Signed-off-by: Stanislav Knot <[email protected]>
@sknot-rh sknot-rh changed the title Graphql and REST REST endpoints Feb 1, 2021
Signed-off-by: Stanislav Knot <[email protected]>
@sknot-rh sknot-rh requested a review from ppatierno February 11, 2021 14:34
Signed-off-by: Stanislav Knot <[email protected]>
Base automatically changed from master to main March 25, 2021 11:14
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants