Skip to content

Commit 44a0e26

Browse files
authored
Merge pull request #2209 from RedisInsight/feature/RI-4530-fixed_subscription_autodiscovery
Feature/ri 4530 fixed subscription autodiscovery
2 parents 0f31fde + d2f031e commit 44a0e26

File tree

94 files changed

+3211
-2098
lines changed

Some content is hidden

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

94 files changed

+3211
-2098
lines changed

redisinsight/api/.jest.setup.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Workaround for @Type test coverage
2+
jest.mock("class-transformer", () => {
3+
return {
4+
...(jest.requireActual("class-transformer") as Object),
5+
Type: (f: Function) => f() && jest.requireActual("class-transformer").Type(f),
6+
};
7+
});

redisinsight/api/config/default.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ export default {
7474
},
7575
redis_cloud: {
7676
url: process.env.REDIS_CLOUD_URL || 'https://api-cloudapi.qa.redislabs.com/v1',
77+
cloudDiscoveryTimeout: parseInt(process.env.RI_CLOUD_DISCOVERY_TIMEOUT, 10) || 60 * 1000, // 1 min
78+
cloudDatabaseConnectionTimeout: parseInt(process.env.RI_CLOUD_DATABASE_CONNECTION_TIMEOUT, 10) || 30 * 1000,
7779
},
7880
redis_clients: {
7981
idleSyncInterval: parseInt(process.env.CLIENTS_IDLE_SYNC_INTERVAL, 10) || 1000 * 60 * 60, // 1hr

redisinsight/api/config/ormconfig.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { BrowserHistoryEntity } from 'src/modules/browser/entities/browser-histo
1717
import { CustomTutorialEntity } from 'src/modules/custom-tutorial/entities/custom-tutorial.entity';
1818
import { FeatureEntity } from 'src/modules/feature/entities/feature.entity';
1919
import { FeaturesConfigEntity } from 'src/modules/feature/entities/features-config.entity';
20+
import { CloudDatabaseDetailsEntity } from 'src/modules/cloud/autodiscovery/entities/cloud-database-details.entity';
2021
import migrations from '../migration';
2122
import * as config from '../src/utils/config';
2223

@@ -44,6 +45,7 @@ const ormConfig = {
4445
CustomTutorialEntity,
4546
FeatureEntity,
4647
FeaturesConfigEntity,
48+
CloudDatabaseDetailsEntity,
4749
],
4850
migrations,
4951
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { MigrationInterface, QueryRunner } from "typeorm";
2+
3+
export class CloudDatabaseDetails1687166457712 implements MigrationInterface {
4+
name = 'CloudDatabaseDetails1687166457712'
5+
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
await queryRunner.query(`CREATE TABLE "database_cloud_details" ("id" varchar PRIMARY KEY NOT NULL, "cloudId" integer NOT NULL, "subscriptionType" varchar NOT NULL, "planMemoryLimit" integer, "memoryLimitMeasurementUnit" integer, "databaseId" varchar, CONSTRAINT "REL_f41ee5027391b3be8ad95e3d15" UNIQUE ("databaseId"))`);
8+
await queryRunner.query(`CREATE TABLE "temporary_database_cloud_details" ("id" varchar PRIMARY KEY NOT NULL, "cloudId" integer NOT NULL, "subscriptionType" varchar NOT NULL, "planMemoryLimit" integer, "memoryLimitMeasurementUnit" integer, "databaseId" varchar, CONSTRAINT "REL_f41ee5027391b3be8ad95e3d15" UNIQUE ("databaseId"), CONSTRAINT "FK_f41ee5027391b3be8ad95e3d158" FOREIGN KEY ("databaseId") REFERENCES "database_instance" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
9+
await queryRunner.query(`INSERT INTO "temporary_database_cloud_details"("id", "cloudId", "subscriptionType", "planMemoryLimit", "memoryLimitMeasurementUnit", "databaseId") SELECT "id", "cloudId", "subscriptionType", "planMemoryLimit", "memoryLimitMeasurementUnit", "databaseId" FROM "database_cloud_details"`);
10+
await queryRunner.query(`DROP TABLE "database_cloud_details"`);
11+
await queryRunner.query(`ALTER TABLE "temporary_database_cloud_details" RENAME TO "database_cloud_details"`);
12+
}
13+
14+
public async down(queryRunner: QueryRunner): Promise<void> {
15+
await queryRunner.query(`ALTER TABLE "database_cloud_details" RENAME TO "temporary_database_cloud_details"`);
16+
await queryRunner.query(`CREATE TABLE "database_cloud_details" ("id" varchar PRIMARY KEY NOT NULL, "cloudId" integer NOT NULL, "subscriptionType" varchar NOT NULL, "planMemoryLimit" integer, "memoryLimitMeasurementUnit" integer, "databaseId" varchar, CONSTRAINT "REL_f41ee5027391b3be8ad95e3d15" UNIQUE ("databaseId"))`);
17+
await queryRunner.query(`INSERT INTO "database_cloud_details"("id", "cloudId", "subscriptionType", "planMemoryLimit", "memoryLimitMeasurementUnit", "databaseId") SELECT "id", "cloudId", "subscriptionType", "planMemoryLimit", "memoryLimitMeasurementUnit", "databaseId" FROM "temporary_database_cloud_details"`);
18+
await queryRunner.query(`DROP TABLE "temporary_database_cloud_details"`);
19+
await queryRunner.query(`DROP TABLE "database_cloud_details"`);
20+
}
21+
22+
}

redisinsight/api/migration/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { databaseRecommendations1681900503586 } from './1681900503586-database-r
3333
import { databaseRecommendationParams1683006064293 } from './1683006064293-database-recommendation-params';
3434
import { Feature1684931530343 } from './1684931530343-feature';
3535
import { DatabaseRedisServer1686719451753 } from './1686719451753-database-redis-server';
36+
import { CloudDatabaseDetails1687166457712 } from './1687166457712-cloud-database-details';
3637

3738
export default [
3839
initialMigration1614164490968,
@@ -70,4 +71,5 @@ export default [
7071
databaseRecommendationParams1683006064293,
7172
Feature1684931530343,
7273
DatabaseRedisServer1686719451753,
74+
CloudDatabaseDetails1687166457712,
7375
];

redisinsight/api/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@
143143
".spec.ts$"
144144
],
145145
"testEnvironment": "node",
146+
"setupFilesAfterEnv": ["<rootDir>/../.jest.setup.ts"],
146147
"moduleNameMapper": {
147148
"src/(.*)": "<rootDir>/$1",
148149
"apiSrc/(.*)": "<rootDir>/$1",
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
import {
2+
CloudAccountInfo,
3+
CloudDatabase, CloudDatabaseProtocol,
4+
CloudDatabaseStatus,
5+
CloudSubscription,
6+
CloudSubscriptionStatus, CloudSubscriptionType, ICloudApiAccount, ICloudApiDatabase, ICloudApiSubscription,
7+
} from 'src/modules/cloud/autodiscovery/models';
8+
import {
9+
AddCloudDatabaseDto, AddCloudDatabaseResponse,
10+
CloudAuthDto,
11+
GetCloudSubscriptionDatabaseDto,
12+
GetCloudSubscriptionDatabasesDto,
13+
} from 'src/modules/cloud/autodiscovery/dto';
14+
import { ActionStatus } from 'src/common/models';
15+
16+
export const mockCloudApiAccount: ICloudApiAccount = {
17+
id: 40131,
18+
name: 'Redis Labs',
19+
createdTimestamp: '2018-12-23T15:15:31Z',
20+
updatedTimestamp: '2020-06-03T13:16:59Z',
21+
key: {
22+
name: 'QA-HashedIn-Test-API-Key-2',
23+
accountId: 40131,
24+
accountName: 'Redis Labs',
25+
allowedSourceIps: ['0.0.0.0/0'],
26+
createdTimestamp: '2020-04-06T09:22:38Z',
27+
owner: {
28+
name: 'Cloud Account',
29+
30+
},
31+
httpSourceIp: '198.141.36.229',
32+
},
33+
};
34+
35+
export const mockCloudAccountInfo = Object.assign(new CloudAccountInfo(), {
36+
accountId: mockCloudApiAccount.id,
37+
accountName: mockCloudApiAccount.name,
38+
ownerEmail: mockCloudApiAccount.key.owner.email,
39+
ownerName: mockCloudApiAccount.key.owner.name,
40+
});
41+
42+
export const mockCloudApiSubscription: ICloudApiSubscription = {
43+
id: 108353,
44+
name: 'external CA',
45+
status: CloudSubscriptionStatus.Active,
46+
paymentMethodId: 8240,
47+
memoryStorage: 'ram',
48+
storageEncryption: false,
49+
numberOfDatabases: 7,
50+
subscriptionPricing: [
51+
{
52+
type: 'Shards',
53+
typeDetails: 'high-throughput',
54+
quantity: 2,
55+
quantityMeasurement: 'shards',
56+
pricePerUnit: 0.124,
57+
priceCurrency: 'USD',
58+
pricePeriod: 'hour',
59+
},
60+
],
61+
cloudDetails: [
62+
{
63+
provider: 'AWS',
64+
cloudAccountId: 16424,
65+
totalSizeInGb: 0.0323,
66+
regions: [
67+
{
68+
region: 'us-east-1',
69+
networking: [
70+
{
71+
deploymentCIDR: '10.0.0.0/24',
72+
subnetId: 'subnet-0a2dd5829daf83024',
73+
},
74+
],
75+
preferredAvailabilityZones: ['us-east-1a'],
76+
multipleAvailabilityZones: false,
77+
},
78+
],
79+
},
80+
],
81+
};
82+
83+
export const mockCloudSubscription = Object.assign(new CloudSubscription(), {
84+
id: mockCloudApiSubscription.id,
85+
type: CloudSubscriptionType.Flexible,
86+
name: mockCloudApiSubscription.name,
87+
numberOfDatabases: mockCloudApiSubscription.numberOfDatabases,
88+
provider: mockCloudApiSubscription.cloudDetails[0].provider,
89+
region: mockCloudApiSubscription.cloudDetails[0].regions[0].region,
90+
status: mockCloudApiSubscription.status,
91+
});
92+
93+
export const mockCloudSubscriptionFixed = Object.assign(new CloudSubscription(), {
94+
...mockCloudSubscription,
95+
type: CloudSubscriptionType.Fixed,
96+
});
97+
98+
export const mockCloudApiDatabase: ICloudApiDatabase = {
99+
databaseId: 50859754,
100+
name: 'bdb',
101+
protocol: CloudDatabaseProtocol.Redis,
102+
provider: 'GCP',
103+
region: 'us-central1',
104+
redisVersionCompliance: '5.0.5',
105+
status: CloudDatabaseStatus.Active,
106+
memoryLimitInGb: 1.0,
107+
memoryUsedInMb: 6.0,
108+
memoryStorage: 'ram',
109+
supportOSSClusterApi: false,
110+
dataPersistence: 'none',
111+
replication: true,
112+
dataEvictionPolicy: 'volatile-lru',
113+
throughputMeasurement: {
114+
by: 'operations-per-second',
115+
value: 25000,
116+
},
117+
activatedOn: '2019-12-31T09:38:41Z',
118+
lastModified: '2019-12-31T09:38:41Z',
119+
publicEndpoint:
120+
'redis-14621.c34097.us-central1-mz.gcp.qa-cloud.rlrcp.com:14621',
121+
privateEndpoint:
122+
'redis-14621.internal.c34097.us-central1-mz.gcp.qa-cloud.rlrcp.com:14621',
123+
replicaOf: {
124+
endpoints: [
125+
'redis-19669.c9244.us-central1-mz.gcp.cloud.rlrcp.com:19669',
126+
'redis-14074.c9243.us-central1-mz.gcp.cloud.rlrcp.com:14074',
127+
],
128+
},
129+
clustering: {
130+
numberOfShards: 1,
131+
regexRules: [],
132+
hashingPolicy: 'standard',
133+
},
134+
security: {
135+
sslClientAuthentication: false,
136+
sourceIps: ['0.0.0.0/0'],
137+
},
138+
modules: [
139+
{
140+
id: 1,
141+
name: 'ReJSON',
142+
version: 'v10007',
143+
},
144+
],
145+
alerts: [],
146+
};
147+
148+
export const mockCloudApiDatabaseFixed: ICloudApiDatabase = {
149+
...mockCloudApiDatabase,
150+
protocol: CloudDatabaseProtocol.Stack,
151+
planMemoryLimit: 256,
152+
memoryLimitMeasurementUnit: 'MB',
153+
};
154+
155+
export const mockCloudDatabase = Object.assign(new CloudDatabase(), {
156+
subscriptionId: mockCloudSubscription.id,
157+
subscriptionType: CloudSubscriptionType.Flexible,
158+
databaseId: mockCloudApiDatabase.databaseId,
159+
name: mockCloudApiDatabase.name,
160+
publicEndpoint: mockCloudApiDatabase.publicEndpoint,
161+
status: mockCloudApiDatabase.status,
162+
sslClientAuthentication: false,
163+
modules: ['ReJSON'],
164+
options: {
165+
enabledBackup: false,
166+
enabledClustering: false,
167+
enabledDataPersistence: false,
168+
enabledRedisFlash: false,
169+
enabledReplication: true,
170+
isReplicaDestination: true,
171+
persistencePolicy: 'none',
172+
},
173+
cloudDetails: {
174+
cloudId: mockCloudApiDatabase.databaseId,
175+
subscriptionType: CloudSubscriptionType.Flexible,
176+
},
177+
});
178+
179+
export const mockCloudDatabaseFixed = Object.assign(new CloudDatabase(), {
180+
...mockCloudDatabase,
181+
subscriptionType: CloudSubscriptionType.Fixed,
182+
cloudDetails: {
183+
cloudId: mockCloudApiDatabase.databaseId,
184+
subscriptionType: CloudSubscriptionType.Fixed,
185+
planMemoryLimit: mockCloudApiDatabaseFixed.planMemoryLimit,
186+
memoryLimitMeasurementUnit: mockCloudApiDatabaseFixed.memoryLimitMeasurementUnit,
187+
},
188+
});
189+
190+
export const mockCloudDatabaseFromList = Object.assign(new CloudDatabase(), {
191+
...mockCloudDatabase,
192+
options: {
193+
...mockCloudDatabase.options,
194+
isReplicaSource: false,
195+
},
196+
});
197+
198+
export const mockCloudDatabaseFromListFixed = Object.assign(new CloudDatabase(), {
199+
...mockCloudDatabaseFixed,
200+
options: {
201+
...mockCloudDatabaseFixed.options,
202+
isReplicaSource: false,
203+
},
204+
});
205+
206+
export const mockCloudApiSubscriptionDatabases = {
207+
accountId: mockCloudAccountInfo.accountId,
208+
subscription: [
209+
{
210+
subscriptionId: mockCloudSubscription.id,
211+
numberOfDatabases: mockCloudSubscription.numberOfDatabases,
212+
databases: [mockCloudApiDatabase],
213+
},
214+
],
215+
};
216+
217+
export const mockCloudApiSubscriptionDatabasesFixed = {
218+
...mockCloudApiSubscriptionDatabases,
219+
subscription: {
220+
...mockCloudApiSubscriptionDatabases.subscription[0],
221+
databases: [mockCloudApiDatabaseFixed],
222+
},
223+
};
224+
225+
export const mockCloudAuthDto: CloudAuthDto = {
226+
apiKey: 'api_key',
227+
apiSecret: 'api_secret_key',
228+
};
229+
230+
export const mockGetCloudSubscriptionDatabasesDto = Object.assign(new GetCloudSubscriptionDatabasesDto(), {
231+
subscriptionId: mockCloudSubscription.id,
232+
subscriptionType: mockCloudSubscription.type,
233+
});
234+
235+
export const mockGetCloudSubscriptionDatabasesDtoFixed = Object.assign(new GetCloudSubscriptionDatabasesDto(), {
236+
subscriptionId: mockCloudSubscription.id,
237+
subscriptionType: CloudSubscriptionType.Fixed,
238+
});
239+
240+
export const mockGetCloudSubscriptionDatabaseDto = Object.assign(new GetCloudSubscriptionDatabaseDto(), {
241+
subscriptionId: mockCloudSubscription.id,
242+
subscriptionType: mockCloudSubscription.type,
243+
databaseId: mockCloudDatabase.databaseId,
244+
});
245+
246+
export const mockGetCloudSubscriptionDatabaseDtoFixed = Object.assign(new GetCloudSubscriptionDatabaseDto(), {
247+
...mockGetCloudSubscriptionDatabaseDto,
248+
subscriptionType: mockCloudSubscriptionFixed.type,
249+
});
250+
251+
export const mockAddCloudDatabaseDto = Object.assign(new AddCloudDatabaseDto(), {
252+
...mockGetCloudSubscriptionDatabaseDto,
253+
});
254+
255+
export const mockAddCloudDatabaseDtoFixed = Object.assign(new AddCloudDatabaseDto(), {
256+
...mockGetCloudSubscriptionDatabaseDto,
257+
subscriptionType: CloudSubscriptionType.Fixed,
258+
});
259+
260+
export const mockAddCloudDatabaseResponse = Object.assign(new AddCloudDatabaseResponse(), {
261+
...mockAddCloudDatabaseDto,
262+
status: ActionStatus.Success,
263+
message: 'Added',
264+
databaseDetails: mockCloudDatabase,
265+
});
266+
267+
export const mockAddCloudDatabaseResponseFixed = Object.assign(new AddCloudDatabaseResponse(), {
268+
...mockAddCloudDatabaseDtoFixed,
269+
status: ActionStatus.Success,
270+
message: 'Added',
271+
databaseDetails: mockCloudDatabaseFixed,
272+
});
273+
274+
export const mockCloudAutodiscoveryAnalytics = jest.fn(() => ({
275+
sendGetRECloudSubsSucceedEvent: jest.fn(),
276+
sendGetRECloudSubsFailedEvent: jest.fn(),
277+
sendGetRECloudDbsSucceedEvent: jest.fn(),
278+
sendGetRECloudDbsFailedEvent: jest.fn(),
279+
}));

redisinsight/api/src/__mocks__/databases.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515
mockSshOptionsPrivateKey,
1616
mockSshOptionsPrivateKeyEntity,
1717
} from 'src/__mocks__/ssh';
18+
import { CloudDatabaseDetails, CloudSubscriptionType } from 'src/modules/cloud/autodiscovery/models';
19+
import { CloudDatabaseDetailsEntity } from 'src/modules/cloud/autodiscovery/entities/cloud-database-details.entity';
1820

1921
export const mockDatabaseId = 'a77b23c1-7816-4ea4-b61f-d37795a0f805-db-id';
2022

@@ -38,11 +40,31 @@ export const mockDatabase = Object.assign(new Database(), {
3840
version: '7.0',
3941
});
4042

43+
export const mockDatabaseCloudDetails = Object.assign(new CloudDatabaseDetails(), {
44+
subscriptionType: CloudSubscriptionType.Fixed,
45+
cloudId: 500001,
46+
planMemoryLimit: 256,
47+
memoryLimitMeasurementUnit: 'MB',
48+
});
49+
50+
export const mockDatabaseWithCloudDetails = Object.assign(new Database(), {
51+
...mockDatabase,
52+
cloudDetails: mockDatabaseCloudDetails,
53+
});
54+
4155
export const mockDatabaseEntity = Object.assign(new DatabaseEntity(), {
4256
...mockDatabase,
4357
encryption: null,
4458
});
4559

60+
export const mockDatabaseEntityWithCloudDetails = Object.assign(new DatabaseEntity(), {
61+
...mockDatabaseEntity,
62+
cloudDetails: Object.assign(new CloudDatabaseDetailsEntity(), {
63+
id: 'some-uuid',
64+
...mockDatabaseCloudDetails,
65+
}),
66+
});
67+
4668
export const mockDatabaseWithSshBasic = Object.assign(new Database(), {
4769
...mockDatabase,
4870
ssh: true,

0 commit comments

Comments
 (0)