Skip to content

Commit

Permalink
Save table filter into configuration file
Browse files Browse the repository at this point in the history
  • Loading branch information
jdeniau committed Dec 18, 2024
1 parent 55f643b commit 308d07c
Show file tree
Hide file tree
Showing 11 changed files with 310 additions and 84 deletions.
132 changes: 127 additions & 5 deletions src/configuration/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
getConfiguration,
setActiveDatabase,
setActiveTable,
setTableFilter,
testables,
} from '.';

Expand Down Expand Up @@ -412,7 +413,7 @@ describe('set connection appState', async () => {
slug: 'prod',
appState: {
activeDatabase: 'db',
activeTableByDatabase: {},
configByDatabase: {},
},
},
},
Expand Down Expand Up @@ -445,7 +446,7 @@ describe('set connection appState', async () => {
slug: 'prod',
appState: {
activeDatabase: 'db',
activeTableByDatabase: {},
configByDatabase: {},
},
},
},
Expand Down Expand Up @@ -492,9 +493,130 @@ describe('set connection appState', async () => {
slug: 'prod',
appState: {
activeDatabase: 'db',
activeTableByDatabase: {
db: 'table',
db2: 'table2',
configByDatabase: {
db: {
activeTable: 'table',
tables: {},
},
db2: {
activeTable: 'table2',
tables: {},
},
},
},
},
},
},
null,
2
),
'utf-8',
expect.any(Function)
);
});

test('setActiveTable with appState but no configByDatabase', async () => {
mockExistingConfig({
version: 1,
theme: DEFAULT_THEME.name,
locale: DEFAULT_LOCALE,

connections: {
prod: {
name: 'prod',
host: 'prod',
user: 'root',
port: 3306,
password: Buffer.from('encrypted-password').toString('base64'),
slug: 'prod',
// @ts-expect-error -- testing edge case with existing configuration
appState: {
activeDatabase: 'ticketing',
},
},
},
});

await setActiveDatabase('prod', 'db');
await setActiveTable('prod', 'db', 'table');

expect(mockWriteFile).toHaveBeenCalledWith(
'userData/config/config.json',
JSON.stringify(
{
version: 1,
theme: DEFAULT_THEME.name,
locale: DEFAULT_LOCALE,
connections: {
prod: {
name: 'prod',
host: 'prod',
user: 'root',
port: 3306,
password: Buffer.from('encrypted-password').toString('base64'),
slug: 'prod',
appState: {
activeDatabase: 'db',
configByDatabase: {
db: {
activeTable: 'table',
tables: {},
},
},
},
},
},
},
null,
2
),
'utf-8',
expect.any(Function)
);
});
});

