Skip to content

Commit e8dcf9f

Browse files
committed
feat(flags): add bulkMigrateFeatureFlags mode
1 parent 689de23 commit e8dcf9f

File tree

10 files changed

+343
-1
lines changed

10 files changed

+343
-1
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,5 @@ dist/
7272
.DS_Store
7373

7474
*.out
75+
76+
scripts/*.json

.npmignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,4 @@ typings/
7070

7171
*.out
7272

73-
.dockerImageWhitelist
73+
scripts/*.json

API.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ as api grouping util classes are attached to this class.
7979
* [.updateFeatureFlag(projectKey, featureFlagKey, patchComment)](#LaunchDarklyUtilsFlags+updateFeatureFlag) ⇒ <code>Promise</code>
8080
* [.toggleFeatureFlag(projectKey, featureFlagKey, environmentKeyQuery, value)](#LaunchDarklyUtilsFlags+toggleFeatureFlag) ⇒ <code>Promise</code>
8181
* [.migrateFeatureFlag(projectKey, featureFlagKey, fromEnv, toEnv, includeState)](#LaunchDarklyUtilsFlags+migrateFeatureFlag) ⇒ <code>Promise</code>
82+
* [.bulkMigrateFeatureFlags(projectKey, featureFlagKeys, fromEnv, toEnv, includeState)](#LaunchDarklyUtilsFlags+bulkMigrateFeatureFlags) ⇒ <code>Promise</code>
8283

8384
<a name="new_LaunchDarklyUtilsFlags_new"></a>
8485

@@ -191,6 +192,24 @@ targets, rules, fallthrough, offVariation, prerequisites and optionally the flag
191192
| toEnv | <code>string</code> | environment to copy flag attributes to |
192193
| includeState | <code>boolean</code> | optionally copy boolean state true/false |
193194

195+
<a name="LaunchDarklyUtilsFlags+bulkMigrateFeatureFlags"></a>
196+
197+
### launchDarklyUtilsFlags.bulkMigrateFeatureFlags(projectKey, featureFlagKeys, fromEnv, toEnv, includeState) ⇒ <code>Promise</code>
198+
Migrate multiple feature flags properties between environments in a project. this includes:
199+
targets, rules, fallthrough, offVariation, prerequisites and optionally the flags on/off state.
200+
201+
**Kind**: instance method of [<code>LaunchDarklyUtilsFlags</code>](#LaunchDarklyUtilsFlags)
202+
**Fulfil**: <code>Object</code> updated feature flag json array
203+
**Reject**: <code>Error</code> object with message
204+
205+
| Param | Type | Description |
206+
| --- | --- | --- |
207+
| projectKey | <code>string</code> | project identifier |
208+
| featureFlagKeys | <code>string</code> | comma-separated feature flag identifiers |
209+
| fromEnv | <code>string</code> | environment to copy flag attributes from |
210+
| toEnv | <code>string</code> | environment to copy flag attributes to |
211+
| includeState | <code>boolean</code> | optionally copy boolean state true/false |
212+
194213
<a name="LaunchDarklyUtilsMembers"></a>
195214

196215
## LaunchDarklyUtilsMembers

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,14 @@ The following modes are supported. This info is also available via: `ldutils -h
7575
| updateFeatureFlag | projectKey, featureFlagKey, patchComment |
7676
| toggleFeatureFlag | projectKey, featureFlagKey, environmentKeyQuery, enabled |
7777
| migrateFeatureFlag | projectKey, featureFlagKey, fromEnv, toEnv, includeState |
78+
| bulkMigrateFeatureFlags | projectKey, featureFlagKeys, fromEnv, toEnv, includeState |
7879

7980
- `migrateFeatureFlag` mode is used to copy flag attributes between environments. This covers: targets, rules, fallthrough, offVariation, prerequisites and optionally the flag on/off state. eg. to migrate a flag from dev to test env.
8081

8182
```
8283
ldutils migrateFeatureFlag my-project my-flag dev test
8384
```
85+
> use `bulkMigrateFeatureFlags`with a comma separated list of flags ids to migrations multiple flags in one operation.
8486
8587
#### Custom roles
8688

ldutils

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ new LaunchDarklyUtils().create(process.env.LAUNCHDARKLY_API_TOKEN, log).then(fun
7171
});
7272
});
7373

74+
program
75+
.command(`bulkMigrateFeatureFlags <projectKey> <featureFlagKeys> <fromEnv> <toEnv> (includeState)`)
76+
.description('migrate multiple flag settings from one environment to another')
77+
.action(function(projectKey, featureFlagKeys, fromEnv, toEnv, includeState) {
78+
ldUtils.flags.bulkMigrateFeatureFlags(projectKey, featureFlagKeys, fromEnv, toEnv, includeState).then(function(response) {
79+
console.log(json.plain(response));
80+
});
81+
});
82+
7483
program
7584
.command('getCustomRoles')
7685
.description('get all custom roles in account')

scripts/jqJsonDiff.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/bash
2+
3+
display_usage() {
4+
echo "This script must be run with two files to compare."
5+
echo "eg. ./jqJsonDiff.sh ./before.json ../after.json"
6+
}
7+
8+
if [ $# -eq 0 ]; then
9+
display_usage
10+
exit 1
11+
fi
12+
13+
diff <(cat "$1" | jq . --sort-keys) <(cat "$2" | jq . --sort-keys)

src/LaunchDarklyUtilsFlags.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,26 @@ export class LaunchDarklyUtilsFlags {
182182
return this.updateFeatureFlag(projectKey, featureFlagKey, patchComment);
183183
});
184184
}
185+
186+
/**
187+
* Migrate multiple feature flags properties between environments in a project. this includes:
188+
* targets, rules, fallthrough, offVariation, prerequisites and optionally the flags on/off state.
189+
* @param {string} projectKey - project identifier
190+
* @param {string} featureFlagKeys - comma-separated feature flag identifiers
191+
* @param {string} fromEnv - environment to copy flag attributes from
192+
* @param {string} toEnv - environment to copy flag attributes to
193+
* @param {boolean} includeState - optionally copy boolean state true/false
194+
* @returns {Promise}
195+
* @fulfil {Object} updated feature flag json array
196+
* @reject {Error} object with message
197+
*/
198+
async bulkMigrateFeatureFlags(projectKey, featureFlagKeys, fromEnv, toEnv, includeState) {
199+
let promises = [];
200+
201+
featureFlagKeys.split(',').forEach(key => {
202+
promises.push(this.migrateFeatureFlag(projectKey, key, fromEnv, toEnv, includeState));
203+
});
204+
205+
return Promise.all(promises);
206+
}
185207
}

