Skip to content

Commit

Permalink
feat: Add support for toggle types (#618)
Browse files Browse the repository at this point in the history
  • Loading branch information
ivarconr committed Aug 6, 2020
1 parent 045a2dc commit 6568457
Show file tree
Hide file tree
Showing 17 changed files with 258 additions and 32 deletions.
14 changes: 13 additions & 1 deletion docs/api/admin/feature-toggles-api.md
Expand Up @@ -18,6 +18,7 @@ This endpoint is the one all admin ui should use to fetch all available feature
{
"name": "Feature.A",
"description": "lorem ipsum",
"type": "release",
"enabled": false,
"strategies": [
{
Expand Down Expand Up @@ -68,6 +69,7 @@ Used to fetch details about a specific featureToggle. This is mostly provded to
{
"name": "Feature.A",
"description": "lorem ipsum..",
"type": "release",
"enabled": false,
"strategies": [
{
Expand All @@ -89,6 +91,7 @@ Used to fetch details about a specific featureToggle. This is mostly provded to
{
"name": "Feature.A",
"description": "lorem ipsum..",
"type": "release",
"enabled": false,
"strategies": [
{
Expand All @@ -99,7 +102,12 @@ Used to fetch details about a specific featureToggle. This is mostly provded to
}
```

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

**Notes:**

- _name_ **must be globally unique**, otherwise you will get a _403-response_.
- _type_ is optional. If not defined it defaults to `release`

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

Expand All @@ -113,6 +121,7 @@ Returns 200-respose if the feature toggle was created successfully.
{
"name": "Feature.A",
"description": "lorem ipsum..",
"type": "release",
"enabled": false,
"strategies": [
{
Expand Down Expand Up @@ -150,6 +159,7 @@ None
{
"name": "Feature.A",
"description": "lorem ipsum..",
"type": "release",
"enabled": true,
"strategies": [
{
Expand Down Expand Up @@ -177,6 +187,7 @@ None
{
"name": "Feature.A",
"description": "lorem ipsum..",
"type": "release",
"enabled": false,
"strategies": [
{
Expand Down Expand Up @@ -205,6 +216,7 @@ Used to fetch list of archived feature toggles
{
"name": "Feature.A",
"description": "lorem ipsum",
"type": "release",
"enabled": false,
"strategies": [
{
Expand Down
50 changes: 50 additions & 0 deletions docs/api/admin/feature-types-api.md
@@ -0,0 +1,50 @@
---
id: events
title: /api/admin/feature-types
---

# Feature Types API

`GET: http://unleash.host.com/api/admin/feature-types`

Used to fetch all feature types defined in the unleash system.

**Response**

```json
{
"version": 1,
"types": [
{
"id": "release",
"name": "Release",
"description": "Used to enable trunk-based development for teams practicing Continuous Delivery.",
"lifetimeDays": 40
},
{
"id": "experiment",
"name": "Experiment",
"description": "Used to perform multivariate or A/B testing.",
"lifetimeDays": 40
},
{
"id": "ops",
"name": "Operational",
"description": "Used to control operational aspects of the system behavior.",
"lifetimeDays": 7
},
{
"id": "killswitch",
"name": "Kill switch",
"description": "Used to to gracefully degrade system functionality.",
"lifetimeDays": null
},
{
"id": "permission",
"name": "Permission",
"description": "Used to change the features or product experience that certain users receive.",
"lifetimeDays": null
}
]
}
```
3 changes: 3 additions & 0 deletions docs/api/client/feature-toggles-api.md
Expand Up @@ -27,6 +27,7 @@ This endpoint should never return anything besides a valid _20X or 304-response_
{
"name": "Feature.A",
"description": "lorem ipsum",
"type": "release",
"enabled": false,
"strategies": [
{
Expand All @@ -39,6 +40,7 @@ This endpoint should never return anything besides a valid _20X or 304-response_
},
{
"name": "Feature.B",
"type": "killswitch",
"description": "lorem ipsum",
"enabled": true,
"strategies": [
Expand Down Expand Up @@ -76,6 +78,7 @@ Used to fetch details about a specific feature toggle. This is mainly provided t
{
"name": "Feature.A",
"description": "lorem ipsum..",
"type": "release",
"enabled": false,
"strategies": [
{
Expand Down
60 changes: 35 additions & 25 deletions docs/database-schema.md
Expand Up @@ -9,21 +9,21 @@ This document describes our current database schema used in PostgreSQL. We use d

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) |
| 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) |
| 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_

Expand All @@ -36,14 +36,15 @@ Used by db-migrate module to keep track of migrations.

## 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 | |
| strategies | json | 2147483647 | 1 | (null) | |
| **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 | |
| strategies | json | 2147483647 | 1 | (null) | |
| type | varchar | 2147483647 | 1 | release | |

## Table: _client_strategies_

Expand All @@ -65,8 +66,17 @@ Used by db-migrate module to keep track of migrations.

## Table: _client_metrics_

| COLUMN_NAME | TYPE_NAME | COLUMN_SIZE | NULLABLE | COLUMN_DEF |
| ----------- | --------- | ----------- | -------- | ------------------------------------------ |
| id | serial | 10 | 0 | nextval('client_metrics_id_seq'::regclass) |
| created_at | timestamp | 29 | 1 | now() |
| metrics | json | 2147483647 | 1 | (null) |
| COLUMN_NAME | TYPE_NAME | COLUMN_SIZE | NULLABLE | COLUMN_DEF |
| --- | --- | --- | --- | --- |
| id | serial | 10 | 0 | nextval('client_metrics_id_seq'::regclass) |
| created_at | timestamp | 29 | 1 | now() |
| metrics | json | 2147483647 | 1 | (null) |

## Table: _feature_types_

| COLUMN_NAME | TYPE_NAME | COLUMN_SIZE | NULLABLE | COLUMN_DEF |
| ------------- | --------- | ----------- | -------- | ---------- |
| id | varchar | 255 | 0 | (null) |
| name | varchar | | 0 | (null) |
| description | varchar | | 1 | (null) |
| lifetime_days | integer | | 1 | (null) |
3 changes: 3 additions & 0 deletions lib/db/feature-toggle-store.js
Expand Up @@ -13,6 +13,7 @@ const NotFoundError = require('../error/notfound-error');
const FEATURE_COLUMNS = [
'name',
'description',
'type',
'enabled',
'strategies',
'variants',
Expand Down Expand Up @@ -97,6 +98,7 @@ class FeatureToggleStore {
return {
name: row.name,
description: row.description,
type: row.type,
enabled: row.enabled > 0,
strategies: row.strategies,
variants: row.variants,
Expand All @@ -108,6 +110,7 @@ class FeatureToggleStore {
return {
name: data.name,
description: data.description,
type: data.type,
enabled: data.enabled ? 1 : 0,
archived: data.archived ? 1 : 0,
strategies: JSON.stringify(data.strategies),
Expand Down
29 changes: 29 additions & 0 deletions lib/db/feature-type-store.js
@@ -0,0 +1,29 @@
'use strict';

const COLUMNS = ['id', 'name', 'description', 'lifetime_days'];
const TABLE = 'feature_types';

class FeatureToggleStore {
constructor(db, getLogger) {
this.db = db;
this.getLogger = getLogger('feature-type-store.js');
}

getAll() {
return this.db
.select(COLUMNS)
.from(TABLE)
.map(this.rowToFeatureType);
}

rowToFeatureType(row) {
return {
id: row.id,
name: row.name,
description: row.description,
lifetimeDays: row.lifetime_days,
};
}
}

module.exports = FeatureToggleStore;
2 changes: 2 additions & 0 deletions lib/db/index.js
Expand Up @@ -3,6 +3,7 @@
const { createDb } = require('./db-pool');
const EventStore = require('./event-store');
const FeatureToggleStore = require('./feature-toggle-store');
const FeatureTypeStore = require('./feature-type-store');
const StrategyStore = require('./strategy-store');
const ClientInstanceStore = require('./client-instance-store');
const ClientMetricsDb = require('./client-metrics-db');
Expand All @@ -22,6 +23,7 @@ module.exports.createStores = (config, eventBus) => {
db,
eventStore,
featureToggleStore: new FeatureToggleStore(db, eventStore, getLogger),
featureTypeStore: new FeatureTypeStore(db, getLogger),
strategyStore: new StrategyStore(db, eventStore, getLogger),
clientApplicationsStore: new ClientApplicationsStore(
db,
Expand Down
1 change: 1 addition & 0 deletions lib/routes/admin-api/feature-schema.js
Expand Up @@ -54,6 +54,7 @@ const featureShema = joi
.keys({
name: nameType,
enabled: joi.boolean().default(false),
type: joi.string().default('release'),
description: joi
.string()
.allow('')
Expand Down
7 changes: 7 additions & 0 deletions lib/routes/admin-api/feature-schema.test.js
Expand Up @@ -17,6 +17,7 @@ test('should require URL firendly name', t => {
test('should be valid toggle name', t => {
const toggle = {
name: 'app.name',
type: 'release',
enabled: false,
strategies: [{ name: 'default' }],
};
Expand All @@ -28,6 +29,7 @@ test('should be valid toggle name', t => {
test('should strip extra variant fields', t => {
const toggle = {
name: 'app.name',
type: 'release',
enabled: false,
strategies: [{ name: 'default' }],
variants: [
Expand All @@ -47,6 +49,7 @@ test('should strip extra variant fields', t => {
test('should allow weightType=fix', t => {
const toggle = {
name: 'app.name',
type: 'release',
enabled: false,
strategies: [{ name: 'default' }],
variants: [
Expand All @@ -65,6 +68,7 @@ test('should allow weightType=fix', t => {
test('should disallow weightType=unknown', t => {
const toggle = {
name: 'app.name',
type: 'release',
enabled: false,
strategies: [{ name: 'default' }],
variants: [
Expand All @@ -86,6 +90,7 @@ test('should disallow weightType=unknown', t => {
test('should be possible to define variant overrides', t => {
const toggle = {
name: 'app.name',
type: 'release',
enabled: false,
strategies: [{ name: 'default' }],
variants: [
Expand All @@ -112,6 +117,7 @@ test('variant overrides must have corect shape', async t => {
t.plan(1);
const toggle = {
name: 'app.name',
type: 'release',
enabled: false,
strategies: [{ name: 'default' }],
variants: [
Expand Down Expand Up @@ -139,6 +145,7 @@ test('variant overrides must have corect shape', async t => {
test('should keep constraints', t => {
const toggle = {
name: 'app.constraints',
type: 'release',
enabled: false,
strategies: [
{
Expand Down
22 changes: 22 additions & 0 deletions lib/routes/admin-api/feature-type.js
@@ -0,0 +1,22 @@
'use strict';

const Controller = require('../controller');

const version = 1;

class FeatureTypeController extends Controller {
constructor(config) {
super(config);
this.featureTypeStore = config.stores.featureTypeStore;
this.logger = config.getLogger('/admin-api/feature-type.js');

this.get('/', this.getAllFeatureTypes);
}

async getAllFeatureTypes(req, res) {
const types = await this.featureTypeStore.getAll();
res.json({ version, types });
}
}

module.exports = FeatureTypeController;

0 comments on commit 6568457

Please sign in to comment.