storyblok 3.17.2 → 3.19.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 +63 -36
- package/package.json +1 -1
- package/src/cli.js +10 -3
- package/src/tasks/pull-components.js +25 -18
- package/src/tasks/push-components.js +32 -9
- package/src/utils/api.js +42 -13
- package/src/utils/get-questions.js +54 -15
- package/src/utils/index.js +2 -1
- package/src/utils/save-file-factory.js +16 -0
- package/tests/constants.js +44 -3
- package/tests/units/pull-components.spec.js +81 -29
- package/tests/units/sync-components.spec.js +3 -1
package/README.md
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
[](https://www.npmjs.com/package/storyblok)
|
|
7
7
|
[](ttps://img.shields.io/npm/dt/storyblok.svg)
|
|
8
|
-
[](https://github.com/storyblok/storyblok/issues?q=is%3Aopen+is%3Aissue)
|
|
9
|
-
[](https://github.com/storyblok/storyblok/issues?q=is%3Aissue+is%3Aclosed)
|
|
8
|
+
[](https://github.com/storyblok/storyblok/issues?q=is%3Aopen+is%3Aissue)
|
|
9
|
+
[](https://github.com/storyblok/storyblok-cli/issues?q=is%3Aissue+is%3Aclosed)
|
|
10
10
|
|
|
11
11
|
## BREAKING CHANGE
|
|
12
12
|
|
|
@@ -22,6 +22,50 @@ $ npm i storyblok -g
|
|
|
22
22
|
|
|
23
23
|
## Commands
|
|
24
24
|
|
|
25
|
+
|
|
26
|
+
### login
|
|
27
|
+
|
|
28
|
+
Login to the Storyblok cli
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
$ storyblok login
|
|
32
|
+
```
|
|
33
|
+
#### Login options
|
|
34
|
+
|
|
35
|
+
##### Options for Login with email and password
|
|
36
|
+
* `email`: your user's email address
|
|
37
|
+
* `password`: your user's password
|
|
38
|
+
|
|
39
|
+
##### Options for Login with token (Recomended to SSO user's but works with all user accounts)
|
|
40
|
+
* `token`: your access token
|
|
41
|
+
|
|
42
|
+
**For Both login options you nedd to pass the region**
|
|
43
|
+
|
|
44
|
+
* `region`: your user's region (default: `eu`). You can use `us`, `cn` or `eu`. This region will be used for the other cli's commands.
|
|
45
|
+
|
|
46
|
+
#### Login with token flag
|
|
47
|
+
You can also add the token directly from the login’s command, like the example below:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
$ storyblok login --token <YOUR_OUTH_TOKEN> --region eu
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### logout
|
|
54
|
+
|
|
55
|
+
Logout from the Storyblok cli
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
$ storyblok logout
|
|
59
|
+
```
|
|
60
|
+
### user
|
|
61
|
+
|
|
62
|
+
Get the currently logged in user
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
$ storyblok user
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
|
|
25
69
|
### select
|
|
26
70
|
|
|
27
71
|
Usage to kickstart a boilerplate, fieldtype or theme
|
|
@@ -44,27 +88,33 @@ $ storyblok pull-languages --space <SPACE_ID>
|
|
|
44
88
|
|
|
45
89
|
### pull-components
|
|
46
90
|
|
|
47
|
-
Download your space's components schema as json.
|
|
91
|
+
Download your space's components schema as json. By default this command will download 2 files: 1 for the components and 1 for the presets; But if you pass a flag `--separate-files or --sf` the command will create file for each component and presets. And also you could pass a path `--path or -p` to save your components and presets.
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
$ storyblok pull-components --space <SPACE_ID> # Will save files like components-1234.json
|
|
95
|
+
```
|
|
48
96
|
|
|
49
97
|
```sh
|
|
50
|
-
$ storyblok pull-components --space <SPACE_ID> --
|
|
98
|
+
$ storyblok pull-components --space <SPACE_ID> --separate-files # Will save files like feature-1234.json grid-1234.json
|
|
51
99
|
```
|
|
52
100
|
|
|
53
101
|
#### Options
|
|
54
102
|
|
|
55
103
|
* `space`: your space id
|
|
104
|
+
* `separate-files`: boolean flag to save components and presets in single files instead a file with all
|
|
105
|
+
* `path`: the path to save your components and preset files
|
|
56
106
|
|
|
57
107
|
### push-components
|
|
58
108
|
|
|
59
109
|
Push your components file to your/another space
|
|
60
110
|
|
|
61
111
|
```sh
|
|
62
|
-
$ storyblok push-components <SOURCE> --space <SPACE_ID> --
|
|
112
|
+
$ storyblok push-components <SOURCE> --space <SPACE_ID> --presets-source <PRESETS_SOURCE>
|
|
63
113
|
```
|
|
64
114
|
|
|
65
115
|
#### Parameters
|
|
66
116
|
|
|
67
|
-
* `source`: can be a URL or path to JSON file.
|
|
117
|
+
* `source`: can be a URL or path to JSON file, the path to a json file could be to a single or multiple files separated by comma, like `./pages-1234.json,../User/components/grid-1234.json`
|
|
68
118
|
|
|
69
119
|
Using an **URL**
|
|
70
120
|
|
|
@@ -72,12 +122,18 @@ Using an **URL**
|
|
|
72
122
|
$ storyblok push-components https://raw.githubusercontent.com/storyblok/nuxtdoc/master/seed.components.json --space 67819
|
|
73
123
|
```
|
|
74
124
|
|
|
75
|
-
Using a **path** to file
|
|
125
|
+
Using a **path** to a single file
|
|
76
126
|
|
|
77
127
|
```sh
|
|
78
128
|
$ storyblok push-components ./components.json --space 67819
|
|
79
129
|
```
|
|
80
130
|
|
|
131
|
+
Using a **path** to a multiple files
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
$ storyblok push-components ./page.json,../grid.json,./feature.json --space 67819
|
|
135
|
+
```
|
|
136
|
+
|
|
81
137
|
#### Options
|
|
82
138
|
|
|
83
139
|
* `space`: your space id
|
|
@@ -188,35 +244,6 @@ Create a space in Storyblok and select the boilerplate to use
|
|
|
188
244
|
$ storyblok quickstart
|
|
189
245
|
```
|
|
190
246
|
|
|
191
|
-
### logout
|
|
192
|
-
|
|
193
|
-
Logout from the Storyblok cli
|
|
194
|
-
|
|
195
|
-
```sh
|
|
196
|
-
$ storyblok logout
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### login
|
|
200
|
-
|
|
201
|
-
Login to the Storyblok cli
|
|
202
|
-
|
|
203
|
-
```sh
|
|
204
|
-
$ storyblok login
|
|
205
|
-
```
|
|
206
|
-
#### Options
|
|
207
|
-
|
|
208
|
-
* `email`: your user's email address
|
|
209
|
-
* `password`: your user's password
|
|
210
|
-
* `region`: your user's region (default: `eu`). You can use `us`, `cn` or `eu`. This region will be used for the other cli's commands.
|
|
211
|
-
|
|
212
|
-
### user
|
|
213
|
-
|
|
214
|
-
Get the currently logged in user
|
|
215
|
-
|
|
216
|
-
```sh
|
|
217
|
-
$ storyblok user
|
|
218
|
-
```
|
|
219
|
-
|
|
220
247
|
### generate-migration
|
|
221
248
|
|
|
222
249
|
Create a migration file (with the name `change_<COMPONENT>_<FIELD>.js`) inside the `migrations` folder. Check **Migrations** section to more details
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -39,14 +39,18 @@ program
|
|
|
39
39
|
program
|
|
40
40
|
.command(COMMANDS.LOGIN)
|
|
41
41
|
.description('Login to the Storyblok cli')
|
|
42
|
+
.option('-t, --token <token>', 'Token to login directly without questions, like for CI enviroments')
|
|
43
|
+
.option('-r, --region <region>', 'Region of the user')
|
|
42
44
|
.action(async (options) => {
|
|
45
|
+
const { token, region } = options
|
|
46
|
+
|
|
43
47
|
if (api.isAuthorized()) {
|
|
44
48
|
console.log(chalk.green('✓') + ' The user has been already logged. If you want to change the logged user, you must logout and login again')
|
|
45
49
|
return
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
try {
|
|
49
|
-
await api.processLogin()
|
|
53
|
+
await api.processLogin(token, region)
|
|
50
54
|
process.exit(0)
|
|
51
55
|
} catch (e) {
|
|
52
56
|
console.log(chalk.red('X') + ' An error occurred when logging the user: ' + e.message)
|
|
@@ -115,10 +119,13 @@ program
|
|
|
115
119
|
// pull-components
|
|
116
120
|
program
|
|
117
121
|
.command(COMMANDS.PULL_COMPONENTS)
|
|
122
|
+
.option('--sf, --separate-files [value]', 'Argument to create a single file for each component')
|
|
123
|
+
.option('-p, --path <path>', 'Path to save the component files')
|
|
118
124
|
.description("Download your space's components schema as json")
|
|
119
|
-
.action(async () => {
|
|
125
|
+
.action(async (options) => {
|
|
120
126
|
console.log(`${chalk.blue('-')} Executing pull-components task`)
|
|
121
127
|
const space = program.space
|
|
128
|
+
const { separateFiles, path } = options
|
|
122
129
|
if (!space) {
|
|
123
130
|
console.log(chalk.red('X') + ' Please provide the space as argument --space YOUR_SPACE_ID.')
|
|
124
131
|
process.exit(0)
|
|
@@ -130,7 +137,7 @@ program
|
|
|
130
137
|
}
|
|
131
138
|
|
|
132
139
|
api.setSpaceId(space)
|
|
133
|
-
await tasks.pullComponents(api, { space })
|
|
140
|
+
await tasks.pullComponents(api, { space, separateFiles, path })
|
|
134
141
|
} catch (e) {
|
|
135
142
|
errorHandler(e, COMMANDS.PULL_COMPONENTS)
|
|
136
143
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
2
1
|
const chalk = require('chalk')
|
|
2
|
+
const saveFileFactory = require('../utils/save-file-factory')
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @method getNameFromComponentGroups
|
|
@@ -20,11 +20,11 @@ const getNameFromComponentGroups = (groups, uuid) => {
|
|
|
20
20
|
/**
|
|
21
21
|
* @method pullComponents
|
|
22
22
|
* @param {Object} api
|
|
23
|
-
* @param {Object} options { space: Number }
|
|
23
|
+
* @param {Object} options { space: Number, separateFiles: Boolean, path: String }
|
|
24
24
|
* @return {Promise<Object>}
|
|
25
25
|
*/
|
|
26
26
|
const pullComponents = async (api, options) => {
|
|
27
|
-
const { space } = options
|
|
27
|
+
const { space, separateFiles, path } = options
|
|
28
28
|
|
|
29
29
|
try {
|
|
30
30
|
const componentGroups = await api.getComponentGroups()
|
|
@@ -41,33 +41,40 @@ const pullComponents = async (api, options) => {
|
|
|
41
41
|
}
|
|
42
42
|
})
|
|
43
43
|
|
|
44
|
+
if (separateFiles) {
|
|
45
|
+
for (const comp in components) {
|
|
46
|
+
const compFileName = `${components[comp].name}-${space}.json`
|
|
47
|
+
const data = JSON.stringify(components[comp], null, 2)
|
|
48
|
+
saveFileFactory(compFileName, data, path)
|
|
49
|
+
}
|
|
50
|
+
console.log(`${chalk.green('✓')} We've saved your components in files with the names of each component`)
|
|
51
|
+
|
|
52
|
+
if (presets.length === 0) return
|
|
53
|
+
|
|
54
|
+
for (const preset in presets) {
|
|
55
|
+
const presetFileName = `${presets[preset].name}-${space}.json`
|
|
56
|
+
const data = JSON.stringify(presets[preset], null, 2)
|
|
57
|
+
saveFileFactory(presetFileName, data, path)
|
|
58
|
+
}
|
|
59
|
+
console.log(`${chalk.green('✓')} We've saved your presets in files with the names of each preset`)
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
44
63
|
const file = `components.${space}.json`
|
|
45
64
|
const data = JSON.stringify({ components }, null, 2)
|
|
46
65
|
|
|
47
66
|
console.log(`${chalk.green('✓')} We've saved your components in the file: ${file}`)
|
|
48
67
|
|
|
49
|
-
|
|
50
|
-
if (err) {
|
|
51
|
-
Promise.reject(err)
|
|
52
|
-
return
|
|
53
|
-
}
|
|
68
|
+
saveFileFactory(file, data, path)
|
|
54
69
|
|
|
55
|
-
|
|
56
|
-
})
|
|
70
|
+
if (presets.length === 0) return
|
|
57
71
|
|
|
58
72
|
const presetsFile = `presets.${space}.json`
|
|
59
73
|
const presetsData = JSON.stringify({ presets }, null, 2)
|
|
60
74
|
|
|
61
75
|
console.log(`${chalk.green('✓')} We've saved your presets in the file: ${presetsFile}`)
|
|
62
76
|
|
|
63
|
-
|
|
64
|
-
if (err) {
|
|
65
|
-
Promise.reject(err)
|
|
66
|
-
return
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
Promise.resolve(presetsFile)
|
|
70
|
-
})
|
|
77
|
+
saveFileFactory(presetsFile, presetsData, path)
|
|
71
78
|
} catch (e) {
|
|
72
79
|
console.error(`${chalk.red('X')} An error ocurred in pull-components task when load components data`)
|
|
73
80
|
return Promise.reject(new Error(e))
|
|
@@ -21,30 +21,53 @@ const getGroupByUuid = (groups, uuid) => {
|
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Get the data from a local or remote JSON file
|
|
24
|
-
* @param {string}
|
|
24
|
+
* @param {string} path the local path or remote url of the file
|
|
25
25
|
* @returns {Promise<Object>} return the data from the source or an error
|
|
26
26
|
*/
|
|
27
|
-
const
|
|
28
|
-
if (!
|
|
27
|
+
const getDataFromPath = async (path) => {
|
|
28
|
+
if (!path) {
|
|
29
29
|
return {}
|
|
30
30
|
}
|
|
31
|
+
const sources = path.split(',')
|
|
32
|
+
const isList = sources.length > 1
|
|
31
33
|
|
|
32
34
|
try {
|
|
33
|
-
if (isUrl(
|
|
34
|
-
return (await axios.get(
|
|
35
|
+
if (isUrl(path)) {
|
|
36
|
+
return (await axios.get(path)).data
|
|
35
37
|
} else {
|
|
36
|
-
return JSON.parse(fs.readFileSync(
|
|
38
|
+
if (!isList) return JSON.parse(fs.readFileSync(sources[0], 'utf8'))
|
|
39
|
+
|
|
40
|
+
const data = []
|
|
41
|
+
sources.forEach((source) => {
|
|
42
|
+
data.push(JSON.parse(fs.readFileSync(source, 'utf8')))
|
|
43
|
+
})
|
|
44
|
+
return data
|
|
37
45
|
}
|
|
38
46
|
} catch (err) {
|
|
39
|
-
console.error(`${chalk.red('X')} Can not load json file from ${
|
|
47
|
+
console.error(`${chalk.red('X')} Can not load json file from ${path}`)
|
|
40
48
|
return Promise.reject(err)
|
|
41
49
|
}
|
|
42
50
|
}
|
|
43
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Creat an array based in the content parameter and the key provided
|
|
54
|
+
* @param {object} content the data to create a list
|
|
55
|
+
* @param {string} key key to serch in the content
|
|
56
|
+
* @returns {Array} return the data from the source or an error
|
|
57
|
+
*/
|
|
58
|
+
const createContentList = (content, key) => {
|
|
59
|
+
if (content[key]) return content[key]
|
|
60
|
+
else if (Array.isArray(content)) return [...content]
|
|
61
|
+
else return [content]
|
|
62
|
+
}
|
|
63
|
+
|
|
44
64
|
module.exports = async (api, { source, presetsSource }) => {
|
|
45
65
|
try {
|
|
46
|
-
const
|
|
47
|
-
const
|
|
66
|
+
const rawComponents = await getDataFromPath(source)
|
|
67
|
+
const components = createContentList(rawComponents, 'components')
|
|
68
|
+
const rawPresets = await getDataFromPath(presetsSource)
|
|
69
|
+
const presets = createContentList(rawPresets, 'presets')
|
|
70
|
+
|
|
48
71
|
return push(api, components, presets)
|
|
49
72
|
} catch (err) {
|
|
50
73
|
console.error(`${chalk.red('X')} Can not push invalid json - please provide a valid json file`)
|
package/src/utils/api.js
CHANGED
|
@@ -35,7 +35,8 @@ module.exports = {
|
|
|
35
35
|
return path
|
|
36
36
|
},
|
|
37
37
|
|
|
38
|
-
async login (
|
|
38
|
+
async login (content) {
|
|
39
|
+
const { email, password, region } = content
|
|
39
40
|
try {
|
|
40
41
|
const response = await axios.post(`${this.apiSwitcher(region)}users/login`, {
|
|
41
42
|
email: email,
|
|
@@ -68,10 +69,10 @@ module.exports = {
|
|
|
68
69
|
otp_attempt: code
|
|
69
70
|
})
|
|
70
71
|
|
|
71
|
-
return this.persistCredentials(email, newResponse.data || {}, region)
|
|
72
|
+
return this.persistCredentials(email, newResponse.data.access_token || {}, region)
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
return this.persistCredentials(email, data, region)
|
|
75
|
+
return this.persistCredentials(email, data.access_token, region)
|
|
75
76
|
} catch (e) {
|
|
76
77
|
return Promise.reject(e)
|
|
77
78
|
}
|
|
@@ -92,27 +93,43 @@ module.exports = {
|
|
|
92
93
|
}
|
|
93
94
|
},
|
|
94
95
|
|
|
95
|
-
persistCredentials (email,
|
|
96
|
-
const token = this.extractToken(data)
|
|
96
|
+
persistCredentials (email, token = null, region = 'eu') {
|
|
97
97
|
if (token) {
|
|
98
98
|
this.oauthToken = token
|
|
99
99
|
creds.set(email, token, region)
|
|
100
100
|
|
|
101
|
-
return Promise.resolve(
|
|
101
|
+
return Promise.resolve(token)
|
|
102
102
|
}
|
|
103
103
|
return Promise.reject(new Error('The code could not be authenticated.'))
|
|
104
104
|
},
|
|
105
105
|
|
|
106
|
-
async processLogin () {
|
|
106
|
+
async processLogin (token = null, region = null) {
|
|
107
107
|
try {
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
if (token && region) {
|
|
109
|
+
await this.loginWithToken({ token, region })
|
|
110
|
+
console.log(chalk.green('✓') + ' Log in successfully! Token has been added to .netrc file.')
|
|
111
|
+
return Promise.resolve({ token, region })
|
|
112
|
+
}
|
|
110
113
|
|
|
111
|
-
|
|
114
|
+
let content = {}
|
|
115
|
+
await inquirer
|
|
116
|
+
.prompt(getQuestions('login-strategy'))
|
|
117
|
+
.then(async ({ strategy }) => {
|
|
118
|
+
content = await inquirer.prompt(getQuestions(strategy))
|
|
119
|
+
})
|
|
120
|
+
.catch((error) => {
|
|
121
|
+
console.log(error)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
if (!content.token) {
|
|
125
|
+
await this.login(content)
|
|
126
|
+
} else {
|
|
127
|
+
await this.loginWithToken(content)
|
|
128
|
+
}
|
|
112
129
|
|
|
113
130
|
console.log(chalk.green('✓') + ' Log in successfully! Token has been added to .netrc file.')
|
|
114
131
|
|
|
115
|
-
return Promise.resolve(
|
|
132
|
+
return Promise.resolve(content)
|
|
116
133
|
} catch (e) {
|
|
117
134
|
if (e.response && e.response.data && e.response.data.error) {
|
|
118
135
|
console.error(chalk.red('X') + ' An error ocurred when login the user: ' + e.response.data.error)
|
|
@@ -125,8 +142,20 @@ module.exports = {
|
|
|
125
142
|
}
|
|
126
143
|
},
|
|
127
144
|
|
|
128
|
-
|
|
129
|
-
|
|
145
|
+
async loginWithToken (content) {
|
|
146
|
+
const { token, region } = content
|
|
147
|
+
try {
|
|
148
|
+
const { data } = await axios.get(`${this.apiSwitcher(region)}users/me`, {
|
|
149
|
+
headers: {
|
|
150
|
+
Authorization: token
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
this.persistCredentials(data.user.email, token, region)
|
|
155
|
+
return data.user
|
|
156
|
+
} catch (e) {
|
|
157
|
+
return Promise.reject(e)
|
|
158
|
+
}
|
|
130
159
|
},
|
|
131
160
|
|
|
132
161
|
logout (unauthorized) {
|
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
const getOptions = (subCommand, argv = {}, api = {}) => {
|
|
2
2
|
let email = ''
|
|
3
|
+
const moreOptions = [
|
|
4
|
+
'delete-templates',
|
|
5
|
+
'pull-components',
|
|
6
|
+
'push-components',
|
|
7
|
+
'scaffold'
|
|
8
|
+
]
|
|
9
|
+
const regionInput = {
|
|
10
|
+
type: 'input',
|
|
11
|
+
name: 'region',
|
|
12
|
+
message: 'Enter your user\'s region (us, eu or cn):',
|
|
13
|
+
validate: function (value) {
|
|
14
|
+
const flagList = ['us', 'cn', 'eu']
|
|
15
|
+
if (flagList.indexOf(value) > -1) {
|
|
16
|
+
return true
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return 'Please enter a valid region: us, eu or cn'
|
|
20
|
+
}
|
|
21
|
+
}
|
|
3
22
|
|
|
4
23
|
if (subCommand === 'select') {
|
|
5
24
|
return [
|
|
@@ -103,7 +122,29 @@ const getOptions = (subCommand, argv = {}, api = {}) => {
|
|
|
103
122
|
]
|
|
104
123
|
}
|
|
105
124
|
|
|
106
|
-
if (subCommand === 'login') {
|
|
125
|
+
if (subCommand === 'login-strategy') {
|
|
126
|
+
return [
|
|
127
|
+
{
|
|
128
|
+
type: 'list',
|
|
129
|
+
name: 'strategy',
|
|
130
|
+
message: 'Select the login strategy: ',
|
|
131
|
+
choices: [
|
|
132
|
+
{
|
|
133
|
+
name: 'With email and password (Common users with storyblok account)',
|
|
134
|
+
value: 'login-with-email',
|
|
135
|
+
short: 'Email'
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: 'With Token (Most recommended for SSO users)',
|
|
139
|
+
value: 'login-with-token',
|
|
140
|
+
short: 'Token'
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (subCommand === 'login-with-email') {
|
|
107
148
|
return [
|
|
108
149
|
{
|
|
109
150
|
type: 'input',
|
|
@@ -129,29 +170,27 @@ const getOptions = (subCommand, argv = {}, api = {}) => {
|
|
|
129
170
|
return 'Please enter a valid password:'
|
|
130
171
|
}
|
|
131
172
|
},
|
|
173
|
+
regionInput
|
|
174
|
+
]
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (subCommand === 'login-with-token') {
|
|
178
|
+
return [
|
|
132
179
|
{
|
|
133
180
|
type: 'input',
|
|
134
|
-
name: '
|
|
135
|
-
message: 'Enter your
|
|
181
|
+
name: 'token',
|
|
182
|
+
message: 'Enter your token:',
|
|
136
183
|
validate: function (value) {
|
|
137
|
-
|
|
138
|
-
if (flagList.indexOf(value) > -1) {
|
|
184
|
+
if (value.length > 0) {
|
|
139
185
|
return true
|
|
140
186
|
}
|
|
141
|
-
|
|
142
|
-
return 'Please enter a valid region: us, eu or cn'
|
|
187
|
+
return 'Please enter a valid token:'
|
|
143
188
|
}
|
|
144
|
-
}
|
|
189
|
+
},
|
|
190
|
+
regionInput
|
|
145
191
|
]
|
|
146
192
|
}
|
|
147
193
|
|
|
148
|
-
const moreOptions = [
|
|
149
|
-
'delete-templates',
|
|
150
|
-
'pull-components',
|
|
151
|
-
'push-components',
|
|
152
|
-
'scaffold'
|
|
153
|
-
]
|
|
154
|
-
|
|
155
194
|
if (moreOptions.indexOf(subCommand) > -1) {
|
|
156
195
|
const loginQuestions = [
|
|
157
196
|
{
|
package/src/utils/index.js
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
|
|
3
|
+
const saveFileFactory = async (fileName, content, path = './') => {
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
fs.writeFile(`${path}${fileName}`, content, err => {
|
|
6
|
+
if (err) {
|
|
7
|
+
Promise.reject(err)
|
|
8
|
+
return
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
Promise.resolve(true)
|
|
12
|
+
})
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = saveFileFactory
|
package/tests/constants.js
CHANGED
|
@@ -3,7 +3,7 @@ const PASSWORD_TEST = 'test'
|
|
|
3
3
|
const TOKEN_TEST = 'storyblok1234'
|
|
4
4
|
const REGION_TEST = 'eu'
|
|
5
5
|
|
|
6
|
-
// use functions to always returns
|
|
6
|
+
// use functions to always returns 'new' data
|
|
7
7
|
const FAKE_COMPONENTS = () => [
|
|
8
8
|
{
|
|
9
9
|
name: 'teaser',
|
|
@@ -136,7 +136,7 @@ const FAKE_COMPONENTS = () => [
|
|
|
136
136
|
}
|
|
137
137
|
]
|
|
138
138
|
|
|
139
|
-
// use functions to always returns
|
|
139
|
+
// use functions to always returns 'new' data
|
|
140
140
|
const FAKE_STORIES = () => [
|
|
141
141
|
{
|
|
142
142
|
name: 'About',
|
|
@@ -249,6 +249,46 @@ const FAKE_SPACE_OPTIONS = () => ({
|
|
|
249
249
|
use_translated_stories: false
|
|
250
250
|
})
|
|
251
251
|
|
|
252
|
+
const FAKE_PRESET = () => ({
|
|
253
|
+
id: 123,
|
|
254
|
+
name: 'page_preset',
|
|
255
|
+
preset: {
|
|
256
|
+
_uid: '7dce995b-07ed-4e5b-a4bb-5d22447252d8',
|
|
257
|
+
body: [
|
|
258
|
+
{
|
|
259
|
+
_uid: '995e84c1-a08d-45cd-b121-e4db45e9cf50',
|
|
260
|
+
headline: 'Hello world!',
|
|
261
|
+
component: 'teaser'
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
_uid: 'a6e118ec-1a57-4f0f-b1e9-1ed625a82751',
|
|
265
|
+
columns: [
|
|
266
|
+
{
|
|
267
|
+
_uid: '9b0a9bed-e891-4edc-8f5e-bc29e7ec785c',
|
|
268
|
+
name: 'Feature 1',
|
|
269
|
+
component: 'feature'
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
_uid: '07863609-7518-48b9-8d28-b1e8037818e2',
|
|
273
|
+
name: 'Feature 2',
|
|
274
|
+
component: 'feature'
|
|
275
|
+
}
|
|
276
|
+
],
|
|
277
|
+
component: 'grid'
|
|
278
|
+
}
|
|
279
|
+
],
|
|
280
|
+
component: 'page'
|
|
281
|
+
},
|
|
282
|
+
component_id: 3481284,
|
|
283
|
+
space_id: 200378,
|
|
284
|
+
created_at: '2023-02-24T16:49:14.723Z',
|
|
285
|
+
updated_at: '2023-02-24T16:49:14.723Z',
|
|
286
|
+
image: '',
|
|
287
|
+
color: '',
|
|
288
|
+
icon: '',
|
|
289
|
+
description: 'page preset'
|
|
290
|
+
})
|
|
291
|
+
|
|
252
292
|
module.exports = {
|
|
253
293
|
EMAIL_TEST,
|
|
254
294
|
TOKEN_TEST,
|
|
@@ -257,5 +297,6 @@ module.exports = {
|
|
|
257
297
|
FAKE_COMPONENTS,
|
|
258
298
|
FAKE_SPACES,
|
|
259
299
|
FAKE_SPACE_OPTIONS,
|
|
260
|
-
REGION_TEST
|
|
300
|
+
REGION_TEST,
|
|
301
|
+
FAKE_PRESET
|
|
261
302
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const pullComponents = require('../../src/tasks/pull-components')
|
|
3
|
-
const { FAKE_COMPONENTS } = require('../constants')
|
|
3
|
+
const { FAKE_COMPONENTS, FAKE_PRESET } = require('../constants')
|
|
4
4
|
|
|
5
5
|
jest.mock('fs')
|
|
6
6
|
|
|
@@ -24,36 +24,12 @@ describe('testing pullComponents', () => {
|
|
|
24
24
|
})
|
|
25
25
|
})
|
|
26
26
|
|
|
27
|
-
it('
|
|
27
|
+
it('pull components should be call fs.writeFile correctly and generate component file', async () => {
|
|
28
28
|
const SPACE = 12345
|
|
29
|
-
const BODY = {
|
|
30
|
-
components: [
|
|
31
|
-
{
|
|
32
|
-
name: 'teaser',
|
|
33
|
-
display_name: null,
|
|
34
|
-
created_at: '2019-10-15T17:00:32.212Z',
|
|
35
|
-
id: 581153,
|
|
36
|
-
schema: {
|
|
37
|
-
headline: {
|
|
38
|
-
type: 'text'
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
image: null,
|
|
42
|
-
preview_field: null,
|
|
43
|
-
is_root: false,
|
|
44
|
-
preview_tmpl: null,
|
|
45
|
-
is_nestable: true,
|
|
46
|
-
all_presets: [],
|
|
47
|
-
preset_id: null,
|
|
48
|
-
real_name: 'teaser',
|
|
49
|
-
component_group_uuid: null
|
|
50
|
-
}
|
|
51
|
-
]
|
|
52
|
-
}
|
|
53
29
|
|
|
54
30
|
const api = {
|
|
55
31
|
getComponents () {
|
|
56
|
-
return Promise.resolve(
|
|
32
|
+
return Promise.resolve([FAKE_COMPONENTS()[0]])
|
|
57
33
|
},
|
|
58
34
|
getComponentGroups () {
|
|
59
35
|
return Promise.resolve([])
|
|
@@ -73,9 +49,85 @@ describe('testing pullComponents', () => {
|
|
|
73
49
|
.then(_ => {
|
|
74
50
|
const [path, data] = fs.writeFile.mock.calls[0]
|
|
75
51
|
|
|
76
|
-
expect(fs.writeFile.mock.calls.length).toBe(
|
|
52
|
+
expect(fs.writeFile.mock.calls.length).toBe(1)
|
|
77
53
|
expect(path).toBe(`./${expectFileName}`)
|
|
78
|
-
expect(JSON.parse(data)).toEqual(
|
|
54
|
+
expect(JSON.parse(data)).toEqual({ components: [FAKE_COMPONENTS()[0]] })
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('pull components should be call fs.writeFile correctly and generate a component and preset files', async () => {
|
|
59
|
+
const SPACE = 12345
|
|
60
|
+
|
|
61
|
+
const api = {
|
|
62
|
+
getComponents () {
|
|
63
|
+
return Promise.resolve([FAKE_COMPONENTS()[0]])
|
|
64
|
+
},
|
|
65
|
+
getComponentGroups () {
|
|
66
|
+
return Promise.resolve([])
|
|
67
|
+
},
|
|
68
|
+
getPresets () {
|
|
69
|
+
return Promise.resolve(FAKE_PRESET())
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const options = {
|
|
74
|
+
space: SPACE
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const expectComponentFileName = `components.${SPACE}.json`
|
|
78
|
+
const expectPresetFileName = `presets.${SPACE}.json`
|
|
79
|
+
|
|
80
|
+
return pullComponents(api, options)
|
|
81
|
+
.then(_ => {
|
|
82
|
+
const [compPath, compData] = fs.writeFile.mock.calls[0]
|
|
83
|
+
const [presetPath, presetData] = fs.writeFile.mock.calls[1]
|
|
84
|
+
|
|
85
|
+
expect(fs.writeFile.mock.calls.length).toBe(2)
|
|
86
|
+
|
|
87
|
+
expect(compPath).toBe(`./${expectComponentFileName}`)
|
|
88
|
+
expect(JSON.parse(compData)).toEqual({ components: [FAKE_COMPONENTS()[0]] })
|
|
89
|
+
|
|
90
|
+
expect(presetPath).toBe(`./${expectPresetFileName}`)
|
|
91
|
+
expect(JSON.parse(presetData)).toEqual({ presets: FAKE_PRESET() })
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('pull components should be call fs.writeFile and generate a single file for each component', async () => {
|
|
96
|
+
const SPACE = 12345
|
|
97
|
+
|
|
98
|
+
const api = {
|
|
99
|
+
getComponents () {
|
|
100
|
+
return Promise.resolve(FAKE_COMPONENTS())
|
|
101
|
+
},
|
|
102
|
+
getComponentGroups () {
|
|
103
|
+
return Promise.resolve([])
|
|
104
|
+
},
|
|
105
|
+
getPresets () {
|
|
106
|
+
return Promise.resolve([])
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const options = {
|
|
111
|
+
space: SPACE,
|
|
112
|
+
separateFiles: true
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return pullComponents(api, options)
|
|
116
|
+
.then(_ => {
|
|
117
|
+
expect(fs.writeFile.mock.calls.length).toBe(FAKE_COMPONENTS().length)
|
|
118
|
+
|
|
119
|
+
for (const comp in FAKE_COMPONENTS()) {
|
|
120
|
+
const compFileName = `${FAKE_COMPONENTS()[comp].name}-${SPACE}.json`
|
|
121
|
+
let data = FAKE_COMPONENTS()[comp]
|
|
122
|
+
const [compPath, compData] = fs.writeFile.mock.calls[comp]
|
|
123
|
+
|
|
124
|
+
if (FAKE_COMPONENTS()[comp].name === 'logo') {
|
|
125
|
+
data = { ...FAKE_COMPONENTS()[comp], component_group_name: '' }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
expect(compPath).toBe(`./${compFileName}`)
|
|
129
|
+
expect(JSON.parse(compData)).toEqual(data)
|
|
130
|
+
}
|
|
79
131
|
})
|
|
80
132
|
})
|
|
81
133
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const sync = require('../../src/tasks/sync')
|
|
2
2
|
const PresetsLib = jest.requireActual('../../src/utils/presets-lib')
|
|
3
|
-
const { TOKEN_TEST, FAKE_COMPONENTS } = require('../constants')
|
|
3
|
+
const { TOKEN_TEST, EMAIL_TEST, REGION_TEST, FAKE_COMPONENTS } = require('../constants')
|
|
4
|
+
const creds = require('../../src/utils/creds')
|
|
4
5
|
|
|
5
6
|
const FAKE_COMPONENTS_TO_TEST = {
|
|
6
7
|
'001': {
|
|
@@ -217,6 +218,7 @@ const TARGET_SPACE_TEST = '002'
|
|
|
217
218
|
|
|
218
219
|
describe('testing syncComponents', () => {
|
|
219
220
|
beforeAll(() => {
|
|
221
|
+
creds.set(EMAIL_TEST, TOKEN_TEST, REGION_TEST)
|
|
220
222
|
// we need to execute once this function to test it
|
|
221
223
|
const _types = ['components']
|
|
222
224
|
|