Skip to content

Commit

Permalink
Merge pull request #147 from finn-no/feature/aggregate_strategies_api
Browse files Browse the repository at this point in the history
Feature/aggregate strategies api
  • Loading branch information
ivarconr authored Sep 7, 2016
2 parents 4a527a6 + 428bf04 commit 32ddc56
Show file tree
Hide file tree
Showing 22 changed files with 725 additions and 71 deletions.
285 changes: 285 additions & 0 deletions docs/api-v1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
# API

## Feature Toggles

### Fetching Feature Toggles

**GET: http://unleash.host.com/features**

This endpoint is the one all clients should use to fetch all available feature toggles
from the _unleash-server_. The response returns all active feature toggles and their
current strategy configuration. A feature toggle will have _at least_ one configured strategy.
A strategy will have a `name` and `parameters` map.

> _Note:_ Clients should perfer the `strategies` property.
> Legacy properties (`strategy` & `parameters`) will be kept until **version 2** of the format.
This endpoint should never return anything besides a valid *20X or 304-response*. It will also
include a `Etag`-header. The value of this header can be used by clients as the value of
the `If-None-Match`-header in the request to prevent a data transfer if the clients already
has the latest response locally.

**Example response:**
```json
{
"version": 1,
"features": [
{
"name": "Feature.A",
"description": "lorem ipsum",
"enabled": false,
"strategies": [
{
"name": "default",
"parameters": {}
}
],
"strategy": "default",
"parameters": {}
},
{
"name": "Feature.B",
"description": "lorem ipsum",
"enabled": true,
"strategies": [
{
"name": "ActiveForUserWithId",
"parameters": {
"userIdList": "123,221,998"
}
},
{
"name": "GradualRolloutRandom",
"parameters": {
"percentage": "10"
}
}
],
"strategy": "ActiveForUserWithId",
"parameters": {
"userIdList": "123,221,998"
}
}
]
}
```

**GET: http://unleash.host.com/features/:featureName**

Used to fetch details about a specific featureToggle. This is mostly provded to make it easy to
debug the API and should not be used by the client implementations.

> _Notice_: You will not get a version property when fetching a specific feature toggle by name.
```json
{
"name": "Feature.A",
"description": "lorem ipsum..",
"enabled": false,
"strategies": [
{
"name": "default",
"parameters": {}
}
],
"strategy": "default",
"parameters": {}
}
```


### Create a new Feature Toggle

**POST: http://unleash.host.com/features/**

**Body:**
```json
{
"name": "Feature.A",
"description": "lorem ipsum..",
"enabled": false,
"strategies": [
{
"name": "default",
"parameters": {}
}
]
}
```

Used by the admin-dashboard to create a new feature toggles. The name **must be unique**,
otherwise you will get a _403-response_.

Returns 200-respose if the feature toggle was created successfully.

### Update a Feature Toggle

**PUT: http://unleash.host.com/features/:toggleName**

**Body:**
```json
{
"name": "Feature.A",
"description": "lorem ipsum..",
"enabled": false,
"strategies": [
{
"name": "default",
"parameters": {}
}
]
}
```

Used by the admin dashboard to update a feature toggles. The name has to match an
existing features toggle.

Returns 200-respose if the feature toggle was updated successfully.

### Archive a Feature Toggle

**DELETE: http://unleash.host.com/features/:toggleName**

Used to archive a feature toggle. A feature toggle can never be totally be deleted,
but can be archived. This is a design decision to make sure that a old feature
toggle suddnely reapear by some one else reusing the same name.

## Archive

### Fetch archived toggles

**GET http://unleash.host.com/archive/features**

Used to fetch list of archived feature toggles

**Example response:**
```json
{
"version": 1,
"features": [
{
"name": "Feature.A",
"description": "lorem ipsum",
"enabled": false,
"strategies": [
{
"name": "default",
"parameters": {}
}
],
"strategy": "default",
"parameters": {}
}
]
}
```

### Revive feature toggle

**POST http://unleash.host.com//archive/revive**

**Body:**
```json
{
"name": "Feature.A"
}
```

Used to revive a feature toggle.


## Strategies

### Fetch Strategies
**GET: http://unleash.host.com/strategies**

Used to fetch all defined strategies and their defined paramters.

**Response**

```json
{
version: 1,
strategies: [
{
name: "default",
description: "Default on/off strategy.",
parametersTemplate: null
},
{
name: "ActiveForUserWithEmail",
description: "A comma separated list of email adresses this feature should be active for.",
parametersTemplate: {
emails: "string"
}
},
{
name: "Accounts",
description: "Enable for user accounts",
parametersTemplate: {
Accountname: "string"
}
}
]}
```

### Create strategy

**POST: http://unleash.host.com/strategies**

**Body**

```json
{
name: "ActiveForUserWithEmail",
description: "A comma separated list of email adresses this feature should be active for.",
parametersTemplate: {
emails: "string"
}
}
```

Used to create a new Strategy. Name must be unique.



