Feature Flipping

  • Tags : /
  • Latest : 0.3.2
  • Last Updated: 15 May 2011
  • Grails version : 1.3.5 > *
  • Authors : null
0 vote
Dependency :
compile ":feature-flipper:0.3.2"

Documentation

Summary

Description

Description

The feature-flipper plugin provides a mechanism to turn features on and off within a grails application. It is inspired by Flikr's 'flipping out' work in this space to do the same, and the implementation is quite similar.

This is foundational if you want to support a Continuous Deployment strategy and need to move unreleased features, or un-tested code into a production environment, but want to turn it off. It also allows you to do A/B testing if you choose by turning certain features on for certain users, while leaving it off for everyone else.

Currently, the plugin provides the ability to set features per server and per roles if you so desire, or you can use the same configuration files across all servers.

Features

  • Configuration is stored within an XML file at the application root or at a location you define (don't worry, you don't need to know the XML, its just a way to persist the state).
  • You can have per-environment configurations. So that you can have all the features on while you develop and selected features on in production.
  • Adds a convenience method to all Controllers and Services called hasFeature() that can test whether a feature is enabled for the current app
  • Also provides a Tag library to do the same, and allows you to test whether features are available in GSPs
  • A FeatureFlipper service is also provided so that you administer your features (UI for this is coming)
  • Features are read from the XML file on startup and stored in the ServletContext
  • When editing features (CRUD), they are updated in the ServletContext as well as persisted to the XML file
  • NEW (+) - Now supports groovy ConfigSlurper Style files

Configuration

To get started, you install the plugin;

grails install-plugin feature-flipper

You can start using the feature-flipper right away, or you can customise it further by changing the location of the XML configuration file, its name and default features (see below)

To change the location of the config file, set the following your Config.groovy;

featureFlipper.flipperConfigPath = 'path/to/config/file.groovy'

NB: Now in version 0.2, this file can either be the old XML file structure or now supporting ConfigSlurper Style files. You simply point it to either file, the plugin works out the rest.

You simply wrap the above in environment specific declarations if you need to do so;

environments {
        production {
               featureFlipper.flipperConfigPath = 'path/to/config/file.groovy'
       }
}

Example: Using it in your Code & Deploying

When you are ready to start introducing new features, you first decide what you want to call the feature. For the sake of this example, lets say you want to introduce a feature that enables users to invite their friends, and you want to introduce it to users that have the role ROLE_BETA and want to deploy it to one of your servers in your cluster. Lets call this feature "InviteFriends"

You simply go ahead and write your code in your controllers and services as you normally would, and wrap anything that you want to switch on/off with an if statement like so;

if (hasFeature("InviteFreinds", ["ROLE_BETA"])) {
  //add your code that supports Invite Friends functionality
}

In your GSPs, you can similarly do;

<ff:hasFeature name="InviteFriends" roles="${["ROLE_BETA"]}">
  //your code
</ff:hasFeature>

By default, if the features are not configured in XML Config file, they will be ON by default, so you can go ahead and develop this, and then just setup the configuration when you are ready to deploy. This can be done with the helper Service Class, or by writing the XML by hand.

Deploying

To deploy, you simply configure the features and deploy the XML with the application to the servers. Because config files can be different per server, you have quite a bit of flexibility.

Some Notes about the methods and tags

The hasFeature() method that is available in all controllers and methods, has two signatures;

  • hasFeature(featureName) : here you simply pass the featureName, and it will pass true if the feature is enabled, or it will also pass true if the feature is yet to be defined (this is so you can develop without worrying about it configuration)
  • hasFeature(featureName, Roles (+)) : This is as above, but allows you to pass an Array of Roles. Note that this is security library agnostic, and simply assumes you that no matter what security method you are using, you are able to pass the current user's Roles as an Array.
NOTE: If any of the Roles that are assigned to the feature match any of the roles that are assigned to the user, then the features will return enabled. Therefore its a 'OR' statement if you like.

Additionally, the <ff:HasFeature/> tag allows you to;

  • Pass no attributes : Will always evaluate to true
  • Pass the name of the feature as name="" : will test whether that feature is enabled or not
  • Pass the name and the roles as roles="${ (+)}" : will test whether the feature is enabled and for the roles passed as per the above method.

Customising your Config

Although you don't really have to mess with the Config Files, you can do and its quite simple. The FeatureFlipperService typically manages this, but there are some times that you may want to manually set this up. There are two options with the files. Version 0.1 of the plugin uses an XML config file, while Version 0.2 now also supports a ConfigSlurper style config files (recommended).

ConfigSlurper Style

features=[
	[
		"name":"FeatureName", 
		"description":"Feature Description", 
		"active":true, 
		"roles":[]
	], [
		"name":"OtherFeature", 
		"description":"Other Feature Description", 
		"active":false, 
		"roles":[]
	]
]

NB: The best bet is to just create this through the admin interface and not worry about having to hand write these files.

XML Style

The schema is;

<features>
  <featureitem>
    <name>NameOfFeature</name>
    <description>Description of the Feature</description>
    <active>true</active>
    <roles>
      <role>ROLE_USER</role>
      <role>ROLE_ADMIN</role>
    </roles>
  </featureitem>
  <featureitem>
    .....
  </featureitem>
</features>

The nodes are;

  • NAME : THe name of the feature, this should contain no spaces and only letters, and is what is passed in the methods and tag above as the feature name
  • DESCRIPTION : Something descriptive to help you know what the feature is
  • ACTIVE : Boolean true/false
  • ROLES : An array of ROLE, if any match, and the feature is enabled, it will pass true
  • ROLE : A string ROLE

FeatureFlipper Service

Additionally, you have a FeatureFlipperService that allows you to manage the features. Typically, you would use this in the controller that would be used to manage the features. It has the following functions;

  • listFeatures() : returns all features as an array of Feature Objects
  • getFeature(featureName) : returns a single Feature Object for the given name
  • setFeature(feature) : either updates (if the name exists) or creates a new feature (if the name doesn't exist and is unique) in the list of available features, and updates the XML file. 'feature' is a Map Object that contains; String name, String description, Boolean active, Array of Strings roles.
  • removeFeature("featureName") : removes the feature with the given name from the list of available features and updates the XML file

Future Plans

This should be a good start, but the following is planned for the future;

  • script to auto generate Admin screens to manage features
  • Support for persisting Features in JSON format to file and Database