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
18 changes: 17 additions & 1 deletion redisinsight/api/config/features-config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": 2.3402,
"version": 2.3403,
"features": {
"insightsRecommendations": {
"flag": true,
Expand Down Expand Up @@ -47,6 +47,22 @@
}
}
},
"cloudSsoRecommendedSettings": {
"flag": true,
"perc": [[0, 50]],
"filters": [
{
"name": "config.server.buildType",
"value": "ELECTRON",
"cond": "eq"
},
{
"name": "agreements.analytics",
"value": true,
"cond": "eq"
}
]
},
"redisModuleFilter": {
"flag": true,
"perc": [[0, 100]],
Expand Down
6 changes: 3 additions & 3 deletions redisinsight/api/src/__mocks__/feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,12 +182,12 @@ export const mockFeatureSso = Object.assign(new Feature(), {
redisStackPreview: [
{
provider: 'AWS',
regions: ['us-east-2', 'ap-southeast-1', 'sa-east-1']
regions: ['us-east-2', 'ap-southeast-1', 'sa-east-1'],
},
{
provider: 'GCP',
regions: ['asia-northeast1', 'europe-west1', 'us-central1']
}
regions: ['asia-northeast1', 'europe-west1', 'us-central1'],
},
],
},
},
Expand Down
5 changes: 4 additions & 1 deletion redisinsight/api/src/modules/cloud/job/cloud-job.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
import {
CloudJob,
CreateFreeSubscriptionAndDatabaseCloudJob,
ImportFreeDatabaseCloudJob
ImportFreeDatabaseCloudJob,
} from 'src/modules/cloud/job/jobs';
import { CloudJobName } from 'src/modules/cloud/job/constants';
import { CreateFreeDatabaseCloudJob } from 'src/modules/cloud/job/jobs/create-free-database.cloud-job';
Expand All @@ -16,6 +16,7 @@ import { DatabaseService } from 'src/modules/database/database.service';
import { CloudDatabaseAnalytics } from 'src/modules/cloud/database/cloud-database.analytics';
import { CloudRequestUtm } from 'src/modules/cloud/common/models';
import { CloudCapiKeyService } from 'src/modules/cloud/capi-key/cloud-capi-key.service';
import { CloudSubscriptionApiService } from 'src/modules/cloud/subscription/cloud-subscription.api.service';

@Injectable()
export class CloudJobFactory {
Expand All @@ -26,6 +27,7 @@ export class CloudJobFactory {
private readonly cloudDatabaseAnalytics: CloudDatabaseAnalytics,
private readonly databaseService: DatabaseService,
private readonly cloudCapiKeyService: CloudCapiKeyService,
private readonly cloudSubscriptionApiService: CloudSubscriptionApiService,
) {}

async create(
Expand Down Expand Up @@ -53,6 +55,7 @@ export class CloudJobFactory {
cloudDatabaseAnalytics: this.cloudDatabaseAnalytics,
databaseService: this.databaseService,
cloudCapiKeyService: this.cloudCapiKeyService,
cloudSubscriptionApiService: this.cloudSubscriptionApiService,
},
);
case CloudJobName.CreateFreeDatabase:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsNumber } from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsBoolean, IsNotEmpty, IsNumber, IsOptional, ValidateIf } from 'class-validator';

