diff --git a/lib/api/pulsar/pulsar_sink_api.dart b/lib/api/pulsar/pulsar_sink_api.dart index 1bc09e8..c548af1 100644 --- a/lib/api/pulsar/pulsar_sink_api.dart +++ b/lib/api/pulsar/pulsar_sink_api.dart @@ -53,4 +53,18 @@ class PulsarSinkApi { List jsonResponse = json.decode(response.body) as List; return jsonResponse.map((name) => new SinkResp(name)).toList(); } + + static Future getSink( + String host, int port, String tenant, String namespace, String sinkName) async { + var url = + 'http://$host:${port.toString()}/admin/v3/sinks/$tenant/$namespace/$sinkName'; + final response = await http.get(Uri.parse(url)); + if (HttpUtil.abnormal(response.statusCode)) { + log('ErrorCode is ${response.statusCode}, body is ${response.body}'); + throw Exception( + 'ErrorCode is ${response.statusCode}, body is ${response.body}'); + } + Map jsonResponse = json.decode(response.body) as Map; + return SinkConfigResp.fromJson(jsonResponse); + } } diff --git a/lib/api/pulsar/pulsar_source_api.dart b/lib/api/pulsar/pulsar_source_api.dart index 7721ef6..6534692 100644 --- a/lib/api/pulsar/pulsar_source_api.dart +++ b/lib/api/pulsar/pulsar_source_api.dart @@ -51,4 +51,18 @@ class PulsarSourceApi { List jsonResponse = json.decode(response.body) as List; return jsonResponse.map((name) => new SourceResp(name)).toList(); } + + static Future getSource( + String host, int port, String tenant, String namespace, String sourceName) async { + var url = + 'http://$host:${port.toString()}/admin/v3/sources/$tenant/$namespace/$sourceName'; + final response = await http.get(Uri.parse(url)); + if (HttpUtil.abnormal(response.statusCode)) { + log('ErrorCode is ${response.statusCode}, body is ${response.body}'); + throw Exception( + 'ErrorCode is ${response.statusCode}, body is ${response.body}'); + } + Map jsonResponse = json.decode(response.body) as Map; + return SourceConfigResp.fromJson(jsonResponse); + } } diff --git a/lib/main.dart b/lib/main.dart index 5a3ec82..8ad1a04 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,6 +14,8 @@ import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_instance_list_view_model import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_instance_view_model.dart'; import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_namespace_view_model.dart'; import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_partitioned_topic_view_model.dart'; +import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_sink_view_model.dart'; +import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_source_view_model.dart'; import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_tenant_view_model.dart'; import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_topic_view_model.dart'; import 'package:provider/provider.dart'; @@ -85,6 +87,14 @@ class MyApp extends StatelessWidget { final args = settings.arguments as PulsarTopicViewModel; return RouteGen.pulsarTopic(args); } + if (settings.name == PageRouteConst.PulsarSource) { + final args = settings.arguments as PulsarSourceViewModel; + return RouteGen.pulsarSource(args); + } + if (settings.name == PageRouteConst.PulsarSink) { + final args = settings.arguments as PulsarSinkViewModel; + return RouteGen.pulsarSink(args); + } }, ); } diff --git a/lib/module/pulsar/pulsar_sink.dart b/lib/module/pulsar/pulsar_sink.dart index fe81410..2bbf232 100644 --- a/lib/module/pulsar/pulsar_sink.dart +++ b/lib/module/pulsar/pulsar_sink.dart @@ -23,6 +23,23 @@ class SinkConfigReq { } } +class SinkConfigResp { + final String name; + final String tenant; + final String namespace; + final List inputs; + final Map configs; + final String archive; + + SinkConfigResp(this.name, this.tenant, this.namespace, this.inputs, + this.configs, this.archive); + + factory SinkConfigResp.fromJson(Map map) { + return SinkConfigResp(map["name"], map["tenant"], map["namespace"], + map["inputs"], map["configs"], map["archive"]); + } +} + class SinkResp { final String sinkName; @@ -31,6 +48,11 @@ class SinkResp { SinkResp deepCopy() { return new SinkResp(this.sinkName); } + + @override + String toString() { + return 'SinkResp{sinkName: $sinkName}'; + } } class PulsarSink { diff --git a/lib/module/pulsar/pulsar_source.dart b/lib/module/pulsar/pulsar_source.dart index 51aa877..8896488 100644 --- a/lib/module/pulsar/pulsar_source.dart +++ b/lib/module/pulsar/pulsar_source.dart @@ -21,6 +21,23 @@ class SourceConfigReq { } } +class SourceConfigResp { + final String name; + final String tenant; + final String namespace; + final String topicName; + final Map configs; + final String archive; + + SourceConfigResp(this.name, this.tenant, this.namespace, this.topicName, + this.configs, this.archive); + + factory SourceConfigResp.fromJson(Map map) { + return SourceConfigResp(map["name"], map["tenant"], map["namespace"], + map["topicName"], map["configs"], map["archive"]); + } +} + class SourceResp { final String sourceName; diff --git a/lib/route/page_route_const.dart b/lib/route/page_route_const.dart index fce978c..c62d466 100644 --- a/lib/route/page_route_const.dart +++ b/lib/route/page_route_const.dart @@ -9,4 +9,6 @@ class PageRouteConst { static const String PulsarNamespace = '/pulsar/instance/tenant/namespace'; static const String PulsarPartitionedTopic = '/pulsar/instance/tenant/namespace/partitioned-topic'; static const String PulsarTopic = '/pulsar/instance/tenant/namespace/topic'; + static const String PulsarSource = '/pulsar/instance/tenant/namespace/source'; + static const String PulsarSink = '/pulsar/instance/tenant/namespace/sink'; } diff --git a/lib/route/route_gen.dart b/lib/route/route_gen.dart index 98fb80a..d66d227 100644 --- a/lib/route/route_gen.dart +++ b/lib/route/route_gen.dart @@ -2,11 +2,15 @@ import 'package:flutter/material.dart'; import 'package:paas_dashboard_flutter/ui/pulsar/pulsar_instance.dart'; import 'package:paas_dashboard_flutter/ui/pulsar/screen/pulsar_namespace.dart'; import 'package:paas_dashboard_flutter/ui/pulsar/screen/pulsar_partitioned_topic.dart'; +import 'package:paas_dashboard_flutter/ui/pulsar/screen/pulsar_sink.dart'; +import 'package:paas_dashboard_flutter/ui/pulsar/screen/pulsar_source.dart'; import 'package:paas_dashboard_flutter/ui/pulsar/screen/pulsar_tenant.dart'; import 'package:paas_dashboard_flutter/ui/pulsar/screen/pulsar_topic.dart'; import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_instance_view_model.dart'; import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_namespace_view_model.dart'; import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_partitioned_topic_view_model.dart'; +import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_sink_view_model.dart'; +import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_source_view_model.dart'; import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_tenant_view_model.dart'; import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_topic_view_model.dart'; import 'package:provider/provider.dart'; @@ -53,4 +57,20 @@ class RouteGen { child: PulsarTopic(), )); } + + static Route pulsarSource(PulsarSourceViewModel viewModel) { + return MaterialPageRoute( + builder: (context) => ChangeNotifierProvider( + create: (context) => viewModel, + child: PulsarSourceScreen(), + )); + } + + static Route pulsarSink(PulsarSinkViewModel viewModel) { + return MaterialPageRoute( + builder: (context) => ChangeNotifierProvider( + create: (context) => viewModel, + child: PulsarSinkScreen(), + )); + } } diff --git a/lib/ui/pulsar/screen/pulsar_sink.dart b/lib/ui/pulsar/screen/pulsar_sink.dart new file mode 100644 index 0000000..5f4160d --- /dev/null +++ b/lib/ui/pulsar/screen/pulsar_sink.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:paas_dashboard_flutter/generated/l10n.dart'; +import 'package:paas_dashboard_flutter/ui/pulsar/widget/pulsar_sink_basic.dart'; +import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_sink_basic_view_model.dart'; +import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_sink_view_model.dart'; +import 'package:provider/provider.dart'; + +class PulsarSinkScreen extends StatefulWidget { + PulsarSinkScreen(); + + @override + State createState() { + return new _PulsarSinkScreenState(); + } +} + +class _PulsarSinkScreenState extends State { + _PulsarSinkScreenState(); + + @override + Widget build(BuildContext context) { + final vm = Provider.of(context); + return DefaultTabController( + length: 1, + child: Scaffold( + appBar: AppBar( + title: Text( + 'Pulsar Sink Tenant ${vm.tenant} -> Namespace ${vm.namespace} -> Topic ${vm.sinkName}'), + bottom: TabBar( + tabs: [ + Tab(text: S.of(context).basic), + ], + ), + ), + body: TabBarView( + children: [ + ChangeNotifierProvider( + create: (context) => PulsarSinkBasicViewModel( + vm.pulsarInstancePo, + vm.tenantResp, + vm.namespaceResp, + vm.sinkResp), + child: PulsarSinkBasicWidget(), + ).build(context), + ], + ), + ), + ); + } +} diff --git a/lib/ui/pulsar/screen/pulsar_source.dart b/lib/ui/pulsar/screen/pulsar_source.dart new file mode 100644 index 0000000..8a90bc4 --- /dev/null +++ b/lib/ui/pulsar/screen/pulsar_source.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:paas_dashboard_flutter/generated/l10n.dart'; +import 'package:paas_dashboard_flutter/ui/pulsar/widget/pulsar_source_basic.dart'; +import 'package:paas_dashboard_flutter/ui/pulsar/widget/pulsar_topic_basic.dart'; +import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_source_basic_view_model.dart'; +import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_source_view_model.dart'; +import 'package:provider/provider.dart'; + +class PulsarSourceScreen extends StatefulWidget { + PulsarSourceScreen(); + + @override + State createState() { + return new _PulsarSourceScreenState(); + } +} + +class _PulsarSourceScreenState extends State { + _PulsarSourceScreenState(); + + @override + Widget build(BuildContext context) { + final vm = Provider.of(context); + return DefaultTabController( + length: 1, + child: Scaffold( + appBar: AppBar( + title: Text( + 'Pulsar Source Tenant ${vm.tenant} -> Namespace ${vm.namespace} -> Topic ${vm.sourceName}'), + bottom: TabBar( + tabs: [ + Tab(text: S.of(context).basic), + ], + ), + ), + body: TabBarView( + children: [ + ChangeNotifierProvider( + create: (context) => PulsarSourceBasicViewModel( + vm.pulsarInstancePo, + vm.tenantResp, + vm.namespaceResp, + vm.sourceResp), + child: PulsarSourceBasicWidget(), + ).build(context), + ], + ), + ), + ); + } +} diff --git a/lib/ui/pulsar/widget/pulsar_sink_basic.dart b/lib/ui/pulsar/widget/pulsar_sink_basic.dart new file mode 100644 index 0000000..7731330 --- /dev/null +++ b/lib/ui/pulsar/widget/pulsar_sink_basic.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; +import 'package:paas_dashboard_flutter/generated/l10n.dart'; +import 'package:paas_dashboard_flutter/ui/util/exception_util.dart'; +import 'package:paas_dashboard_flutter/ui/util/spinner_util.dart'; +import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_sink_basic_view_model.dart'; +import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_topic_basic_view_model.dart'; +import 'package:provider/provider.dart'; + +class PulsarSinkBasicWidget extends StatefulWidget { + PulsarSinkBasicWidget(); + + @override + State createState() { + return new PulsarSinkBasicWidgetState(); + } +} + +class PulsarSinkBasicWidgetState + extends State { + @override + void initState() { + super.initState(); + final vm = Provider.of(context, + listen: false); + vm.fetch(); + } + + @override + Widget build(BuildContext context) { + final vm = Provider.of(context); + if (vm.loading) { + WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { + SpinnerUtil.create(); + }); + } + ExceptionUtil.processLoadException(vm, context); + ExceptionUtil.processOpException(vm, context); + var refreshButton = + TextButton(onPressed: () {}, child: Text(S.of(context).refresh)); + var body = ListView( + children: [ + Container( + height: 50, + child: ListView( + scrollDirection: Axis.horizontal, + shrinkWrap: true, + children: [refreshButton], + ), + ), + Container( + height: 50, + child: ListView( + scrollDirection: Axis.horizontal, + shrinkWrap: true, + children: [ + Text( + 'inputs is ${vm.inputs}', + style: new TextStyle(fontSize: 20), + ), + ], + ), + ), + Container( + height: 50, + child: ListView( + scrollDirection: Axis.horizontal, + shrinkWrap: true, + children: [ + Text( + 'configs is ${vm.configs}', + style: new TextStyle(fontSize: 20), + ), + ], + ), + ), + Container( + height: 50, + child: ListView( + scrollDirection: Axis.horizontal, + shrinkWrap: true, + children: [ + Text( + 'archive is ${vm.archive}', + style: new TextStyle(fontSize: 20), + ), + ], + ), + ), + ], + ); + return body; + } + +} diff --git a/lib/ui/pulsar/widget/pulsar_sink_list.dart b/lib/ui/pulsar/widget/pulsar_sink_list.dart index 21bca53..af7d0e8 100644 --- a/lib/ui/pulsar/widget/pulsar_sink_list.dart +++ b/lib/ui/pulsar/widget/pulsar_sink_list.dart @@ -50,15 +50,15 @@ class PulsarSinkListWidgetState extends State { vm.setDataConverter((item) => DataRow( onSelectChanged: (bool? selected) { Navigator.pushNamed( - context, PageRouteConst.PulsarPartitionedTopic, + context, PageRouteConst.PulsarSink, arguments: item.deepCopy()); }, cells: [ DataCell( - Text(item.name), + Text(item.sinkName), ), DataCellUtil.newDellDataCell(() { - vm.deleteSink(item.name); + vm.deleteSink(item.sinkName); }), ])); var topicsTable = SingleChildScrollView( diff --git a/lib/ui/pulsar/widget/pulsar_source_basic.dart b/lib/ui/pulsar/widget/pulsar_source_basic.dart new file mode 100644 index 0000000..443e46b --- /dev/null +++ b/lib/ui/pulsar/widget/pulsar_source_basic.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; +import 'package:paas_dashboard_flutter/generated/l10n.dart'; +import 'package:paas_dashboard_flutter/ui/util/exception_util.dart'; +import 'package:paas_dashboard_flutter/ui/util/spinner_util.dart'; +import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_source_basic_view_model.dart'; +import 'package:paas_dashboard_flutter/vm/pulsar/pulsar_topic_basic_view_model.dart'; +import 'package:provider/provider.dart'; + +class PulsarSourceBasicWidget extends StatefulWidget { + PulsarSourceBasicWidget(); + + @override + State createState() { + return new PulsarSourceBasicWidgetState(); + } +} + +class PulsarSourceBasicWidgetState + extends State { + @override + void initState() { + super.initState(); + final vm = Provider.of(context, + listen: false); + vm.fetch(); + } + + @override + Widget build(BuildContext context) { + final vm = Provider.of(context); + if (vm.loading) { + WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { + SpinnerUtil.create(); + }); + } + ExceptionUtil.processLoadException(vm, context); + ExceptionUtil.processOpException(vm, context); + var refreshButton = + TextButton(onPressed: () {}, child: Text(S.of(context).refresh)); + var body = ListView( + children: [ + Container( + height: 50, + child: ListView( + scrollDirection: Axis.horizontal, + shrinkWrap: true, + children: [refreshButton], + ), + ), + Container( + height: 50, + child: ListView( + scrollDirection: Axis.horizontal, + shrinkWrap: true, + children: [ + Text( + 'output is ${vm.topicName}', + style: new TextStyle(fontSize: 20), + ), + ], + ), + ), + Container( + height: 50, + child: ListView( + scrollDirection: Axis.horizontal, + shrinkWrap: true, + children: [ + Text( + 'configs is ${vm.configs}', + style: new TextStyle(fontSize: 20), + ), + ], + ), + ), + Container( + height: 50, + child: ListView( + scrollDirection: Axis.horizontal, + shrinkWrap: true, + children: [ + Text( + 'archive is ${vm.archive}', + style: new TextStyle(fontSize: 20), + ), + ], + ), + ), + ], + ); + return body; + } + +} diff --git a/lib/ui/pulsar/widget/pulsar_source_list.dart b/lib/ui/pulsar/widget/pulsar_source_list.dart index f06ae7f..177190f 100644 --- a/lib/ui/pulsar/widget/pulsar_source_list.dart +++ b/lib/ui/pulsar/widget/pulsar_source_list.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:paas_dashboard_flutter/generated/l10n.dart'; +import 'package:paas_dashboard_flutter/route/page_route_const.dart'; import 'package:paas_dashboard_flutter/ui/component/searchable_title.dart'; import 'package:paas_dashboard_flutter/ui/util/data_cell_util.dart'; import 'package:paas_dashboard_flutter/ui/util/exception_util.dart'; @@ -45,8 +46,12 @@ class PulsarSourceListWidgetState extends State { } ExceptionUtil.processLoadExceptionPageable(vm, context); ExceptionUtil.processOpExceptionPageable(vm, context); - vm.setDataConverter( - (item) => DataRow(onSelectChanged: (bool? selected) {}, cells: [ + vm.setDataConverter((item) => DataRow( + onSelectChanged: (bool? selected) { + Navigator.pushNamed(context, PageRouteConst.PulsarSource, + arguments: item.deepCopy()); + }, + cells: [ DataCell( Text(item.sourceName), ), diff --git a/lib/vm/pulsar/pulsar_sink_basic_view_model.dart b/lib/vm/pulsar/pulsar_sink_basic_view_model.dart new file mode 100644 index 0000000..52fe323 --- /dev/null +++ b/lib/vm/pulsar/pulsar_sink_basic_view_model.dart @@ -0,0 +1,68 @@ +import 'package:paas_dashboard_flutter/api/pulsar/pulsar_sink_api.dart'; +import 'package:paas_dashboard_flutter/module/pulsar/pulsar_namespace.dart'; +import 'package:paas_dashboard_flutter/module/pulsar/pulsar_sink.dart'; +import 'package:paas_dashboard_flutter/module/pulsar/pulsar_tenant.dart'; +import 'package:paas_dashboard_flutter/persistent/po/pulsar_instance_po.dart'; +import 'package:paas_dashboard_flutter/vm/base_load_view_model.dart'; + +class PulsarSinkBasicViewModel extends BaseLoadViewModel { + final PulsarInstancePo pulsarInstancePo; + final TenantResp tenantResp; + final NamespaceResp namespaceResp; + final SinkResp sinkResp; + List inputs = []; + Map configs = {}; + String archive = ""; + + PulsarSinkBasicViewModel(this.pulsarInstancePo, this.tenantResp, + this.namespaceResp, this.sinkResp); + + PulsarSinkBasicViewModel deepCopy() { + return new PulsarSinkBasicViewModel(pulsarInstancePo.deepCopy(), + tenantResp.deepCopy(), namespaceResp.deepCopy(), sinkResp.deepCopy()); + } + + int get id { + return this.pulsarInstancePo.id; + } + + String get name { + return this.pulsarInstancePo.name; + } + + String get host { + return this.pulsarInstancePo.host; + } + + int get port { + return this.pulsarInstancePo.port; + } + + String get tenant { + return this.tenantResp.tenant; + } + + String get namespace { + return this.namespaceResp.namespace; + } + + String get sinkName { + return this.sinkResp.sinkName; + } + + Future fetch() async { + try { + final SinkConfigResp sinkConfigResp = await PulsarSinkApi.getSink( + host, port, tenant, namespace, sinkName); + this.inputs = sinkConfigResp.inputs; + this.configs = sinkConfigResp.configs; + this.archive = sinkConfigResp.archive; + loadSuccess(); + } on Exception catch (e) { + loadException = e; + loading = false; + } + notifyListeners(); + } + +} diff --git a/lib/vm/pulsar/pulsar_source_basic_view_model.dart b/lib/vm/pulsar/pulsar_source_basic_view_model.dart new file mode 100644 index 0000000..7955993 --- /dev/null +++ b/lib/vm/pulsar/pulsar_source_basic_view_model.dart @@ -0,0 +1,68 @@ +import 'package:paas_dashboard_flutter/api/pulsar/pulsar_source_api.dart'; +import 'package:paas_dashboard_flutter/module/pulsar/pulsar_namespace.dart'; +import 'package:paas_dashboard_flutter/module/pulsar/pulsar_source.dart'; +import 'package:paas_dashboard_flutter/module/pulsar/pulsar_tenant.dart'; +import 'package:paas_dashboard_flutter/persistent/po/pulsar_instance_po.dart'; +import 'package:paas_dashboard_flutter/vm/base_load_view_model.dart'; + +class PulsarSourceBasicViewModel extends BaseLoadViewModel { + final PulsarInstancePo pulsarInstancePo; + final TenantResp tenantResp; + final NamespaceResp namespaceResp; + final SourceResp sourceResp; + String topicName = ""; + Map configs = {}; + String archive = ""; + + PulsarSourceBasicViewModel(this.pulsarInstancePo, this.tenantResp, + this.namespaceResp, this.sourceResp); + + PulsarSourceBasicViewModel deepCopy() { + return new PulsarSourceBasicViewModel(pulsarInstancePo.deepCopy(), + tenantResp.deepCopy(), namespaceResp.deepCopy(), sourceResp.deepCopy()); + } + + int get id { + return this.pulsarInstancePo.id; + } + + String get name { + return this.pulsarInstancePo.name; + } + + String get host { + return this.pulsarInstancePo.host; + } + + int get port { + return this.pulsarInstancePo.port; + } + + String get tenant { + return this.tenantResp.tenant; + } + + String get namespace { + return this.namespaceResp.namespace; + } + + String get sourceName { + return this.sourceResp.sourceName; + } + + Future fetch() async { + try { + final SourceConfigResp sourceConfigResp = await PulsarSourceApi.getSource( + host, port, tenant, namespace, sourceName); + this.topicName = sourceConfigResp.topicName; + this.configs = sourceConfigResp.configs; + this.archive = sourceConfigResp.archive; + loadSuccess(); + } on Exception catch (e) { + loadException = e; + loading = false; + } + notifyListeners(); + } + +} diff --git a/test/api/pulsar/pulsar_sink_api_query_test.dart b/test/api/pulsar/pulsar_sink_api_query_test.dart new file mode 100644 index 0000000..5f5d614 --- /dev/null +++ b/test/api/pulsar/pulsar_sink_api_query_test.dart @@ -0,0 +1,9 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:paas_dashboard_flutter/api/pulsar/pulsar_sink_api.dart'; + +void main() { + test("test_create_sink", () async { + var list = await PulsarSinkApi.getSinkList("localhost", 8080, "public", "default"); + print(list); + }); +}