Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement feature flag #11070

Merged
merged 13 commits into from
Aug 13, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,6 @@
# All feature flags should be entered and documented in this file. If a feature is irreversible (e.g. data migration)
# then document the feature flag well enough.

# Legacy aggregation wizard flag:
# This flag enable the usage of the legacy AggregationControls in place of the new AggregationWizard.
# It can be enabled with 'legacy-aggregation-wizard=on', the flag is disabled by default.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
import React from 'react';
import PropTypes from 'prop-types';

import useFeature from 'hooks/useFeature';

type Props = {
name: string;
fallback?: React.ReactElement,
children: React.ReactElement,
}

const IfFeatureEnabled = ({ name, fallback, children }: Props) => {
const hasFeature = useFeature(name);

if (hasFeature) {
return children;
}

return fallback;
};

IfFeatureEnabled.propTypes = {
name: PropTypes.string.isRequired,
fallback: PropTypes.node,
children: PropTypes.node.isRequired,
};

IfFeatureEnabled.defaultProps = {
fallback: null,
};

export default IfFeatureEnabled;
38 changes: 38 additions & 0 deletions graylog2-web-interface/src/components/features/withFeature.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
import React from 'react';
import PropTypes from 'prop-types';

import IfFeatureEnabled from './IfFeatureEnabled';

const withFeature = <Props extends {}>(
featureName: string,
Component: React.ComponentType<Props>,
): React.ComponentType<Props> => {
return (props) => (
<IfFeatureEnabled name={featureName}>
<Component {...props} />
</IfFeatureEnabled>
);
};

withFeature.propTypes = {
featureName: PropTypes.string,
Component: PropTypes.node,
};

export default withFeature;
24 changes: 24 additions & 0 deletions graylog2-web-interface/src/hooks/useFeature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/

import AppConfig from 'util/AppConfig';

const useFeature = (name: string) => {
return AppConfig.isFeatureEnabled(name);
};

export default useFeature;
14 changes: 11 additions & 3 deletions graylog2-web-interface/src/util/AppConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
import * as Immutable from 'immutable';

declare global {
const DEVELOPMENT: boolean | undefined;
Expand All @@ -28,6 +29,7 @@ export type AppConfigs = {
rootTimeZone: string,
isCloud: boolean,
pluginUISettings: { [key: string]: {} },
featureFlags: { [key: string]: string },
};

declare global {
Expand All @@ -40,7 +42,15 @@ const appConfig = (): AppConfigs => {
return (window.appConfig || {}) as AppConfigs;
};

const getEnabledFeatures = () => {
return Immutable.Map(appConfig().featureFlags)
.filter((value) => value.trim().toLowerCase() === 'on')
.keySeq().toList()
.filter((s) => typeof s === 'string');
};

const AppConfig = {
features: getEnabledFeatures(),
gl2ServerUrl() {
if (typeof (GRAYLOG_API_URL) !== 'undefined') {
// The GRAYLOG_API_URL variable will be set by webpack via the DefinePlugin.
Expand All @@ -62,9 +72,7 @@ const AppConfig = {
},

isFeatureEnabled(feature: string) {
// eslint-disable-next-line no-undef
return typeof (FEATURES) !== 'undefined' && FEATURES.split(',')
.filter((s) => typeof s === 'string')
return this.features && this.features
.map((s) => s.trim().toLowerCase())
.includes(feature.toLowerCase());
},
Expand Down