ever-co / ever-gauzy

Ever® Gauzy™ - Open Business Management Platform (ERP/CRM/HRM/ATS/PM) - https://gauzy.co
https://gauzy.co
GNU Affero General Public License v3.0
2.21k stars 525 forks source link

Feature: implement features toggle #2493

Closed evereq closed 3 years ago

evereq commented 3 years ago

We have few aspects of features toggle in Gauzy

1) We want to make sure that some features in Gauzy can be easily disabled/enabled for ALL users in each tenant/organization or globally. So let's say we want the "Jobs" feature to be disabled in Tenant A, but enabled in tenant B. For feature "Proposals", we may want to disable it for company C, but enable for company D in Tenant X. For feature "Income/Expense" tracking we may want to disable it globally (for example of course), and so on. I.e. each feature (kind of "module") should be possible to enable/disable globally easy way and should be possible to enable/disable for a specific tenant. We want such functionality to be build-in to Gauzy and NOT use any other dependencies (in contrast to below).

I think technically, we can make it by having just 2 tables in DB (similar to how we do it already with 'integration' and with 'report' etc):

a) "feature" - the table where we will just store a relatively long list of features we have in Gauzy, with name, with "code" (it will be some capitalized short name of the feature with the prefix "FEATURE_", e.g. "FEATURE_INCOME" or "FEATURE_TIME_TRACKING", more about that below), some description about each feature, some image related to each feature stored locally (we do the same for Reports, usually it will be just some screenshot of feature, but later we will add nice images). Each feature also should store a link (relative) to the page in Gauzy UI which is "entry" point for work with that feature, e.g.. users for managing of users or organization/teams for managing of org teams etc. I.e. that table is just a collection of features we have.

We already have a page http://localhost:4200/#/onboarding/complete which display such information:

image

I think we "hardcoded" all that data, but it's really an opportunity now to move data to the "feature" table and display it dynamically. Plus of course, we will use the same data for features management in the Gauzy, which Admins and Super Admins will be able to do for specific Organizations / Tenants.

b) "feature_organization" table. It will connect features to organizations and tenants (so there will be organizationId nullable field and tenantId, not nullable field) and there we will have flag "isEnabled" (we have the same column in the report_organization table already). Such a flag will be used to detect if a specific feature enabled or disabled for a given Organization / Tenant. If a record has organizationId null, it will mean that such feature enabled / disabled by default for all organizations in that tenant. And of course, each organization may be configured using its own record with organizationId set to some value.

Having DB structures described above, we can make sure that all "feature_organization" data loaded (for current tenant/organization of user) into UI (browser) from some server endpoint and browser display / hide relevant menu items.

Note the focus, for now, should be on hiding relevant menu items/functionality on pages, not in the "security" aspect (e.g. when feature disabled, we may want later to make sure relevant endpoints can't be called in the server for the user of specific tenant/org). We will build later Guards for the backend, but they will be more complex because of more requirements.

When we will do all that, we should also work on the additional functionality described below (and use the same lib https://github.com/willmendesneto/ngx-feature-toggle in Gauzy UI both for our own "features" enable/disable and for what described below).

2) We want to make sure that some tiny features in Gauzy can be easily disabled/enabled on different conditions (e.g. for a specific user or for 10% of all users etc) and that should be managed by a third-party popular service (of course open-source). That will be totally optional and used for large SaaS / Enterprise deployments when new features might be initially disabled and next go live for some limited amount of beta users etc. We plan to use https://github.com/Unleash/unleash for that.

So for flow 2), we are going to use:

3) We will need to seed the feature / feature_organization table during basic and full seeds. I.e. for all tenants, all features by default should be seed as enabled. However, there is one important difference in how it will work for these seeds compared to others. So we will load settings from the .env file and during feature tables seeds, for each feature, we will verify if relevant env var is set to false and seed such feature as disabled in that case for all tenants (if env var not set or set to true, we seed as an enabled feature). The idea is that if the .env file has "FEATURE_TIME_TRACKING=false", then for ALL tenants, the time tracking feature will be stored as disabled in DB (IsEnabled=true in feature_organization table). We need that functionality so it will be easy to create different distributions of Gauzy with many features disabled. Let's also add to the .env.sample file a full list of features codes which we seed to DB and set all of them to =true (at the end of the .env.sample file of course as such list will be huge).