test/LaunchDarklyUtilsFlags.spec.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,39 @@ describe('LaunchDarklyUtilsFlags', function() {
137137
});
138138
});
139139
});
140+
141+
describe('bulkMigrateFeatureFlags', function() {
142+
before(done => {
143+
let scope = nock('https://app.launchdarkly.com')
144+
.get('/api/v2/flags/sample-project/sort.order')
145+
.replyWithFile(200, __dirname + '/fixtures/feature-flags-get.json', {
146+
'Content-Type': 'application/json'
147+
})
148+
.patch('/api/v2/flags/sample-project/sort.order')
149+
.replyWithFile(200, __dirname + '/fixtures/feature-flags-update.json', {
150+
'Content-Type': 'application/json'
151+
})
152+
.get('/api/v2/flags/sample-project/sort.order2')
153+
.replyWithFile(200, __dirname + '/fixtures/feature-flags-get-two.json', {
154+
'Content-Type': 'application/json'
155+
})
156+
.patch('/api/v2/flags/sample-project/sort.order2')
157+
.replyWithFile(200, __dirname + '/fixtures/feature-flags-update-two.json', {
158+
'Content-Type': 'application/json'
159+
});
160+
assert(scope);
161+
done();
162+
});
163+
164+
it('should make expected api call and return results', async function() {
165+
let expected = [];
166+
expected.push(JSON.parse(fs.readFileSync(__dirname + '/fixtures/feature-flags-update.json', 'utf-8')));
167+
expected.push(JSON.parse(fs.readFileSync(__dirname + '/fixtures/feature-flags-update-two.json', 'utf-8')));
168+
return ldutils.flags
169+
.bulkMigrateFeatureFlags('sample-project', 'sort.order,sort.order2', 'test', 'production')
170+
.then(actual => {
171+
expect(actual).to.deep.equal(expected);
172+
});
173+
});
174+
});
140175
});
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
{
2+
"name": "Alternate sort order2",
3+
"kind": "boolean",
4+
"key": "sort.order2",
5+
"creationDate": 1443652232590,
6+
"includeInSnippet": false,
7+
"variations": [
8+
{
9+
"value": true,
10+
"name": "true"
11+
},
12+
{
13+
"value": false,
14+
"name": "false"
15+
}
16+
],
17+
"temporary": false,
18+
"tags": [
19+
"th"
20+
],
21+
"_links": {
22+
"parent": {
23+
"href": "/api/v2/flags/default",
24+
"type": "application/json"
25+
},
26+
"self": {
27+
"href": "/api/v2/flags/default/sort.order2",
28+
"type": "application/json"
29+
}
30+
},
31+
"maintainerId": "561c579cd8fd5c2704000001",
32+
"_maintainer": {
33+
"_links": {
34+
"parent": {
35+
"href": "/internal/account/members",
36+
"type": "application/json"
37+
},
38+
"self": {
39+
"href": "/internal/account/members/561c579cd8fd5c2704000001",
40+
"type": "application/json"
41+
}
42+
},
43+
"_id": "561c579cd8fd5c2704000001",
44+
"firstName": "John",
45+
"lastName": "Test",
46+
"role": "reader",
47+
"email": "john+test@launchdarkly.com",
48+
"_pendingInvite": false,
49+
"isBeta": false,
50+
"customRoles": [
51+
"569983b05a64a106b7000002"
52+
]
53+
},
54+
"goalIds": [
55+
"567340e3846da0c33d6e1afd",
56+
"567340e4846da0c33d6e1b05"
57+
],
58+
"environments": {
59+
"test": {
60+
"on": true,
61+
"archived": false,
62+
"salt": "c29ydC5vcmRlcg==",
63+
"sel": "fc724d64836544268660932f4ff8c7de",
64+
"lastModified": 1469316314506,
65+
"version": 1,
66+
"targets": [
67+
{
68+
"values": [],
69+
"variation": 0
70+
},
71+
{
72+
"values": [],
73+
"variation": 1
74+
}
75+
],
76+
"rules": [],
77+
"fallthrough": {
78+
"variation": 1
79+
},
80+
"prerequisites": [],
81+
"_site": {
82+
"href": "/default/test/features/sort.order2",
83+
"type": "text/html"
84+
}
85+
},
86+
"production": {
87+
"on": true,
88+
"archived": false,
89+
"salt": "c29ydC5vcmRlcg==",
90+
"sel": "8de1085cb7354b0ab41c0e778376dfd3",
91+
"lastModified": 1469131558260,
92+
"version": 81,
93+
"targets": [
94+
{
95+
"values": [
96+
"Gerhard.Little@yahoo.com"
97+
],
98+
"variation": 0
99+
},
100+
{
101+
"values": [
102+
"1461797806429-33-861961230",
103+
"1461797806426-2--1056309393"
104+
],
105+
"variation": 1
106+
}
107+
],
108+
"rules": [],
109+
"fallthrough": {
110+
"variation": 0
111+
},
112+
"offVariation": 1,
113+
"prerequisites": [],
114+
"_site": {
115+
"href": "/default/production/features/sort.order2",
116+
"type": "text/html"
117+
}
118+
}
119+
}
120+
}

0 commit comments

Comments
 (0)