storyblok 3.19.1 → 3.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -37,7 +37,10 @@ $ storyblok login
37
37
  * `password`: your user's password
38
38
 
39
39
  ##### Options for Login with token (Recomended to SSO user's but works with all user accounts)
40
- * `token`: your access token
40
+ * `token`: your personal access token
41
+
42
+ **Get your personal access token**
43
+ * Go to [https://app.storyblok.com/#/me/account?tab=token](https://app.storyblok.com/#/me/account?tab=token) and click on Generate new token.
41
44
 
42
45
  **For Both login options you nedd to pass the region**
43
46
 
@@ -47,7 +50,7 @@ $ storyblok login
47
50
  You can also add the token directly from the login’s command, like the example below:
48
51
 
49
52
  ```sh
50
- $ storyblok login --token <YOUR_OUTH_TOKEN> --region eu
53
+ $ storyblok login --token <PERSONAL_ACCESS_TOKEN> --region eu
51
54
  ```
52
55
 
53
56
  ### logout
@@ -225,6 +228,10 @@ $ storyblok sync --type <COMMAND> --source <SPACE_ID> --target <SPACE_ID>
225
228
  * `type`: describe the command type to execute. Can be: `folders`, `components`, `stories`, `datasources` or `roles`. It's possible pass multiple types separated by comma (`,`).
226
229
  * `source`: the source space to use to sync
227
230
  * `target`: the target space to use to sync
231
+ * `starts-with`: sync only stories that starts with the given string
232
+ * `filter`: sync stories based on the given filter. Required Options: Required options: `--keys`, `--operations`, `--values`
233
+ * `keys`: Multiple keys should be separated by comma. Example: `--keys key1,key2`, `--keys key1`
234
+ * `operations`: Operations to be used for filtering. Can be: `is`, `in`, `not_in`, `like`, `not_like`, `any_in_array`, `all_in_array`, `gt_date`, `lt_date`, `gt_int`, `lt_int`, `gt_float`, `lt_float`. Multiple operations should be separated by comma.
228
235
 
229
236
  #### Examples
230
237
 
@@ -234,6 +241,15 @@ $ storyblok sync --type components --source 00001 --target 00002
234
241
 
235
242
  # Sync components and stories from `00001` space to `00002` space
236
243
  $ storyblok sync --type components,stories --source 00001 --target 00002
244
+
245
+ # Sync only stories that starts with `myStartsWithString` from `00001` space to `00002` space
246
+ $ storyblok sync --type stories --source 00001 --target 00002 --starts-with myStartsWithString
247
+
248
+ # Sync only stories with a category field like `reference` from `00001` space to `00002` space
249
+ $ storyblok sync --type stories --source 00001 --target 00002 --filter --keys category --operations like --values reference
250
+
251
+ # Sync only stories with a category field like `reference` and a name field not like `demo` from `00001` space to `00002` space
252
+ $ storyblok sync --type stories --source 00001 --target 00002 --filter --keys category,name --operations like,not_like --values reference,demo
237
253
  ```
238
254
 
239
255
  ### quickstart
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storyblok",
3
- "version": "3.19.1",
3
+ "version": "3.21.0",
4
4
  "description": "A simple CLI to start Storyblok from your command line.",
5
5
  "repository": {
6
6
  "type": "git",
package/src/cli.js CHANGED
@@ -12,7 +12,7 @@ const updateNotifier = require('update-notifier')
12
12
  const pkg = require('../package.json')
13
13
 
14
14
  const tasks = require('./tasks')
15
- const { getQuestions, lastStep, api, creds } = require('./utils')
15
+ const { getQuestions, lastStep, api, creds, buildFilterQuery } = require('./utils')
16
16
  const { SYNC_TYPES, COMMANDS } = require('./constants')
17
17
 
18
18
  clear()
@@ -268,6 +268,12 @@ program
268
268
  .requiredOption('--type <TYPE>', 'Define what will be sync. Can be components, folders, stories, datasources or roles')
269
269
  .requiredOption('--source <SPACE_ID>', 'Source space id')
270
270
  .requiredOption('--target <SPACE_ID>', 'Target space id')
271
+ .option('--starts-with <STARTS_WITH>', 'Sync only stories that starts with the given string')
272
+ .option('--filter', 'Enable filter options to sync only stories that match the given filter. Required options: --keys; --operations; --values')
273
+ .option('--keys <KEYS>', 'Field names in your story object which should be used for filtering. Multiple keys should separated by comma.')
274
+ .option('--operations <OPERATIONS>', 'Operations to be used for filtering. Can be: is, in, not_in, like, not_like, any_in_array, all_in_array, gt_date, lt_date, gt_int, lt_int, gt_float, lt_float. Multiple operations should be separated by comma.')
275
+ .option('--values <VALUES>', 'Values to be used for filtering. Any string or number. If you want to use multiple values, separate them with a comma. Multiple values should be separated by comma.')
276
+ .option('--components-groups <UUIDs>', 'Synchronize components based on their group UUIDs separated by commas')
271
277
  .action(async (options) => {
272
278
  console.log(`${chalk.blue('-')} Sync data between spaces\n`)
273
279
 
@@ -279,9 +285,17 @@ program
279
285
  const {
280
286
  type,
281
287
  source,
282
- target
288
+ target,
289
+ startsWith,
290
+ filter,
291
+ keys,
292
+ operations,
293
+ values,
294
+ componentsGroups
283
295
  } = options
284
296
 
297
+ const _componentsGroups = componentsGroups ? componentsGroups.split(',') : null
298
+
285
299
  const _types = type.split(',') || []
286
300
  _types.forEach(_type => {
287
301
  if (!SYNC_TYPES.includes(_type)) {
@@ -289,13 +303,17 @@ program
289
303
  }
290
304
  })
291
305
 
292
- const token = creds.get().token || null
306
+ const filterQuery = filter ? buildFilterQuery(keys, operations, values) : undefined
293
307
 
308
+ const token = creds.get().token || null
294
309
  await tasks.sync(_types, {
295
310
  api,
296
311
  token,
297
312
  source,
298
- target
313
+ target,
314
+ startsWith,
315
+ filterQuery,
316
+ _componentsGroups
299
317
  })
300
318
 
301
319
  console.log('\n' + chalk.green('✓') + ' Sync data between spaces successfully completed')
@@ -18,6 +18,7 @@ class SyncComponents {
18
18
  this.oauthToken = options.oauthToken
19
19
  this.client = api.getClient()
20
20
  this.presetsLib = new PresetsLib({ oauthToken: options.oauthToken, targetSpaceId: this.targetSpaceId })
21
+ this.componentsGroups = options.componentsGroups
21
22
  }
22
23
 
23
24
  async sync () {
@@ -70,6 +71,14 @@ class SyncComponents {
70
71
 
71
72
  const sourceGroupUuid = component.component_group_uuid
72
73
 
74
+ if (this.componentsGroups && !this.componentsGroups.includes(sourceGroupUuid)) {
75
+ console.log(
76
+ chalk.yellow("-") +
77
+ ` Component ${component.name} does not belong to the ${this.componentsGroups} group(s).`
78
+ );
79
+ continue;
80
+ }
81
+
73
82
  // if the component belongs to a group
74
83
  if (sourceGroupUuid) {
75
84
  const sourceGroup = findByProperty(
package/src/tasks/sync.js CHANGED
@@ -3,6 +3,7 @@ const chalk = require('chalk')
3
3
  const SyncComponents = require('./sync-commands/components')
4
4
  const SyncDatasources = require('./sync-commands/datasources')
5
5
  const { capitalize } = require('../utils')
6
+ const { startsWith } = require('lodash/string')
6
7
 
7
8
  const SyncSpaces = {
8
9
  targetComponents: [],
@@ -14,7 +15,10 @@ const SyncSpaces = {
14
15
  this.sourceSpaceId = options.source
15
16
  this.targetSpaceId = options.target
16
17
  this.oauthToken = options.token
18
+ this.startsWith = options.startsWith
19
+ this.filterQuery = options.filterQuery
17
20
  this.client = api.getClient()
21
+ this.componentsGroups = options._componentsGroups
18
22
  },
19
23
 
20
24
  async getStoryWithTranslatedSlugs (sourceStory, targetStory) {
@@ -56,7 +60,9 @@ const SyncSpaces = {
56
60
  }
57
61
 
58
62
  const all = await this.client.getAll(`spaces/${this.sourceSpaceId}/stories`, {
59
- story_only: 1
63
+ story_only: 1,
64
+ ...(this.startsWith ? { starts_with: this.startsWith } : {}),
65
+ ...(this.filterQuery ? { filter_query: this.filterQuery } : {})
60
66
  })
61
67
 
62
68
  for (let i = 0; i < all.length; i++) {
@@ -226,7 +232,8 @@ const SyncSpaces = {
226
232
  const syncComponentsInstance = new SyncComponents({
227
233
  sourceSpaceId: this.sourceSpaceId,
228
234
  targetSpaceId: this.targetSpaceId,
229
- oauthToken: this.oauthToken
235
+ oauthToken: this.oauthToken,
236
+ componentsGroups: this.componentsGroups
230
237
  })
231
238
 
232
239
  try {
@@ -0,0 +1,23 @@
1
+ const buildFilterQuery = (keys, operations, values) => {
2
+ const operators = ['is', 'in', 'not_in', 'like', 'not_like', 'any_in_array', 'all_in_array', 'gt_date', 'lt_date', 'gt_int', 'lt_int', 'gt_float', 'lt_float']
3
+ if (!keys || !operations || !values) {
4
+ throw new Error('Filter options are required: --keys; --operations; --values')
5
+ }
6
+ const _keys = keys.split(',')
7
+ const _operations = operations.split(',')
8
+ const _values = values.split(',')
9
+ if (_keys.length !== _operations.length || _keys.length !== _values.length) {
10
+ throw new Error('The number of keys, operations and values must be the same')
11
+ }
12
+ const invalidOperators = _operations.filter((o) => !operators.includes(o))
13
+ if (invalidOperators.length) {
14
+ throw new Error('Invalid operator(s) applied for filter: ' + invalidOperators.join(' '))
15
+ }
16
+ const filterQuery = {}
17
+ _keys.forEach((key, index) => {
18
+ filterQuery[key] = { [_operations[index]]: _values[index] }
19
+ })
20
+ return filterQuery
21
+ }
22
+
23
+ module.exports = buildFilterQuery
@@ -6,5 +6,6 @@ module.exports = {
6
6
  capitalize: require('./capitalize'),
7
7
  findByProperty: require('./find-by-property'),
8
8
  parseError: require('./parse-error'),
9
+ buildFilterQuery: require('./build-filter-query'),
9
10
  saveFileFactory: require('./save-file-factory')
10
- }
11
+ }