Note: in some feature, we may have a system which also will dynamically remove code of specific features when preparing specifically limited distributions of Gauzy. This is our end goal with features toggle described above so that we can ship different limited versions of Gauzy pre-configured for specific scenarios with only required code (modules)

4) When a new organization created (e.g.new org created in UI), we don't need to make sure relevant records added into the feature_organization table, because values of tenant will be used by default. However if a new tenant is created (e.g. when a new user registers), we should seed enabled/disabled features according to the same .env file into tenant settings in feature_organization table (i.e. record where organizationId is null for the specific tenant).

So our goal is to make sure that features enabled / disabled according to .env file (for all tenants initially, when tenant created) and we allow Admin users to enabled/disable features for specific Tenants or Organizations.

See:

Additional Features Toggle libs / soft (will not be used for now):

rahul-rocket commented 3 years ago

@evereq I have pushed code for feature toggles. Can you please review it and let me know that, I am on correct track? I am working on unleash-client integration on backend.

Feature Branch : feat/#2493-features-toggle

Question : Where we have to add toggle feature menu to allow admin/super admin to change for organization/tenant in Gauzy UI?

evereq commented 3 years ago

@rahul-rathore-576 think we should add features toggle inside the "Settings" menu item. We can add a sub-menu called "Features". On that page we should show cards for each feature, very much similar to the screenshot from the description of this issue above, but with the ability to "enable" / "disable" each feature (e.g. using Switch for now). Note: after the feature enabled / disabled, think the most simple would be to just reload the app completely (refresh page) in the browser (on the same page, i.e. no need to redirect to another page). Otherwise can be some bugs etc.

We should also plan that some of the features will be "Paid" (mostly when we add "plugins" / "apps") so we may need to have boolean field "isPaid" in the DB and if set to true, we should also show button "Buy" somewhere in the card. Please do that and I will create a separate ticket for what happens if the user clicks "Buy" later.

evereq commented 3 years ago

@rahul-rathore-576 ok, reviewed code, looks good. The only thing which I think is important: you probably added too many features :D I.e. some features are minor, for example, say we have a feature "FEATURE_PROPOSAL" (major "Proposals" feature) but now we also have "FEATURE_PROPOSAL_TEMPLATE" (something internal for proposals feature, related to how it works). In some cases, it's even more. That's is actually good, not bad, but now I think we should "organize" features, by creating some hierarchy. So for now, let's just add one more field to the features table called "parentId" (nullable of course). That way if some features have parentId = null, we think about them as main features in the system, e.g. "Invoicing", "Expenses", "Income" etc. But say feature called "Recurring Expenses" should have parentId set to Id of the "Expenses" feature. Our goal to have a much smaller list of high-level features as parents and each of them includes some more features inside for more fine control.

Next, in UI we can show that "cards" for the main features only, but inside each card, we can potentially show child features with their own switch to enable/disable.

Note: I don't think there will be more than 1 level in such hierarchy for now, so in UI you can assume it's that we have parent -> child without the ability for a child feature to have own children features for now. Easier to build UI and make it more simple.

I am not sure if Unleash supports such a hierarchy of features. If not, we can just use ALL features to enable/disable them inside Unleash because that will be used by developers / DevOps, not by end-users, so it's fine if we have there in the Unleash large (flat) list of features in the Gauzy.

Not sure if I mentioned before, but Unleash settings should "override" anything in Gauzy DB / configs etc. I.e. using Unleash DevOps should be able to disable some features quickly and (if needed) globally, no matter what currently saved in Gauzy DB for a given tenant/organization. For example, let's say feature "Time Tracking" enabled for some tenants in Gauzy DB and disabled for other tenants in Gauzy DB (and in Unleash such feature enabled). Next, if someone set that "Time Tracking" to be disabled for 10% of users in Unleash, such users will not be able to access that feature, even if in DB they have it enabled. Same true in other direction, e.g. if some feature disabled in Gauzy DB, but in Unleash we enabled it for 10% of all users, such users will see that feature enabled! (useful for when we introduce some new feature and want to "enforce" it, even if people never enable it explicitly because it has not existed before) etc. So Unleash settings always are the source of truth. But if Unleash is not available for some reason, then Gauzy DB / settings will be used (e.g. in local installations without Unleash)

Does it make sense in your view?