Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export abstract class CommandExecutionRepository {
abstract getList(databaseId: string): Promise<ShortCommandExecution[]>;
abstract getOne(databaseId: string, id: string): Promise<CommandExecution>;
abstract delete(databaseId: string, id: string): Promise<void>;
abstract deleteAll(databaseId: string): Promise<void>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,14 @@ describe('LocalCommandExecutionRepository', () => {
);
});
});
describe('deleteAll', () => {
it('Should not return anything on delete', async () => {
repository.delete.mockResolvedValueOnce(1);
expect(await service.deleteAll(mockDatabase.id)).toEqual(
undefined,
);
});
});
describe('cleanupDatabaseHistory', () => {
it('Should should not return anything on cleanup', async () => {
mockQueryBuilderGetManyRaw.mockReturnValueOnce([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,19 @@ export class LocalCommandExecutionRepository extends CommandExecutionRepository
this.logger.log('Command execution deleted');
}

/**
* Delete all items
*
* @param databaseId
*/
async deleteAll(databaseId: string): Promise<void> {
this.logger.log('Delete all command executions');

await this.commandExecutionRepository.delete({ databaseId });

this.logger.log('Command executions deleted');
}

/**
* Clean history for particular database to fit 30 items limitation
* @param databaseId
Expand Down
12 changes: 12 additions & 0 deletions redisinsight/api/src/modules/workbench/workbench.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,16 @@ export class WorkbenchController {
): Promise<void> {
return this.service.deleteCommandExecution(databaseId, id);
}

@ApiEndpoint({
description: 'Delete command executions',
statusCode: 200,
})
@Delete('/command-executions')
@ApiRedisParams()
async deleteCommandExecutions(
@Param('dbInstance') databaseId: string,
): Promise<void> {
return this.service.deleteCommandExecutions(databaseId);
}
}
12 changes: 12 additions & 0 deletions redisinsight/api/src/modules/workbench/workbench.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ const mockCommandExecutionRepository = () => ({
getList: jest.fn(),
getOne: jest.fn(),
delete: jest.fn(),
deleteAll: jest.fn(),
});

describe('WorkbenchService', () => {
Expand Down Expand Up @@ -389,6 +390,17 @@ describe('WorkbenchService', () => {
mockCommandExecution.id,
);

expect(result).toEqual(undefined);
});
});
describe('deleteCommandExecutions', () => {
it('should not return anything on delete', async () => {
commandExecutionProvider.deleteAll.mockResolvedValueOnce('some response');

const result = await service.deleteCommandExecutions(
mockWorkbenchClientMetadata.databaseId,
);

expect(result).toEqual(undefined);
});
});
Expand Down
9 changes: 9 additions & 0 deletions redisinsight/api/src/modules/workbench/workbench.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,15 @@ export class WorkbenchService {
this.analyticsService.sendCommandDeletedEvent(databaseId);
}

/**
* Delete command executions by databaseId
*
* @param databaseId
*/
async deleteCommandExecutions(databaseId: string): Promise<void> {
await this.commandExecutionRepository.deleteAll(databaseId);
}

/**
* Check if workbench allows such command
* @param commandLine
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {
expect,
describe,
it,
deps,
validateApiCall,
} from '../deps';
const { server, request, constants, rte, localDb } = deps;

// endpoint to test
const endpoint = (
instanceId = constants.TEST_INSTANCE_ID,
) =>
request(server).delete(`/${constants.API.DATABASES}/${instanceId}/workbench/command-executions`);

const mainCheckFn = async (testCase) => {
it(testCase.name, async () => {
// additional checks before test run
if (testCase.before) {
await testCase.before();
}

await validateApiCall({
endpoint,
...testCase,
});

// additional checks after test pass
if (testCase.after) {
await testCase.after();
}
});
};

describe('DELETE /databases/:instanceId/workbench/command-executions', () => {
describe('Common', () => {
[
{
name: 'Should return 404 not found when incorrect instance',
endpoint: () => endpoint(
constants.TEST_NOT_EXISTED_INSTANCE_ID,
),
statusCode: 404,
responseBody: {
statusCode: 404,
message: 'Invalid database instance id.',
error: 'Not Found'
},
},
{
name: 'Should return 0 array when no history items yet',
before: async () => {
await localDb.generateNCommandExecutions({
databaseId: constants.TEST_INSTANCE_ID,
id: constants.TEST_COMMAND_EXECUTION_ID_1,
}, 2);
},
after: async () => {
expect(await (await localDb.getRepository(localDb.repositories.COMMAND_EXECUTION)).count({})).to.eq(0)
},
},
].map(mainCheckFn);
});
});
3 changes: 3 additions & 0 deletions redisinsight/ui/src/components/query-card/QueryCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export interface Props {
summary?: ResultsSummary
createdAt?: Date
loading?: boolean
clearing?: boolean
isNotStored?: boolean
executionTime?: number
db?: number
Expand Down Expand Up @@ -80,6 +81,7 @@ const QueryCard = (props: Props) => {
onQueryProfile,
onQueryReRun,
loading,
clearing,
emptyCommand,
isNotStored,
executionTime,
Expand Down Expand Up @@ -173,6 +175,7 @@ const QueryCard = (props: Props) => {
isFullScreen={isFullScreen}
query={command}
loading={loading}
clearing={clearing}
createdAt={createdAt}
message={message}
queryType={queryType}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export interface Props {
queryType: WBQueryType
selectedValue: string
loading?: boolean
clearing?: boolean
executionTime?: number
emptyCommand?: boolean
db?: number
Expand Down Expand Up @@ -96,6 +97,7 @@ const QueryCardHeader = (props: Props) => {
toggleFullScreen,
query = '',
loading,
clearing,
message,
createdAt,
mode,
Expand Down Expand Up @@ -169,6 +171,7 @@ const QueryCardHeader = (props: Props) => {
const handleQueryDelete = (event: React.MouseEvent) => {
eventStop(event)
onQueryDelete()
sendEvent(TelemetryEvent.WORKBENCH_CLEAR_RESULT_CLICKED, query)
}

const handleQueryReRun = (event: React.MouseEvent) => {
Expand Down Expand Up @@ -404,7 +407,7 @@ const QueryCardHeader = (props: Props) => {
</EuiFlexItem>
<EuiFlexItem grow={false} className={styles.buttonIcon}>
<EuiButtonIcon
disabled={loading}
disabled={loading || clearing}
iconType="trash"
aria-label="Delete command"
data-testid="delete-command"
Expand Down
1 change: 0 additions & 1 deletion redisinsight/ui/src/constants/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ enum ApiEndpoints {
SETTINGS_AGREEMENTS_SPEC = 'settings/agreements/spec',

WORKBENCH_COMMAND_EXECUTIONS = 'workbench/command-executions',
WORKBENCH_COMMANDS_EXECUTION = 'workbench/commands-execution',

PROFILER = 'profiler',
PROFILER_LOGS = 'profiler/logs',
Expand Down
33 changes: 33 additions & 0 deletions redisinsight/ui/src/pages/workbench/WorkbenchPage.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,39 @@ describe('Telemetry', () => {
rawMode: true,
}
})

sendEventTelemetry.mockRestore()
})

it('should call proper telemetry on delete', async () => {
const sendEventTelemetryMock = jest.fn()

sendEventTelemetry.mockImplementation(() => sendEventTelemetryMock)

render(<WorkbenchPage />)

fireEvent.click(screen.getByTestId('delete-command'))

expect(sendEventTelemetry).toBeCalledWith({
event: TelemetryEvent.WORKBENCH_CLEAR_RESULT_CLICKED,
eventData: {
databaseId: 'instanceId',
command: 'info'
}
})

sendEventTelemetry.mockRestore()

fireEvent.click(screen.getByTestId('clear-history-btn'))

expect(sendEventTelemetry).toBeCalledWith({
event: TelemetryEvent.WORKBENCH_CLEAR_ALL_RESULTS_CLICKED,
eventData: {
databaseId: 'instanceId'
}
})

sendEventTelemetry.mockRestore()
})
})
describe('Raw mode', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useContext } from 'react'
import cx from 'classnames'
import { EuiIcon, EuiText } from '@elastic/eui'
import { EuiButtonEmpty, EuiIcon, EuiText } from '@elastic/eui'

import { Theme } from 'uiSrc/constants'
import { ProfileQueryType } from 'uiSrc/pages/workbench/constants'
Expand All @@ -17,22 +17,26 @@ import styles from './styles.module.scss'

export interface Props {
items: CommandExecutionUI[]
clearing: boolean
activeMode: RunQueryMode
activeResultsMode?: ResultsMode
scrollDivRef: React.Ref<HTMLDivElement>
onQueryReRun: (query: string, commandId?: Nullable<string>, executeParams?: CodeButtonParams) => void
onQueryDelete: (commandId: string) => void
onAllQueriesDelete: () => void
onQueryOpen: (commandId: string) => void
onQueryProfile: (query: string, commandId?: Nullable<string>, executeParams?: CodeButtonParams) => void
}
const WBResults = (props: Props) => {
const {
items = [],
clearing,
activeMode,
activeResultsMode,
onQueryReRun,
onQueryProfile,
onQueryDelete,
onAllQueriesDelete,
onQueryOpen,
scrollDivRef
} = props
Expand Down Expand Up @@ -69,6 +73,20 @@ const WBResults = (props: Props) => {

return (
<div className={cx(styles.container)}>
{!!items.length && (
<div className={styles.header}>
<EuiButtonEmpty
size="s"
iconType="trash"
iconSize="s"
className={styles.clearAllBtn}
onClick={() => onAllQueriesDelete?.()}
data-testid="clear-history-btn"
>
Clear Results
</EuiButtonEmpty>
</div>
)}
<div ref={scrollDivRef} />
{items.map((
{
Expand All @@ -93,6 +111,7 @@ const WBResults = (props: Props) => {
isOpen={isOpen}
result={result}
summary={summary}
clearing={clearing}
loading={loading}
command={command}
createdAt={createdAt}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@
overflow: auto;
}

.header {
height: 42px;
display: flex;
align-items: center;
justify-content: flex-end;
padding: 0 12px;
}

.noResults {
width: 326px;
height: 100%;
Expand Down Expand Up @@ -42,3 +50,14 @@
width: 42px !important;
height: 42px !important;
}

.clearAllBtn {
font-size: 14px !important;

:global {
.euiIcon {
width: 14px !important;
height: 14px !important;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import WBResults from './WBResults'

export interface Props {
items: CommandExecutionUI[]
clearing: boolean
activeMode: RunQueryMode
activeResultsMode: ResultsMode
scrollDivRef: React.Ref<HTMLDivElement>
onQueryReRun: (query: string, commandId?: Nullable<string>, executeParams?: CodeButtonParams) => void
onQueryOpen: (commandId: string) => void
onQueryDelete: (commandId: string) => void
onAllQueriesDelete: () => void
onQueryProfile: (query: string, commandId?: Nullable<string>, executeParams?: CodeButtonParams) => void
}

Expand Down
Loading