Skip to content

Commit d09fe32

Browse files
authored
Feature: Switch to Typespec contract description model (#1131)
1 parent d542c46 commit d09fe32

37 files changed

+5843
-15
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,4 @@ node_modules/
5151
.gradle
5252
@rerun.txt
5353
test-results
54+
frontend/test-report.xml

api/src/main/java/io/kafbat/ui/mapper/ClusterMapper.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,13 @@ default ConfigSynonymDTO toConfigSynonym(ConfigEntry.ConfigSynonym config) {
118118

119119
ReplicaDTO toReplica(InternalReplica replica);
120120

121+
@Mapping(target = "connectorsCount", ignore = true)
122+
@Mapping(target = "failedConnectorsCount", ignore = true)
123+
@Mapping(target = "tasksCount", ignore = true)
124+
@Mapping(target = "failedTasksCount", ignore = true)
125+
@Mapping(target = "version", ignore = true)
126+
@Mapping(target = "commit", ignore = true)
127+
@Mapping(target = "clusterId", ignore = true)
121128
ConnectDTO toKafkaConnect(ClustersProperties.ConnectCluster connect);
122129

123130
List<ClusterDTO.FeaturesEnum> toFeaturesEnum(List<ClusterFeature> features);

api/src/main/java/io/kafbat/ui/mapper/DynamicConfigMapper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import io.kafbat.ui.model.ApplicationConfigPropertiesAuthOauth2ResourceServerOpaquetokenDTO;
88
import io.kafbat.ui.model.ApplicationConfigPropertiesDTO;
99
import io.kafbat.ui.model.ApplicationConfigPropertiesKafkaClustersInnerDTO;
10-
import io.kafbat.ui.model.ApplicationConfigPropertiesRbacRolesInnerPermissionsInnerDTO;
10+
import io.kafbat.ui.model.RbacPermissionDTO;
1111
import io.kafbat.ui.model.rbac.Permission;
1212
import io.kafbat.ui.util.DynamicConfigOperations;
1313
import java.util.Optional;
@@ -32,7 +32,7 @@ default String map(Resource resource) {
3232
@Mapping(source = "metrics.store", target = "metrics.store", ignore = true)
3333
ApplicationConfigPropertiesKafkaClustersInnerDTO map(ClustersProperties.Cluster cluster);
3434

35-
default Permission map(ApplicationConfigPropertiesRbacRolesInnerPermissionsInnerDTO perm) {
35+
default Permission map(RbacPermissionDTO perm) {
3636
Permission permission = new Permission();
3737
permission.setResource(perm.getResource().getValue());
3838
permission.setActions(perm.getActions().stream().map(ActionDTO::getValue).toList());

api/src/test/java/io/kafbat/ui/service/mcp/McpSpecificationGeneratorTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ void testConvertController() {
8787
"clusterName", Map.of("type", "string"),
8888
"topicName", Map.of("type", "string"),
8989
"topicUpdate", SCHEMA_GENERATOR.generateSchema(TopicUpdateDTO.class)
90-
), List.of("clusterName", "topicName"), false, null, null)
90+
), List.of("clusterName", "topicName", "topicUpdate"), false, null, null)
9191
)
9292
);
9393
assertThat(tools).allMatch(tool ->

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ ext {
3535
release = resolveBooleanProperty("release")
3636
includeFrontend = resolveBooleanProperty("include-frontend", release)
3737
buildDockerImages = resolveBooleanProperty("build-docker-images", release)
38+
useTypeSpec = resolveBooleanProperty("typespec", true)
3839
runE2e = resolveBooleanProperty("run-e2e")
3940
}
4041

contract-typespec/api/acls.tsp

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import "@typespec/openapi";
2+
3+
namespace Api;
4+
5+
using TypeSpec.Http;
6+
using OpenAPI;
7+
8+
@route("/api/clusters/{clusterName}/acls")
9+
@tag("Acls")
10+
interface AclApi {
11+
@summary("listKafkaAcls")
12+
@get
13+
@operationId("listAcls")
14+
listAcls(
15+
@path clusterName: string,
16+
@query resourceType?: KafkaAclResourceType,
17+
@query resourceName?: string,
18+
@query namePatternType?: KafkaAclNamePatternType,
19+
@query search?: string
20+
): KafkaAcl[];
21+
22+
@route("/csv")
23+
@summary("getAclAsCsv")
24+
@get
25+
@operationId("getAclAsCsv")
26+
getAclAsCsv(@path clusterName: string): string;
27+
28+
@route("/csv")
29+
@summary("syncAclsCsv")
30+
@post
31+
@operationId("syncAclsCsv")
32+
syncAclsCsv(@path clusterName: string, @body content: string): void | ApiBadRequestResponse;
33+
34+
@post
35+
@operationId("createAcl")
36+
@summary("createAcl")
37+
createAcl(@path clusterName: string, @body acl: KafkaAcl): void | ApiBadRequestResponse;
38+
39+
@summary("deleteAcl")
40+
@delete
41+
@operationId("deleteAcl")
42+
@summary("deleteAcl")
43+
deleteAcl(
44+
@path clusterName: string,
45+
@body acl: KafkaAcl,
46+
): void | ApiNotFoundResponse;
47+
48+
@route("/consumer")
49+
@post
50+
@operationId("createConsumerAcl")
51+
@summary("createConsumerAcl")
52+
createConsumerAcl(
53+
@path clusterName: string,
54+
@body payload: CreateConsumerAcl,
55+
): void | ApiBadRequestResponse;
56+
57+
@route("/producer")
58+
@summary("createProducerAcl")
59+
@operationId("createProducerAcl")
60+
@post
61+
createProducerAcl(
62+
@path clusterName: string,
63+
@body payload: CreateProducerAcl,
64+
): void | ApiBadRequestResponse;
65+
66+
@route("/streamapp")
67+
@summary("createStreamAppAcl")
68+
@post
69+
@operationId("createStreamAppAcl")
70+
createStreamAppAcl(
71+
@path clusterName: string,
72+
@body payload: CreateStreamAppAcl,
73+
): void | ApiBadRequestResponse;
74+
}
75+
76+
model KafkaAcl {
77+
resourceType: KafkaAclResourceType;
78+
resourceName: string; // "*" if acl can be applied to any resource of given type
79+
namePatternType: KafkaAclNamePatternType;
80+
principal: string;
81+
host: string;
82+
operation: KafkaAclOpeations;
83+
permission: "ALLOW" | "DENY";
84+
}
85+
86+
alias KafkaAclOpeations =
87+
"UNKNOWN"
88+
| "ALL"
89+
| "READ"
90+
| "WRITE"
91+
| "CREATE"
92+
| "DELETE"
93+
| "ALTER"
94+
| "DESCRIBE"
95+
| "CLUSTER_ACTION"
96+
| "DESCRIBE_CONFIGS"
97+
| "ALTER_CONFIGS"
98+
| "IDEMPOTENT_WRITE"
99+
| "CREATE_TOKENS"
100+
| "DESCRIBE_TOKENS";
101+
102+
enum KafkaAclResourceType {
103+
UNKNOWN,
104+
TOPIC,
105+
GROUP,
106+
CLUSTER,
107+
TRANSACTIONAL_ID,
108+
DELEGATION_TOKEN,
109+
USER,
110+
}
111+
112+
enum KafkaAclNamePatternType {
113+
MATCH,
114+
LITERAL,
115+
PREFIXED,
116+
}
117+
118+
model CreateConsumerAcl {
119+
principal?: string;
120+
host?: string;
121+
topics?: string[];
122+
topicsPrefix?: string;
123+
consumerGroups?: string[];
124+
consumerGroupsPrefix?: string;
125+
}
126+
127+
model CreateProducerAcl {
128+
principal?: string;
129+
host?: string;
130+
topics?: string[];
131+
topicsPrefix?: string;
132+
transactionalId?: string;
133+
transactionsIdPrefix?: string;
134+
idempotent?: boolean = false;
135+
}
136+
137+
model CreateStreamAppAcl {
138+
principal?: string;
139+
host?: string;
140+
inputTopics?: string[];
141+
outputTopics?: string[];
142+
applicationId?: string;
143+
}

contract-typespec/api/auth.tsp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import "@typespec/openapi";
2+
3+
namespace Api;
4+
5+
using TypeSpec.Http;
6+
using OpenAPI;
7+
8+
@tag("Authorization")
9+
interface AuthorizationApi {
10+
@route("/api/authorization")
11+
@summary("Get user authorization related info")
12+
@operationId("getUserAuthInfo")
13+
@get
14+
getUserAuthInfo(): AuthenticationInfo;
15+
}
16+
17+
@route("/login")
18+
@summary("Authenticate")
19+
@operationId("authenticate")
20+
@post
21+
@tag("Unmapped")
22+
op authenticate(@header contentType: "application/x-www-form-urlencoded", @body form: LoginForm): void | Http.Response<401>;
23+
24+
25+
model LoginForm {
26+
username?: string;
27+
password?: string;
28+
}
29+
30+
model AuthenticationInfo {
31+
@doc("true if role based access control is enabled and granular permission access is required")
32+
rbacEnabled: boolean;
33+
userInfo?: UserInfo;
34+
}
35+
36+
model UserInfo {
37+
username: string;
38+
permissions: UserPermission[];
39+
}
40+
41+
42+
model UserPermission {
43+
clusters: string[];
44+
resource: ResourceType;
45+
value?: string;
46+
actions: Action[];
47+
}

contract-typespec/api/brokers.tsp

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import "@typespec/openapi";
2+
3+
namespace Api;
4+
5+
using TypeSpec.Http;
6+
using OpenAPI;
7+
8+
@route("/api/clusters/{clusterName}/brokers")
9+
@tag("Brokers")
10+
interface BrokersApi {
11+
@get
12+
@operationId("getBrokers")
13+
@summary("getBrokers")
14+
getBrokers(@path clusterName: string): Broker[];
15+
16+
@get
17+
@route("/{id}/configs")
18+
@operationId("getBrokerConfig")
19+
@summary("getBrokerConfig")
20+
getBrokerConfig(@path clusterName: string, @path id: int32): BrokerConfig[];
21+
22+
@put
23+
@route("/{id}/configs/{name}")
24+
@operationId("updateBrokerConfigByName")
25+
@summary("updateBrokerConfigByName")
26+
updateBrokerConfigByName(
27+
@path clusterName: string,
28+
@path id: int32,
29+
@path name: string,
30+
@body config: BrokerConfigItem,
31+
): void | ApiBadRequestResponse;
32+
33+
@get
34+
@route("/{id}/metrics")
35+
@operationId("getBrokersMetrics")
36+
@summary("getBrokersMetrics")
37+
getBrokersMetrics(@path clusterName: string, @path id: int32): BrokerMetrics;
38+
39+
@get
40+
@route("/logdirs")
41+
@operationId("getAllBrokersLogdirs")
42+
@summary("getAllBrokersLogdirs")
43+
getAllBrokersLogdirs(
44+
@path clusterName: string,
45+
@query broker?: int32[],
46+
): BrokersLogdirs[];
47+
48+
@patch(#{implicitOptionality: true})
49+
@route("/{id}/logdirs")
50+
@operationId("updateBrokerTopicPartitionLogDir")
51+
@summary("updateBrokerTopicPartitionLogDir")
52+
updateBrokerTopicPartitionLogDir(
53+
@path clusterName: string,
54+
@path id: int32,
55+
@body update: BrokerLogdirUpdate,
56+
): void | ApiBadRequestResponse;
57+
}
58+
59+
model Broker {
60+
id: int32;
61+
host?: string;
62+
port?: int32;
63+
bytesInPerSec?: float64;
64+
bytesOutPerSec?: float64;
65+
partitionsLeader?: int32;
66+
partitions?: int32;
67+
inSyncPartitions?: int32;
68+
partitionsSkew?: float64;
69+
leadersSkew?: float64;
70+
}
71+
72+
model BrokerLogdirUpdate {
73+
topic?: string;
74+
partition?: int32;
75+
logDir?: string;
76+
}
77+
78+
model BrokerConfig {
79+
name: string;
80+
value: string;
81+
source: ConfigSource;
82+
isSensitive: boolean;
83+
isReadOnly: boolean;
84+
synonyms?: ConfigSynonym[];
85+
}
86+
87+
model BrokerConfigItem {
88+
value?: string;
89+
}
90+
91+
model BrokerLogdirs {
92+
name?: string;
93+
error?: string;
94+
topics?: TopicLogdirs[];
95+
}
96+
97+
model TopicLogdirs {
98+
name?: string;
99+
partitions?: TopicPartitionLogdir[];
100+
}
101+
102+
model BrokerMetrics {
103+
segmentSize?: int64;
104+
segmentCount?: int32;
105+
metrics?: Metric[];
106+
}
107+
108+
model BrokersLogdirs {
109+
name?: string;
110+
error?: string;
111+
topics?: BrokerTopicLogdirs[];
112+
}
113+
114+
model BrokerTopicLogdirs {
115+
name?: string;
116+
partitions?: BrokerTopicPartitionLogdir[];
117+
}
118+
119+
model BrokerTopicPartitionLogdir {
120+
...TopicPartitionLogdir;
121+
broker?: int32;
122+
}
123+
124+
model TopicPartitionLogdir {
125+
partition?: int32;
126+
size?: int64;
127+
offsetLag?: int64;
128+
}

0 commit comments

Comments
 (0)