export class CreateSubscriptionAndDatabaseCloudJobDataDto {
@ApiProperty({
description: 'Plan id for create a subscription.',
type: Number,
})
@ValidateIf((object) => !object.isRecommendedSettings)
@IsNumber()
@IsNotEmpty()
planId: number;

@ApiPropertyOptional({
description: 'Use recommended settings',
type: Boolean,
})
@IsBoolean()
@IsOptional()
isRecommendedSettings?: boolean;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { sortBy } from 'lodash';
import { CloudJob, CloudJobOptions, CreateFreeDatabaseCloudJob } from 'src/modules/cloud/job/jobs';
import { CloudTaskCapiService } from 'src/modules/cloud/task/cloud-task.capi.service';
import { CloudSubscriptionCapiService } from 'src/modules/cloud/subscription/cloud-subscription.capi.service';
Expand All @@ -10,15 +11,18 @@ import { Database } from 'src/modules/database/models/database';
import { CloudDatabaseAnalytics } from 'src/modules/cloud/database/cloud-database.analytics';
import { CloudCapiKeyService } from 'src/modules/cloud/capi-key/cloud-capi-key.service';
import { CloudSubscription } from 'src/modules/cloud/subscription/models';
import { CloudSubscriptionApiService } from '../../subscription/cloud-subscription.api.service';
import { CloudSubscriptionPlanResponse } from '../../subscription/dto';

export class CreateFreeSubscriptionAndDatabaseCloudJob extends CloudJob {
protected name = CloudJobName.CreateFreeDatabase;

constructor(
readonly options: CloudJobOptions,

private readonly data: {
planId: number,
private data: {
planId?: number,
isRecommendedSettings?: boolean,
},

protected readonly dependencies: {
Expand All @@ -28,12 +32,15 @@ export class CreateFreeSubscriptionAndDatabaseCloudJob extends CloudJob {
cloudDatabaseAnalytics: CloudDatabaseAnalytics,
databaseService: DatabaseService,
cloudCapiKeyService: CloudCapiKeyService,
cloudSubscriptionApiService: CloudSubscriptionApiService,
},
) {
super(options);
}

async iteration(): Promise<Database> {
let planId = this.data?.planId;

this.logger.log('Create free subscription and database');

this.checkSignal();
Expand All @@ -42,9 +49,15 @@ export class CreateFreeSubscriptionAndDatabaseCloudJob extends CloudJob {

this.logger.debug('Get or create free subscription');

if (this.data?.isRecommendedSettings) {
const plans = await this.dependencies.cloudSubscriptionApiService.getSubscriptionPlans(this.options.sessionMetadata);

planId = this.getRecommendedPlanId(plans);
}

const freeSubscription: CloudSubscription = await this.runChildJob(
CreateFreeSubscriptionCloudJob,
this.data,
{ planId },
this.options,
);

Expand All @@ -68,4 +81,9 @@ export class CreateFreeSubscriptionAndDatabaseCloudJob extends CloudJob {

return database;
}

private getRecommendedPlanId(plans: CloudSubscriptionPlanResponse[]) {
const defaultPlan = sortBy(plans, ['details.displayOrder']);
return defaultPlan[0]?.id;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { CloudSubscriptionCapiProvider } from './providers/cloud-subscription.ca
],
exports: [
CloudSubscriptionCapiService,
CloudSubscriptionApiService,
],
})
export class CloudSubscriptionModule {}
1 change: 1 addition & 0 deletions redisinsight/api/src/modules/feature/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export enum FeatureConfigConfigDestination {
export enum KnownFeatures {
InsightsRecommendations = 'insightsRecommendations',
CloudSso = 'cloudSso',
CloudSsoRecommendedSettings = 'cloudSsoRecommendedSettings',
RedisModuleFilter = 'redisModuleFilter',
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export const knownFeatures: Record<KnownFeatures, IFeatureFlag> = {
storage: FeatureStorage.Database,
factory: CloudSsoFeatureFlag.getFeature,
},
[KnownFeatures.CloudSsoRecommendedSettings]: {
name: KnownFeatures.CloudSsoRecommendedSettings,
storage: FeatureStorage.Database,
},
[KnownFeatures.RedisModuleFilter]: {
name: KnownFeatures.RedisModuleFilter,
storage: FeatureStorage.Database,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { FeatureFlagProvider } from 'src/modules/feature/providers/feature-flag/
import { SettingsService } from 'src/modules/settings/settings.service';
import { KnownFeatures } from 'src/modules/feature/constants';
import {
InsightsRecommendationsFlagStrategy,
} from 'src/modules/feature/providers/feature-flag/strategies/insights-recommendations.flag.strategy';
CommonFlagStrategy,
} from 'src/modules/feature/providers/feature-flag/strategies/common.flag.strategy';
import { DefaultFlagStrategy } from 'src/modules/feature/providers/feature-flag/strategies/default.flag.strategy';
import { knownFeatures } from 'src/modules/feature/constants/known-features';

Expand All @@ -37,9 +37,13 @@ describe('FeatureFlagProvider', () => {
});

describe('getStrategy', () => {
it('should return insights strategy', async () => {
it('should return common strategy', async () => {
expect(await service.getStrategy(KnownFeatures.InsightsRecommendations))
.toBeInstanceOf(InsightsRecommendationsFlagStrategy);
.toBeInstanceOf(CommonFlagStrategy);
});
it('should return common strategy', async () => {
expect(await service.getStrategy(KnownFeatures.CloudSsoRecommendedSettings))
.toBeInstanceOf(CommonFlagStrategy);
});
it('should return default strategy when directly called', async () => {
expect(await service.getStrategy('default'))
Expand All @@ -54,7 +58,7 @@ describe('FeatureFlagProvider', () => {
describe('calculate', () => {
it('should calculate ', async () => {
jest.spyOn(service, 'getStrategy')
.mockReturnValue(mockInsightsRecommendationsFlagStrategy as unknown as InsightsRecommendationsFlagStrategy);
.mockReturnValue(mockInsightsRecommendationsFlagStrategy as unknown as CommonFlagStrategy);

expect(await service.calculate(
knownFeatures[KnownFeatures.InsightsRecommendations],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { Injectable } from '@nestjs/common';
import { FeatureFlagStrategy } from 'src/modules/feature/providers/feature-flag/strategies/feature.flag.strategy';
import {
InsightsRecommendationsFlagStrategy,
} from 'src/modules/feature/providers/feature-flag/strategies/insights-recommendations.flag.strategy';
CommonFlagStrategy,
} from 'src/modules/feature/providers/feature-flag/strategies/common.flag.strategy';
import { DefaultFlagStrategy } from 'src/modules/feature/providers/feature-flag/strategies/default.flag.strategy';
import { FeaturesConfigService } from 'src/modules/feature/features-config.service';
import { SettingsService } from 'src/modules/settings/settings.service';
import { IFeatureFlag, KnownFeatures } from 'src/modules/feature/constants';
import { CloudSsoFlagStrategy } from 'src/modules/feature/providers/feature-flag/strategies/cloud-sso.flag.strategy';
import { Feature } from 'src/modules/feature/model/feature';
import { SimpleFlagStrategy } from 'src/modules/feature/providers/feature-flag/strategies/simple.flag.strategy';
import { WithDataFlagStrategy } from 'src/modules/feature/providers/feature-flag/strategies/with-data.flag.strategy';

@Injectable()
export class FeatureFlagProvider {
Expand All @@ -23,15 +23,19 @@ export class FeatureFlagProvider {
this.featuresConfigService,
this.settingsService,
));
this.strategies.set(KnownFeatures.InsightsRecommendations, new InsightsRecommendationsFlagStrategy(
this.strategies.set(KnownFeatures.InsightsRecommendations, new CommonFlagStrategy(
this.featuresConfigService,
this.settingsService,
));
this.strategies.set(KnownFeatures.CloudSso, new CloudSsoFlagStrategy(
this.featuresConfigService,
this.settingsService,
));
this.strategies.set(KnownFeatures.RedisModuleFilter, new SimpleFlagStrategy(
this.strategies.set(KnownFeatures.CloudSsoRecommendedSettings, new CommonFlagStrategy(
this.featuresConfigService,
this.settingsService,
));
this.strategies.set(KnownFeatures.RedisModuleFilter, new WithDataFlagStrategy(
this.featuresConfigService,
this.settingsService,
));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { FeatureFlagStrategy } from 'src/modules/feature/providers/feature-flag/
import { Feature } from 'src/modules/feature/model/feature';
import { IFeatureFlag } from 'src/modules/feature/constants';

export class InsightsRecommendationsFlagStrategy extends FeatureFlagStrategy {
export class CommonFlagStrategy extends FeatureFlagStrategy {
async calculate(knownFeature: IFeatureFlag, featureConfig: any): Promise<Feature> {
const isInRange = await this.isInTargetRange(featureConfig?.perc);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { SettingsService } from 'src/modules/settings/settings.service';
import { FeaturesConfigService } from 'src/modules/feature/features-config.service';
import { FeatureFlagStrategy } from 'src/modules/feature/providers/feature-flag/strategies/feature.flag.strategy';
import {
InsightsRecommendationsFlagStrategy,
} from 'src/modules/feature/providers/feature-flag/strategies/insights-recommendations.flag.strategy';
CommonFlagStrategy,
} from 'src/modules/feature/providers/feature-flag/strategies/common.flag.strategy';
import {
FeatureConfigFilter,
FeatureConfigFilterAnd,
Expand Down Expand Up @@ -45,7 +45,7 @@ describe('FeatureFlagStrategy', () => {

settingsService = module.get(SettingsService);
featuresConfigService = module.get(FeaturesConfigService);
service = new InsightsRecommendationsFlagStrategy(
service = new CommonFlagStrategy(
featuresConfigService as unknown as FeaturesConfigService,
settingsService as unknown as SettingsService,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { FeatureFlagStrategy } from 'src/modules/feature/providers/feature-flag/
import { Feature } from 'src/modules/feature/model/feature';
import { IFeatureFlag } from 'src/modules/feature/constants';

export class SimpleFlagStrategy extends FeatureFlagStrategy {
export class WithDataFlagStrategy extends FeatureFlagStrategy {
async calculate(knownFeature: IFeatureFlag, featureConfig: any): Promise<Feature> {
const isInRange = await this.isInTargetRange(featureConfig?.perc);

Expand Down