describe('setTableFilter', () => {
test('with basic configuration', async () => {
mockExistingConfig();

await setTableFilter('prod', 'db', 'sometable', 'id = 1');

expect(mockWriteFile).toHaveBeenCalledWith(
'userData/config/config.json',
JSON.stringify(
{
version: 1,
theme: DEFAULT_THEME.name,
locale: DEFAULT_LOCALE,

connections: {
local: {
name: 'local',
host: 'localhost',
user: 'root',
port: 3306,
password: Buffer.from('encrypted-password').toString('base64'),
slug: 'local',
},
prod: {
name: 'prod',
host: 'prod',
user: 'root',
port: 3306,
password: Buffer.from('encrypted-password').toString('base64'),
slug: 'prod',
appState: {
activeDatabase: '',
configByDatabase: {
db: {
activeTable: '',
tables: {
sometable: {
currentFilter: 'id = 1',
},
},
},
},
},
},
Expand Down
109 changes: 96 additions & 13 deletions src/configuration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { DEFAULT_LOCALE } from './locale';
import { DEFAULT_THEME } from './themes';
import {
Configuration,
DatabaseConfig,
EncryptedConfiguration,
EncryptedConnectionObject,
} from './type';
Expand Down Expand Up @@ -180,7 +181,7 @@ export function setActiveDatabase(connectionSlug: string, database: string) {
if (!connection.appState) {
connection.appState = {
activeDatabase: '',
activeTableByDatabase: {},
configByDatabase: {},
};
}

Expand All @@ -193,25 +194,57 @@ export function setActiveTable(
connectionSlug: string,
database: string,
tableName: string
) {
): void {
const config = getConfiguration();

const connection = config.connections[connectionSlug];

if (!connection) {
if (!config.connections[connectionSlug]) {
return;
}

if (!connection.appState) {
connection.appState = {
activeDatabase: '',
activeTableByDatabase: {},
};
const connection = ensureConnectionAppStateExist(
config.connections[connectionSlug]
);

const newConfig = ensureConnectionAppStateIsCorrect(
connection.appState.configByDatabase[database]
);

connection.appState.configByDatabase[database] = {
...newConfig,
activeTable: tableName,
};

writeConfiguration(config);
}

export function setTableFilter(
connectionSlug: string,
database: string,
tableName: string,
filter: string
): void {
const config = getConfiguration();

if (!config.connections[connectionSlug]) {
return;
}

connection.appState.activeTableByDatabase = {
...connection.appState.activeTableByDatabase,
[database]: tableName,
const connection = ensureConnectionAppStateExist(
config.connections[connectionSlug]
);

console.log(connection);

const newConfig = ensureConnectionAppStateIsCorrect(
connection.appState.configByDatabase[database]
);

newConfig.tables[tableName] = {
currentFilter: filter,
};

connection.appState.configByDatabase[database] = {
...newConfig,
};

writeConfiguration(config);
Expand All @@ -225,6 +258,55 @@ export function saveWindowState(windowState: WindowState): void {
writeConfiguration(config);
}

function hasAppState(
connection: EncryptedConnectionObject
): connection is EncryptedConnectionObject &
Required<Pick<EncryptedConnectionObject, 'appState'>> {
return typeof connection.appState !== 'undefined';
}

function ensureConnectionAppStateExist(
connection: EncryptedConnectionObject
): EncryptedConnectionObject &
Required<Pick<EncryptedConnectionObject, 'appState'>> {
if (hasAppState(connection)) {
if (!connection.appState.configByDatabase) {
connection.appState.configByDatabase = {};
}

return connection;
}

connection.appState = {
activeDatabase: '',
configByDatabase: {},
};

if (!hasAppState(connection)) {
throw new Error('Could not create app state for connection');
}

return connection;
}

function ensureConnectionAppStateIsCorrect(
databaseConfig: Partial<DatabaseConfig>
): DatabaseConfig {
if (!databaseConfig) {
databaseConfig = {};
}

if (!databaseConfig.activeTable) {
databaseConfig.activeTable = '';
}

if (!databaseConfig.tables) {
databaseConfig.tables = {};
}

return databaseConfig as DatabaseConfig;
}

const IPC_EVENT_BINDING = {
[CONFIGURATION_CHANNEL.GET]: getConfiguration,
[CONFIGURATION_CHANNEL.ADD_CONNECTION]: addConnectionToConfig,
Expand All @@ -233,6 +315,7 @@ const IPC_EVENT_BINDING = {
[CONFIGURATION_CHANNEL.CHANGE_LANGUAGE]: changeLanguage,
[CONFIGURATION_CHANNEL.SET_ACTIVE_DATABASE]: setActiveDatabase,
[CONFIGURATION_CHANNEL.SET_ACTIVE_TABLE]: setActiveTable,
[CONFIGURATION_CHANNEL.SET_TABLE_FILTER]: setTableFilter,
} as const;

export function bindIpcMain(ipcMain: Electron.IpcMain): void {
Expand Down
11 changes: 10 additions & 1 deletion src/configuration/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,18 @@ export type Configuration = {
windowState?: WindowState;
};

type TableConfig = {
currentFilter?: string;
};

export type DatabaseConfig = {
activeTable: string;
tables: Record<string, TableConfig>;
};

type ConnectionAppState = {
activeDatabase: string;
activeTableByDatabase: Record<string, string>;
configByDatabase: Record<string, DatabaseConfig>;
};

export type EncryptedConnectionObject = {
Expand Down
8 changes: 8 additions & 0 deletions src/preload/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ interface Config {
tableName: string
): Promise<void>;

setTableFilter(
connectionSlug: string,
database: string,
tableName: string,
filter: string
): Promise<void>;

editConnection(
connectionSlug: string,
connection: ConnectionObjectWithoutSlug
Expand All @@ -35,5 +42,6 @@ export const config: Config = {
changeLanguage: bindChannel(CONFIGURATION_CHANNEL.CHANGE_LANGUAGE),
setActiveDatabase: bindChannel(CONFIGURATION_CHANNEL.SET_ACTIVE_DATABASE),
setActiveTable: bindChannel(CONFIGURATION_CHANNEL.SET_ACTIVE_TABLE),
setTableFilter: bindChannel(CONFIGURATION_CHANNEL.SET_TABLE_FILTER),
editConnection: bindChannel(CONFIGURATION_CHANNEL.EDIT_CONNECTION),
};
1 change: 1 addition & 0 deletions src/preload/configurationChannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export enum CONFIGURATION_CHANNEL {
CHANGE_LANGUAGE = 'config:language:change',
SET_ACTIVE_DATABASE = 'config:connection:setActiveDatabase',
SET_ACTIVE_TABLE = 'config:connection:setActiveTable',
SET_TABLE_FILTER = 'config:connection:setTableFilter',
}
16 changes: 6 additions & 10 deletions src/renderer/component/Query/WhereFilter.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
import { ReactElement, useRef, useState } from 'react';
import { Button, Space } from 'antd';
import { Form } from 'react-router-dom';
import { useTranslation } from '../../../i18n';
import { RawSqlEditor } from '../MonacoEditor/RawSqlEditor';

interface Props {
onSubmit: (where: string) => void;
defaultValue: string;
}

function WhereFilter({ defaultValue, onSubmit }: Props): ReactElement {
function WhereFilter({ defaultValue }: Props): ReactElement {
const { t } = useTranslation();
const [where, setWhere] = useState<string>(defaultValue);
const ref = useRef<HTMLFormElement>(null);

return (
<form
ref={ref}
onSubmit={(e) => {
e.preventDefault();
onSubmit(where);
}}
>
<Form ref={ref}>
<input type="hidden" name="where" value={where} />

<Space.Compact style={{ width: '100%', marginBottom: '0.5em' }}>
<RawSqlEditor
defaultValue={where}
Expand All @@ -45,7 +41,7 @@ function WhereFilter({ defaultValue, onSubmit }: Props): ReactElement {
{t('filter')}
</Button>
</Space.Compact>
</form>
</Form>
);
}

Expand Down
Loading

0 comments on commit 308d07c

Please sign in to comment.