diff --git a/doc/sphinx-guides/source/developers/configuration.rst b/doc/sphinx-guides/source/developers/configuration.rst index fb15fea7900..9ed20299de3 100644 --- a/doc/sphinx-guides/source/developers/configuration.rst +++ b/doc/sphinx-guides/source/developers/configuration.rst @@ -109,3 +109,17 @@ always like ``dataverse..newname...=old.property.name``. Note this d aliases. Details can be found in ``edu.harvard.iq.dataverse.settings.source.AliasConfigSource`` + +Adding a Feature Flag +^^^^^^^^^^^^^^^^^^^^^ + +Some parts of our codebase might be opt-in only. Experimental or optional feature previews can be switched on using our +usual configuration mechanism, a JVM setting. + +Feature flags are implemented in the enumeration ``edu.harvard.iq.dataverse.settings.FeatureFlags``, which allows for +convenient usage of it anywhere in the codebase. When adding a flag, please add it to the enum, think of a default +status, add some Javadocs about the flagged feature and add a ``@since`` tag to make it easier to identify when a flag +has been introduced. + +We want to maintain a list of all :ref:`feature flags ` in the :ref:`configuration guide `, +please add yours to the list. \ No newline at end of file diff --git a/doc/sphinx-guides/source/installation/config.rst b/doc/sphinx-guides/source/installation/config.rst index 4d173cad783..e0fd7ebe40c 100644 --- a/doc/sphinx-guides/source/installation/config.rst +++ b/doc/sphinx-guides/source/installation/config.rst @@ -2153,6 +2153,32 @@ See also these related database settings: - :ref:`:Authority` - :ref:`:Shoulder` +.. _feature-flags: + +Feature Flags +------------- + +Certain features might be deactivated because they are experimental and/or opt-in previews. If you want to enable these, +please find all known feature flags below. Any of these flags can be activated using a boolean value +(case-insensitive, one of "true", "1", "YES", "Y", "ON") for the setting. + +.. list-table:: + :widths: 35 50 15 + :header-rows: 1 + :align: left + + * - Flag Name + - Description + - Default status + * - Example flag + - Replace this with something real + - ``Off`` + +**Note:** Can be set via any `supported MicroProfile Config API source`_, e.g. the environment variable +``DATAVERSE_FEATURE_XXX``. + + + .. _:ApplicationServerSettings: Application Server Settings diff --git a/doc/sphinx-guides/source/installation/oidc.rst b/doc/sphinx-guides/source/installation/oidc.rst index a40ef758dc7..1fdfcce63b5 100644 --- a/doc/sphinx-guides/source/installation/oidc.rst +++ b/doc/sphinx-guides/source/installation/oidc.rst @@ -86,4 +86,3 @@ configuration option. For details, see :doc:`config`. .. hint:: In contrast to our :doc:`oauth2`, you can use multiple providers by creating distinct configurations enabled by the same technology and without modifying the Dataverse Software code base (standards for the win!). - diff --git a/src/main/java/edu/harvard/iq/dataverse/settings/FeatureFlags.java b/src/main/java/edu/harvard/iq/dataverse/settings/FeatureFlags.java new file mode 100644 index 00000000000..6c4ef9d06a4 --- /dev/null +++ b/src/main/java/edu/harvard/iq/dataverse/settings/FeatureFlags.java @@ -0,0 +1,78 @@ +package edu.harvard.iq.dataverse.settings; + +import java.util.Objects; + +/** + *

+ * This enum holds so-called "feature flags" aka "feature gates", etc. It can be used throughout the application + * to avoid activating or using experimental functionality or feature previews that are opt-in. + *

+ * The current implementation reuses {@link JvmSettings} to interpret any + * boolean values + * (true == case-insensitive one of "true", "1", "YES", "Y", "ON") and hook into the usual settings system + * (any MicroProfile Config Source available). + *

+ * If you add any new flags, please add it here, think of a default status, add some Javadocs about the flagged + * feature and add a "@since" tag to make it easier to identify when a flag has been introduced. + *

+ * When removing a flag because of a feature being removed, drop the entry. When a feature matures, drop the flag, + * too! Flags are not meant to be switches for configuration! + *

+ * + * @see Configuration Guide + * @see Developer Guide + */ +public enum FeatureFlags { + + /* None yet - please add the first here. Example code with JavaDoc example: + * + * /** + * * Your (short) description of what will happen when enabling here. + * * @apiNote Raise flag by setting "dataverse.feature.flag-name" + * * @since Dataverse 5.13 + * * / + * DESCRIPTIVE_NAME_HERE("flag-name"), + * + */ + + // This is used for testing only, thus no documentation! Might be removed when adding the first real flag + // and replace it in the unit test! + TEST_MODE("test_mode"), + + ; + + final String flag; + final boolean defaultStatus; + + /** + * Construct a flag with default status "off". + * + * @param flag This flag name will be used to create a scoped String with {@link JvmSettings#FEATURE_FLAG}, + * making it available as "dataverse.feature.${flag}". + */ + FeatureFlags(String flag) { + this(flag, false); + } + + /** + * Construct a flag. + * @param flag This flag name will be used to create a scoped String with {@link JvmSettings#FEATURE_FLAG}, + * making it available as "dataverse.feature.${flag}". + * @param defaultStatus A sensible default should be given here. Probably this will be "false" for most + * experimental feature previews. + */ + FeatureFlags(String flag, boolean defaultStatus) { + Objects.requireNonNull(flag); + this.flag = flag; + this.defaultStatus = defaultStatus; + } + + /** + * Determine the status of this flag via {@link JvmSettings}. + * @return True or false, depending on the configuration or {@link #defaultStatus} if not found. + */ + public boolean enabled() { + return JvmSettings.FEATURE_FLAG.lookupOptional(Boolean.class, flag).orElse(defaultStatus); + } + +} diff --git a/src/main/java/edu/harvard/iq/dataverse/settings/JvmSettings.java b/src/main/java/edu/harvard/iq/dataverse/settings/JvmSettings.java index ed3a161075b..370bd631dd9 100644 --- a/src/main/java/edu/harvard/iq/dataverse/settings/JvmSettings.java +++ b/src/main/java/edu/harvard/iq/dataverse/settings/JvmSettings.java @@ -68,6 +68,12 @@ public enum JvmSettings { SCOPE_API(PREFIX, "api"), API_SIGNING_SECRET(SCOPE_API, "signing-secret"), + // FEATURE FLAGS SETTINGS + SCOPE_FLAGS(PREFIX, "feature"), + // This is a special placeholder-type setting entry, to be filled in by FeatureFlag entries during lookup. + // Avoids adding flag entries twice. + FEATURE_FLAG(SCOPE_FLAGS), + ; private static final String SCOPE_SEPARATOR = "."; diff --git a/src/test/java/edu/harvard/iq/dataverse/settings/FeatureFlagsTest.java b/src/test/java/edu/harvard/iq/dataverse/settings/FeatureFlagsTest.java new file mode 100644 index 00000000000..7ee23d33621 --- /dev/null +++ b/src/test/java/edu/harvard/iq/dataverse/settings/FeatureFlagsTest.java @@ -0,0 +1,21 @@ +package edu.harvard.iq.dataverse.settings; + +import edu.harvard.iq.dataverse.util.testing.JvmSetting; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class FeatureFlagsTest { + + @Test + void testFlagDisabled() { + assertFalse(FeatureFlags.TEST_MODE.enabled()); + } + + @Test + @JvmSetting(key = JvmSettings.FEATURE_FLAG, value = "on", varArgs = "test_mode") + void testFlagEnabled() { + assertTrue(FeatureFlags.TEST_MODE.enabled()); + } + +} \ No newline at end of file