-
Notifications
You must be signed in to change notification settings - Fork 8
REST endpoints #6
base: main
Are you sure you want to change the base?
Conversation
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]>
Signed-off-by: Stanislav Knot <[email protected]>
#id: ID | ||
name: String | ||
|
||
"""@transient""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"""@transient""" |
extend type Mutation { | ||
createTopic(input: CreateTopicInput!): TopicDescription | ||
updateTopic(input: MutateTopicInput!): TopicDescription | ||
deleteTopic(name: String!): String |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 """
kafka-admin/src/main/resources/graphql-schema/kafka-admin.graphql
Outdated
Show resolved
Hide resolved
|
||
extend type Query { | ||
topicList(filter: String): [TopicOnlyName!] | ||
topicDescription(name: String!): TopicDescription! |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
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. |
Signed-off-by: Stanislav Knot <[email protected]>
I'm curious - do we have any ability to generate id for the topics? |
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 |
It will be good to actually have ID for the topics. If that id will be identified by name. |
docker/deployment.yaml
Outdated
- name: KAFKA_ADMIN_BOOTSTRAP_SERVERS | ||
value: "my-cluster-kafka-bootstrap:9092" | ||
- name: VERTXWEB_ENVIRONMENT | ||
value: "dev" |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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"?
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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]>
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]>
Signed-off-by: Stanislav Knot <[email protected]>
088fe5d
to
0c5c183
Compare
graphql/src/main/java/io/strimzi/admin/graphql/GraphQLService.java
Outdated
Show resolved
Hide resolved
@@ -0,0 +1,119 @@ | |||
/* |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
kafka-admin/src/main/java/io/strimzi/admin/kafka/admin/handlers/CommonHandler.java
Outdated
Show resolved
Hide resolved
kafka-admin/src/main/java/io/strimzi/admin/kafka/admin/handlers/CommonHandler.java
Outdated
Show resolved
Hide resolved
kafka-admin/src/main/java/io/strimzi/admin/kafka/admin/handlers/TopicCreateHandler.java
Outdated
Show resolved
Hide resolved
Signed-off-by: Stanislav Knot <[email protected]>
kafka-admin/src/main/java/io/strimzi/admin/kafka/admin/handlers/TopicListHandler.java
Outdated
Show resolved
Hide resolved
Signed-off-by: Stanislav Knot <[email protected]>
eadc177
to
6d7c5b2
Compare
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]>
fc53741
to
9065c63
Compare
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]>
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.