Skip to content

Commit

Permalink
Support Mongo collection filter, Mysql/Mongo Data export to csv
Browse files Browse the repository at this point in the history
  • Loading branch information
HaitaoDeng committed Mar 1, 2022
1 parent a779203 commit ac99269
Show file tree
Hide file tree
Showing 17 changed files with 345 additions and 52 deletions.
118 changes: 93 additions & 25 deletions lib/api/mongo/mongo_tables_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import 'dart:collection';
import 'package:mongo_dart/mongo_dart.dart';
import 'package:paas_dashboard_flutter/module/mongo/mongo_sql_result.dart';
import 'package:paas_dashboard_flutter/module/mongo/mongo_table.dart';
import 'package:paas_dashboard_flutter/ui/component/dynamic_filter_table.dart';

class MongoTablesApi {
static Future<List<TableResp>> getTableList(
Expand All @@ -35,32 +36,99 @@ class MongoTablesApi {
}).toList();
}

static Future<MongoSqlResult> getTableData(
String addr, String username, String password, String databaseName, String tableName) async {
var db = await Db.create(addr + "/" + databaseName);
await db.open();
var collection = db.collection(tableName);
SelectorBuilder builder = SelectorBuilder().limit(100);
List<Map<String, dynamic>> data = await collection.find(builder).toList();
db.close();
MongoSqlResult result = MongoSqlResult.create();
if (data.isEmpty) {
static Future<MongoSqlResult> getTableData(String addr, String username, String password, String databaseName,
String tableName, SelectorBuilder builder) async {
try {
var db = await Db.create(addr + "/" + databaseName);
await db.open();
var collection = db.collection(tableName);
List<Map<String, dynamic>> data = await collection.find(builder).toList();
db.close();
MongoSqlResult result = MongoSqlResult.create();
if (data.isEmpty) {
return result;
}
LinkedHashSet<String> fieldNames = new LinkedHashSet();
List<List<Object?>> sqldata = [];
data.forEach((element) {
fieldNames.addAll(element.keys);
});
data.forEach((element) {
List<Object?> temp = [];
fieldNames.forEach((fieldName) {
temp.add(element[fieldName]);
});
sqldata.add(temp);
});
result.fieldName = fieldNames;
result.data = sqldata;
return result;
} catch (e) {
throw Exception('Exception: ${e.toString()}');
}
}

static SelectorBuilder getSelectorBuilder(List<DropDownButtonData>? filters) {
SelectorBuilder builder = SelectorBuilder();
if (filters == null || filters.isEmpty) {
return builder.limit(100);
}
builder = builderSelector(filters[0]);
for (int i = 1; i < filters.length; i++) {
DropDownButtonData data = filters[i];
SelectorBuilder builderTemp = builderSelector(data);
if (filters[i - 1].join) {
builder.and(builderTemp);
} else {
builder.or(builderTemp);
}
}
return builder;
}

static SelectorBuilder builderSelector(DropDownButtonData data) {
dynamic value = parseValue(data.value ?? "", data.type);
String column = data.column;
switch (data.op) {
case OP.EQ:
return where.eq(column, value);
case OP.NOT_EQ:
return where.ne(column, value);
case OP.LT:
return where.lt(column, value);
case OP.GT:
return where.gt(column, value);
case OP.LT_EQ:
return where.lte(column, value);
case OP.GT_EQ:
return where.gte(column, value);
case OP.NULL:
return where.notExists(column);
case OP.NOT_NULL:
return where.exists(column);
case OP.INCLUDE:
return where.oneFrom(
column, data.value == null ? [] : data.value!.split(",").map((e) => parseValue(e, data.type)).toList());
case OP.EXCLUDE:
return where.nin(
column, data.value == null ? [] : data.value!.split(",").map((e) => parseValue(e, data.type)).toList());
case OP.BEGIN:
return where.match(column, "^" + value);
case OP.END:
return where.match(column, value + "\$");
case OP.CONTAIN:
return where.match(column, "^.*" + value + ".*\$");
}
}

static dynamic parseValue(String value, TYPE type) {
switch (type) {
case TYPE.OBJECT_ID:
return ObjectId.parse(value);
case TYPE.TEXT:
return value;
case TYPE.NUMBER:
return value.contains(".") ? double.parse(value) : int.parse(value);
}
LinkedHashSet<String> fieldNames = new LinkedHashSet();
List<List<Object?>> sqldata = [];
data.forEach((element) {
fieldNames.addAll(element.keys);
});
data.forEach((element) {
List<Object?> temp = [];
fieldNames.forEach((fieldName) {
temp.add(element[fieldName]);
});
sqldata.add(temp);
});
result.fieldName = fieldNames;
result.data = sqldata;
return result;
}
}
2 changes: 1 addition & 1 deletion lib/api/mysql/mysql_databases_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ class MysqlDatabaseApi {
} else if (OP.NULL == op || OP.NOT_NULL == op) {
return "";
} else {
return type == TYPE.TEXT ? sprintf("'%s'", [value]) : value;
return type == TYPE.NUMBER ? value : sprintf("'%s'", [value]);
}
}

