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

Commit

Permalink
Merge pull request #102 from slashbaseide/develop
Browse files Browse the repository at this point in the history
Merge develop: New version
  • Loading branch information
paraswaykole authored Mar 20, 2023
2 parents 7cba442 + f995aca commit efaaf83
Show file tree
Hide file tree
Showing 19 changed files with 173 additions and 58 deletions.
6 changes: 3 additions & 3 deletions frontend/src/components/dbfragments/showdata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ const DBShowDataFragment = (_: DBShowDataPropType) => {
}, [queryFilter])


const fetchData = async (fetchCount: boolean) => {
const fetchData = async (isFirstFetch: boolean) => {
if (!dataModel) return
try {
const result = await dispatch(getDBDataInDataModel({ tabId: currentTab.id, dbConnectionId: dbConnection!.id, schemaName: dataModel!.schemaName ?? '', name: dataModel!.name, queryLimit, queryOffset, fetchCount, queryFilter, querySort })).unwrap()
if (fetchCount) {
const result = await dispatch(getDBDataInDataModel({ tabId: currentTab.id, dbConnectionId: dbConnection!.id, schemaName: dataModel!.schemaName ?? '', name: dataModel!.name, queryLimit, queryOffset, isFirstFetch, queryFilter, querySort })).unwrap()
if (isFirstFetch) {
setQueryCount(result.data.count)
}
} catch (e) {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/dbfragments/table/editablecell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const EditableCell = ({
}

const onSave = async () => {
onSaveCell(original["0"], id, value)
onSaveCell(index, original, id, value)
}

const isEditingCell = editCell.length == 2 && editCell[0] === index && editCell[1] === id
Expand Down
46 changes: 35 additions & 11 deletions frontend/src/components/dbfragments/table/table.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import styles from './table.module.scss'
import React, { useState, useRef, useContext } from 'react'
import React, { useState, useContext } from 'react'
import { Cell, useRowSelect, useTable } from 'react-table'
import toast from 'react-hot-toast'
import { DBConnection, DBQueryData, Tab } from '../../../data/models'
import EditableCell from './editablecell'
import AddModal from './addmodal'
import ConfirmModal from '../../widgets/confirmModal'
import { useAppDispatch, useAppSelector } from '../../../redux/hooks'
import { useAppDispatch } from '../../../redux/hooks'
import { deleteDBData, setQueryData, updateDBSingleData } from '../../../redux/dataModelSlice'
import { DBConnType } from '../../../data/defaults'
import TabContext from '../../layouts/tabcontext'
Expand Down Expand Up @@ -41,7 +41,7 @@ const Table = ({ queryData, dbConnection, mSchema, mName, isEditable, showHeader
[queryData]
)

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

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

const onSaveCell = async (ctid: string, columnIdx: string, newValue: string) => {
const onSaveCell = async (rowIdx: number, originalValue: any, columnIdx: string, newValue: string) => {
if (dbConnection.type === DBConnType.MYSQL && queryData.pkeys?.length === 0) {
return toast.error("to perform edit operation primary keys are required on the table!")
}
const columnName = queryData.columns[parseInt(columnIdx)]
const result = await dispatch(updateDBSingleData({ dbConnectionId: dbConnection.id, schemaName: mSchema, name: mName, id: ctid, columnName, newValue, columnIdx })).unwrap()
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)), {}))
const result = await dispatch(updateDBSingleData({ dbConnectionId: dbConnection.id, schemaName: mSchema, name: mName, id: uniqueId, columnName, newValue, columnIdx })).unwrap()
if (result.success) {
const rowIdx = queryData!.rows.findIndex(x => x["0"] === ctid)
if (rowIdx !== -1) {
const newQueryData: DBQueryData = { ...queryData!, rows: [...queryData!.rows] }
newQueryData!.rows[rowIdx] = { ...newQueryData!.rows[rowIdx], 0: result.data.ctid }
if (dbConnection.type === DBConnType.POSTGRES) {
newQueryData!.rows[rowIdx] = { ...newQueryData!.rows[rowIdx], 0: result.data.ctid }
} else {
newQueryData!.rows[rowIdx] = { ...newQueryData!.rows[rowIdx] }
}
newQueryData!.rows[rowIdx][columnIdx] = newValue
dispatch(setQueryData({ data: newQueryData, tabId: activeTab.id }))
} else {
Expand Down Expand Up @@ -116,11 +123,16 @@ const Table = ({ queryData, dbConnection, mSchema, mName, isEditable, showHeader
const newState: any = state // temporary typescript hack
const selectedRowIds: any = newState.selectedRowIds
const selectedRows: number[] = Object.keys(selectedRowIds).map(x => parseInt(x))
const selectedCTIDs = rows.filter((_, i) => selectedRows.includes(i)).map(x => x.original['0']).filter(x => x)
const selectedIDs = dbConnection.type === DBConnType.POSTGRES ?
rows.filter((_, i) => selectedRows.includes(i)).map(x => x.original['0']).filter(x => x)
: 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))

const deleteRows = async () => {
if (selectedCTIDs.length > 0) {
const result = await dispatch(deleteDBData({ dbConnectionId: dbConnection.id, schemaName: mSchema, name: mName, selectedIDs: selectedCTIDs })).unwrap()
if (dbConnection.type === DBConnType.MYSQL && queryData.pkeys?.length === 0) {
return toast.error("to perform delete operation primary keys are required on the table!")
}
if (selectedIDs.length > 0) {
const result = await dispatch(deleteDBData({ dbConnectionId: dbConnection.id, schemaName: mSchema, name: mName, selectedIDs: selectedIDs })).unwrap()
if (result.success) {
toast.success('rows deleted')
const filteredRows = queryData!.rows.filter((_, i) => !selectedRows.includes(i))
Expand Down Expand Up @@ -150,6 +162,11 @@ const Table = ({ queryData, dbConnection, mSchema, mName, isEditable, showHeader
}
onFilterChanged(filter)
}
const onFilterClear = () => {
let filter: string[] | undefined = undefined
setFilterValue(['default', 'default', ''])
onFilterChanged(filter)
}

const changeSort = (newSortIdx: string) => {
if (!isEditable) {
Expand Down Expand Up @@ -216,11 +233,18 @@ const Table = ({ queryData, dbConnection, mSchema, mName, isEditable, showHeader
<p className="control">
<button className="button" onClick={onFilter}>Filter</button>
</p>
{(filterValue[0] !== 'default' || filterValue[1] !== 'default') && <p className="control">
<button className="button" onClick={onFilterClear} >
<span className="icon is-small">
<i className="fas fa-circle-xmark" />
</span>
</button>
</p>}
</div>
</div>
{isEditable && <React.Fragment>
<div className="column is-3 is-flex is-justify-content-flex-end">
<button className="button" disabled={dbConnection.type === DBConnType.MYSQL || selectedCTIDs.length === 0} onClick={() => { setIsDeleting(true) }}>
<button className="button" disabled={selectedIDs.length === 0} onClick={() => { setIsDeleting(true) }}>
<span className="icon is-small">
<i className="fas fa-trash" />
</span>
Expand Down
14 changes: 13 additions & 1 deletion frontend/src/components/layouts/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useAppDispatch, useAppSelector } from '../../redux/hooks'
import styles from './footer.module.scss'
import Constants from '../../constants'
import { useEffect } from 'react'
import { checkConnection, selectDBConnection, selectIsDBConnected } from '../../redux/dbConnectionSlice'
import { checkConnection, selectDBConnection, selectIsDBConnected, getDBDataModels, resetDBDataModels } from '../../redux/dbConnectionSlice'


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

const refreshDataModels = () => {
dispatch(resetDBDataModels())
dispatch(getDBDataModels({ dbConnId: dbConnection!.id }))
}

return (
<footer className={styles.footer}>
Expand All @@ -42,6 +46,14 @@ const Footer = (_: FooterPropType) => {
<span>{(isDBConnected !== undefined && isDBConnected) ? "connected" : "not connected"}</span>
</button>)
}
{isDBConnected === true &&
(<button className={styles.button + " is-small"} onClick={refreshDataModels}>
<span className="icon is-small">
<i className="fas fa-sync" />
</span>
<span>refresh data models</span>
</button>)
}
</div>
<div>
<button className={styles.button + " is-small"} onClick={openSupport}>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/data/models.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export interface DBQueryData {
keys: string[]
data: any[]
count?: number
pkeys?: string[]
}

export interface DBQueryResult {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/events/eventService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,10 @@ const deleteDBSingleDataModelIndex = async function (dbConnId: string, schemaNam
return response
}

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

Expand Down
30 changes: 15 additions & 15 deletions frontend/src/pages/project/newdb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const NewDBPage: FunctionComponent<{}> = () => {

const [data, setData] = useState({
dbName: "",
dbType: DBConnType.POSTGRES ,
dbType: DBConnType.POSTGRES,
dbScheme: "",
dbHost: "",
dbPort: "",
Expand All @@ -39,13 +39,13 @@ const NewDBPage: FunctionComponent<{}> = () => {
dbUseSSL: false,
})

const handleChange = (e:React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement >) => {
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
const type = e.target.type

const name = e.target.name

const value = type === "checkbox"
? (e.target as HTMLInputElement).checked
? (e.target as HTMLInputElement).checked
: e.target.value

setData(prevData => ({
Expand Down Expand Up @@ -81,7 +81,7 @@ const NewDBPage: FunctionComponent<{}> = () => {
sshKeyFile: data.dbSSHKeyFile,
useSSL: data.dbUseSSL,
}

try {
await dispatch(addNewDBConn(payload)).unwrap()
navigate(Constants.APP_PATHS.PROJECT.path.replace('[id]', project.id))
Expand All @@ -97,17 +97,17 @@ const NewDBPage: FunctionComponent<{}> = () => {
<div className="form-container">
<InputTextField
label='Display Name: '
name='dbName'
value={data.dbName}
name='dbName'
value={data.dbName}
onChange={e => handleChange(e)}
placeholder="Enter a display name for database"
placeholder="Enter a display name for database"
/>
<div className="field">
<label className="label">Database Type:</label>
<div className="control">
<div className="select">
<select name="dbType" onChange={(e: React.ChangeEvent<HTMLSelectElement>) => { setData((prev)=> ({...prev, [e.target.name]:e.target.value, dbScheme :""}))}}>
<option value={DBConnType.POSTGRES}>PostgresSQL</option>
<select name="dbType" onChange={(e: React.ChangeEvent<HTMLSelectElement>) => { setData((prev) => ({ ...prev, [e.target.name]: e.target.value, dbScheme: "" })) }}>
<option value={DBConnType.POSTGRES}>PostgreSQL</option>
<option value={DBConnType.MONGO}>MongoDB</option>
<option value={DBConnType.MYSQL}>MySQL</option>
</select>
Expand All @@ -118,7 +118,7 @@ const NewDBPage: FunctionComponent<{}> = () => {
<label className="label">Scheme:</label>
<div className="control">
<div className="select">
<select name='dbScheme' onChange={e => handleChange(e)}>
<select name='dbScheme' onChange={e => handleChange(e)}>
<option value="default">Select scheme</option>
<option value="mongodb">mongodb</option>
<option value="mongodb+srv">mongodb+srv</option>
Expand Down Expand Up @@ -154,11 +154,11 @@ const NewDBPage: FunctionComponent<{}> = () => {
onChange={e => handleChange(e)}
placeholder="Enter Database username"
/>
<PasswordInputField
<PasswordInputField
label='Database Password:'
name='dbPassword'
value={data.dbPassword}
onChange={e=>handleChange(e)}
onChange={e => handleChange(e)}
placeholder="Enter database password"
/>
<div className="field">
Expand Down Expand Up @@ -194,7 +194,7 @@ const NewDBPage: FunctionComponent<{}> = () => {
name='dbUseSSL'
type="checkbox"
defaultChecked={false}
onChange={e=>handleChange(e)} />
onChange={e => handleChange(e)} />
&nbsp;Enable SSL
<span className="help">If you are connecting to database which enforce/require SSL connection. (Example: Azure CosmosDB)</span>
</label>
Expand All @@ -217,11 +217,11 @@ const NewDBPage: FunctionComponent<{}> = () => {
placeholder="Enter SSH User"
/>
{(data.dbUseSSH === DBConnectionUseSSHType.PASSWORD || data.dbUseSSH === DBConnectionUseSSHType.PASSKEYFILE) &&
<PasswordInputField
<PasswordInputField
label='SSH Password:'
name='dbSSHPassword'
value={data.dbSSHPassword}
onChange={e=>handleChange(e)}
onChange={e => handleChange(e)}
placeholder="Enter SSH Password"
/>
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/redux/dataModelSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ const createInitialTabState = (state: QueryDataModelState, tabId: string) => {
export const getDBDataInDataModel = createAsyncThunk(
'dataModel/getDBDataInDataModel',
async (payload: any, { rejectWithValue }: any) => {
const { dbConnectionId, schemaName, name, queryLimit, queryOffset, fetchCount, queryFilter, querySort } = payload
const result = await eventService.getDBDataInDataModel(dbConnectionId, schemaName, name, queryLimit, queryOffset, fetchCount, queryFilter, querySort)
const { dbConnectionId, schemaName, name, queryLimit, queryOffset, isFirstFetch, queryFilter, querySort } = payload
const result = await eventService.getDBDataInDataModel(dbConnectionId, schemaName, name, queryLimit, queryOffset, isFirstFetch, queryFilter, querySort)
if (result.success) {
return {
data: result.data
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/redux/dbConnectionSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ export const dbConnectionSlice = createSlice({
name: 'dbConnection',
initialState,
reducers: {
reset: (state) => initialState
reset: (state) => initialState,
resetDBDataModels: (state) => ({ ...state, isDBDataModelsFetched: false })
},
extraReducers: (builder) => {
builder
Expand Down Expand Up @@ -184,7 +185,7 @@ export const dbConnectionSlice = createSlice({
},
})

export const { reset } = dbConnectionSlice.actions
export const { reset, resetDBDataModels } = dbConnectionSlice.actions

export const selectDBConnection = (state: AppState) => state.dbConnection.dbConnection
export const selectIsDBConnected = (state: AppState) => state.dbConnection.isDBConnected
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ code {
.maincontent {
padding: 10px;
width: 100%;
height: calc(100% - 42px);
height: 100%;
overflow-x: scroll;
overflow-y: scroll;
}
Expand Down
4 changes: 2 additions & 2 deletions internal/controllers/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ func (QueryController) RunQuery(dbConnectionId, query string) (map[string]interf
return data, nil
}

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

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

data, err := queryengines.GetData(dbConn.ToQEConnection(), schema, name, limit, offset, fetchCount, filter, sort, getQueryConfigsForProjectMember(dbConn))
data, err := queryengines.GetData(dbConn.ToQEConnection(), schema, name, limit, offset, isFirstFetch, filter, sort, getQueryConfigsForProjectMember(dbConn))
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion internal/events/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (QueryEventListeners) GetData(ctx context.Context) {
sort = utils.InterfaceArrayToStringArray(data["sort"].([]interface{}))
}
analytics.SendLowCodeDataViewEvent()
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)
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)
if err != nil {
runtime.EventsEmit(ctx, responseEventName, map[string]interface{}{
"success": false,
Expand Down
Loading

0 comments on commit efaaf83

Please sign in to comment.