Skip to content
This repository was archived by the owner on Apr 28, 2024. It is now read-only.

Commit efaaf83

Browse files
authored
Merge pull request #102 from slashbaseide/develop
Merge develop: New version
2 parents 7cba442 + f995aca commit efaaf83

File tree

19 files changed

+173
-58
lines changed

19 files changed

+173
-58
lines changed

frontend/src/components/dbfragments/showdata.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ const DBShowDataFragment = (_: DBShowDataPropType) => {
5959
}, [queryFilter])
6060

6161

62-
const fetchData = async (fetchCount: boolean) => {
62+
const fetchData = async (isFirstFetch: boolean) => {
6363
if (!dataModel) return
6464
try {
65-
const result = await dispatch(getDBDataInDataModel({ tabId: currentTab.id, dbConnectionId: dbConnection!.id, schemaName: dataModel!.schemaName ?? '', name: dataModel!.name, queryLimit, queryOffset, fetchCount, queryFilter, querySort })).unwrap()
66-
if (fetchCount) {
65+
const result = await dispatch(getDBDataInDataModel({ tabId: currentTab.id, dbConnectionId: dbConnection!.id, schemaName: dataModel!.schemaName ?? '', name: dataModel!.name, queryLimit, queryOffset, isFirstFetch, queryFilter, querySort })).unwrap()
66+
if (isFirstFetch) {
6767
setQueryCount(result.data.count)
6868
}
6969
} catch (e) {

frontend/src/components/dbfragments/table/editablecell.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const EditableCell = ({
3434
}
3535

3636
const onSave = async () => {
37-
onSaveCell(original["0"], id, value)
37+
onSaveCell(index, original, id, value)
3838
}
3939

4040
const isEditingCell = editCell.length == 2 && editCell[0] === index && editCell[1] === id

frontend/src/components/dbfragments/table/table.tsx

+35-11
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import styles from './table.module.scss'
2-
import React, { useState, useRef, useContext } from 'react'
2+
import React, { useState, useContext } from 'react'
33
import { Cell, useRowSelect, useTable } from 'react-table'
44
import toast from 'react-hot-toast'
55
import { DBConnection, DBQueryData, Tab } from '../../../data/models'
66
import EditableCell from './editablecell'
77
import AddModal from './addmodal'
88
import ConfirmModal from '../../widgets/confirmModal'
9-
import { useAppDispatch, useAppSelector } from '../../../redux/hooks'
9+
import { useAppDispatch } from '../../../redux/hooks'
1010
import { deleteDBData, setQueryData, updateDBSingleData } from '../../../redux/dataModelSlice'
1111
import { DBConnType } from '../../../data/defaults'
1212
import TabContext from '../../layouts/tabcontext'
@@ -41,7 +41,7 @@ const Table = ({ queryData, dbConnection, mSchema, mName, isEditable, showHeader
4141
[queryData]
4242
)
4343

44-
const displayColumns = queryData.columns.filter(col => col !== 'ctid')
44+
const displayColumns = dbConnection.type === DBConnType.POSTGRES ? queryData.columns.filter(col => col !== 'ctid') : queryData.columns
4545
const ctidExists = queryData.columns.length !== displayColumns.length
4646

4747
const columns = React.useMemo(
@@ -65,14 +65,21 @@ const Table = ({ queryData, dbConnection, mSchema, mName, isEditable, showHeader
6565
setEditCell([])
6666
}
6767

68-
const onSaveCell = async (ctid: string, columnIdx: string, newValue: string) => {
68+
const onSaveCell = async (rowIdx: number, originalValue: any, columnIdx: string, newValue: string) => {
69+
if (dbConnection.type === DBConnType.MYSQL && queryData.pkeys?.length === 0) {
70+
return toast.error("to perform edit operation primary keys are required on the table!")
71+
}
6972
const columnName = queryData.columns[parseInt(columnIdx)]
70-
const result = await dispatch(updateDBSingleData({ dbConnectionId: dbConnection.id, schemaName: mSchema, name: mName, id: ctid, columnName, newValue, columnIdx })).unwrap()
73+
const uniqueId = dbConnection.type === DBConnType.POSTGRES ? originalValue["0"] : JSON.stringify(queryData.pkeys!.map((pkey) => ({ [pkey]: originalValue[queryData.columns.findIndex(x => x === pkey)] })).reduce(((r, c) => Object.assign(r, c)), {}))
74+
const result = await dispatch(updateDBSingleData({ dbConnectionId: dbConnection.id, schemaName: mSchema, name: mName, id: uniqueId, columnName, newValue, columnIdx })).unwrap()
7175
if (result.success) {
72-
const rowIdx = queryData!.rows.findIndex(x => x["0"] === ctid)
7376
if (rowIdx !== -1) {
7477
const newQueryData: DBQueryData = { ...queryData!, rows: [...queryData!.rows] }
75-
newQueryData!.rows[rowIdx] = { ...newQueryData!.rows[rowIdx], 0: result.data.ctid }
78+
if (dbConnection.type === DBConnType.POSTGRES) {
79+
newQueryData!.rows[rowIdx] = { ...newQueryData!.rows[rowIdx], 0: result.data.ctid }
80+
} else {
81+
newQueryData!.rows[rowIdx] = { ...newQueryData!.rows[rowIdx] }
82+
}
7683
newQueryData!.rows[rowIdx][columnIdx] = newValue
7784
dispatch(setQueryData({ data: newQueryData, tabId: activeTab.id }))
7885
} else {
@@ -116,11 +123,16 @@ const Table = ({ queryData, dbConnection, mSchema, mName, isEditable, showHeader
116123
const newState: any = state // temporary typescript hack
117124
const selectedRowIds: any = newState.selectedRowIds
118125
const selectedRows: number[] = Object.keys(selectedRowIds).map(x => parseInt(x))
119-
const selectedCTIDs = rows.filter((_, i) => selectedRows.includes(i)).map(x => x.original['0']).filter(x => x)
126+
const selectedIDs = dbConnection.type === DBConnType.POSTGRES ?
127+
rows.filter((_, i) => selectedRows.includes(i)).map(x => x.original['0']).filter(x => x)
128+
: rows.filter((_, i) => selectedRows.includes(i)).map(x => queryData.pkeys!.map((pkey) => ({ [pkey]: x.original[queryData.columns.findIndex(x => x === pkey)] }))).map(x => x.reduce(((r, c) => Object.assign(r, c)), {})).map(x => JSON.stringify(x))
120129

121130
const deleteRows = async () => {
122-
if (selectedCTIDs.length > 0) {
123-
const result = await dispatch(deleteDBData({ dbConnectionId: dbConnection.id, schemaName: mSchema, name: mName, selectedIDs: selectedCTIDs })).unwrap()
131+
if (dbConnection.type === DBConnType.MYSQL && queryData.pkeys?.length === 0) {
132+
return toast.error("to perform delete operation primary keys are required on the table!")
133+
}
134+
if (selectedIDs.length > 0) {
135+
const result = await dispatch(deleteDBData({ dbConnectionId: dbConnection.id, schemaName: mSchema, name: mName, selectedIDs: selectedIDs })).unwrap()
124136
if (result.success) {
125137
toast.success('rows deleted')
126138
const filteredRows = queryData!.rows.filter((_, i) => !selectedRows.includes(i))
@@ -150,6 +162,11 @@ const Table = ({ queryData, dbConnection, mSchema, mName, isEditable, showHeader
150162
}
151163
onFilterChanged(filter)
152164
}
165+
const onFilterClear = () => {
166+
let filter: string[] | undefined = undefined
167+
setFilterValue(['default', 'default', ''])
168+
onFilterChanged(filter)
169+
}
153170

154171
const changeSort = (newSortIdx: string) => {
155172
if (!isEditable) {
@@ -216,11 +233,18 @@ const Table = ({ queryData, dbConnection, mSchema, mName, isEditable, showHeader
216233
<p className="control">
217234
<button className="button" onClick={onFilter}>Filter</button>
218235
</p>
236+
{(filterValue[0] !== 'default' || filterValue[1] !== 'default') && <p className="control">
237+
<button className="button" onClick={onFilterClear} >
238+
<span className="icon is-small">
239+
<i className="fas fa-circle-xmark" />
240+
</span>
241+
</button>
242+
</p>}
219243
</div>
220244
</div>
221245
{isEditable && <React.Fragment>
222246
<div className="column is-3 is-flex is-justify-content-flex-end">
223-
<button className="button" disabled={dbConnection.type === DBConnType.MYSQL || selectedCTIDs.length === 0} onClick={() => { setIsDeleting(true) }}>
247+
<button className="button" disabled={selectedIDs.length === 0} onClick={() => { setIsDeleting(true) }}>
224248
<span className="icon is-small">
225249
<i className="fas fa-trash" />
226250
</span>

frontend/src/components/layouts/footer.tsx

+13-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useAppDispatch, useAppSelector } from '../../redux/hooks'
33
import styles from './footer.module.scss'
44
import Constants from '../../constants'
55
import { useEffect } from 'react'
6-
import { checkConnection, selectDBConnection, selectIsDBConnected } from '../../redux/dbConnectionSlice'
6+
import { checkConnection, selectDBConnection, selectIsDBConnected, getDBDataModels, resetDBDataModels } from '../../redux/dbConnectionSlice'
77

88

99
type FooterPropType = {}
@@ -29,6 +29,10 @@ const Footer = (_: FooterPropType) => {
2929
navigate(Constants.APP_PATHS.SETTINGS_SUPPORT.path)
3030
}
3131

32+
const refreshDataModels = () => {
33+
dispatch(resetDBDataModels())
34+
dispatch(getDBDataModels({ dbConnId: dbConnection!.id }))
35+
}
3236

3337
return (
3438
<footer className={styles.footer}>
@@ -42,6 +46,14 @@ const Footer = (_: FooterPropType) => {
4246
<span>{(isDBConnected !== undefined && isDBConnected) ? "connected" : "not connected"}</span>
4347
</button>)
4448
}
49+
{isDBConnected === true &&
50+
(<button className={styles.button + " is-small"} onClick={refreshDataModels}>
51+
<span className="icon is-small">
52+
<i className="fas fa-sync" />
53+
</span>
54+
<span>refresh data models</span>
55+
</button>)
56+
}
4557
</div>
4658
<div>
4759
<button className={styles.button + " is-small"} onClick={openSupport}>

frontend/src/data/models.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export interface DBQueryData {
5454
keys: string[]
5555
data: any[]
5656
count?: number
57+
pkeys?: string[]
5758
}
5859

5960
export interface DBQueryResult {

frontend/src/events/eventService.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,10 @@ const deleteDBSingleDataModelIndex = async function (dbConnId: string, schemaNam
9595
return response
9696
}
9797

98-
const getDBDataInDataModel = async function (dbConnId: string, schemaName: string, mName: string, limit: number, offset: number, fetchCount: boolean, filter?: string[], sort?: string[]): Promise<ApiResult<DBQueryData>> {
98+
const getDBDataInDataModel = async function (dbConnId: string, schemaName: string, mName: string, limit: number, offset: number, isFirstFetch: boolean, filter?: string[], sort?: string[]): Promise<ApiResult<DBQueryData>> {
9999
const responseEventName = Events.GET_DATA.RESPONSE.replaceAll("[schema.name]", schemaName + "." + mName)
100100
const response = responseEvent<ApiResult<DBQueryData>>(responseEventName)
101-
EventsEmit(Events.GET_DATA.REQUEST, responseEventName, { dbConnectionId: dbConnId, schema: schemaName, name: mName, fetchCount, limit, offset, filter, sort })
101+
EventsEmit(Events.GET_DATA.REQUEST, responseEventName, { dbConnectionId: dbConnId, schema: schemaName, name: mName, isFirstFetch, limit, offset, filter, sort })
102102
return response
103103
}
104104

frontend/src/pages/project/newdb.tsx

+15-15
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const NewDBPage: FunctionComponent<{}> = () => {
2424

2525
const [data, setData] = useState({
2626
dbName: "",
27-
dbType: DBConnType.POSTGRES ,
27+
dbType: DBConnType.POSTGRES,
2828
dbScheme: "",
2929
dbHost: "",
3030
dbPort: "",
@@ -39,13 +39,13 @@ const NewDBPage: FunctionComponent<{}> = () => {
3939
dbUseSSL: false,
4040
})
4141

42-
const handleChange = (e:React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement >) => {
42+
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
4343
const type = e.target.type
4444

4545
const name = e.target.name
4646

4747
const value = type === "checkbox"
48-
? (e.target as HTMLInputElement).checked
48+
? (e.target as HTMLInputElement).checked
4949
: e.target.value
5050

5151
setData(prevData => ({
@@ -81,7 +81,7 @@ const NewDBPage: FunctionComponent<{}> = () => {
8181
sshKeyFile: data.dbSSHKeyFile,
8282
useSSL: data.dbUseSSL,
8383
}
84-
84+
8585
try {
8686
await dispatch(addNewDBConn(payload)).unwrap()
8787
navigate(Constants.APP_PATHS.PROJECT.path.replace('[id]', project.id))
@@ -97,17 +97,17 @@ const NewDBPage: FunctionComponent<{}> = () => {
9797
<div className="form-container">
9898
<InputTextField
9999
label='Display Name: '
100-
name='dbName'
101-
value={data.dbName}
100+
name='dbName'
101+
value={data.dbName}
102102
onChange={e => handleChange(e)}
103-
placeholder="Enter a display name for database"
103+
placeholder="Enter a display name for database"
104104
/>
105105
<div className="field">
106106
<label className="label">Database Type:</label>
107107
<div className="control">
108108
<div className="select">
109-
<select name="dbType" onChange={(e: React.ChangeEvent<HTMLSelectElement>) => { setData((prev)=> ({...prev, [e.target.name]:e.target.value, dbScheme :""}))}}>
110-
<option value={DBConnType.POSTGRES}>PostgresSQL</option>
109+
<select name="dbType" onChange={(e: React.ChangeEvent<HTMLSelectElement>) => { setData((prev) => ({ ...prev, [e.target.name]: e.target.value, dbScheme: "" })) }}>
110+
<option value={DBConnType.POSTGRES}>PostgreSQL</option>
111111
<option value={DBConnType.MONGO}>MongoDB</option>
112112
<option value={DBConnType.MYSQL}>MySQL</option>
113113
</select>
@@ -118,7 +118,7 @@ const NewDBPage: FunctionComponent<{}> = () => {
118118
<label className="label">Scheme:</label>
119119
<div className="control">
120120
<div className="select">
121-
<select name='dbScheme' onChange={e => handleChange(e)}>
121+
<select name='dbScheme' onChange={e => handleChange(e)}>
122122
<option value="default">Select scheme</option>
123123
<option value="mongodb">mongodb</option>
124124
<option value="mongodb+srv">mongodb+srv</option>
@@ -154,11 +154,11 @@ const NewDBPage: FunctionComponent<{}> = () => {
154154
onChange={e => handleChange(e)}
155155
placeholder="Enter Database username"
156156
/>
157-
<PasswordInputField
157+
<PasswordInputField
158158
label='Database Password:'
159159
name='dbPassword'
160160
value={data.dbPassword}
161-
onChange={e=>handleChange(e)}
161+
onChange={e => handleChange(e)}
162162
placeholder="Enter database password"
163163
/>
164164
<div className="field">
@@ -194,7 +194,7 @@ const NewDBPage: FunctionComponent<{}> = () => {
194194
name='dbUseSSL'
195195
type="checkbox"
196196
defaultChecked={false}
197-
onChange={e=>handleChange(e)} />
197+
onChange={e => handleChange(e)} />
198198
&nbsp;Enable SSL
199199
<span className="help">If you are connecting to database which enforce/require SSL connection. (Example: Azure CosmosDB)</span>
200200
</label>
@@ -217,11 +217,11 @@ const NewDBPage: FunctionComponent<{}> = () => {
217217
placeholder="Enter SSH User"
218218
/>
219219
{(data.dbUseSSH === DBConnectionUseSSHType.PASSWORD || data.dbUseSSH === DBConnectionUseSSHType.PASSKEYFILE) &&
220-
<PasswordInputField
220+
<PasswordInputField
221221
label='SSH Password:'
222222
name='dbSSHPassword'
223223
value={data.dbSSHPassword}
224-
onChange={e=>handleChange(e)}
224+
onChange={e => handleChange(e)}
225225
placeholder="Enter SSH Password"
226226
/>
227227
}

frontend/src/redux/dataModelSlice.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ const createInitialTabState = (state: QueryDataModelState, tabId: string) => {
3333
export const getDBDataInDataModel = createAsyncThunk(
3434
'dataModel/getDBDataInDataModel',
3535
async (payload: any, { rejectWithValue }: any) => {
36-
const { dbConnectionId, schemaName, name, queryLimit, queryOffset, fetchCount, queryFilter, querySort } = payload
37-
const result = await eventService.getDBDataInDataModel(dbConnectionId, schemaName, name, queryLimit, queryOffset, fetchCount, queryFilter, querySort)
36+
const { dbConnectionId, schemaName, name, queryLimit, queryOffset, isFirstFetch, queryFilter, querySort } = payload
37+
const result = await eventService.getDBDataInDataModel(dbConnectionId, schemaName, name, queryLimit, queryOffset, isFirstFetch, queryFilter, querySort)
3838
if (result.success) {
3939
return {
4040
data: result.data

frontend/src/redux/dbConnectionSlice.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ export const dbConnectionSlice = createSlice({
139139
name: 'dbConnection',
140140
initialState,
141141
reducers: {
142-
reset: (state) => initialState
142+
reset: (state) => initialState,
143+
resetDBDataModels: (state) => ({ ...state, isDBDataModelsFetched: false })
143144
},
144145
extraReducers: (builder) => {
145146
builder
@@ -184,7 +185,7 @@ export const dbConnectionSlice = createSlice({
184185
},
185186
})
186187

187-
export const { reset } = dbConnectionSlice.actions
188+
export const { reset, resetDBDataModels } = dbConnectionSlice.actions
188189

189190
export const selectDBConnection = (state: AppState) => state.dbConnection.dbConnection
190191
export const selectIsDBConnected = (state: AppState) => state.dbConnection.isDBConnected

frontend/src/styles/globals.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ code {
7979
.maincontent {
8080
padding: 10px;
8181
width: 100%;
82-
height: calc(100% - 42px);
82+
height: 100%;
8383
overflow-x: scroll;
8484
overflow-y: scroll;
8585
}

internal/controllers/query.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ func (QueryController) RunQuery(dbConnectionId, query string) (map[string]interf
2727
return data, nil
2828
}
2929

30-
func (QueryController) GetData(dbConnId, schema, name string, fetchCount bool, limit int, offset int64,
30+
func (QueryController) GetData(dbConnId, schema, name string, isFirstFetch bool, limit int, offset int64,
3131
filter, sort []string) (map[string]interface{}, error) {
3232

3333
dbConn, err := dao.DBConnection.GetDBConnectionByID(dbConnId)
3434
if err != nil {
3535
return nil, errors.New("there was some problem")
3636
}
3737

38-
data, err := queryengines.GetData(dbConn.ToQEConnection(), schema, name, limit, offset, fetchCount, filter, sort, getQueryConfigsForProjectMember(dbConn))
38+
data, err := queryengines.GetData(dbConn.ToQEConnection(), schema, name, limit, offset, isFirstFetch, filter, sort, getQueryConfigsForProjectMember(dbConn))
3939
if err != nil {
4040
return nil, err
4141
}

internal/events/query.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func (QueryEventListeners) GetData(ctx context.Context) {
6969
sort = utils.InterfaceArrayToStringArray(data["sort"].([]interface{}))
7070
}
7171
analytics.SendLowCodeDataViewEvent()
72-
responsedata, err := queryController.GetData(data["dbConnectionId"].(string), data["schema"].(string), data["name"].(string), data["fetchCount"].(bool), int(data["limit"].(float64)), int64(data["offset"].(float64)), filter, sort)
72+
responsedata, err := queryController.GetData(data["dbConnectionId"].(string), data["schema"].(string), data["name"].(string), data["isFirstFetch"].(bool), int(data["limit"].(float64)), int64(data["offset"].(float64)), filter, sort)
7373
if err != nil {
7474
runtime.EventsEmit(ctx, responseEventName, map[string]interface{}{
7575
"success": false,

0 commit comments

Comments
 (0)