Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
791e660
#RI-4488 - fe part for feature flags
rsergeenko May 8, 2023
801e3ad
#RI-4489 initial implementation of features flags
May 10, 2023
5b1d863
change response structure
May 10, 2023
1860bbf
#RI-4498 - update telemetry events
rsergeenko May 12, 2023
8d01880
#RI-4498 - update telemetry params
rsergeenko May 15, 2023
8ce8cad
#RI-4489 add filters to beta features + rework structure
May 15, 2023
60272e0
fix tests
rsergeenko May 15, 2023
23eb2e2
#RI-4489 fix transformer
May 15, 2023
b236a5d
Merge pull request #2067 from RedisInsight/fe/feature/RI-4399-beta_fe…
rsergeenko May 15, 2023
481f357
#RI-4489 fix conf update
May 15, 2023
04854c2
Merge branch 'feature/RI-4399-beta_features' into be/feature/RI-4489-…
May 15, 2023
f62347b
add tests for hide recommendations
vlad-dargel May 15, 2023
5a77fb4
#RI-4489 add migrations + start UTests
May 15, 2023
42291ae
Merge branch 'be/feature/RI-4489-beta_features' into e2e/feature/RI-4…
vlad-dargel May 15, 2023
ceb2482
#RI-4489 UTests + endpoint for manually sync
May 15, 2023
19ecdd7
fix for changeAnalyticsSwitcher
vlad-dargel May 15, 2023
f8bc420
Merge branch 'be/feature/RI-4489-beta_features' into e2e/feature/RI-4…
vlad-dargel May 15, 2023
dae96f8
fix
vlad-dargel May 15, 2023
e456b03
add changes
vlad-dargel May 17, 2023
58bade1
fix
vlad-dargel May 17, 2023
1d009f8
create static server container
vlad-dargel May 17, 2023
46b222f
fix for dockerfile
vlad-dargel May 17, 2023
18659c1
upd
vlad-dargel May 17, 2023
2e42da3
updates for static server
vlad-dargel May 17, 2023
f2bc70c
fix
vlad-dargel May 17, 2023
1203908
upd of env variable
vlad-dargel May 17, 2023
228fbce
Merge branch 'e2e/feature/RI-4399-hide_recommendations_for_users' of …
vlad-dargel May 17, 2023
d04b63e
upd features config
vlad-dargel May 18, 2023
105c91d
#RI-4489 filters + UTests + reworks
May 18, 2023
7deef95
Merge branch 'be/feature/RI-4489-beta_features' into e2e/feature/RI-4…
vlad-dargel May 18, 2023
aa46166
upd
vlad-dargel May 18, 2023
903885d
fix
vlad-dargel May 18, 2023
5a4b9ff
fix
vlad-dargel May 18, 2023
4dc57ab
Merge branch 'be/feature/RI-4489-beta_features' into e2e/feature/RI-4…
vlad-dargel May 18, 2023
ca85515
#RI-4489 fix bind issue + UTests
May 18, 2023
81bf91c
Merge branch 'be/feature/RI-4489-beta_features' into e2e/feature/RI-4…
vlad-dargel May 18, 2023
a81e288
#RI-4489 UTests (complete) + analytics + rename feature
May 21, 2023
1359f38
#4399 - rename feature
rsergeenko May 22, 2023
e44c426
#RI-4489 ITests
May 22, 2023
7e81895
#RI-4489 fix Itests
May 22, 2023
7bc7895
Merge branch 'feature/RI-4399-beta_features' into be/feature/RI-4489-…
May 22, 2023
851cbde
Merge branch 'be/feature/RI-4489-beta_features' into e2e/feature/RI-4…
vlad-dargel May 22, 2023
70ae489
console log added
vlad-dargel May 22, 2023
bd7597b
Merge branch 'e2e/feature/RI-4399-hide_recommendations_for_users' of …
vlad-dargel May 22, 2023
ccd9e40
add tests with changable static server file
vlad-dargel May 22, 2023
6cc96f7
#RI-4489 fix ITests
May 22, 2023
390073b
Merge pull request #2076 from RedisInsight/be/feature/RI-4489-beta_fe…
May 22, 2023
cda0209
Merge branch 'feature/RI-4075_livetime_recommendations' into feature/…
May 22, 2023
0b87084
updates for filters
vlad-dargel May 22, 2023
357e50d
Merge branch 'feature/RI-4399-beta_features' into e2e/feature/RI-4399…
vlad-dargel May 22, 2023
dadb810
updates for static server config
vlad-dargel May 23, 2023
5980939
update
vlad-dargel May 23, 2023
e3f77b3
upd
vlad-dargel May 23, 2023
9c30e13
fixes
vlad-dargel May 23, 2023
dbf28c1
fix for fs feature config sync
vlad-dargel May 23, 2023
22c4262
add fs-extra
vlad-dargel May 23, 2023
347cf80
add logs for debug
vlad-dargel May 23, 2023
0bec784
#RI-4399 fix tests
May 23, 2023
72acec1
return parallelism and updates for live recommendations
vlad-dargel May 23, 2023
6ef9fd2
fix for live recommendations
vlad-dargel May 24, 2023
bcd02bb
Merge branch 'feature/RI-4075_livetime_recommendations' into feature/…
May 24, 2023
8bf7895
#RI-4399 fix UTests + ITests (code)
May 24, 2023
e2f6906
#RI-4399 fix recommendations ITests (todo: investigate false vs undef…
May 24, 2023
e144bf6
run all tests to verify
May 24, 2023
6feebe4
fix for live rec
vlad-dargel May 24, 2023
1d8867d
Merge branch 'feature/RI-4399-beta_features' into e2e/feature/RI-4399…
vlad-dargel May 24, 2023
278d332
fixes for failed tests
vlad-dargel May 24, 2023
956d857
fix unkown aliases (probably docker DNS conf)
May 24, 2023
c26330b
rollback circleci config
May 24, 2023
9c7640d
#RI-4568 fix number type to be float in local db for conrtolNumber
May 24, 2023
3707aea
Merge pull request #2127 from RedisInsight/be/feature/fix-tests
May 24, 2023
f2cf825
Merge pull request #2125 from RedisInsight/e2e/feature/RI-4399-hide_r…
vlad-dargel May 24, 2023
e149059
wording fix and some unstable tests
vlad-dargel May 24, 2023
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
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Add reviewers for the most sensitive folders
/.github/ egor.zalenski@softeq.com artem.horuzhenko@softeq.com
/.circleci/ egor.zalenski@softeq.com artem.horuzhenko@softeq.com
/redisinsight/api/config/features-config.json viktar.starastsenka@redis.com egor.zalenski@softeq.com artem.horuzhenko@softeq.com
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ vendor