Expand Down
5 changes: 5 additions & 0 deletions lib/l10n/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"consume": "consume",
"consumer": "Consumer",
"consumerList": "Consumer List",
"custom": "Custom",
"data": "Data",
"database": "Database",
"delete": "Delete",
Expand All @@ -25,6 +26,8 @@
"email": "email",
"entryId": "entryId",
"execute": "execute",
"export": "export",
"failure": "Failure",
"filter": "filter",
"forceDelete": "forceDelete",
"isLeader": "Is Leader",
Expand All @@ -40,6 +43,7 @@
"producer": "Producer",
"producerList": "Producer List",
"refresh": "Refresh",
"result": "Result",
"searchByMessageId": "Search by MessageId, type is ledgerId entryId, submit with enter key",
"searchByMessageIdWithHint": "Search by MessageId, single search type should be ledgerId entryId, multi search type should be ledgerId entryId entryId, submit with enter key",
"searchByNamespace": "Search by Namespace Name",
Expand All @@ -52,6 +56,7 @@
"subscription": "subscription",
"subscriptionList": "Subscription list",
"subscriptionName": "Subscription Name",
"success": "Success",
"sqlQuery": "sql query",
"sqlQueryMessage": "Please enter sql statement",
"tableColumn": "Table Column",
Expand Down
5 changes: 5 additions & 0 deletions lib/l10n/intl_zh.arb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"consume": "消费",
"consumer": "消费者",
"consumerList": "消费者列表",
"custom": "自定义",
"data": "数据",
"database": "数据库",
"delete": "删除",
Expand All @@ -25,6 +26,8 @@
"email": "邮箱",
"entryId": "编号",
"execute": "执行",
"export": "导出",
"failure": "失败",
"filter": "过滤",
"forceDelete": "强制删除",
"isLeader": "是否是主节点",
Expand All @@ -40,6 +43,7 @@
"producer": "生产者",
"producerList": "生产者列表",
"refresh": "刷新",
"result": "结果",
"searchByMessageId": "通过messageId查询消息,格式ledgerId entryId,按enter键进行查询。",
"searchByMessageIdWithHint": "通过messageId查询消息,单条查询格式ledgerId entryId,范围查询格式位ledgerId entryId entryId,按enter键进行查询。",
"searchByNamespace": "按命名空间名称搜索",
Expand All @@ -52,6 +56,7 @@
"subscription": "订阅",
"subscriptionList": "订阅列表",
"subscriptionName": "订阅名称",
"success": "成功",
"sqlQuery": "sql 查询",
"sqlQueryMessage": "请输入需要执行的SQL",
"tableColumn": "字段",
Expand Down
2 changes: 1 addition & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:window_size/window_size.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:paas_dashboard_flutter/generated/l10n.dart';
import 'package:paas_dashboard_flutter/route/page_route_const.dart';
Expand Down Expand Up @@ -64,6 +63,7 @@ import 'package:paas_dashboard_flutter/vm/redis/redis_instance_list_view_model.d
import 'package:paas_dashboard_flutter/vm/sql/sql_list_view_model.dart';
import 'package:paas_dashboard_flutter/vm/sql/sql_view_model.dart';
import 'package:provider/provider.dart';
import 'package:window_size/window_size.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();
Expand Down
8 changes: 5 additions & 3 deletions lib/module/mongo/mongo_sql_result.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ class MongoSqlResult {

MongoSqlResult(this.fieldName, this.data);

// column cannot empty
Set<String> get getFieldName {
if (fieldName.isEmpty) {
fieldName.add("");
}
return fieldName;
}

Expand All @@ -43,8 +47,6 @@ class MongoSqlResult {
}

factory MongoSqlResult.create() {
return MongoSqlResult(HashSet.identity(), [
['']
]);
return MongoSqlResult(HashSet.identity(), []);
}
}
49 changes: 49 additions & 0 deletions lib/module/util/csv_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//

