Skip to content

Commit

Permalink
fix(dashboard): added FF to FE
Browse files Browse the repository at this point in the history
fix(dashboard): added FF to FE

fix(dashboard): added FF to FE

fix(dashboard): change default sorting

fix(api-service): compiles68457b4c79403ba223d4f360925eb0422e33dffb

fix(api-service): compiles68457b4c79403ba223d4f360925eb0422e33dffb

fix(api-service): compiles
  • Loading branch information
tatarco committed Feb 13, 2025
1 parent 2e10294 commit 0036870
Show file tree
Hide file tree
Showing 44 changed files with 1,490 additions and 651 deletions.
9 changes: 1 addition & 8 deletions .idea/runConfigurations/RUN_LOCAL_ENV.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .source
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/* eslint-disable global-require */
import sinon from 'sinon';
import { expect } from 'chai';
import { ApiServiceLevelEnum } from '@novu/shared';
// eslint-disable-next-line no-restricted-imports
import { StripeBillingIntervalEnum } from '@novu/ee-billing/src/stripe/types';
import { ApiServiceLevelEnum, StripeBillingIntervalEnum } from '@novu/shared';

const mockCheckoutSessionCompletedEvent = {
type: 'checkout.session.completed',
Expand Down Expand Up @@ -158,7 +156,7 @@ describe('webhook event - checkout.session.completed #novu-v2', () => {
throw new Error('ee-billing does not exist');
}

const { CheckoutSessionCompletedHandler, VerifyCustomer, GetPrices } = eeBilling;
const { CheckoutSessionCompletedHandler, VerifyCustomer, GetStripePlanPriceUseCase } = eeBilling;

let verifyCustomerStub: sinon.SinonStub;
let getPricesStub: sinon.SinonStub;
Expand All @@ -171,7 +169,7 @@ describe('webhook event - checkout.session.completed #novu-v2', () => {

beforeEach(() => {
verifyCustomerStub = sinon.stub(VerifyCustomer.prototype, 'execute').resolves(verifyCustomerMock);
getPricesStub = sinon.stub(GetPrices.prototype, 'execute').resolves(getPricesMock);
getPricesStub = sinon.stub(GetStripePlanPriceUseCase.prototype, 'execute').resolves(getPricesMock);
});

afterEach(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/* eslint-disable global-require */
import sinon from 'sinon';
import { expect } from 'chai';
// eslint-disable-next-line no-restricted-imports
import { StripeBillingIntervalEnum } from '@novu/ee-billing/src/stripe/types';
import { ApiServiceLevelEnum } from '@novu/shared';

import { ApiServiceLevelEnum, StripeBillingIntervalEnum } from '@novu/shared';

const checkoutSessionCreateParamsMock = {
mode: 'subscription',
Expand Down
12 changes: 4 additions & 8 deletions apps/api/src/app/billing/e2e/create-subscription.e2e-ee.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
/* eslint-disable global-require */
import sinon from 'sinon';
import { expect } from 'chai';
import { ApiServiceLevelEnum } from '@novu/shared';
import { ApiServiceLevelEnum, StripeBillingIntervalEnum } from '@novu/shared';
// eslint-disable-next-line no-restricted-imports
import {
StripeBillingIntervalEnum,
StripeUsageTypeEnum,
StripeSubscriptionStatusEnum,
} from '@novu/ee-billing/src/stripe/types';
import { StripeSubscriptionStatusEnum, StripeUsageTypeEnum } from '@novu/ee-billing/src/stripe/types';

describe('CreateSubscription #novu-v2', () => {
const eeBilling = require('@novu/ee-billing');
if (!eeBilling) {
throw new Error('ee-billing does not exist');
}

const { CreateSubscription, GetPrices, UpdateServiceLevel, CreateSubscriptionCommand } = eeBilling;
const { CreateSubscription, GetStripePlanPriceUseCase, UpdateServiceLevel, CreateSubscriptionCommand } = eeBilling;

const stripeStub = {
subscriptions: {
Expand Down Expand Up @@ -53,7 +49,7 @@ describe('CreateSubscription #novu-v2', () => {
};

beforeEach(() => {
getPricesStub = sinon.stub(GetPrices.prototype, 'execute').resolves({
getPricesStub = sinon.stub(GetStripePlanPriceUseCase.prototype, 'execute').resolves({
metered: [
{
id: 'price_id_notifications',
Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/app/billing/e2e/create-usage-records.e2e-ee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
import { Logger } from '@nestjs/common';
import sinon from 'sinon';
import { expect } from 'chai';
import { ApiServiceLevelEnum } from '@novu/shared';
import { ApiServiceLevelEnum, StripeBillingIntervalEnum } from '@novu/shared';
// eslint-disable-next-line no-restricted-imports
import { StripeBillingIntervalEnum, StripeUsageTypeEnum } from '@novu/ee-billing/src/stripe/types';
import { StripeUsageTypeEnum } from '@novu/ee-billing/src/stripe/types';

const mockMonthlyBusinessSubscription = {
id: 'subscription_id',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/* eslint-disable global-require */
import sinon from 'sinon';
import { expect } from 'chai';
import { ApiServiceLevelEnum } from '@novu/shared';
// eslint-disable-next-line no-restricted-imports
import { StripeBillingIntervalEnum } from '@novu/ee-billing/src/stripe/types';
import { ApiServiceLevelEnum, StripeBillingIntervalEnum } from '@novu/shared';

const mockCustomerSubscriptionCreatedEvent = {
data: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/* eslint-disable global-require */
import sinon from 'sinon';
import { expect } from 'chai';
import { ApiServiceLevelEnum } from '@novu/shared';
// eslint-disable-next-line no-restricted-imports
import { StripeBillingIntervalEnum } from '@novu/ee-billing/src/stripe/types';
import { ApiServiceLevelEnum, StripeBillingIntervalEnum } from '@novu/shared';

const verifyCustomerMock = {
customer: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import sinon from 'sinon';
import { Test } from '@nestjs/testing';
import { CacheService, MockCacheService } from '@novu/application-generic';
import { ApiServiceLevelEnum, GetSubscriptionDto } from '@novu/shared';
import { GetEventResourceUsage, GetPlatformNotificationUsage, GetSubscription } from '@novu/ee-billing';
import { GetEventResourceUsage, GetSubscription } from '@novu/ee-billing';
import { randomUUID } from 'node:crypto';
import { AppModule } from '../../../app.module';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* eslint-disable global-require */
import sinon from 'sinon';
import { expect } from 'chai';
import { EnvironmentRepository, NotificationRepository, CommunityOrganizationRepository } from '@novu/dal';
import { CommunityOrganizationRepository, EnvironmentRepository, NotificationRepository } from '@novu/dal';
import { UserSession } from '@novu/testing';
import { ApiServiceLevelEnum, isClerkEnabled } from '@novu/shared';

Expand Down
4 changes: 1 addition & 3 deletions apps/api/src/app/billing/e2e/get-prices.e2e-ee.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/* eslint-disable global-require */
import sinon from 'sinon';
import { expect } from 'chai';
import { ApiServiceLevelEnum } from '@novu/shared';
// eslint-disable-next-line no-restricted-imports
import { StripeBillingIntervalEnum } from '@novu/ee-billing/src/stripe/types';
import { ApiServiceLevelEnum, StripeBillingIntervalEnum } from '@novu/shared';

describe('GetPrices #novu-v2', () => {
const eeBilling = require('@novu/ee-billing');
Expand Down
6 changes: 3 additions & 3 deletions apps/api/src/app/bridge/bridge.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
CreateMessageTemplate,
CreateWorkflow,
DeleteMessageTemplate,
DeletePreferencesUseCase,
DeleteWorkflowUseCase,
GetPreferences,
GetWorkflowByIdsUseCase,
Expand All @@ -12,8 +13,7 @@ import {
UpdateWorkflow,
UpsertControlValuesUseCase,
UpsertPreferences,
DeletePreferencesUseCase,
TierRestrictionsValidateUsecase,
ValidateContentTierLimits,
} from '@novu/application-generic';
import { CommunityOrganizationRepository, PreferencesRepository } from '@novu/dal';
import { SharedModule } from '../shared/shared.module';
Expand All @@ -40,7 +40,7 @@ const PROVIDERS = [
DeletePreferencesUseCase,
UpsertControlValuesUseCase,
BuildVariableSchemaUsecase,
TierRestrictionsValidateUsecase,
ValidateContentTierLimits,
CommunityOrganizationRepository,
ExtractVariables,
BuildStepIssuesUsecase,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import { encryptApiKey } from '@novu/application-generic';
import { EnvironmentRepository, NotificationGroupRepository } from '@novu/dal';

import { EnvironmentEnum, PROTECTED_ENVIRONMENTS } from '@novu/shared';
import { undefined } from 'zod';
import { CreateNovuIntegrationsCommand } from '../../../integrations/usecases/create-novu-integrations/create-novu-integrations.command';
import { CreateNovuIntegrations } from '../../../integrations/usecases/create-novu-integrations/create-novu-integrations.usecase';
import { CreateDefaultLayout, CreateDefaultLayoutCommand } from '../../../layouts/usecases';
import { GenerateUniqueApiKey } from '../generate-unique-api-key/generate-unique-api-key.usecase';
import { CreateEnvironmentCommand } from './create-environment.command';
import { ValidateTiersUseCase } from './validate-tiers-use.case';
import { TierValidationTypeEnum } from './tier-validation-type.enum';

@Injectable()
export class CreateEnvironment {
Expand All @@ -19,17 +22,20 @@ export class CreateEnvironment {
private notificationGroupRepository: NotificationGroupRepository,
private generateUniqueApiKey: GenerateUniqueApiKey,
private createDefaultLayoutUsecase: CreateDefaultLayout,
private createNovuIntegrationsUsecase: CreateNovuIntegrations
private createNovuIntegrationsUsecase: CreateNovuIntegrations,
private tierValidator: ValidateTiersUseCase
) {}

async execute(command: CreateEnvironmentCommand) {
const environmentCount = await this.environmentRepository.count({
_organizationId: command.organizationId,
});
this.tierValidator.execute({
organizationId: command.organizationId,
validationType: TierValidationTypeEnum.ENVIRONMENT_COUNT,
valueToValidate: environmentCount,
});

if (environmentCount >= 10) {
throw new BadRequestException('Organization cannot have more than 10 environments');
}
const normalizedName = command.name.trim();

if (!command.system) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum TierValidationTypeEnum {
ENVIRONMENT_COUNT = 'ENVIRONMENT_COUNT',
WORKFLOW_COUNT = 'WORKFLOW_COUNT',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import {
ApiServiceLevelEnum,
FeatureFlags,
FeatureFlagsKeysEnum,
FeatureNameEnum,
getFeatureForTierAsBoolean,
getFeatureForTierAsNumber,
} from '@novu/shared';
import { OrganizationRepository } from '@novu/dal';
import { Injectable } from '@nestjs/common';
import { GetFeatureFlag, GetFeatureFlagCommand, TierRestrictionsValidateCommand } from '@novu/application-generic';
import { ValidateTiersCommand } from './validate-tiers.command';
import { TierValidationTypeEnum } from './tier-validation-type.enum';

@Injectable()
export class ValidateTiersUseCase {
constructor(
private organizationRepository: OrganizationRepository,
private getFeatureFlag: GetFeatureFlag
) {}
async execute(validateTiersCommand: ValidateTiersCommand): Promise<void> {
const organization = await this.organizationRepository.findById(validateTiersCommand.organizationId);
const featureFlags = await this.getFeatureFlags(validateTiersCommand);
if (!organization || !organization.apiServiceLevel) {
throw new Error(`Organization not found ${JSON.stringify(organization)}`);
}
if (validateTiersCommand.validationType === TierValidationTypeEnum.ENVIRONMENT_COUNT) {
this.validateEnvironmentCount(organization.apiServiceLevel, featureFlags);
}
if (validateTiersCommand.validationType === TierValidationTypeEnum.WORKFLOW_COUNT) {
this.validateWorkflowCount(validateTiersCommand, organization.apiServiceLevel, featureFlags);
}
}
private async getFeatureFlags(command: TierRestrictionsValidateCommand): Promise<Partial<FeatureFlags>> {
const featureFlags: Partial<FeatureFlags> = {};

const featureFlagKeys = [FeatureFlagsKeysEnum.IS_2025_Q1_TIERING_ENABLED];

for (const flagKey of featureFlagKeys) {
const { key, value } = await this.getFeatureFlagStatus(command, flagKey);
featureFlags[key] = value;
}

return featureFlags;
}
private async getFeatureFlagStatus(
command: TierRestrictionsValidateCommand,
key: FeatureFlagsKeysEnum
): Promise<{ key: FeatureFlagsKeysEnum; value: boolean }> {
const status = await this.getFeatureFlag.execute(
GetFeatureFlagCommand.create({
userId: 'system',
environmentId: 'system',
organizationId: command.organizationId,
key,
})
);

return { key, value: status };
}

private validateEnvironmentCount(apiServiceLevel: ApiServiceLevelEnum, featureFlags: Partial<FeatureFlags>) {
const allowedToAdd = getFeatureForTierAsBoolean(
FeatureNameEnum.CUSTOM_ENVIRONMENTS_BOOLEAN,
apiServiceLevel,
featureFlags
);
if (!allowedToAdd) {
throw new Error(`You have exceeded the maximum number of environments allowed for the [${apiServiceLevel}] tier`);
}
}

private validateWorkflowCount(
validateTiersCommand: ValidateTiersCommand,
apiServiceLevel: ApiServiceLevelEnum,
featureFlags: Partial<FeatureFlags>
) {
const numberOfWorkflows = validateTiersCommand.valueToValidate;
const maxWorkflows = getFeatureForTierAsNumber(
FeatureNameEnum.PLATFORM_MAX_WORKFLOWS,
apiServiceLevel,
featureFlags,
false
);
if (maxWorkflows === -1) {
return;
}
if (numberOfWorkflows >= maxWorkflows) {
throw new Error(`You have exceeded the maximum number of workflows allowed for the [${apiServiceLevel}] tier`);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { TierValidationTypeEnum } from './tier-validation-type.enum';

export class ValidateTiersCommand {
organizationId: string;
validationType: TierValidationTypeEnum;
valueToValidate: number;
}
2 changes: 2 additions & 0 deletions apps/api/src/app/environments-v1/usecases/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { GetMxRecord } from '../../inbound-parse/usecases/get-mx-record/get-mx-record.usecase';
import { CreateEnvironment } from './create-environment/create-environment.usecase';
import { ValidateTiersUseCase } from './create-environment/validate-tiers-use.case';
import { DeleteEnvironment } from './delete-environment/delete-environment.usecase';
import { GenerateUniqueApiKey } from './generate-unique-api-key/generate-unique-api-key.usecase';
import { GetApiKeys } from './get-api-keys/get-api-keys.usecase';
Expand All @@ -10,6 +11,7 @@ import { UpdateEnvironment } from './update-environment/update-environment.useca

export const USE_CASES = [
GetMxRecord,
ValidateTiersUseCase,
CreateEnvironment,
UpdateEnvironment,
GenerateUniqueApiKey,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EnvironmentRepository, IntegrationRepository, CommunityOrganizationRepository } from '@novu/dal';
import { CommunityOrganizationRepository, EnvironmentRepository, IntegrationRepository } from '@novu/dal';
import { UserSession } from '@novu/testing';
import { expect } from 'chai';
import {
Expand Down
Loading

0 comments on commit 0036870

Please sign in to comment.