# E2E tests report
/tests/e2e/report
/tests/e2e/results
/tests/e2e/remote
/tests/e2e/.redisinsight-v2

# Parcel
Expand Down
8 changes: 7 additions & 1 deletion redisinsight/api/config/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,12 +195,18 @@ export default {
},
],
connections: {
timeout: parseInt(process.env.CONNECTIONS_TIMEOUT_DEFAULT, 10) || 30 * 1_000 // 30 sec
timeout: parseInt(process.env.CONNECTIONS_TIMEOUT_DEFAULT, 10) || 30 * 1_000, // 30 sec
},
redisStack: {
id: process.env.BUILD_TYPE === 'REDIS_STACK' ? process.env.REDIS_STACK_DATABASE_ID || 'redis-stack' : undefined,
name: process.env.REDIS_STACK_DATABASE_NAME,
host: process.env.REDIS_STACK_DATABASE_HOST,
port: process.env.REDIS_STACK_DATABASE_PORT,
},
features_config: {
url: process.env.RI_FEATURES_CONFIG_URL
// eslint-disable-next-line max-len
|| 'https://raw.githubusercontent.com/RedisInsight/RedisInsight/main/redisinsight/api/config/features-config.json',
syncInterval: parseInt(process.env.RI_FEATURES_CONFIG_SYNC_INTERVAL, 10) || 1_000 * 60 * 60 * 4, // 4h
},
};
21 changes: 21 additions & 0 deletions redisinsight/api/config/features-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"version": 1,
"features": {
"insightsRecommendations": {
"flag": true,
"perc": [],
"filters": [
{
"name": "agreements.analytics",
"value": true,
"cond": "eq"
},
{
"name": "config.server.buildType",
"value": "ELECTRON",
"cond": "eq"
}
]
}
}
}
4 changes: 4 additions & 0 deletions redisinsight/api/config/ormconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { DatabaseEntity } from 'src/modules/database/entities/database.entity';
import { SshOptionsEntity } from 'src/modules/ssh/entities/ssh-options.entity';
import { BrowserHistoryEntity } from 'src/modules/browser/entities/browser-history.entity';
import { CustomTutorialEntity } from 'src/modules/custom-tutorial/entities/custom-tutorial.entity';
import { FeatureEntity } from 'src/modules/feature/entities/feature.entity';
import { FeaturesConfigEntity } from 'src/modules/feature/entities/features-config.entity';
import migrations from '../migration';
import * as config from '../src/utils/config';