import 'dart:io';

import 'package:csv/csv.dart';
import 'package:file_picker/file_picker.dart';

class CsvUtils {
static Future<bool> export(List<String> header, List<List<dynamic>> data) async {
try {
String? outputFile = await FilePicker.platform.saveFile(
dialogTitle: 'Please select an output file:',
fileName: 'dashboard-output-file',
allowedExtensions: ['csv'],
type: FileType.custom,
);

if (outputFile != null) {
File file = File(outputFile + ".csv");
List<List<dynamic>> rows = [];
rows.add(header);
rows.addAll(data);
String csv = const ListToCsvConverter().convert(rows);
file.writeAsString(csv);
return true;
}
} on Exception catch (e) {
throw Exception('Error to export ' + e.toString());
}
return false;
}
}
1 change: 1 addition & 0 deletions lib/route/page_route_const.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class PageRouteConst {
static const String Mongo = '/mongo';
static const String MongoInstance = '/mongo/instance';
static const String MongoDatabase = '/mongo/instance/database';
static const String MongoSql = '/mongo/instance/database/sql';
static const String MongoTable = '/mongo/instance/database/table';
static const String Mysql = '/mysql';
static const String MysqlInstance = '/mysql/instance';
Expand Down
30 changes: 26 additions & 4 deletions lib/ui/component/dynamic_filter_table.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,19 +132,37 @@ class _DynamicFilterTableState extends State<DynamicFilterTable> {
List<DropdownMenuItem<TYPE>> rs = [];
rs.add(new DropdownMenuItem(child: Text("TEXT"), value: TYPE.TEXT));
rs.add(new DropdownMenuItem(child: Text("NUMBER"), value: TYPE.NUMBER));
rs.add(new DropdownMenuItem(child: Text("ObjectId"), value: TYPE.OBJECT_ID));
return rs;
}

DataRow getDataRow(DropDownButtonData data) {
List<DataCell> cells = [];
List<DropdownMenuItem> columnItems =
_notifier.columns.map((e) => new DropdownMenuItem(child: Text(e.toString()), value: e)).toList();
DropdownMenuItem<String> customItem = new DropdownMenuItem(
child: new TextField(
decoration: InputDecoration(labelText: S.of(context).custom),
controller: new TextEditingController(text: rowData[data.index].custom ? rowData[data.index].column : ""),
onChanged: (text) {
rowData[data.index].column = text;
rowData[data.index].custom = true;
},
),
value: "",
);
columnItems.add(customItem);
DropdownButton itemButton = DropdownButton(
items: _notifier.columns.map((e) => new DropdownMenuItem(child: Text(e.toString()), value: e)).toList(),
items: columnItems,
onChanged: (value) {
setState(() {
rowData[data.index].column = value;
if (value != "") {
rowData[data.index].custom = false;
rowData[data.index].column = value;
}
});
},
value: rowData[data.index].column,
value: rowData[data.index].custom ? "" : rowData[data.index].column,
isExpanded: true,
);
DropdownButton opButton = DropdownButton(
Expand Down Expand Up @@ -211,16 +229,20 @@ class _DynamicFilterTableState extends State<DynamicFilterTable> {

enum OP { EQ, NOT_EQ, LT, GT, LT_EQ, GT_EQ, NULL, NOT_NULL, INCLUDE, EXCLUDE, BEGIN, END, CONTAIN }

enum TYPE { NUMBER, TEXT }
enum TYPE { NUMBER, TEXT, OBJECT_ID }

class DropDownButtonData {
// whether hide cell
bool hiddenValue = false;
int index;
String column;
OP op;
String? value;
// row connector: AND/OR
bool join;
TYPE type = TYPE.TEXT;
// is custom column
bool custom = false;

DropDownButtonData(this.column, this.op, this.index, this.join);
}
Expand Down
Loading

0 comments on commit ac99269

Please sign in to comment.