Skip to content

Commit

Permalink
feat: add feature flags script and func for on\off
Browse files Browse the repository at this point in the history
  • Loading branch information
TomatoVan committed Apr 8, 2024
1 parent a04902e commit 115ed34
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 6 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ module.exports = {
'no-undef': 'off',
'react/no-array-index-key': 'off',
'react/jsx-no-useless-fragment': 'off',
'react/no-unstable-nested-components': 'warn',
'paths-fixes/path-checker': ['error', { alias: '@' }],
'paths-fixes/layer-imports': [
'error',
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,24 @@ Clear.args = {

----

### Работа с feature-flags

Разрешено использование feature flags только с помощью хелпера toggleFeatures

в него передается объект с опциями

{
name: название фича-флага,
on: функция, которая отработает после Включения фичи
of: функция, которая отработает после Выключения фичи
}

Для автоматического удаления фичи использовать скрипт remove-feature.ts,
который принимает 2 аргумента
1. Название удаляемого фича-флага
2. Состояние (on\off)

----

## Сущности (entities)

Expand Down
2 changes: 2 additions & 0 deletions extractedTranslations/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"123": "123",
"Admin panel": "",
"All": "",
"Article ratings coming soon!": "",
"Articles app": "",
"Articles not found": "",
"Back to list": "",
Expand Down Expand Up @@ -80,6 +81,7 @@
"Закрыть": "",
"Обновить страницу": "",
"Отправить": "",
"Оценка статей скоро появится!": "",
"Произошла непредвиденная ошибка": "",
"Статьи не найдены": ""
}
78 changes: 78 additions & 0 deletions scripts/remove-feature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Node, Project, SyntaxKind } from 'ts-morph';

const removedFeatureName = process.argv[2]; // example isArticleEnabled
const featureState = process.argv[3]; // example off\on

if (!removedFeatureName) {
throw new Error('Enter a feature name');
}

if (!featureState) {
throw new Error('Enter a feature state (on or off)');
}

if (featureState !== 'on' && featureState !== 'off') {
throw new Error('Incorrect feature state (on or off)');
}

const project = new Project({});

project.addSourceFilesAtPaths('src/**/*.ts');
project.addSourceFilesAtPaths('src/**/*.tsx');

const files = project.getSourceFiles();

function isToggleFunction(node: Node) {
let isToggleFeatures = false;

node.forEachChild((child) => {
if (
child.isKind(SyntaxKind.Identifier) &&
child.getText() === 'toggleFeatures'
) {
isToggleFeatures = true;
}
});

return isToggleFeatures;
}

files.forEach((sourceFile) => {
sourceFile.forEachDescendant((node) => {
if (node.isKind(SyntaxKind.CallExpression) && isToggleFunction(node)) {
const objectOptions = node.getFirstDescendantByKind(
SyntaxKind.ObjectLiteralExpression,
);

if (!objectOptions) return;

const offFunctionProperty = objectOptions.getProperty('off');
const onFunctionProperty = objectOptions.getProperty('on');

const featureNameProperty = objectOptions.getProperty('name');

const onFunction = onFunctionProperty?.getFirstDescendantByKind(
SyntaxKind.ArrowFunction,
);
const offFunction = offFunctionProperty?.getFirstDescendantByKind(
SyntaxKind.ArrowFunction,
);
const featureName = featureNameProperty
?.getFirstDescendantByKind(SyntaxKind.StringLiteral)
?.getText()
.slice(1, -1);

if (featureName !== removedFeatureName) return;

if (featureState === 'on') {
node.replaceWithText(onFunction?.getBody().getText() ?? '');
}

if (featureState === 'off') {
node.replaceWithText(offFunction?.getBody().getText() ?? '');
}
}
});
});

project.save();
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { memo } from 'react';
import { useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import {
DynamicModuleLoader,
ReducersList,
Expand All @@ -13,30 +14,34 @@ import { articleDetailsPageReducer } from '../../model/slices';
import { ArticleDetails } from '../../../../entities/Article';
import { ArticleRating } from '@/features/articleRating';
import { Page } from '@/widgets/Page';
import { getFeatureFlag } from '@/shared/lib/features';
import { Counter } from '@/entities/Counter';
import { toggleFeatures } from '@/shared/lib/features';
import { Card } from '@/shared/ui/Card';

const reducers: ReducersList = {
articleDetailsPage: articleDetailsPageReducer,
};

const ArticlesDetailsPage = (props: any) => {
const { id } = useParams<{ id: string }>();
const isArticleRatingEnabled = getFeatureFlag('isArticleRatingEnabled');
const isCounterEnabled = getFeatureFlag('isCounterEnabled');
const { t } = useTranslation();

if (!id) {
return null;
}

const articleRatingCard = toggleFeatures({
name: 'isArticleRatingEnabled',
on: () => <ArticleRating articleId={id} />,
off: () => <Card>{t('Article ratings coming soon!')}</Card>,
});

return (
<DynamicModuleLoader reducers={reducers}>
<Page>
<VStack gap="16" max>
<ArticlesDetailsPageHeader />
<ArticleDetails id={id} />
{isCounterEnabled && <Counter />}
{isArticleRatingEnabled && <ArticleRating articleId={id} />}
{articleRatingCard}
<ArticleRecommendationsList />
<ArticleDetailsComments id={id} />
</VStack>
Expand Down
1 change: 1 addition & 0 deletions src/shared/lib/features/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { setFeatureFlags, getFeatureFlag } from './setGetFeatures';
export { toggleFeatures } from './toggleFeatures';
20 changes: 20 additions & 0 deletions src/shared/lib/features/toggleFeatures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { FeatureFlags } from '@/shared/types/featureFlags';
import { getFeatureFlag } from './setGetFeatures';

interface ToggleFeaturesOptions<T> {
name: keyof FeatureFlags;
on: () => T;
off: () => T;
}

export function toggleFeatures<T>({
off,
on,
name,
}: ToggleFeaturesOptions<T>): T {
if (getFeatureFlag(name)) {
return on();
}

return off();
}

0 comments on commit 115ed34

Please sign in to comment.