Expand All @@ -40,6 +42,8 @@ const ormConfig = {
BrowserHistoryEntity,
SshOptionsEntity,
CustomTutorialEntity,
FeatureEntity,
FeaturesConfigEntity,
],
migrations,
};
Expand Down
13 changes: 11 additions & 2 deletions redisinsight/api/config/test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
export default {
server: {
env: 'test',
requestTimeout: 1000,
requestTimeout: parseInt(process.env.REQUEST_TIMEOUT, 10) || 1000,
},
db: {
synchronize: process.env.DB_SYNC ? process.env.DB_SYNC === 'true' : true,
migrationsRun: process.env.DB_MIGRATIONS ? process.env.DB_MIGRATIONS === 'true' : false,
},
profiler: {
logFileIdleThreshold: parseInt(process.env.PROFILER_LOG_FILE_IDLE_THRESHOLD, 10) || 1000 * 2, // 3sec
},
notifications: {
updateUrl: 'https://s3.amazonaws.com/redisinsight.test/public/tests/notifications.json',
updateUrl: process.env.NOTIFICATION_UPDATE_URL
|| 'https://s3.amazonaws.com/redisinsight.test/public/tests/notifications.json',
},
features_config: {
url: process.env.RI_FEATURES_CONFIG_URL
|| 'http://localhost:5551/remote/features-config.json',
},
};
16 changes: 16 additions & 0 deletions redisinsight/api/migration/1684931530343-feature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class Feature1684931530343 implements MigrationInterface {
name = 'Feature1684931530343'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "features" ("name" varchar PRIMARY KEY NOT NULL, "flag" boolean NOT NULL)`);
await queryRunner.query(`CREATE TABLE "features_config" ("id" varchar PRIMARY KEY NOT NULL, "controlNumber" float, "data" varchar NOT NULL, "updatedAt" datetime NOT NULL DEFAULT (datetime('now')))`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "features_config"`);
await queryRunner.query(`DROP TABLE "features"`);
}

}
2 changes: 2 additions & 0 deletions redisinsight/api/migration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { databaseCompressor1678182722874 } from './1678182722874-database-compre
import { customTutorials1677135091633 } from './1677135091633-custom-tutorials';
import { databaseRecommendations1681900503586 } from './1681900503586-database-recommendations';
import { databaseRecommendationParams1683006064293 } from './1683006064293-database-recommendation-params';
import { Feature1684931530343 } from './1684931530343-feature';

