Frontend improvements for Databases plugin
Summary: Specs here: https://fb.quip.com/aPPOAWMraRMT Screenshots: https://pxl.cl/Gngf https://pxl.cl/Gngg https://pxl.cl/Gngd https://pxl.cl/GxQr https://pxl.cl/Gngh https://pxl.cl/Gngk https://pxl.cl/Gngl https://pxl.cl/Gngm Reviewed By: quanturium Differential Revision: D16266093 fbshipit-source-id: a5408b974875dcabcbd6055ccbb2818d0c1b25f6
This commit is contained in:
committed by
Facebook Github Bot
parent
dbf0e3db43
commit
fe56c8471c
@@ -1,7 +1,6 @@
|
||||
[ignore]
|
||||
.*/scripts/.*
|
||||
.*/coverage/.*
|
||||
.*/node_modules/.*
|
||||
.*/build/.*
|
||||
.*/dist/.*
|
||||
.*/static/.*
|
||||
@@ -9,6 +8,7 @@
|
||||
.*/website/.*
|
||||
<PROJECT_ROOT>/src/plugins/sections/d3/d3.js$
|
||||
.*\.tsx
|
||||
.*flow-typed/.*
|
||||
|
||||
[libs]
|
||||
flow-typed
|
||||
|
||||
@@ -173,11 +173,14 @@ public class ObjectMapper {
|
||||
DatabaseExecuteSqlResponse databaseExecuteSqlResponse) {
|
||||
|
||||
FlipperArray.Builder columnBuilder = new FlipperArray.Builder();
|
||||
if (databaseExecuteSqlResponse.columns != null) {
|
||||
for (String columnName : databaseExecuteSqlResponse.columns) {
|
||||
columnBuilder.put(columnName);
|
||||
}
|
||||
}
|
||||
|
||||
FlipperArray.Builder rowBuilder = new FlipperArray.Builder();
|
||||
if (databaseExecuteSqlResponse.values != null) {
|
||||
for (List<Object> row : databaseExecuteSqlResponse.values) {
|
||||
FlipperArray.Builder valueBuilder = new FlipperArray.Builder();
|
||||
for (Object item : row) {
|
||||
@@ -185,6 +188,7 @@ public class ObjectMapper {
|
||||
}
|
||||
rowBuilder.put(valueBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
return new FlipperObject.Builder()
|
||||
.put("type", databaseExecuteSqlResponse.type)
|
||||
|
||||
193
flow-typed/npm/sql-formatter_vx.x.x.js
vendored
Normal file
193
flow-typed/npm/sql-formatter_vx.x.x.js
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
// flow-typed signature: 21bf70a7f73a77579f4cc048adda0919
|
||||
// flow-typed version: <<STUB>>/sql-formatter_v2.3.3/flow_v0.102.0
|
||||
|
||||
/**
|
||||
* This is an autogenerated libdef stub for:
|
||||
*
|
||||
* 'sql-formatter'
|
||||
*
|
||||
* Fill this stub out by replacing all the `any` types.
|
||||
*
|
||||
* Once filled out, we encourage you to share your work with the
|
||||
* community by sending a pull request to:
|
||||
* https://github.com/flowtype/flow-typed
|
||||
*/
|
||||
|
||||
declare module 'sql-formatter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* We include stubs for each file inside this npm package in case you need to
|
||||
* require those files directly. Feel free to delete any files that aren't
|
||||
* needed.
|
||||
*/
|
||||
declare module 'sql-formatter/dist/sql-formatter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/dist/sql-formatter.min' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/lib/core/Formatter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/lib/core/Indentation' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/lib/core/InlineBlock' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/lib/core/Params' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/lib/core/Tokenizer' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/lib/core/tokenTypes' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/lib/languages/Db2Formatter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/lib/languages/N1qlFormatter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/lib/languages/PlSqlFormatter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/lib/languages/StandardSqlFormatter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/lib/sqlFormatter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/src/core/Formatter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/src/core/Indentation' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/src/core/InlineBlock' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/src/core/Params' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/src/core/Tokenizer' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/src/core/tokenTypes' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/src/languages/Db2Formatter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/src/languages/N1qlFormatter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/src/languages/PlSqlFormatter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/src/languages/StandardSqlFormatter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'sql-formatter/src/sqlFormatter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
// Filename aliases
|
||||
declare module 'sql-formatter/dist/sql-formatter.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/dist/sql-formatter'>;
|
||||
}
|
||||
declare module 'sql-formatter/dist/sql-formatter.min.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/dist/sql-formatter.min'>;
|
||||
}
|
||||
declare module 'sql-formatter/lib/core/Formatter.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/lib/core/Formatter'>;
|
||||
}
|
||||
declare module 'sql-formatter/lib/core/Indentation.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/lib/core/Indentation'>;
|
||||
}
|
||||
declare module 'sql-formatter/lib/core/InlineBlock.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/lib/core/InlineBlock'>;
|
||||
}
|
||||
declare module 'sql-formatter/lib/core/Params.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/lib/core/Params'>;
|
||||
}
|
||||
declare module 'sql-formatter/lib/core/Tokenizer.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/lib/core/Tokenizer'>;
|
||||
}
|
||||
declare module 'sql-formatter/lib/core/tokenTypes.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/lib/core/tokenTypes'>;
|
||||
}
|
||||
declare module 'sql-formatter/lib/languages/Db2Formatter.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/lib/languages/Db2Formatter'>;
|
||||
}
|
||||
declare module 'sql-formatter/lib/languages/N1qlFormatter.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/lib/languages/N1qlFormatter'>;
|
||||
}
|
||||
declare module 'sql-formatter/lib/languages/PlSqlFormatter.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/lib/languages/PlSqlFormatter'>;
|
||||
}
|
||||
declare module 'sql-formatter/lib/languages/StandardSqlFormatter.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/lib/languages/StandardSqlFormatter'>;
|
||||
}
|
||||
declare module 'sql-formatter/lib/sqlFormatter.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/lib/sqlFormatter'>;
|
||||
}
|
||||
declare module 'sql-formatter/src/core/Formatter.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/src/core/Formatter'>;
|
||||
}
|
||||
declare module 'sql-formatter/src/core/Indentation.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/src/core/Indentation'>;
|
||||
}
|
||||
declare module 'sql-formatter/src/core/InlineBlock.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/src/core/InlineBlock'>;
|
||||
}
|
||||
declare module 'sql-formatter/src/core/Params.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/src/core/Params'>;
|
||||
}
|
||||
declare module 'sql-formatter/src/core/Tokenizer.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/src/core/Tokenizer'>;
|
||||
}
|
||||
declare module 'sql-formatter/src/core/tokenTypes.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/src/core/tokenTypes'>;
|
||||
}
|
||||
declare module 'sql-formatter/src/languages/Db2Formatter.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/src/languages/Db2Formatter'>;
|
||||
}
|
||||
declare module 'sql-formatter/src/languages/N1qlFormatter.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/src/languages/N1qlFormatter'>;
|
||||
}
|
||||
declare module 'sql-formatter/src/languages/PlSqlFormatter.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/src/languages/PlSqlFormatter'>;
|
||||
}
|
||||
declare module 'sql-formatter/src/languages/StandardSqlFormatter.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/src/languages/StandardSqlFormatter'>;
|
||||
}
|
||||
declare module 'sql-formatter/src/sqlFormatter.js' {
|
||||
declare module.exports: $Exports<'sql-formatter/src/sqlFormatter'>;
|
||||
}
|
||||
@@ -48,6 +48,28 @@ type GetTableStructureResponse = {
|
||||
definition: string,
|
||||
};
|
||||
|
||||
type ExecuteSqlRequest = {
|
||||
databaseId: number,
|
||||
value: string,
|
||||
};
|
||||
|
||||
type ExecuteSqlResponse = {
|
||||
type: string,
|
||||
columns: Array<string>,
|
||||
values: Array<Array<Value>>,
|
||||
insertedId: number,
|
||||
affectedCount: number,
|
||||
};
|
||||
|
||||
type GetTableInfoRequest = {
|
||||
databaseId: number,
|
||||
table: string,
|
||||
};
|
||||
|
||||
type GetTableInfoResponse = {
|
||||
definition: string,
|
||||
};
|
||||
|
||||
export class DatabaseClient {
|
||||
client: PluginClient;
|
||||
|
||||
@@ -67,4 +89,12 @@ export class DatabaseClient {
|
||||
GetTableStructureRequest,
|
||||
GetTableStructureResponse,
|
||||
> = params => this.client.call('getTableStructure', params);
|
||||
|
||||
getExecution: ClientCall<ExecuteSqlRequest, ExecuteSqlResponse> = params =>
|
||||
this.client.call('execute', params);
|
||||
|
||||
getTableInfo: ClientCall<
|
||||
GetTableInfoRequest,
|
||||
GetTableInfoResponse,
|
||||
> = params => this.client.call('getTableInfo', params);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,11 @@ import {
|
||||
Input,
|
||||
colors,
|
||||
getStringFromErrorLike,
|
||||
Spacer,
|
||||
Textarea,
|
||||
DetailSidebar,
|
||||
Panel,
|
||||
ManagedDataInspector,
|
||||
} from 'flipper';
|
||||
import {Component} from 'react';
|
||||
import type {
|
||||
@@ -29,6 +34,8 @@ import {DatabaseClient} from './ClientProtocol';
|
||||
import {renderValue} from 'flipper';
|
||||
import type {Value} from 'flipper';
|
||||
import ButtonNavigation from './ButtonNavigation';
|
||||
import sqlFormatter from 'sql-formatter';
|
||||
import dateFormat from 'dateformat';
|
||||
|
||||
const PAGE_SIZE = 50;
|
||||
|
||||
@@ -51,11 +58,17 @@ type DatabasesPluginState = {|
|
||||
pageRowNumber: number,
|
||||
databases: Array<DatabaseEntry>,
|
||||
outdatedDatabaseList: boolean,
|
||||
viewMode: 'data' | 'structure',
|
||||
viewMode: 'data' | 'structure' | 'SQL' | 'tableInfo' | 'queryHistory',
|
||||
error: ?null,
|
||||
currentPage: ?Page,
|
||||
currentStructure: ?Structure,
|
||||
currentSort: ?TableRowSortOrder,
|
||||
query: ?Query,
|
||||
queryResult: ?QueryResult,
|
||||
favorites: Array<string>,
|
||||
executionTime: number,
|
||||
tableInfo: string,
|
||||
queryHistory: Array<Query>,
|
||||
|};
|
||||
|
||||
type Page = {
|
||||
@@ -77,6 +90,18 @@ type Structure = {|
|
||||
indexesValues: Array<TableBodyRow>,
|
||||
|};
|
||||
|
||||
type QueryResult = {
|
||||
table: ?QueriedTable,
|
||||
id: ?number,
|
||||
count: ?number,
|
||||
};
|
||||
|
||||
type QueriedTable = {
|
||||
columns: Array<string>,
|
||||
rows: Array<TableBodyRow>,
|
||||
highlightedRows: Array<number>,
|
||||
};
|
||||
|
||||
type Actions =
|
||||
| SelectDatabaseEvent
|
||||
| SelectDatabaseTableEvent
|
||||
@@ -84,11 +109,18 @@ type Actions =
|
||||
| UpdateViewModeEvent
|
||||
| UpdatePageEvent
|
||||
| UpdateStructureEvent
|
||||
| DisplaySelectEvent
|
||||
| DisplayInsertEvent
|
||||
| DisplayUpdateDeleteEvent
|
||||
| UpdateTableInfoEvent
|
||||
| NextPageEvent
|
||||
| PreviousPageEvent
|
||||
| ExecuteEvent
|
||||
| RefreshEvent
|
||||
| UpdateFavoritesEvent
|
||||
| SortByChangedEvent
|
||||
| GoToRowEvent;
|
||||
| GoToRowEvent
|
||||
| UpdateQueryEvent;
|
||||
|
||||
type DatabaseEntry = {
|
||||
id: number,
|
||||
@@ -96,6 +128,11 @@ type DatabaseEntry = {
|
||||
tables: Array<string>,
|
||||
};
|
||||
|
||||
type Query = {|
|
||||
value: string,
|
||||
time: string,
|
||||
|};
|
||||
|
||||
type UpdateDatabasesEvent = {|
|
||||
databases: Array<{name: string, id: number, tables: Array<string>}>,
|
||||
type: 'UpdateDatabases',
|
||||
@@ -113,7 +150,7 @@ type SelectDatabaseTableEvent = {|
|
||||
|
||||
type UpdateViewModeEvent = {|
|
||||
type: 'UpdateViewMode',
|
||||
viewMode: 'data' | 'structure',
|
||||
viewMode: 'data' | 'structure' | 'SQL' | 'tableInfo' | 'queryHistory',
|
||||
|};
|
||||
|
||||
type UpdatePageEvent = {|
|
||||
@@ -137,6 +174,27 @@ type UpdateStructureEvent = {|
|
||||
indexesValues: Array<Array<any>>,
|
||||
|};
|
||||
|
||||
type DisplaySelectEvent = {|
|
||||
type: 'DisplaySelect',
|
||||
columns: Array<string>,
|
||||
values: Array<Array<any>>,
|
||||
|};
|
||||
|
||||
type DisplayInsertEvent = {|
|
||||
type: 'DisplayInsert',
|
||||
id: number,
|
||||
|};
|
||||
|
||||
type DisplayUpdateDeleteEvent = {|
|
||||
type: 'DisplayUpdateDelete',
|
||||
count: number,
|
||||
|};
|
||||
|
||||
type UpdateTableInfoEvent = {|
|
||||
type: 'UpdateTableInfo',
|
||||
tableInfo: string,
|
||||
|};
|
||||
|
||||
type NextPageEvent = {
|
||||
type: 'NextPage',
|
||||
};
|
||||
@@ -145,10 +203,18 @@ type PreviousPageEvent = {
|
||||
type: 'PreviousPage',
|
||||
};
|
||||
|
||||
type ExecuteEvent = {
|
||||
type: 'Execute',
|
||||
};
|
||||
|
||||
type RefreshEvent = {
|
||||
type: 'Refresh',
|
||||
};
|
||||
|
||||
type UpdateFavoritesEvent = {
|
||||
type: 'UpdateFavorites',
|
||||
};
|
||||
|
||||
type SortByChangedEvent = {
|
||||
type: 'SortByChanged',
|
||||
sortOrder: TableRowSortOrder,
|
||||
@@ -159,6 +225,11 @@ type GoToRowEvent = {
|
||||
row: number,
|
||||
};
|
||||
|
||||
type UpdateQueryEvent = {
|
||||
type: 'UpdateQuery',
|
||||
value: string,
|
||||
};
|
||||
|
||||
function transformRow(
|
||||
columns: Array<string>,
|
||||
row: Array<Value>,
|
||||
@@ -250,6 +321,47 @@ function renderDatabaseIndexes(structure: ?Structure) {
|
||||
);
|
||||
}
|
||||
|
||||
function renderQueryHistory(history: Array<Query>) {
|
||||
if (!history || typeof history === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
const columns = {
|
||||
time: {
|
||||
value: 'Time',
|
||||
resizable: true,
|
||||
},
|
||||
query: {
|
||||
value: 'Query',
|
||||
resizable: true,
|
||||
},
|
||||
};
|
||||
const rows = [];
|
||||
if (history.length > 0) {
|
||||
for (const query of history) {
|
||||
const time = query.time;
|
||||
const value = query.value;
|
||||
rows.push({
|
||||
key: query,
|
||||
columns: {time: {value: time}, query: {value: value}},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<FlexRow grow={true}>
|
||||
<ManagedTable
|
||||
style={{paddingLeft: 16}}
|
||||
floating={false}
|
||||
columns={columns}
|
||||
columnSizes={{time: 75}}
|
||||
zebra={true}
|
||||
rows={rows}
|
||||
horizontallyScrollable={true}
|
||||
/>
|
||||
</FlexRow>
|
||||
);
|
||||
}
|
||||
|
||||
type PageInfoProps = {
|
||||
currentRow: number,
|
||||
count: number,
|
||||
@@ -277,7 +389,6 @@ class PageInfo extends Component<
|
||||
onSubmit(e: SyntheticKeyboardEvent<>) {
|
||||
if (e.key === 'Enter') {
|
||||
const rowNumber = parseInt(this.state.inputValue, 10);
|
||||
console.log(rowNumber);
|
||||
this.props.onChange(rowNumber - 1, this.props.count);
|
||||
this.setState({isOpen: false});
|
||||
}
|
||||
@@ -331,6 +442,12 @@ export default class DatabasesPlugin extends FlipperPlugin<
|
||||
currentPage: null,
|
||||
currentStructure: null,
|
||||
currentSort: null,
|
||||
query: null,
|
||||
queryResult: null,
|
||||
favorites: [],
|
||||
executionTime: 0,
|
||||
tableInfo: '',
|
||||
queryHistory: [],
|
||||
};
|
||||
|
||||
reducers = [
|
||||
@@ -414,6 +531,7 @@ export default class DatabasesPlugin extends FlipperPlugin<
|
||||
return {
|
||||
...state,
|
||||
viewMode: event.viewMode,
|
||||
error: null,
|
||||
};
|
||||
},
|
||||
],
|
||||
@@ -458,6 +576,73 @@ export default class DatabasesPlugin extends FlipperPlugin<
|
||||
};
|
||||
},
|
||||
],
|
||||
[
|
||||
'DisplaySelect',
|
||||
(
|
||||
state: DatabasesPluginState,
|
||||
event: DisplaySelectEvent,
|
||||
): DatabasesPluginState => {
|
||||
return {
|
||||
...state,
|
||||
queryResult: {
|
||||
table: {
|
||||
columns: event.columns,
|
||||
rows: event.values.map((row: Array<Value>, index: number) =>
|
||||
transformRow(event.columns, row, index),
|
||||
),
|
||||
highlightedRows: [],
|
||||
},
|
||||
id: null,
|
||||
count: null,
|
||||
},
|
||||
};
|
||||
},
|
||||
],
|
||||
[
|
||||
'DisplayInsert',
|
||||
(
|
||||
state: DatabasesPluginState,
|
||||
event: DisplayInsertEvent,
|
||||
): DatabasesPluginState => {
|
||||
return {
|
||||
...state,
|
||||
queryResult: {
|
||||
table: null,
|
||||
id: event.id,
|
||||
count: null,
|
||||
},
|
||||
};
|
||||
},
|
||||
],
|
||||
|
||||
[
|
||||
'DisplayUpdateDelete',
|
||||
(
|
||||
state: DatabasesPluginState,
|
||||
event: DisplayUpdateDeleteEvent,
|
||||
): DatabasesPluginState => {
|
||||
return {
|
||||
...state,
|
||||
queryResult: {
|
||||
table: null,
|
||||
id: null,
|
||||
count: event.count,
|
||||
},
|
||||
};
|
||||
},
|
||||
],
|
||||
[
|
||||
'UpdateTableInfo',
|
||||
(
|
||||
state: DatabasesPluginState,
|
||||
event: UpdateTableInfoEvent,
|
||||
): DatabasesPluginState => {
|
||||
return {
|
||||
...state,
|
||||
tableInfo: event.tableInfo,
|
||||
};
|
||||
},
|
||||
],
|
||||
[
|
||||
'NextPage',
|
||||
(
|
||||
@@ -484,6 +669,66 @@ export default class DatabasesPlugin extends FlipperPlugin<
|
||||
};
|
||||
},
|
||||
],
|
||||
[
|
||||
'Execute',
|
||||
(
|
||||
state: DatabasesPluginState,
|
||||
results: ExecuteEvent,
|
||||
): DatabasesPluginState => {
|
||||
const timeBefore = Date.now();
|
||||
if (
|
||||
this.state.query !== null &&
|
||||
typeof this.state.query !== 'undefined'
|
||||
) {
|
||||
this.databaseClient
|
||||
.getExecution({
|
||||
databaseId: state.selectedDatabase,
|
||||
value: this.state.query.value,
|
||||
})
|
||||
.then(data => {
|
||||
this.setState({
|
||||
error: null,
|
||||
executionTime: Date.now() - timeBefore,
|
||||
});
|
||||
if (data.type === 'select') {
|
||||
this.dispatchAction({
|
||||
type: 'DisplaySelect',
|
||||
columns: data.columns,
|
||||
values: data.values,
|
||||
});
|
||||
} else if (data.type === 'insert') {
|
||||
this.dispatchAction({
|
||||
type: 'DisplayInsert',
|
||||
id: data.insertedId,
|
||||
});
|
||||
} else if (data.type === 'update_delete') {
|
||||
this.dispatchAction({
|
||||
type: 'DisplayUpdateDelete',
|
||||
count: data.affectedCount,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
this.setState({error: e});
|
||||
});
|
||||
}
|
||||
let newHistory = this.state.queryHistory;
|
||||
const newQuery = this.state.query;
|
||||
if (
|
||||
newQuery !== null &&
|
||||
typeof newQuery !== 'undefined' &&
|
||||
newHistory !== null &&
|
||||
typeof newHistory !== 'undefined'
|
||||
) {
|
||||
newQuery.time = dateFormat(new Date(), 'hh:MM:ss');
|
||||
newHistory = newHistory.concat(newQuery);
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
queryHistory: newHistory,
|
||||
};
|
||||
},
|
||||
],
|
||||
[
|
||||
'GoToRow',
|
||||
(
|
||||
@@ -519,6 +764,32 @@ export default class DatabasesPlugin extends FlipperPlugin<
|
||||
};
|
||||
},
|
||||
],
|
||||
[
|
||||
'UpdateFavorites',
|
||||
(
|
||||
state: DatabasesPluginState,
|
||||
event: UpdateFavoritesEvent,
|
||||
): DatabasesPluginState => {
|
||||
let newFavorites = state.favorites;
|
||||
if (
|
||||
state.query &&
|
||||
state.query !== null &&
|
||||
typeof state.query !== 'undefined'
|
||||
) {
|
||||
const value = state.query.value;
|
||||
if (newFavorites.includes(value)) {
|
||||
const index = newFavorites.indexOf(value);
|
||||
newFavorites.splice(index, 1);
|
||||
} else {
|
||||
newFavorites = state.favorites.concat(value);
|
||||
}
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
favorites: newFavorites,
|
||||
};
|
||||
},
|
||||
],
|
||||
[
|
||||
'UpdateViewMode',
|
||||
(
|
||||
@@ -528,6 +799,7 @@ export default class DatabasesPlugin extends FlipperPlugin<
|
||||
return {
|
||||
...state,
|
||||
viewMode: event.viewMode,
|
||||
error: null,
|
||||
};
|
||||
},
|
||||
],
|
||||
@@ -542,6 +814,18 @@ export default class DatabasesPlugin extends FlipperPlugin<
|
||||
};
|
||||
},
|
||||
],
|
||||
[
|
||||
'UpdateQuery',
|
||||
(state: DatabasesPluginState, event: UpdateQueryEvent) => {
|
||||
return {
|
||||
...state,
|
||||
query: {
|
||||
value: event.value,
|
||||
time: dateFormat(new Date(), 'hh:MM:ss'),
|
||||
},
|
||||
};
|
||||
},
|
||||
],
|
||||
].reduce((acc, val) => {
|
||||
const name = val[0];
|
||||
const f = val[1];
|
||||
@@ -576,7 +860,6 @@ export default class DatabasesPlugin extends FlipperPlugin<
|
||||
start: newState.pageRowNumber,
|
||||
})
|
||||
.then(data => {
|
||||
console.log(data);
|
||||
this.dispatchAction({
|
||||
type: 'UpdatePage',
|
||||
databaseId: databaseId,
|
||||
@@ -604,7 +887,6 @@ export default class DatabasesPlugin extends FlipperPlugin<
|
||||
table: table,
|
||||
})
|
||||
.then(data => {
|
||||
console.log(data);
|
||||
this.dispatchAction({
|
||||
type: 'UpdateStructure',
|
||||
databaseId: databaseId,
|
||||
@@ -619,6 +901,28 @@ export default class DatabasesPlugin extends FlipperPlugin<
|
||||
this.setState({error: e});
|
||||
});
|
||||
}
|
||||
if (
|
||||
newState.viewMode === 'tableInfo' &&
|
||||
newState.currentStructure === null &&
|
||||
databaseId &&
|
||||
table
|
||||
) {
|
||||
this.databaseClient
|
||||
.getTableInfo({
|
||||
databaseId: databaseId,
|
||||
table: table,
|
||||
})
|
||||
.then(data => {
|
||||
this.dispatchAction({
|
||||
type: 'UpdateTableInfo',
|
||||
tableInfo: data.definition,
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
this.setState({error: e});
|
||||
});
|
||||
}
|
||||
|
||||
if (!previousState.outdatedDatabaseList && newState.outdatedDatabaseList) {
|
||||
this.databaseClient.getDatabases({}).then(databases => {
|
||||
this.dispatchAction({
|
||||
@@ -647,10 +951,29 @@ export default class DatabasesPlugin extends FlipperPlugin<
|
||||
this.dispatchAction({type: 'UpdateViewMode', viewMode: 'structure'});
|
||||
};
|
||||
|
||||
onSQLClicked = () => {
|
||||
this.dispatchAction({type: 'UpdateViewMode', viewMode: 'SQL'});
|
||||
};
|
||||
|
||||
onTableInfoClicked = () => {
|
||||
this.dispatchAction({type: 'UpdateViewMode', viewMode: 'tableInfo'});
|
||||
};
|
||||
|
||||
onQueryHistoryClicked = () => {
|
||||
this.dispatchAction({type: 'UpdateViewMode', viewMode: 'queryHistory'});
|
||||
};
|
||||
|
||||
onRefreshClicked = () => {
|
||||
this.setState({error: null});
|
||||
this.dispatchAction({type: 'Refresh'});
|
||||
};
|
||||
|
||||
onFavoritesClicked = () => {
|
||||
this.dispatchAction({
|
||||
type: 'UpdateFavorites',
|
||||
});
|
||||
};
|
||||
|
||||
onDatabaseSelected = (selected: string) => {
|
||||
const dbId = this.state.databases.find(x => x.name === selected)?.id || 0;
|
||||
this.dispatchAction({
|
||||
@@ -674,10 +997,25 @@ export default class DatabasesPlugin extends FlipperPlugin<
|
||||
this.dispatchAction({type: 'PreviousPage'});
|
||||
};
|
||||
|
||||
onExecuteClicked = () => {
|
||||
this.dispatchAction({type: 'Execute'});
|
||||
};
|
||||
|
||||
onFavoriteClicked = (selected: any) => {
|
||||
this.setState({query: selected.target.value});
|
||||
};
|
||||
|
||||
onGoToRow = (row: number, count: number) => {
|
||||
this.dispatchAction({type: 'GoToRow', row: row});
|
||||
};
|
||||
|
||||
onQueryChanged = (selected: any) => {
|
||||
this.dispatchAction({
|
||||
type: 'UpdateQuery',
|
||||
value: selected.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
renderStructure() {
|
||||
return [
|
||||
renderDatabaseColumns(this.state.currentStructure),
|
||||
@@ -685,6 +1023,153 @@ export default class DatabasesPlugin extends FlipperPlugin<
|
||||
];
|
||||
}
|
||||
|
||||
renderSidebar = (table: QueriedTable) => {
|
||||
if (
|
||||
table.highlightedRows === null ||
|
||||
typeof table.highlightedRows === 'undefined' ||
|
||||
table.highlightedRows.length !== 1
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const id = table.highlightedRows[0];
|
||||
const cols = {
|
||||
col: {
|
||||
value: 'Column',
|
||||
resizable: true,
|
||||
},
|
||||
val: {
|
||||
value: 'Value',
|
||||
resizable: true,
|
||||
},
|
||||
};
|
||||
const colSizes = {
|
||||
col: '35%',
|
||||
val: 'flex',
|
||||
};
|
||||
return (
|
||||
<DetailSidebar width={500}>
|
||||
<Panel
|
||||
padded={true}
|
||||
heading="Row details"
|
||||
floating={false}
|
||||
collapsable={true}
|
||||
grow={true}>
|
||||
<ManagedTable
|
||||
highlightableRows={false}
|
||||
columnSizes={colSizes}
|
||||
multiline={true}
|
||||
columns={cols}
|
||||
autoHeight={true}
|
||||
floating={false}
|
||||
zebra={true}
|
||||
rows={this.sidebarRows(id, table)}
|
||||
/>
|
||||
</Panel>
|
||||
</DetailSidebar>
|
||||
);
|
||||
};
|
||||
|
||||
sidebarRows = (id: number, table: QueriedTable) => {
|
||||
const columns = table.columns;
|
||||
const row = table.rows[id];
|
||||
const sidebarArray = [];
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
sidebarArray.push(
|
||||
this.buildSidebarRow(columns[i], row.columns[columns[i]].value),
|
||||
);
|
||||
}
|
||||
return sidebarArray;
|
||||
};
|
||||
|
||||
buildSidebarRow = (key: string, val: any) => {
|
||||
let output = '';
|
||||
try {
|
||||
output = (
|
||||
<ManagedDataInspector
|
||||
data={JSON.parse(val.props.children)}
|
||||
expandRoot={true}
|
||||
/>
|
||||
);
|
||||
} catch (error) {
|
||||
output = val;
|
||||
}
|
||||
return {
|
||||
columns: {
|
||||
col: {value: <Text>{key}</Text>},
|
||||
val: {
|
||||
value: output,
|
||||
},
|
||||
},
|
||||
key: key,
|
||||
};
|
||||
};
|
||||
|
||||
renderQuery(query: ?QueryResult) {
|
||||
if (!query || query === null) {
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
query.table &&
|
||||
typeof query.table !== 'undefined' &&
|
||||
query.table !== null
|
||||
) {
|
||||
const table = query.table;
|
||||
const columns = table.columns;
|
||||
const rows = table.rows;
|
||||
return (
|
||||
<FlexRow grow={true} style={{paddingTop: 18}}>
|
||||
<ManagedTable
|
||||
floating={false}
|
||||
multiline={true}
|
||||
columnOrder={columns.map(name => ({
|
||||
key: name,
|
||||
visible: true,
|
||||
}))}
|
||||
columns={columns.reduce((acc, val) => {
|
||||
acc[val] = {value: val, resizable: true};
|
||||
return acc;
|
||||
}, {})}
|
||||
zebra={true}
|
||||
rows={rows}
|
||||
horizontallyScrollable={true}
|
||||
onRowHighlighted={highlightedRows => {
|
||||
this.setState({
|
||||
queryResult: {
|
||||
table: {
|
||||
columns: columns,
|
||||
rows: rows,
|
||||
highlightedRows: highlightedRows,
|
||||
},
|
||||
id: null,
|
||||
count: null,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{this.renderSidebar(table)}
|
||||
</FlexRow>
|
||||
);
|
||||
} else if (query.id && query.id !== null) {
|
||||
return (
|
||||
<FlexRow grow={true} style={{paddingTop: 18}}>
|
||||
<Text style={{paddingTop: 8, paddingLeft: 8}}>
|
||||
Row id: {query.id}
|
||||
</Text>
|
||||
</FlexRow>
|
||||
);
|
||||
} else if (query.count && query.count !== null) {
|
||||
return (
|
||||
<FlexRow grow={true} style={{paddingTop: 18}}>
|
||||
<Text style={{paddingTop: 8, paddingLeft: 8}}>
|
||||
Rows affected: {query.count}
|
||||
</Text>
|
||||
</FlexRow>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const tableOptions =
|
||||
(this.state.selectedDatabase &&
|
||||
@@ -697,7 +1182,45 @@ export default class DatabasesPlugin extends FlipperPlugin<
|
||||
|
||||
return (
|
||||
<FlexColumn style={{flex: 1}}>
|
||||
<Toolbar position="top" style={{paddingLeft: 8}}>
|
||||
<Toolbar position="top" style={{paddingLeft: 16}}>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
icon={'data-table'}
|
||||
onClick={this.onDataClicked}
|
||||
selected={this.state.viewMode === 'data'}>
|
||||
Data
|
||||
</Button>
|
||||
<Button
|
||||
icon={'gears-two'}
|
||||
onClick={this.onStructureClicked}
|
||||
selected={this.state.viewMode === 'structure'}>
|
||||
Structure
|
||||
</Button>
|
||||
<Button
|
||||
icon={'magnifying-glass'}
|
||||
onClick={this.onSQLClicked}
|
||||
selected={this.state.viewMode === 'SQL'}>
|
||||
SQL
|
||||
</Button>
|
||||
<Button
|
||||
icon={'info-cursive'}
|
||||
onClick={this.onTableInfoClicked}
|
||||
selected={this.state.viewMode === 'tableInfo'}>
|
||||
Table Info
|
||||
</Button>
|
||||
<Button
|
||||
icon={'on-this-day'}
|
||||
iconSize={12}
|
||||
onClick={this.onQueryHistoryClicked}
|
||||
selected={this.state.viewMode === 'queryHistory'}>
|
||||
Query History
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Toolbar>
|
||||
{this.state.viewMode === 'data' ||
|
||||
this.state.viewMode === 'structure' ||
|
||||
this.state.viewMode === 'tableInfo' ? (
|
||||
<Toolbar position="top" style={{paddingLeft: 16}}>
|
||||
<BoldSpan style={{marginRight: 16}}>Database</BoldSpan>
|
||||
<Select
|
||||
options={this.state.databases
|
||||
@@ -719,31 +1242,117 @@ export default class DatabasesPlugin extends FlipperPlugin<
|
||||
/>
|
||||
<div />
|
||||
<Button onClick={this.onRefreshClicked}>Refresh</Button>
|
||||
<Button style={{marginLeft: 'auto', display: 'none'}}>
|
||||
Execute SQL
|
||||
</Button>
|
||||
</Toolbar>
|
||||
) : null}
|
||||
{this.state.viewMode === 'SQL' ? (
|
||||
<div>
|
||||
<Toolbar position="top" style={{paddingLeft: 16}}>
|
||||
<BoldSpan style={{marginRight: 16}}>Database</BoldSpan>
|
||||
<Select
|
||||
options={this.state.databases
|
||||
.map(x => x.name)
|
||||
.reduce((obj, item) => {
|
||||
obj[item] = item;
|
||||
return obj;
|
||||
}, {})}
|
||||
selected={
|
||||
this.state.databases[this.state.selectedDatabase - 1]?.name
|
||||
}
|
||||
onChange={this.onDatabaseSelected}
|
||||
/>
|
||||
</Toolbar>
|
||||
{
|
||||
<Textarea
|
||||
style={{
|
||||
width: '98%',
|
||||
height: '40%',
|
||||
marginLeft: 16,
|
||||
marginTop: '1%',
|
||||
marginBottom: '1%',
|
||||
}}
|
||||
onChange={this.onQueryChanged.bind(this)}
|
||||
placeholder="Type query here.."
|
||||
value={
|
||||
this.state.query !== null &&
|
||||
typeof this.state.query !== 'undefined'
|
||||
? this.state.query.value
|
||||
: null
|
||||
}
|
||||
/>
|
||||
}
|
||||
<Toolbar
|
||||
position="top"
|
||||
style={{paddingLeft: 16, paddingTop: 24, paddingBottom: 24}}>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
icon={'star'}
|
||||
iconSize={14}
|
||||
iconVariant={
|
||||
this.state.query !== null &&
|
||||
typeof this.state.query !== 'undefined' &&
|
||||
this.state.favorites.includes(this.state.query.value)
|
||||
? 'filled'
|
||||
: 'outline'
|
||||
}
|
||||
onClick={this.onFavoritesClicked}
|
||||
/>
|
||||
{this.state.favorites !== null ? (
|
||||
<Button
|
||||
dropdown={this.state.favorites.map(option => {
|
||||
return {
|
||||
click: () => {
|
||||
this.setState({
|
||||
query: {
|
||||
value: option,
|
||||
time: dateFormat(new Date(), 'hh:MM:ss'),
|
||||
},
|
||||
});
|
||||
this.onQueryChanged.bind(this);
|
||||
},
|
||||
label: option,
|
||||
};
|
||||
})}>
|
||||
Choose from previous queries
|
||||
</Button>
|
||||
) : null}
|
||||
</ButtonGroup>
|
||||
<Spacer />
|
||||
<ButtonGroup>
|
||||
<Button onClick={this.onExecuteClicked}>Execute</Button>
|
||||
</ButtonGroup>
|
||||
</Toolbar>
|
||||
</div>
|
||||
) : null}
|
||||
<FlexColumn grow={true}>
|
||||
{this.state.viewMode === 'data'
|
||||
? renderTable(this.state.currentPage, this)
|
||||
: this.renderStructure()}
|
||||
: null}
|
||||
{this.state.viewMode === 'structure' ? this.renderStructure() : null}
|
||||
{this.state.viewMode === 'SQL'
|
||||
? this.renderQuery(this.state.queryResult)
|
||||
: null}
|
||||
{this.state.viewMode === 'tableInfo' ? (
|
||||
<Textarea
|
||||
style={{
|
||||
width: '98%',
|
||||
height: '100%',
|
||||
marginLeft: '1%',
|
||||
marginTop: '1%',
|
||||
marginBottom: '1%',
|
||||
readOnly: true,
|
||||
}}
|
||||
value={sqlFormatter.format(this.state.tableInfo)}
|
||||
/>
|
||||
) : null}
|
||||
{this.state.viewMode === 'queryHistory'
|
||||
? renderQueryHistory(this.state.queryHistory)
|
||||
: null}
|
||||
</FlexColumn>
|
||||
<Toolbar position="bottom" style={{paddingLeft: 8}}>
|
||||
<FlexRow grow={true}>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
icon={'data-table'}
|
||||
onClick={this.onDataClicked}
|
||||
selected={this.state.viewMode === 'data'}>
|
||||
Data
|
||||
</Button>
|
||||
<Button
|
||||
icon={'gears-two'}
|
||||
onClick={this.onStructureClicked}
|
||||
selected={this.state.viewMode === 'structure'}>
|
||||
Structure
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
{this.state.viewMode === 'SQL' && this.state.executionTime !== 0 ? (
|
||||
<Text> {this.state.executionTime} ms </Text>
|
||||
) : null}
|
||||
{this.state.viewMode === 'data' && this.state.currentPage ? (
|
||||
<PageInfo
|
||||
currentRow={this.state.currentPage.start}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sql-formatter": "^2.3.3",
|
||||
"dateformat": "^3.0.3"
|
||||
},
|
||||
"bugs": {
|
||||
"email": "oncall+flipper@xmail.facebook.com",
|
||||
|
||||
@@ -2,3 +2,19 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
dateformat@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
|
||||
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
|
||||
|
||||
lodash@^4.16.0:
|
||||
version "4.17.14"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba"
|
||||
integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==
|
||||
|
||||
sql-formatter@^2.3.3:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/sql-formatter/-/sql-formatter-2.3.3.tgz#910ef484fbb988a5e510bea4161157e3b80b2f62"
|
||||
integrity sha512-m6pqVXwsm9GkCHC/+gdPvNowI7PNoVTT6OZMWKwXJoP2MvfntfhcfyliIf4/QX6t+DirSJ6XDSiSS70YvZ87Lw==
|
||||
dependencies:
|
||||
lodash "^4.16.0"
|
||||
|
||||
Reference in New Issue
Block a user