# Events

**GET: http://unleash.host.com/events**

Used to fetch all changes in the unleash system.

Event types:

- feature-created
- feature-updated
- feature-archived
- feature-revived
- strategy-created
- strategy-deleted

**Response**

```json
{
"version": 1,
"events":[
{
"id":454,
"type":"feature-updated",
"createdBy":"unknown",
"createdAt":"2016-08-24T11:22:01.354Z",
"data": {
"name":"eid.bankid.mobile",
"description":"",
"strategy":"default",
"enabled":true,
"parameters":{}
},
"diffs": [
{"kind":"E","path":["enabled"],"lhs":false,"rhs":true}
]
}
]
}
```
71 changes: 71 additions & 0 deletions docs/schema.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Schema

## Table: _migrations_

Used by db-migrate module to keep track of migrations.

| NAME | TYPE | SIZE | NULLABLE | COLUMN_DEF |
| ----------- | --------- | ----------- | -------- | -------------------------------------- |
| id | serial | 10 | 0 | nextval('migrations_id_seq'::regclass) |
| name | varchar | 255 | 0 | (null) |
| run_on | timestamp | 29 | 0 | (null) |



## Table: _events_
| NAME | TYPE | SIZE | NULLABLE | COLUMN_DEF |
| ----------- | --------- | ----------- | -------- | ---------------------------------- |
| id | serial | 10 | 0 | nextval('events_id_seq'::regclass) |
| created_at | timestamp | 29 | 1 | now() |
| type | varchar | 255 | 0 | (null) |
| created_by | varchar | 255 | 0 | (null) |
| data | json | 2147483647 | 1 | (null) |


## Table: _strategies_loc
| NAME | TYPE | SIZE | NULLABLE | COLUMN_DEF |
| ------------------- | --------- | ----------- | -------- | ---------- |
| created_at | timestamp | 29 | 1 | now() |
| name | varchar | 255 | 0 | (null) |
| description | text | 2147483647 | 1 | (null) |
| parameters_template | json | 2147483647 | 1 | (null) |


## Table: _features_

| **NAME** | **TYPE** | **SIZE** | **NULLABLE** | **COLUMN_DEF** | **COMMENT** |
| ------------- | --------- | ----------- | ------------ | -------------- | ----------- |
| created_at | timestamp | 29 | 1 | now() | |
| name | varchar | 255 | 0 | (null) | |
| enabled | int4 | 10 | 1 | 0 | |
| description | text | 2147483647 | 1 | (null) | |
| archived | int4 | 10 | 1 | 0 | |
| parameters | json | 2147483647 | 1 | (null) | deprecated (*) |
| strategy_name | varchar | 255 | 1 | (null) | deprecated (*) |
| strategies | json | 2147483647 | 1 | (null) | |

(*) we migrated from `parmaters` and `strategy_name` to `strategies` which should contain an array of these.

For [aggregate strategies](https:/finn-no/unleash/issues/102) we had the following sql to migrate to the strategies column:

```sql
ALTER TABLE features ADD "strategies" json;

--populate the strategies column
UPDATE features
SET strategies = ('[{"name":"'||f.strategy_name||'","parameters":'||f.parameters||'}]')::json
FROM features as f
WHERE f.name = features.name;
```

In order to migrate back, one can use the following sql (it will loose all, but the first activation strategy):

```sql
UPDATE features
SET strategy_name = f.strategies->0->>'name',
parameters = f.strategies->0->'parameters'
FROM features as f
WHERE f.name = features.name;

ALTER TABLE features DROP COLUMN "strategies";
```
10 changes: 3 additions & 7 deletions packages/unleash-api/lib/db/feature.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
const eventType = require('../eventType');
const logger = require('../logger');
const NotFoundError = require('../error/NotFoundError');
const FEATURE_COLUMNS = ['name', 'description', 'enabled', 'strategy_name', 'parameters'];
const FEATURE_COLUMNS = ['name', 'description', 'enabled', 'strategies'];

module.exports = function (db, eventStore) {
eventStore.on(eventType.featureCreated, event => createFeature(event.data));
Expand Down Expand Up @@ -39,18 +39,15 @@ module.exports = function (db, eventStore) {
.map(rowToFeature);
}


function rowToFeature (row) {
if (!row) {
throw new NotFoundError('No feature toggle found');
}

return {
name: row.name,
description: row.description,
enabled: row.enabled > 0,
strategy: row.strategy_name, // eslint-disable-line
parameters: row.parameters,
strategies: row.strategies,
};
}

Expand All @@ -60,8 +57,7 @@ module.exports = function (db, eventStore) {
description: data.description,
enabled: data.enabled ? 1 : 0,
archived: data.archived ? 1 : 0,
strategy_name: data.strategy, // eslint-disable-line
parameters: data.parameters,
strategies: JSON.stringify(data.strategies),
};
}

Expand Down
Loading

0 comments on commit 32ddc56

Please sign in to comment.