export default [
initialMigration1614164490968,
Expand Down Expand Up @@ -66,4 +67,5 @@ export default [
customTutorials1677135091633,
databaseRecommendations1681900503586,
databaseRecommendationParams1683006064293,
Feature1684931530343,
];
2 changes: 1 addition & 1 deletion redisinsight/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand -w 1",
"test:e2e": "jest --config ./test/jest-e2e.json -w 1",
"typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -d ./config/ormconfig.ts",
"test:api": "ts-mocha --paths -p test/api/api.tsconfig.json --config ./test/api/.mocharc.yml",
"test:api": "cross-env NODE_ENV=test ts-mocha --paths -p test/api/api.tsconfig.json --config ./test/api/.mocharc.yml",
"test:api:cov": "nyc --reporter=html --reporter=text --reporter=text-summary yarn run test:api",
"test:api:ci:cov": "nyc -r text -r text-summary -r html yarn run test:api --reporter mocha-multi-reporters --reporter-options configFile=test/api/reporters.json && nyc merge .nyc_output ./coverage/test-run-coverage.json",
"typeorm:migrate": "cross-env NODE_ENV=staging yarn typeorm migration:generate ./migration/migration",
Expand Down
1 change: 1 addition & 0 deletions redisinsight/api/src/__mocks__/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export const mockRepository = jest.fn(() => ({
save: jest.fn(),
insert: jest.fn(),
update: jest.fn(),
upsert: jest.fn(),
delete: jest.fn(),
remove: jest.fn(),
createQueryBuilder: mockCreateQueryBuilder,
Expand Down
219 changes: 219 additions & 0 deletions redisinsight/api/src/__mocks__/feature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
import { FeaturesConfigEntity } from 'src/modules/feature/entities/features-config.entity';
import {
FeatureConfig,
FeatureConfigFilter, FeatureConfigFilterAnd, FeatureConfigFilterOr,
FeaturesConfig,
FeaturesConfigData,
} from 'src/modules/feature/model/features-config';
import { classToClass } from 'src/utils';
import { Feature } from 'src/modules/feature/model/feature';
import { FeatureEntity } from 'src/modules/feature/entities/feature.entity';
import { mockAppSettings } from 'src/__mocks__/app-settings';
import config from 'src/utils/config';
import { KnownFeatures } from 'src/modules/feature/constants';
import * as defaultConfig from '../../config/features-config.json';

export const mockFeaturesConfigId = '1';
export const mockFeaturesConfigVersion = defaultConfig.version + 0.111;
export const mockControlNumber = 7.68;
export const mockControlGroup = '7';

export const mockFeaturesConfigJson = {
version: mockFeaturesConfigVersion,
features: {
[KnownFeatures.InsightsRecommendations]: {
perc: [[1.25, 8.45]],
flag: true,
filters: [
{
name: 'agreements.analytics',
value: true,
cond: 'eq',
},
],
},
},
};

export const mockFeaturesConfigJsonComplex = {
...mockFeaturesConfigJson,
features: {
[KnownFeatures.InsightsRecommendations]: {
...mockFeaturesConfigJson.features[KnownFeatures.InsightsRecommendations],
filters: [
{
or: [
{
name: 'settings.testValue',
value: 'test',
cond: 'eq',
},
{
and: [
{
name: 'agreements.analytics',
value: true,
cond: 'eq',
},
{
or: [
{
name: 'settings.scanThreshold',
value: mockAppSettings.scanThreshold,
cond: 'eq',
},
{
name: 'settings.batchSize',
value: mockAppSettings.batchSize,
cond: 'eq',
},
],
},
],
},
],
},
],
},
},
};

export const mockFeaturesConfigData = Object.assign(new FeaturesConfigData(), {
...mockFeaturesConfigJson,
features: new Map(Object.entries({
[KnownFeatures.InsightsRecommendations]: Object.assign(new FeatureConfig(), {
...mockFeaturesConfigJson.features[KnownFeatures.InsightsRecommendations],
filters: [
Object.assign(new FeatureConfigFilter(), {
...mockFeaturesConfigJson.features[KnownFeatures.InsightsRecommendations].filters[0],
}),
],
}),
})),
});

export const mockFeaturesConfigDataComplex = Object.assign(new FeaturesConfigData(), {
...mockFeaturesConfigJson,
features: new Map(Object.entries({
[KnownFeatures.InsightsRecommendations]: Object.assign(new FeatureConfig(), {
...mockFeaturesConfigJson.features[KnownFeatures.InsightsRecommendations],
filters: [
Object.assign(new FeatureConfigFilterOr(), {
or: [
Object.assign(new FeatureConfigFilter(), {
name: 'settings.testValue',
value: 'test',
cond: 'eq',
}),
Object.assign(new FeatureConfigFilterAnd(), {
and: [
Object.assign(new FeatureConfigFilter(), {
name: 'agreements.analytics',
value: true,
cond: 'eq',
}),
Object.assign(new FeatureConfigFilterOr(), {
or: [
Object.assign(new FeatureConfigFilter(), {
name: 'settings.scanThreshold',
value: mockAppSettings.scanThreshold,
cond: 'eq',
}),
Object.assign(new FeatureConfigFilter(), {
name: 'settings.batchSize',
value: mockAppSettings.batchSize,
cond: 'eq',
}),
],
}),
],
}),
],
}),
],
}),
})),
});

export const mockFeaturesConfig = Object.assign(new FeaturesConfig(), {
controlNumber: mockControlNumber,
data: mockFeaturesConfigData,
});

export const mockFeaturesConfigComplex = Object.assign(new FeaturesConfig(), {
controlNumber: mockControlNumber,
data: mockFeaturesConfigDataComplex,
});

export const mockFeaturesConfigEntity = Object.assign(new FeaturesConfigEntity(), {
...classToClass(FeaturesConfigEntity, mockFeaturesConfig),
id: mockFeaturesConfigId,
});

export const mockFeaturesConfigEntityComplex = Object.assign(new FeaturesConfigEntity(), {
...classToClass(FeaturesConfigEntity, mockFeaturesConfigComplex),
id: mockFeaturesConfigId,
});

export const mockFeature = Object.assign(new Feature(), {
name: KnownFeatures.InsightsRecommendations,
flag: true,
});

export const mockUnknownFeature = Object.assign(new Feature(), {
name: 'unknown',
flag: true,
});

export const mockFeatureEntity = Object.assign(new FeatureEntity(), {
id: 'lr-1',
name: KnownFeatures.InsightsRecommendations,
flag: true,
});

export const mockServerState = {
settings: mockAppSettings,
agreements: mockAppSettings.agreements,
config: config.get(),
env: process.env,
};

export const mockFeaturesConfigRepository = jest.fn(() => ({
getOrCreate: jest.fn().mockResolvedValue(mockFeaturesConfig),
update: jest.fn().mockResolvedValue(mockFeaturesConfig),
}));

export const mockFeatureRepository = jest.fn(() => ({
get: jest.fn().mockResolvedValue(mockFeature),
upsert: jest.fn().mockResolvedValue({ updated: 1 }),
list: jest.fn().mockResolvedValue([mockFeature]),
delete: jest.fn().mockResolvedValue({ deleted: 1 }),
}));

export const mockFeaturesConfigService = jest.fn(() => ({
sync: jest.fn(),
getControlInfo: jest.fn().mockResolvedValue({
controlNumber: mockControlNumber,
controlGroup: mockControlGroup,
}),
}));

export const mockFeatureService = jest.fn(() => ({
isFeatureEnabled: jest.fn().mockResolvedValue(true),
}));

export const mockFeatureAnalytics = jest.fn(() => ({
sendFeatureFlagConfigUpdated: jest.fn(),
sendFeatureFlagConfigUpdateError: jest.fn(),
sendFeatureFlagInvalidRemoteConfig: jest.fn(),
sendFeatureFlagRecalculated: jest.fn(),
}));

export const mockInsightsRecommendationsFlagStrategy = {
calculate: jest.fn().mockResolvedValue(true),
};

export const mockFeatureFlagProvider = jest.fn(() => ({
getStrategy: jest.fn().mockResolvedValue(mockInsightsRecommendationsFlagStrategy),
calculate: jest.fn().mockResolvedValue(true),
}));
1 change: 1 addition & 0 deletions redisinsight/api/src/__mocks__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ export * from './redis-client';
export * from './ssh';
export * from './browser-history';
export * from './database-recommendation';
export * from './feature';
2 changes: 2 additions & 0 deletions redisinsight/api/src/common/decorators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export * from './default';
export * from './data-as-json-string.decorator';
export * from './session';
export * from './client-metadata';
export * from './object-as-map.decorator';
export * from './is-multi-number.decorator';
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {
registerDecorator,
ValidationOptions,
} from 'class-validator';
import { MultiNumberValidator } from 'src/common/validators';

export function IsMultiNumber(validationOptions?: ValidationOptions) {
return (object: any, propertyName: string) => {
registerDecorator({
name: 'IsMultiNumber',
target: object.constructor,
propertyName,
options: validationOptions,
validator: MultiNumberValidator,
});
};
}
Loading