From 76f44bdc311deac0f79d34bbfbec409227513ad3 Mon Sep 17 00:00:00 2001 From: Joana Maia Date: Wed, 18 Oct 2023 18:14:36 +0100 Subject: [PATCH 1/4] Update automations limitation --- .../components/automation-toggle.vue | 28 +++++++++++++++++++ .../automation/pages/automation-list-page.vue | 27 ------------------ 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/frontend/src/modules/automation/components/automation-toggle.vue b/frontend/src/modules/automation/components/automation-toggle.vue index 9b5f845e04..081eedf038 100644 --- a/frontend/src/modules/automation/components/automation-toggle.vue +++ b/frontend/src/modules/automation/components/automation-toggle.vue @@ -4,6 +4,7 @@ :model-value="props.automation.state === 'active'" class="!grow-0 !ml-0" :disabled="!canEnable" + :before-change="beforeChange" @change="handleChange" /> @@ -16,6 +17,9 @@ import { computed, defineProps } from 'vue'; import { useAutomationStore } from '@/modules/automation/store'; import { useStore } from 'vuex'; +import { getWorkflowMax, showWorkflowLimitDialog } from '@/modules/automation/automation-limit'; +import { mapGetters } from '@/shared/vuex/vuex.helpers'; +import { FeatureFlag } from '@/utils/featureFlag'; import { automationTypes } from '../config/automation-types'; const props = defineProps({ @@ -29,14 +33,38 @@ const store = useStore(); const { changePublishState } = useAutomationStore(); +const { currentTenant } = mapGetters('auth'); + const canEnable = computed(() => { const { type } = props.automation; + if (automationTypes[type]?.enableGuard) { return props.automation.state === 'active' || automationTypes[type]?.enableGuard(props.automation, store); } + return true; }); +const beforeChange = () => { + if (props.automation.state === 'active') { + return true; + } + + const isFeatureEnabled = FeatureFlag.isFlagEnabled( + FeatureFlag.flags.automations, + ); + + if (!isFeatureEnabled) { + const planWorkflowCountMax = getWorkflowMax( + currentTenant.value.plan, + ); + + showWorkflowLimitDialog({ planWorkflowCountMax }); + } + + return isFeatureEnabled; +}; + const handleChange = (value) => { changePublishState(props.automation.id, value); }; diff --git a/frontend/src/modules/automation/pages/automation-list-page.vue b/frontend/src/modules/automation/pages/automation-list-page.vue index 27d85c80ca..be3d6bf8c6 100644 --- a/frontend/src/modules/automation/pages/automation-list-page.vue +++ b/frontend/src/modules/automation/pages/automation-list-page.vue @@ -128,10 +128,8 @@ import { storeToRefs } from 'pinia'; import pluralize from 'pluralize'; import AppAutomationForm from '@/modules/automation/components/automation-form.vue'; import AppAutomationListTable from '@/modules/automation/components/list/automation-list-table.vue'; -import { mapGetters } from '@/shared/vuex/vuex.helpers'; import AppAutomationExecutions from '@/modules/automation/components/automation-executions.vue'; import { FeatureFlag } from '@/utils/featureFlag'; -import { getWorkflowMax, showWorkflowLimitDialog } from '@/modules/automation/automation-limit'; import { useStore } from 'vuex'; import config from '@/config'; @@ -158,40 +156,15 @@ const { } = storeToRefs(automationStore); const { getAutomations, changeAutomationFilter } = automationStore; -const { currentTenant } = mapGetters('auth'); - const store = useStore(); const fetchIntegrations = () => store.dispatch('integration/doFetch'); -/** - * Check if tenant has feature flag enabled - */ -const canAddAutomation = () => { - const isFeatureEnabled = FeatureFlag.isFlagEnabled( - FeatureFlag.flags.automations, - ); - - if (!isFeatureEnabled) { - const planWorkflowCountMax = getWorkflowMax( - currentTenant.value.plan, - ); - - showWorkflowLimitDialog({ planWorkflowCountMax }); - } - - return isFeatureEnabled; -}; - // Executions drawer const createAutomation = (type) => { if (!automationTypes[type].canCreate(store)) { return; } - if (!canAddAutomation()) { - return; - } - openAutomationForm.value = true; editAutomation.value = null; automationFormType.value = type; From 7d55bd266fb5a4d8a1e93fe9668734b3a4db4320 Mon Sep 17 00:00:00 2001 From: Joana Maia Date: Thu, 19 Oct 2023 22:11:27 +0100 Subject: [PATCH 2/4] Backend validation for feature flag --- backend/src/api/auth/authMe.ts | 2 +- backend/src/api/automation/index.ts | 3 --- backend/src/database/repositories/automationRepository.ts | 5 +++-- backend/src/feature-flags/getFeatureFlagTenantContext.ts | 2 +- backend/src/services/automationService.ts | 7 ++----- frontend/src/modules/automation/store/actions.js | 3 +++ 6 files changed, 10 insertions(+), 12 deletions(-) diff --git a/backend/src/api/auth/authMe.ts b/backend/src/api/auth/authMe.ts index 6245b54df0..171b47eec2 100644 --- a/backend/src/api/auth/authMe.ts +++ b/backend/src/api/auth/authMe.ts @@ -29,7 +29,7 @@ export default async (req, res) => { ...tenantUser.tenant.dataValues, csvExportCount: Number(await csvExportCountCache.get(tenantUser.tenant.id)) || 0, automationCount: - Number(await AutomationRepository.countAll(req.database, tenantUser.tenant.id)) || 0, + Number(await AutomationRepository.countAllActive(req.database, tenantUser.tenant.id)) || 0, memberEnrichmentCount: Number(await memberEnrichmentCountCache.get(tenantUser.tenant.id)) || 0, } diff --git a/backend/src/api/automation/index.ts b/backend/src/api/automation/index.ts index 52a3470a76..cc58327c01 100644 --- a/backend/src/api/automation/index.ts +++ b/backend/src/api/automation/index.ts @@ -1,7 +1,5 @@ import passport from 'passport' import { safeWrap } from '../../middlewares/errorMiddleware' -import { featureFlagMiddleware } from '../../middlewares/featureFlagMiddleware' -import { FeatureFlag } from '../../types/common' import { API_CONFIG } from '../../conf' import { authMiddleware } from '../../middlewares/authMiddleware' import TenantService from '../../services/tenantService' @@ -33,7 +31,6 @@ export default (app) => { ) app.post( '/tenant/:tenantId/automation', - featureFlagMiddleware(FeatureFlag.AUTOMATIONS, 'entities.automation.errors.planLimitExceeded'), safeWrap(require('./automationCreate').default), ) app.put( diff --git a/backend/src/database/repositories/automationRepository.ts b/backend/src/database/repositories/automationRepository.ts index 669354f9a0..c66aaf8472 100644 --- a/backend/src/database/repositories/automationRepository.ts +++ b/backend/src/database/repositories/automationRepository.ts @@ -42,7 +42,7 @@ export default class AutomationRepository extends RepositoryBase< state: existingActiveAutomations.count >= PLAN_LIMITS[tenant.plan][FeatureFlag.AUTOMATIONS] ? AutomationState.DISABLED - : data.state, + : AutomationState.ACTIVE, tenantId: tenant.id, createdById: currentUser.id, updatedById: currentUser.id, @@ -256,10 +256,11 @@ export default class AutomationRepository extends RepositoryBase< } } - static async countAll(database: any, tenantId: string): Promise { + static async countAllActive(database: any, tenantId: string): Promise { const automationCount = await database.automation.count({ where: { tenantId, + state: AutomationState.ACTIVE, }, useMaster: true, }) diff --git a/backend/src/feature-flags/getFeatureFlagTenantContext.ts b/backend/src/feature-flags/getFeatureFlagTenantContext.ts index ac1fd9bfcd..b6f0f10ccc 100644 --- a/backend/src/feature-flags/getFeatureFlagTenantContext.ts +++ b/backend/src/feature-flags/getFeatureFlagTenantContext.ts @@ -10,7 +10,7 @@ export default async function getFeatureFlagTenantContext( redis: RedisClient, log: Logger, ) { - const automationCount = await AutomationRepository.countAll(database, tenant.id) + const automationCount = await AutomationRepository.countAllActive(database, tenant.id) const csvExportCountCache = new RedisCache(FeatureFlagRedisKey.CSV_EXPORT_COUNT, redis, log) const memberEnrichmentCountCache = new RedisCache( FeatureFlagRedisKey.MEMBER_ENRICHMENT_COUNT, diff --git a/backend/src/services/automationService.ts b/backend/src/services/automationService.ts index fcd6aca4ee..2c0934b40b 100644 --- a/backend/src/services/automationService.ts +++ b/backend/src/services/automationService.ts @@ -39,11 +39,8 @@ export default class AutomationService extends ServiceBase< const txOptions = await this.getTxRepositoryOptions() try { - // create an active automation - const result = await new AutomationRepository(txOptions).create({ - ...req, - state: AutomationState.ACTIVE, - }) + // create an automation + const result = await new AutomationRepository(txOptions).create(req) // check automation type, if hubspot trigger an automation onboard if (req.type === AutomationType.HUBSPOT) { diff --git a/frontend/src/modules/automation/store/actions.js b/frontend/src/modules/automation/store/actions.js index 3ab619fb57..0d4b62ee02 100644 --- a/frontend/src/modules/automation/store/actions.js +++ b/frontend/src/modules/automation/store/actions.js @@ -26,6 +26,9 @@ export default { state: published ? 'active' : 'disabled', }) .then((res) => { + // Make sure that feature flags are updated for automationsCount + store.dispatch('auth/doRefreshCurrentUser'); + this.getAutomations(); return Promise.resolve(res); }); From ae705e9153c2d5e45ba4a243c77cb75649b2be9b Mon Sep 17 00:00:00 2001 From: Joana Maia Date: Thu, 19 Oct 2023 22:12:20 +0100 Subject: [PATCH 3/4] Linting --- backend/src/api/auth/authMe.ts | 3 ++- backend/src/api/automation/index.ts | 5 +---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/backend/src/api/auth/authMe.ts b/backend/src/api/auth/authMe.ts index 171b47eec2..7007c09204 100644 --- a/backend/src/api/auth/authMe.ts +++ b/backend/src/api/auth/authMe.ts @@ -29,7 +29,8 @@ export default async (req, res) => { ...tenantUser.tenant.dataValues, csvExportCount: Number(await csvExportCountCache.get(tenantUser.tenant.id)) || 0, automationCount: - Number(await AutomationRepository.countAllActive(req.database, tenantUser.tenant.id)) || 0, + Number(await AutomationRepository.countAllActive(req.database, tenantUser.tenant.id)) || + 0, memberEnrichmentCount: Number(await memberEnrichmentCountCache.get(tenantUser.tenant.id)) || 0, } diff --git a/backend/src/api/automation/index.ts b/backend/src/api/automation/index.ts index cc58327c01..9230d784df 100644 --- a/backend/src/api/automation/index.ts +++ b/backend/src/api/automation/index.ts @@ -29,10 +29,7 @@ export default (app) => { }, safeWrap(require('./automationSlackCallback').default), ) - app.post( - '/tenant/:tenantId/automation', - safeWrap(require('./automationCreate').default), - ) + app.post('/tenant/:tenantId/automation', safeWrap(require('./automationCreate').default)) app.put( '/tenant/:tenantId/automation/:automationId', safeWrap(require('./automationUpdate').default), From 2836efae7796b1944d38184393e52c026e063ba5 Mon Sep 17 00:00:00 2001 From: Joana Maia Date: Fri, 20 Oct 2023 10:34:39 +0100 Subject: [PATCH 4/4] Fix mandatory field --- backend/src/database/repositories/automationRepository.ts | 2 +- backend/src/services/automationService.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/src/database/repositories/automationRepository.ts b/backend/src/database/repositories/automationRepository.ts index c66aaf8472..b6708cb8ae 100644 --- a/backend/src/database/repositories/automationRepository.ts +++ b/backend/src/database/repositories/automationRepository.ts @@ -42,7 +42,7 @@ export default class AutomationRepository extends RepositoryBase< state: existingActiveAutomations.count >= PLAN_LIMITS[tenant.plan][FeatureFlag.AUTOMATIONS] ? AutomationState.DISABLED - : AutomationState.ACTIVE, + : data.state, tenantId: tenant.id, createdById: currentUser.id, updatedById: currentUser.id, diff --git a/backend/src/services/automationService.ts b/backend/src/services/automationService.ts index 2c0934b40b..cb6b0464b0 100644 --- a/backend/src/services/automationService.ts +++ b/backend/src/services/automationService.ts @@ -40,7 +40,10 @@ export default class AutomationService extends ServiceBase< try { // create an automation - const result = await new AutomationRepository(txOptions).create(req) + const result = await new AutomationRepository(txOptions).create({ + ...req, + state: AutomationState.ACTIVE, + }) // check automation type, if hubspot trigger an automation onboard if (req.type === AutomationType.HUBSPOT) {