Skip to content

Commit ca2e4a4

Browse files
authored
Merge pull request #1377 from RedisInsight/feature/RI-3637_databases-import
#RI-3637 databases import.
2 parents 167e212 + f43e062 commit ca2e4a4

File tree

135 files changed

+7589
-187
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

135 files changed

+7589
-187
lines changed

redisinsight/api/config/stack.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export default {
55
excludeRoutes: [
66
'redis-enterprise/*',
77
'redis-sentinel/*',
8+
{ path: 'databases/import' },
89
{ path: 'databases', method: RequestMethod.POST },
910
{ path: 'databases', method: RequestMethod.DELETE },
1011
{ path: 'databases/:id', method: RequestMethod.DELETE },
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { MigrationInterface, QueryRunner } from "typeorm";
2+
3+
export class databaseNew1670252337342 implements MigrationInterface {
4+
name = 'databaseNew1670252337342'
5+
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
await queryRunner.query(`CREATE TABLE "temporary_database_instance" ("id" varchar PRIMARY KEY NOT NULL, "host" varchar NOT NULL, "port" integer NOT NULL, "name" varchar NOT NULL, "username" varchar, "password" varchar, "tls" boolean, "verifyServerCert" boolean, "lastConnection" datetime, "caCertId" varchar, "clientCertId" varchar, "connectionType" varchar NOT NULL DEFAULT ('STANDALONE'), "nodes" varchar DEFAULT ('[]'), "nameFromProvider" varchar, "sentinelMasterName" varchar, "sentinelMasterUsername" varchar, "sentinelMasterPassword" varchar, "provider" varchar DEFAULT ('UNKNOWN'), "modules" varchar NOT NULL DEFAULT ('[]'), "db" integer, "encryption" varchar, "tlsServername" varchar, "new" boolean, CONSTRAINT "FK_3b9b625266c00feb2d66a9f36e4" FOREIGN KEY ("clientCertId") REFERENCES "client_certificate" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_d1bc747b5938e22b4b708d8e9a5" FOREIGN KEY ("caCertId") REFERENCES "ca_certificate" ("id") ON DELETE SET NULL ON UPDATE NO ACTION)`);
8+
await queryRunner.query(`INSERT INTO "temporary_database_instance"("id", "host", "port", "name", "username", "password", "tls", "verifyServerCert", "lastConnection", "caCertId", "clientCertId", "connectionType", "nodes", "nameFromProvider", "sentinelMasterName", "sentinelMasterUsername", "sentinelMasterPassword", "provider", "modules", "db", "encryption", "tlsServername") SELECT "id", "host", "port", "name", "username", "password", "tls", "verifyServerCert", "lastConnection", "caCertId", "clientCertId", "connectionType", "nodes", "nameFromProvider", "sentinelMasterName", "sentinelMasterUsername", "sentinelMasterPassword", "provider", "modules", "db", "encryption", "tlsServername" FROM "database_instance"`);
9+
await queryRunner.query(`DROP TABLE "database_instance"`);
10+
await queryRunner.query(`ALTER TABLE "temporary_database_instance" RENAME TO "database_instance"`);
11+
}
12+
13+
public async down(queryRunner: QueryRunner): Promise<void> {
14+
await queryRunner.query(`ALTER TABLE "database_instance" RENAME TO "temporary_database_instance"`);
15+
await queryRunner.query(`CREATE TABLE "database_instance" ("id" varchar PRIMARY KEY NOT NULL, "host" varchar NOT NULL, "port" integer NOT NULL, "name" varchar NOT NULL, "username" varchar, "password" varchar, "tls" boolean, "verifyServerCert" boolean, "lastConnection" datetime, "caCertId" varchar, "clientCertId" varchar, "connectionType" varchar NOT NULL DEFAULT ('STANDALONE'), "nodes" varchar DEFAULT ('[]'), "nameFromProvider" varchar, "sentinelMasterName" varchar, "sentinelMasterUsername" varchar, "sentinelMasterPassword" varchar, "provider" varchar DEFAULT ('UNKNOWN'), "modules" varchar NOT NULL DEFAULT ('[]'), "db" integer, "encryption" varchar, "tlsServername" varchar, CONSTRAINT "FK_3b9b625266c00feb2d66a9f36e4" FOREIGN KEY ("clientCertId") REFERENCES "client_certificate" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_d1bc747b5938e22b4b708d8e9a5" FOREIGN KEY ("caCertId") REFERENCES "ca_certificate" ("id") ON DELETE SET NULL ON UPDATE NO ACTION)`);
16+
await queryRunner.query(`INSERT INTO "database_instance"("id", "host", "port", "name", "username", "password", "tls", "verifyServerCert", "lastConnection", "caCertId", "clientCertId", "connectionType", "nodes", "nameFromProvider", "sentinelMasterName", "sentinelMasterUsername", "sentinelMasterPassword", "provider", "modules", "db", "encryption", "tlsServername") SELECT "id", "host", "port", "name", "username", "password", "tls", "verifyServerCert", "lastConnection", "caCertId", "clientCertId", "connectionType", "nodes", "nameFromProvider", "sentinelMasterName", "sentinelMasterUsername", "sentinelMasterPassword", "provider", "modules", "db", "encryption", "tlsServername" FROM "temporary_database_instance"`);
17+
await queryRunner.query(`DROP TABLE "temporary_database_instance"`);
18+
}
19+
20+
}

redisinsight/api/migration/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { databaseAnalysis1664785208236 } from './1664785208236-database-analysis
2121
import { databaseAnalysisExpirationGroups1664886479051 } from './1664886479051-database-analysis-expiration-groups';
2222
import { workbenchExecutionTime1667368983699 } from './1667368983699-workbench-execution-time';
2323
import { database1667477693934 } from './1667477693934-database';
24+
import { databaseNew1670252337342 } from './1670252337342-database-new';
2425

2526
export default [
2627
initialMigration1614164490968,
@@ -46,4 +47,5 @@ export default [
4647
databaseAnalysisExpirationGroups1664886479051,
4748
workbenchExecutionTime1667368983699,
4849
database1667477693934,
50+
databaseNew1670252337342,
4951
];

redisinsight/api/src/__mocks__/common.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const mockQueryBuilderExecute = jest.fn();
3232
export const mockCreateQueryBuilder = jest.fn(() => ({
3333
// where: jest.fn().mockReturnThis(),
3434
where: mockQueryBuilderWhere,
35+
orWhere: mockQueryBuilderWhere,
3536
update: jest.fn().mockReturnThis(),
3637
select: jest.fn().mockReturnThis(),
3738
set: jest.fn().mockReturnThis(),
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { DatabaseImportResponse, DatabaseImportStatus } from 'src/modules/database-import/dto/database-import.response';
2+
import { BadRequestException, ForbiddenException } from '@nestjs/common';
3+
import { mockDatabase, mockSentinelDatabaseWithTlsAuth } from 'src/__mocks__/databases';
4+
import { ValidationException } from 'src/common/exceptions';
5+
import { mockCaCertificate, mockClientCertificate } from 'src/__mocks__/certificates';
6+
import {
7+
InvalidCaCertificateBodyException, InvalidCertificateNameException,
8+
} from 'src/modules/database-import/exceptions';
9+
10+
export const mockDatabasesToImportArray = new Array(10).fill(mockSentinelDatabaseWithTlsAuth);
11+
12+
export const mockDatabaseImportFile = {
13+
originalname: 'filename.json',
14+
mimetype: 'application/json',
15+
size: 1,
16+
buffer: Buffer.from(JSON.stringify(mockDatabasesToImportArray)),
17+
};
18+
19+
export const mockDatabaseImportResultSuccess = {
20+
index: 0,
21+
status: DatabaseImportStatus.Success,
22+
host: mockDatabase.host,
23+
port: mockDatabase.port,
24+
};
25+
26+
export const mockDatabaseImportResultFail = {
27+
index: 0,
28+
status: DatabaseImportStatus.Fail,
29+
host: mockDatabase.host,
30+
port: mockDatabase.port,
31+
errors: [new BadRequestException()],
32+
};
33+
34+
export const mockDatabaseImportResultPartial = {
35+
index: 0,
36+
status: DatabaseImportStatus.Partial,
37+
host: mockDatabase.host,
38+
port: mockDatabase.port,
39+
errors: [new InvalidCaCertificateBodyException()],
40+
};
41+
42+
export const mockDatabaseImportResponse = Object.assign(new DatabaseImportResponse(), {
43+
total: 10,
44+
success: (new Array(5).fill(mockDatabaseImportResultSuccess)).map((v, index) => ({
45+
...v,
46+
index: index + 5,
47+
})),
48+
partial: [
49+
[new InvalidCaCertificateBodyException(), new InvalidCertificateNameException()],
50+
[new InvalidCertificateNameException()],
51+
].map((errors, index) => ({
52+
...mockDatabaseImportResultPartial,
53+
index: index + 3,
54+
errors,
55+
})),
56+
fail: [
57+
new ValidationException('Bad request'),
58+
new BadRequestException(),
59+
new ForbiddenException(),
60+
].map((error, index) => ({
61+
...mockDatabaseImportResultFail,
62+
index,
63+
errors: [error],
64+
})),
65+
});
66+
67+
export const mockDatabaseImportPartialAnalyticsPayload = {
68+
partially: mockDatabaseImportResponse.partial.length,
69+
errors: ['InvalidCaCertificateBodyException', 'InvalidCertificateNameException'],
70+
};
71+
72+
export const mockDatabaseImportFailedAnalyticsPayload = {
73+
failed: mockDatabaseImportResponse.fail.length,
74+
errors: ['ValidationException', 'BadRequestException', 'ForbiddenException'],
75+
};
76+
77+
export const mockDatabaseImportSucceededAnalyticsPayload = {
78+
succeed: mockDatabaseImportResponse.success.length,
79+
};
80+
81+
export const mockDatabaseImportAnalytics = jest.fn(() => ({
82+
sendImportResults: jest.fn(),
83+
sendImportFailed: jest.fn(),
84+
}));
85+
86+
export const mockCertificateImportService = jest.fn(() => ({
87+
processCaCertificate: jest.fn().mockResolvedValue(mockCaCertificate),
88+
processClientCertificate: jest.fn().mockResolvedValue(mockClientCertificate),
89+
}));

redisinsight/api/src/__mocks__/databases.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { mockSentinelMasterDto } from 'src/__mocks__/redis-sentinel';
88
import { pick } from 'lodash';
99
import { RedisDatabaseInfoResponse } from 'src/modules/database/dto/redis-info.dto';
1010
import { DatabaseOverview } from 'src/modules/database/models/database-overview';
11+
import { ClientContext, ClientMetadata } from 'src/common/models';
1112

1213
export const mockDatabaseId = 'a77b23c1-7816-4ea4-b61f-d37795a0f805-db-id';
1314

@@ -25,6 +26,7 @@ export const mockDatabase = Object.assign(new Database(), {
2526
host: '127.0.100.1',
2627
port: 6379,
2728
connectionType: ConnectionType.STANDALONE,
29+
new: false,
2830
});
2931

3032
export const mockDatabaseEntity = Object.assign(new DatabaseEntity(), {
@@ -113,6 +115,17 @@ export const mockClusterDatabaseWithTlsAuthEntity = Object.assign(new DatabaseEn
113115
nodes: JSON.stringify(mockClusterNodes),
114116
});
115117

118+
export const mockNewDatabase = Object.assign(new Database(), {
119+
...mockDatabase,
120+
new: true,
121+
});
122+
123+
export const mockClientMetadata: ClientMetadata = {
124+
session: undefined,
125+
databaseId: mockDatabase.id,
126+
context: ClientContext.Common,
127+
};
128+
116129
export const mockDatabaseOverview: DatabaseOverview = {
117130
version: '6.2.4',
118131
usedMemory: 1,

redisinsight/api/src/__mocks__/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ export * from './redis';
1616
export * from './server';
1717
export * from './redis-enterprise';
1818
export * from './redis-sentinel';
19+
export * from './database-import';

redisinsight/api/src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { ServerModule } from 'src/modules/server/server.module';
1919
import { LocalDatabaseModule } from 'src/local-database.module';
2020
import { CoreModule } from 'src/core.module';
2121
import { AutodiscoveryModule } from 'src/modules/autodiscovery/autodiscovery.module';
22+
import { DatabaseImportModule } from 'src/modules/database-import/database-import.module';
2223
import { DummyAuthMiddleware } from 'src/common/middlewares/dummy-auth.middleware';
2324
import { BrowserModule } from './modules/browser/browser.module';
2425
import { RedisEnterpriseModule } from './modules/redis-enterprise/redis-enterprise.module';
@@ -53,6 +54,7 @@ const PATH_CONFIG = config.get('dir_path');
5354
BulkActionsModule,
5455
ClusterMonitorModule,
5556
DatabaseAnalysisModule,
57+
DatabaseImportModule,
5658
...(SERVER_CONFIG.staticContent
5759
? [
5860
ServeStaticModule.forRoot({
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './validation.exception';
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { BadRequestException } from '@nestjs/common';
2+
3+
export class ValidationException extends BadRequestException {}

0 commit comments

Comments
 (0)