Skip to content

Commit

Permalink
feat(ksql): initial ksql support (#1324)
Browse files Browse the repository at this point in the history
close #115
  • Loading branch information
dweber019 authored and tchiotludo committed Apr 4, 2023
1 parent e372458 commit 6e0deae
Show file tree
Hide file tree
Showing 45 changed files with 2,180 additions and 30 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ Kestra is an infinitely scalable orchestration and scheduling platform, creating
* [TUI](https://www.tui.com)
* [TVG](https://www.tvg.com)
* [Vodeno](https://www.vodeno.com/)
* [Baloise](https://www.baloise.ch/)



Expand Down
3 changes: 3 additions & 0 deletions application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ akhq:
connect:
- name: "connect"
url: "http://connect:8083"
ksqldb:
- name: "ksqldb"
url: "http://ksqldb:8088"
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ dependencies {
// kafka
implementation group: "org.apache.kafka", name: "kafka_" + kafkaScalaVersion, version: kafkaVersion
implementation group: "org.apache.kafka", name: "kafka-clients", version: kafkaVersion
implementation group: "org.apache.kafka", name: "kafka-streams", version: kafkaVersion
implementation group: "io.confluent.ksql", name: "ksqldb-api-client", version: ksqlApiClientVersion
implementation group: "io.confluent", name: "kafka-schema-registry-client", version: confluentVersion
implementation group: "io.confluent", name: "kafka-avro-serializer", version: confluentVersion
implementation group: "io.confluent", name: "kafka-json-schema-serializer", version: confluentVersion
Expand Down Expand Up @@ -176,6 +178,7 @@ dependencies {
testImplementation "io.confluent:kafka-schema-registry:" + confluentVersion + ":tests"
testImplementation "org.apache.kafka:connect-runtime:" + kafkaVersion
testImplementation "org.apache.kafka:connect-file:" + kafkaVersion
testImplementation 'io.confluent.ksql:ksqldb-rest-app:' + ksqlApiClientVersion

testImplementation group: 'org.apache.kafka', name: 'kafka-streams', version: kafkaVersion
testImplementation group: "io.confluent", name: "kafka-streams-avro-serde", version: confluentVersion
Expand Down
83 changes: 83 additions & 0 deletions client/src/containers/KsqlDB/KsqlDBList/KsqlDBInfo/KsqlDBInfo.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React from 'react';
import Table from '../../../../components/Table/Table';
import { uriKsqlDBInfo } from '../../../../utils/endpoints';
import 'react-toastify/dist/ReactToastify.css';
import Root from '../../../../components/Root';

class KsqlDBInfo extends Root {
state = {
clusterId: this.props.clusterId || this.props.match.params.clusterId,
ksqlDBId: this.props.ksqlDBId || this.props.match.params.ksqlDBId,
info: {},
tableData: [],
loading: true
};

componentDidMount() {
this.getInfo();
}

handleInfo() {
const info = this.state.info || {};
let tableData = [
{
title: 'Server version',
value: info['serverVersion']
},
{
title: 'Kafka cluster id',
value: info['kafkaClusterId']
},
{
title: 'Ksql service id',
value: info['ksqlServiceId']
}
];
this.setState({ tableData, loading: false });
}

async getInfo() {
const { clusterId, ksqlDBId } = this.state;

this.setState({ loading: true });
const info = await this.getApi(uriKsqlDBInfo(clusterId, ksqlDBId));
this.setState({ info: info.data }, () => this.handleInfo());
}

render() {
const { tableData, loading } = this.state;
return (
<div className="tab-pane active" role="tabpanel">
<div className="table-responsive">
<Table
loading={loading}
history={this.props.history}
columns={[
{
id: 'title',
name: 'title',
accessor: 'title',
colName: 'Title',
type: 'text',
sortable: false
},
{
id: 'value',
name: 'value',
accessor: 'value',
colName: 'Value',
type: 'text',
sortable: false
}
]}
extraRow
noStripes
data={tableData}
/>
</div>
</div>
);
}
}

export default KsqlDBInfo;
141 changes: 141 additions & 0 deletions client/src/containers/KsqlDB/KsqlDBList/KsqlDBList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Header from '../../Header/Header';
import { getSelectedTab } from '../../../utils/functions';
import { Link } from 'react-router-dom';
import KsqlDBInfo from './KsqlDBInfo/KsqlDBInfo';
import KsqlDBStreams from './KsqlDBStreams/KsqlDBStreams';
import KsqlDBTables from './KsqlDBTables/KsqlDBTables';
import KsqlDBQueries from './KsqlDBQueries/KsqlDBQueries';

class KsqlDBList extends Component {
state = {
clusterId: this.props.history.clusterId || this.props.match.params.clusterId,
ksqlDBId: this.props.history.ksqlDBId || this.props.match.params.ksqlDBId,
selectedTab: 'streams',
roles: JSON.parse(sessionStorage.getItem('roles'))
};

tabs = {
streams: KsqlDBStreams,
tables: KsqlDBTables,
queries: KsqlDBQueries,
info: KsqlDBInfo
};

componentDidMount() {
const { clusterId, ksqlDBId } = this.props.match.params;
const tabSelected = getSelectedTab(this.props, Object.keys(this.tabs));
this.setState({ selectedTab: tabSelected ? tabSelected : 'streams' }, () => {
this.props.history.replace(`/ui/${clusterId}/ksqldb/${ksqlDBId}/${this.state.selectedTab}`);
});
}

componentDidUpdate(prevProps) {
if (this.props.location.pathname !== prevProps.location.pathname) {
const tabSelected = getSelectedTab(this.props, Object.keys(this.tabs));
this.setState({ selectedTab: tabSelected });
}
}

tabClassName = tab => {
const { selectedTab } = this.state;
return selectedTab === tab ? 'nav-link active' : 'nav-link';
};

renderSelectedTab() {
const { clusterId, ksqlDBId, selectedTab } = this.state;
const { history, match, location } = this.props;
const SelectedTab = this.tabs[selectedTab] || KsqlDBStreams;

return (
<SelectedTab
clusterId={clusterId}
ksqlDBId={ksqlDBId}
history={history}
match={match}
location={location}
/>
);
}

render() {
const { clusterId, ksqlDBId } = this.state;
const roles = this.state.roles || {};
return (
<div>
<Header title={`KsqlDB: ${ksqlDBId}`} history={this.props.history} />
<div className="tabs-container">
<ul className="nav nav-tabs" role="tablist">
<li className="nav-item">
<Link
to={`/ui/${clusterId}/ksqldb/${ksqlDBId}/streams`}
className={this.tabClassName('streams')}
>
Streams
</Link>
</li>
<li className="nav-item">
<Link
to={`/ui/${clusterId}/ksqldb/${ksqlDBId}/tables`}
className={this.tabClassName('tables')}
>
Tables
</Link>
</li>
<li className="nav-item">
<Link
to={`/ui/${clusterId}/ksqldb/${ksqlDBId}/queries`}
className={this.tabClassName('queries')}
>
Queries
</Link>
</li>
<li className="nav-item">
<Link
to={`/ui/${clusterId}/ksqldb/${ksqlDBId}/info`}
className={this.tabClassName('info')}
>
Info
</Link>
</li>
</ul>

<div className="tab-content">
<div className="tab-pane active" role="tabpanel">
{this.renderSelectedTab()}
</div>
</div>
</div>
{roles && roles.ksqldb && roles.ksqldb['ksqldb/execute'] && (
<aside>
<li className="aside-button">
<Link
to={`/ui/${clusterId}/ksqldb/${ksqlDBId}/query`}
className="btn btn-primary mr-2"
>
Execute queries
</Link>
</li>
<li className="aside-button">
<Link
to={`/ui/${clusterId}/ksqldb/${ksqlDBId}/statement`}
className="btn btn-primary mr-2"
>
Execute statements
</Link>
</li>
</aside>
)}
</div>
);
}
}

KsqlDBList.propTypes = {
history: PropTypes.object,
location: PropTypes.object,
match: PropTypes.object
};

export default KsqlDBList;
Loading

0 comments on commit 6e0deae

Please sign in to comment.