App

From Ocean Framework Documentation Wiki
Jump to: navigation, search

The App resource has one public URL. (For information about the difference between Public and Internal URLs and HATEOAS conventions, please see API Structure and Conventions.)

/v1/apps

Structure

name
(String) The name of the app.
description
(String) A description of the app.
features
(Object) A hash of the features to activate. See below for the format.
created_at
(String) A datetime string in GMT.
updated_at
(String) A datetime string in GMT.

Hyperlinks

The following hyperlink names are used in the following subsections. To retrieve the URL and Content-Type to use, use the _links hash. Note that the URI may change, and thus you should make no inference on how to construct similar URIs from any previous URI. For more information, refer to Hyperlink URIs are Opaque. You may store hyperlink URIs in permanent storage for later use.

The hyperlinks for an App resource are:

self
as always, the self hyperlink can be used to perform CRUD actions on the App resource.
activated_features
a GET to this hyperlink will return an Object describing which rollout features are to be activated. See below for more information.
creator
the ApiUser who created this App.
updater
the ApiUser who last updated this App.

Structure of features

This is for an app with four feature toggles. There may be many more: some companies may have upwards of 50 feature toggles at any one time.

{"hotel_flow": { 
    "allow_ips": "private",
    "disallow_user_agents": [["IE", "<", 10.0]],
    "allow_countries": ["SE"],
    "allow_groups": ['Beta', 'Developers'],
    "disallow_groups": 'Management'
  },
   
  "css_change": {},
   
  "chat": false,
   
  "green_payment_button_ab_test": { 
    "some_name": {"random": 0.1},
    "allow_user_agents": "Chrome",
    "allow_ips": ["54.144.0.0/16", "54.167.86.12/32"]
  }
}

The hotel_flow feature demonstrates most of the syntax. It will evaluate to true only if the caller comes from any of the standard private networks, but not if the agent is earlier than Internet Explorer 10. The feature will also be restricted to clients in Sweden, which also must belong to the Beta or Developers groups, but not to the Management group.

The css_change feature has no conditions, and therefore always evaluates to true. It's frequently the last stage of rolling out a feature.

The chat feature has been turned off completely; maybe it's not production-ready yet, maybe it was discontinued for some reason. Perhaps we switch it on only when there's a webinar going on. Simply stating a true or false value is frequently very useful, as a feature can be enabled or retired without having to deploy a new client.

The green_payment_button_ab_test restricts the feature to Chrome users from two specific IP ranges, but it's some_name which is really interesting. When a nonstandard condition is specified, and its value is a hash, this signals that the client will have to decide whether to activate the feature or not. In this case, the hash seems to indicate that the chance of the feature being activated is 10%. It's entirely up to the application to decide what the hash means and make a binary decision. The resulting boolean should of course be saved client-side, typically in a cookie, and used for the duration of the session. The idea is that the client calls the API once per session.

The reason that certain decisions are delegated to the client has to do with caching and scalability.

The Client Session

At the beginning of the client session, the client sends a GET activated_features request, with basic data as part of the query string, the returned JSON object will contain a key for each feature with a true or false value. The client stores this locally for the duration of the session (typically in a cookie) and uses it to choose between pieces of code, layouts, CSS, or whatever the feature toggle may be designed to affect.

Clients can call the API both without prior authentication, in which case no username is supplied, or as an authenticated user. Should the client change authentication status, for instance when logging in or out as part of visiting the website the client application presents, another call to the API should be made.

You can use the following conditions:

  • allow_ips
  • disallow_ips
  • allow_user_agents
  • disallow_user_agents
  • allow_countries
  • disallow_countries
  • allow_groups
  • disallow_groups

As stated above, any condition name apart from these will be interpreted as something the client must decide at runtime. A hash will be returned rather than the usual true or false.

Here's an example of an object returned to the client by GET activated_features, based on the App features defined above:

{"hotel_flow": false,
  "css_change": true,
  "chat": false,
  "green_payment_button_ab_test": {"some_name": {"random": 0.1}}

GET self

Retrieves an App resource.

200
The operation was successful.

PUT self

Updates an App resource.

PUT requests, according to the RFC standard, require all resource attributes to be updated simultaneously. However, in Ocean PUT requests have the same semantics as PATCH requests in that it's possible to set individual attributes. The API will reject requests with missing essential attributes. If you receive unknown properties, you must preserve them and pass them back unchanged in PUT and PATCH requests. Other than that, you should ignore them. This enables the API to be upgraded transparently.

200
The operation was successful.

GET activated_features

Returns an Object (a hash) describing the rollout features to activate for an individual session using a given App and a set of inputs given as query arguments. Thus, each run, for each individual client, might activate a different set of features:

  • client-ip - the IP number of the client for which the calculation is done.
  • username - the ApiUser username of the client for which the calculation is done.
  • ua-name -the User Agent name (e.g. "IE").
  • ua-version - the User Agent version (e.g. "8.1").
  • ua-os - the User Agent OS (e.g. "Windows 7").

NB: the returned JSON structure is not a resource but an Object (a Hash).

To calculate the User Agent name, version and OS, the service uses the user_agent_parser gem (https://github.com/toolmantim/user_agent_parser).

This is how you might retrieve the activated features using ocean-rails:

features = Api::RemoteResource.new("/v1/apps/your_client").get(:activated_features, args: {"client-ip"=>ip}).raw

or, if the App resource could be nonexistent:

app = Api::RemoteResource.get("/v1/apps/your_client")
features = app.present? && app.get(:activated_features, args: {"client-ip"=>ip}).raw

Clients can call this hyperlink both without prior authentication, in which case no username is supplied, or as an authenticated user. Should the client change authentication status, for instance when logging in or out as part of visiting the website the client application presents, another call to the API should be made. You should never call this hyperlink for each request. The results are cached, so the backend won't suffer, but it's just unnecessary network traffic, which isn't free (unless it's purely internal). Varnish can cope; it's extremely performant even on small instances.


Return codes:

200
The operation was successful.

DELETE self

Deletes an App resource.

204
The delete operation was successful.

POST /v1/apps

Creates a new App resource.

201
The operation was successful and a new App resource has been created. The response headers will, as is customary, include a standard Location header giving the URI of the newly created resource. To save you the extra request Ocean also returns the new App resource in the response body.