@contentstack/cli-migration 0.1.1-beta.1

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.
Files changed (47) hide show
  1. package/README.md +69 -0
  2. package/oclif.manifest.json +1 -0
  3. package/package.json +68 -0
  4. package/src/actions/action-list.js +32 -0
  5. package/src/actions/index.js +218 -0
  6. package/src/commands/cm/migration.js +182 -0
  7. package/src/config/api-config.js +19 -0
  8. package/src/config/default-options.js +7 -0
  9. package/src/config/index.js +7 -0
  10. package/src/config/master-locale.js +10 -0
  11. package/src/modules/base.js +95 -0
  12. package/src/modules/content-types.js +208 -0
  13. package/src/modules/fields.js +304 -0
  14. package/src/modules/index.js +8 -0
  15. package/src/modules/locale.js +33 -0
  16. package/src/modules/migration.js +106 -0
  17. package/src/modules/parser.js +84 -0
  18. package/src/services/content-types.js +323 -0
  19. package/src/services/index.js +6 -0
  20. package/src/services/locales.js +74 -0
  21. package/src/utils/auto-retry.js +30 -0
  22. package/src/utils/callsite.js +22 -0
  23. package/src/utils/constants.js +219 -0
  24. package/src/utils/contentstack-sdk.js +71 -0
  25. package/src/utils/error-handler.js +21 -0
  26. package/src/utils/error-helper.js +58 -0
  27. package/src/utils/fs-helper.js +14 -0
  28. package/src/utils/get-batches.js +7 -0
  29. package/src/utils/get-config.js +13 -0
  30. package/src/utils/group-by.js +38 -0
  31. package/src/utils/index.js +21 -0
  32. package/src/utils/logger.js +84 -0
  33. package/src/utils/map.js +40 -0
  34. package/src/utils/object-helper.js +9 -0
  35. package/src/utils/request.js +95 -0
  36. package/src/utils/safe-promise.js +3 -0
  37. package/src/utils/schema-helper.js +35 -0
  38. package/src/utils/success-handler.js +12 -0
  39. package/src/validators/api-error.js +18 -0
  40. package/src/validators/base-validator.js +39 -0
  41. package/src/validators/create-content-type-validator.js +58 -0
  42. package/src/validators/edit-content-type-validator.js +56 -0
  43. package/src/validators/field-validator.js +19 -0
  44. package/src/validators/index.js +11 -0
  45. package/src/validators/migration-error.js +18 -0
  46. package/src/validators/schema-validator.js +21 -0
  47. package/src/validators/type-error.js +21 -0
@@ -0,0 +1,219 @@
1
+ 'use strict'
2
+
3
+ exports.mapObject = new Map()
4
+
5
+ exports.version = 3 // TODO: Fetch it from CMA
6
+
7
+ exports.defaultDataType = 'text'
8
+
9
+ exports.MANAGEMENT_SDK = 'MANAGEMENT_SDK'
10
+ exports.MANAGEMENT_SDK = 'MANAGEMENT_TOKEN'
11
+ exports.AUTH_TOKEN = 'AUTH_TOKEN'
12
+ exports.API_KEY = 'API_KEY'
13
+ exports.BRANCH = 'BRANCH'
14
+
15
+ exports.data_type = 'data_type'
16
+ exports.mandatory = 'mandatory'
17
+ exports._default = 'default'
18
+ exports.unique = 'unique'
19
+ exports.display_name = 'display_name'
20
+ exports.reference_to = 'reference_to'
21
+ exports.field_metadata = 'field_metadata'
22
+
23
+ exports.actions = {
24
+ CUSTOM_TASK: 'CUSTOM_TASK',
25
+ CREATE_CT: 'CREATE_CT',
26
+ DELETE_CT: 'DELETE_CT',
27
+ EDIT_CT: 'EDIT_CT',
28
+ LOCALES: 'LOCALES',
29
+ EDIT_FIELD: 'EDIT_FIELD',
30
+ DELETE_FIELD: 'DELETE_FIELD',
31
+ MOVE_FIELD: 'MOVE_FIELD',
32
+ }
33
+
34
+ // Http call max retry
35
+ exports.MAX_RETRY = 3
36
+
37
+ // This key holds the value for http objects in map
38
+ exports.requests = 'REQUESTS'
39
+
40
+ exports.limit = 1 // Limit for concurrent tasks executed parallely
41
+
42
+ exports.nonWritableMethods = ['GET', 'DELETE']
43
+
44
+ exports.ContentType = 'Content type'
45
+ exports.Entry = 'Entry'
46
+
47
+ exports.errorMessageHandler = {
48
+ POST: 'saving',
49
+ GET: 'fetching',
50
+ PUT: 'updating',
51
+ DELETE: 'deleting',
52
+ }
53
+
54
+ exports.successMessageHandler = {
55
+ POST: 'saved',
56
+ GET: 'fetched',
57
+ PUT: 'updated',
58
+ DELETE: 'deleted',
59
+ }
60
+ // map key
61
+ exports.actionMapper = 'actions'
62
+
63
+ exports.batchLimit = 20
64
+
65
+ exports.contentTypeProperties = ['description', 'title', 'uid', 'options', 'force', 'schema']
66
+
67
+ exports.validationAction = {
68
+ create: 'create',
69
+ edit: 'edit',
70
+ customTask: 'customTask',
71
+ transformEntries: 'transformEntries',
72
+ deriveLinkedEntries: 'deriveLinkedEntries',
73
+ transformEntriesToType: 'transformEntriesToType',
74
+ typeError: 'typeError',
75
+ apiError: 'apiError',
76
+ schema: 'schema',
77
+ __migrationError: 'migrationError',
78
+ field: 'field',
79
+ }
80
+
81
+ exports.transformEntriesProperties = [
82
+ {
83
+ name: 'contentType',
84
+ type: 'string',
85
+ mandatory: true,
86
+ },
87
+ {
88
+ name: 'from',
89
+ type: 'array',
90
+ mandatory: true,
91
+ },
92
+ {
93
+ name: 'to',
94
+ type: 'array',
95
+ mandatory: true,
96
+ },
97
+ {
98
+ name: 'shouldPublish',
99
+ type: 'boolean',
100
+ mandatory: false,
101
+ dependsOn: 'environments',
102
+ },
103
+ {
104
+ name: 'environments',
105
+ type: 'array',
106
+ mandatory: false,
107
+ },
108
+ {
109
+ name: 'transformEntryForLocale',
110
+ type: 'function',
111
+ mandatory: true,
112
+ },
113
+ ]
114
+
115
+ exports.deriveLinkedEntriesProperties = [
116
+ {
117
+ name: 'contentType',
118
+ type: 'string',
119
+ mandatory: true,
120
+ },
121
+ {
122
+ name: 'derivedContentType',
123
+ type: 'string',
124
+ mandatory: true,
125
+ },
126
+ {
127
+ name: 'from',
128
+ type: 'array',
129
+ mandatory: true,
130
+ },
131
+ {
132
+ name: 'toReferenceField',
133
+ type: 'string',
134
+ mandatory: true,
135
+ },
136
+ {
137
+ name: 'derivedFields',
138
+ type: 'array',
139
+ mandatory: true,
140
+ },
141
+ {
142
+ name: 'identityKey',
143
+ type: 'function',
144
+ mandatory: true,
145
+ },
146
+ {
147
+ name: 'shouldPublish',
148
+ type: 'boolean',
149
+ mandatory: false,
150
+ dependsOn: 'environments',
151
+ },
152
+ {
153
+ name: 'environments',
154
+ type: 'array',
155
+ mandatory: false,
156
+ },
157
+ {
158
+ name: 'deriveEntryForLocale',
159
+ type: 'function',
160
+ mandatory: true,
161
+ },
162
+ ]
163
+
164
+ exports.transformEntriesToTypeProperties = [
165
+ {
166
+ name: 'sourceContentType',
167
+ type: 'string',
168
+ mandatory: true,
169
+ },
170
+ {
171
+ name: 'targetContentType',
172
+ type: 'string',
173
+ mandatory: true,
174
+ },
175
+ {
176
+ name: 'from',
177
+ type: 'array',
178
+ mandatory: true,
179
+ },
180
+ {
181
+ name: 'shouldPublish',
182
+ type: 'boolean',
183
+ mandatory: false,
184
+ dependsOn: 'environments',
185
+ },
186
+ {
187
+ name: 'environments',
188
+ type: 'array',
189
+ mandatory: false,
190
+ },
191
+ {
192
+ name: 'removeOldEntries',
193
+ type: 'boolean',
194
+ mandatory: false,
195
+ },
196
+ {
197
+ name: 'identityKey',
198
+ type: 'function',
199
+ mandatory: true,
200
+ },
201
+ {
202
+ name: 'transformEntryForLocale',
203
+ type: 'function',
204
+ mandatory: true,
205
+ },
206
+ ]
207
+
208
+ exports.SDK_ACTIONS = {
209
+ CONTENTTYPE_GET: 'CONTENTTYPE_GET',
210
+ CONTENTTYPE_POST: 'CONTENTTYPE_POST',
211
+ CONTENTTYPE_DELETE: 'CONTENTTYPE_GET',
212
+ CONTENTTYPE_PUT: 'CONTENTTYPE_PUT',
213
+ LOCALES_GET: 'LOCALES_GET',
214
+ ENTRY_GET: 'ENTRY_GET',
215
+ ENTRY_POST: 'ENTRY_POST',
216
+ ENTRY_PUT: 'ENTRY_PUT',
217
+ ENTRY_DELETE: 'ENTRY_DELETE',
218
+ ENTRY_PUBLISH: 'ENTRY_PUBLISH',
219
+ }
@@ -0,0 +1,71 @@
1
+ 'use strict'
2
+ /** Dependencies */
3
+
4
+ // Map helper
5
+ const {getMapInstance, getDataWithAction, get} = require('./map')
6
+ // Constants
7
+ const {MANAGEMENT_SDK, SDK_ACTIONS} = require('./constants')
8
+ // List of actions
9
+ const {
10
+ CONTENTTYPE_DELETE,
11
+ CONTENTTYPE_GET,
12
+ CONTENTTYPE_POST,
13
+ CONTENTTYPE_PUT,
14
+ LOCALES_GET,
15
+ ENTRY_DELETE,
16
+ ENTRY_GET,
17
+ ENTRY_POST,
18
+ ENTRY_PUBLISH,
19
+ ENTRY_PUT,
20
+ } = SDK_ACTIONS
21
+
22
+ module.exports = ({action, id, sdkAction}) => {
23
+ return async _data => {
24
+ _data = getData(_data, id, action)
25
+
26
+ const mapInstance = getMapInstance()
27
+ const managementSdk = get(MANAGEMENT_SDK, mapInstance)
28
+ const {stack} = managementSdk
29
+
30
+ let response
31
+
32
+ switch (sdkAction) {
33
+ case CONTENTTYPE_GET:
34
+ response = await stack.contentType(id).fetch()
35
+ return response
36
+ case CONTENTTYPE_POST:
37
+ response = await stack.contentType().create(_data)
38
+ return response
39
+ case CONTENTTYPE_PUT:
40
+ // const contentType = await stack.contentType(id).fetch();
41
+ response = await stack.contentType(_data).update()
42
+ return response
43
+ case CONTENTTYPE_DELETE:
44
+ response = await stack.contentType(id).delete()
45
+ return response
46
+ case LOCALES_GET:
47
+ return response
48
+ case ENTRY_GET:
49
+ return response
50
+ case ENTRY_POST:
51
+ return response
52
+ case ENTRY_PUBLISH:
53
+ return response
54
+ case ENTRY_DELETE:
55
+ return response
56
+ case ENTRY_PUT:
57
+ return response
58
+ default:
59
+ }
60
+ }
61
+ }
62
+
63
+ function getData(_data, id, action) {
64
+ let mapInstance = getMapInstance()
65
+
66
+ let data = _data ? _data : getDataWithAction(id, mapInstance, action)
67
+
68
+ // return stringify(data);
69
+ return data
70
+ }
71
+
@@ -0,0 +1,21 @@
1
+ 'use strict'
2
+
3
+ const {error} = require('./logger')
4
+ const {errorMessageHandler} = require('./constants')
5
+
6
+ module.exports = (data, type, method, err) => {
7
+ if (data && type && method) {
8
+ error(`Error occurred while ${errorMessageHandler[method]} ${type}: ${data}.`)
9
+ }
10
+
11
+ if (err.errorMessage) {
12
+ error(err.errorMessage)
13
+ }
14
+ if (err instanceof Error && err && err.message && err.stack) {
15
+ error(err.message)
16
+ // error(err.stack)
17
+ } else {
18
+ error(err)
19
+ }
20
+ // throw new Error(err);
21
+ }
@@ -0,0 +1,58 @@
1
+
2
+ const {highlight} = require('cardinal')
3
+ const {keys} = Object
4
+ const chalk = require('chalk')
5
+
6
+ const {readFile} = require('./fs-helper')
7
+ const groupBy = require('./group-by')
8
+
9
+ const getLineWithContext = (lines, lineNumber, context) => {
10
+ const line = (lineNumber - 1)
11
+
12
+ const firstLine = (line > context) ? (line - context) : 0
13
+ const lastLine = (line + context) < lines.length ? line + context : lines.length
14
+
15
+ return {
16
+ before: lines.slice(firstLine, line),
17
+ line: lines[line],
18
+ after: lines.slice(line + 1, lastLine + 1),
19
+ }
20
+ }
21
+
22
+ module.exports = errors => {
23
+ const errorsByFile = groupBy(errors, 'file')
24
+ const messages = []
25
+ for (const file of keys(errorsByFile)) {
26
+ const fileContents = readFile(file)
27
+ const highlightedCode = highlight(fileContents, {linenos: true})
28
+ const lines = highlightedCode.split('\n')
29
+
30
+ const fileErrorsMessage = chalk`{red Errors in ${file}}\n\n`
31
+ const errorMessages = errorsByFile[file].map(error => {
32
+ const callsite = error.meta.callsite
33
+ const context = 2
34
+ const {before, line, after} = getLineWithContext(lines, callsite.line, context)
35
+
36
+ const beforeLines = before.map(line => chalk`${line}\n`)
37
+ const afterLines = after.map(line => chalk`${line}\n`)
38
+ const highlightedLine = chalk`{bold ${line}}\n`
39
+
40
+ const formattedCode = beforeLines + highlightedLine + afterLines
41
+ if (error.payload.apiError) {
42
+ return chalk`{red Line ${String(callsite.line)}:} {bold ${error.payload.apiError.message}}\n${formattedCode}`
43
+ }
44
+ if (error.message) {
45
+ return chalk`{red Line ${String(callsite.line)}:} {bold ${error.message}}\n${formattedCode}`
46
+ }
47
+ return chalk`{red Line ${String(callsite.line)}:} {bold something went wrong here.}\n${formattedCode}`
48
+ }).join('\n')
49
+
50
+ messages.push(`${fileErrorsMessage}${errorMessages}`)
51
+ }
52
+ // eslint-disable-next-line
53
+ // console.error(chalk`{red.bold Validation failed}\n\n`);
54
+ // eslint-disable-next-line
55
+ console.log(messages.join('\n'));
56
+ // eslint-disable-next-line
57
+ console.log(chalk`{bold.red Migration unsuccessful}`);
58
+ }
@@ -0,0 +1,14 @@
1
+ 'use strict'
2
+
3
+ const {existsSync, mkdirSync, readFileSync} = require('fs')
4
+
5
+ exports.makeDir = dirname => {
6
+ !this.existsSync(dirname) && mkdirSync(dirname)
7
+ }
8
+
9
+ exports.existsSync = filePath => existsSync(filePath)
10
+
11
+ exports.readFile = filePath => {
12
+ if (!existsSync(filePath)) throw new Error('File does not exist')
13
+ return readFileSync(filePath, 'utf-8')
14
+ }
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ module.exports = (count, batchLimit) => {
4
+ const partitions = Math.ceil(count / batchLimit)
5
+ // Returns array filled with indexes
6
+ return new Array(partitions).fill(null).map((_, i) => i)
7
+ }
@@ -0,0 +1,13 @@
1
+ 'use strict'
2
+
3
+ const {apiConfig} = require('../config')
4
+
5
+ module.exports = ({method, path, sdkAction}) => {
6
+ return {
7
+ ...apiConfig,
8
+ path: path ? `${apiConfig.version}${path}` : apiConfig.version,
9
+ method,
10
+ headers: {...apiConfig.headers},
11
+ sdkAction,
12
+ }
13
+ }
@@ -0,0 +1,38 @@
1
+ 'use strict'
2
+
3
+ module.exports = function groupBy(data, field, i = 0, finalObj = {}, originalArray = []) {
4
+ if (!data) return finalObj
5
+
6
+ if (Array.isArray(data)) {
7
+ groupBy(data[i], field, 0, finalObj, data)
8
+ } else if (field in data) {
9
+ let dataField = data[field]
10
+ if (dataField in finalObj) {
11
+ finalObj[dataField].push(originalArray[i])
12
+ } else {
13
+ finalObj[dataField] = []
14
+ finalObj[dataField].push(originalArray[i])
15
+ }
16
+
17
+ i++
18
+
19
+ // Breaks when i has been incremented more than length of original array
20
+ if (i !== 0 && i >= originalArray.length) return finalObj
21
+ /**
22
+ * After the field is found only then increment i and inspect next item
23
+ * This will restrict iterating through array just to the length of array
24
+ */
25
+ groupBy(originalArray[i], field, i, finalObj, originalArray)
26
+ } else {
27
+ for (let key in data) {
28
+ if (key) {
29
+ let dataKey = data[key]
30
+ if (!Array.isArray(dataKey) && typeof dataKey === 'object') {
31
+ groupBy(dataKey, field, i, finalObj, originalArray)
32
+ }
33
+ }
34
+ }
35
+ }
36
+
37
+ return finalObj
38
+ }
@@ -0,0 +1,21 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ map: require('./map'),
5
+ constants: require('./constants'),
6
+ schemaHelper: require('./schema-helper'),
7
+ objectHelper: require('./object-helper'),
8
+ fsHelper: require('./fs-helper'),
9
+ logger: require('./logger'),
10
+ https: require('./request'),
11
+ safePromise: require('./safe-promise'),
12
+ getConfig: require('./get-config'),
13
+ successHandler: require('./success-handler'),
14
+ errorHandler: require('./error-handler'),
15
+ getCallsite: require('./callsite'),
16
+ errorHelper: require('./error-helper'),
17
+ groupBy: require('./group-by'),
18
+ getBatches: require('./get-batches'),
19
+ autoRetry: require('./auto-retry'),
20
+ contentstackSdk: require('./contentstack-sdk'),
21
+ }
@@ -0,0 +1,84 @@
1
+ 'use strict'
2
+
3
+ const {createLogger, format, transports} = require('winston')
4
+ const {resolve, join} = require('path')
5
+ const {slice} = Array.prototype
6
+ const {stringify} = JSON
7
+
8
+ const {combine, label, printf, colorize} = format
9
+
10
+ // FS helper
11
+ const {makeDir} = require('./fs-helper')
12
+
13
+ const {NODE_ENV} = process.env
14
+
15
+ function getString(args) {
16
+ let str = ''
17
+ if (args && args.length > 0) {
18
+ str = args.map(item =>
19
+ item && typeof item === 'object' ?
20
+ stringify(item) :
21
+ item
22
+ )
23
+ .join(' ')
24
+ .trim()
25
+ }
26
+ return str
27
+ }
28
+
29
+ const customFormat = printf(({level, message}) => {
30
+ return `${level}: ${message}`
31
+ })
32
+
33
+ function init(logFileName) {
34
+ const logsDir = resolve('logs')
35
+ // Create dir if does not exist
36
+ makeDir(logsDir)
37
+
38
+ const logPath = join(logsDir, logFileName + '.log')
39
+ const logger = createLogger({
40
+ format: combine(
41
+ colorize(),
42
+ label({label: 'Migration'}),
43
+ customFormat
44
+ ),
45
+ transports: [
46
+ new transports.File({filename: logPath}),
47
+ new transports.Console(),
48
+ ],
49
+ })
50
+
51
+ let args
52
+ let logString
53
+
54
+ return {
55
+ log: function () {
56
+ args = slice.call(arguments)
57
+ logString = getString(args)
58
+ logString && logger.log('info', logString)
59
+ },
60
+ warn: function () {
61
+ args = slice.call(arguments)
62
+ logString = getString(args)
63
+ logString && logger.log('warn', logString)
64
+ },
65
+ error: function () {
66
+ args = slice.call(arguments)
67
+ logString = getString(args)
68
+ logString && logger.log('error', logString)
69
+ },
70
+ debug: function () {
71
+ args = slice.call(arguments)
72
+ logString = getString(args)
73
+ logString && logger.log('debug', logString)
74
+ },
75
+ }
76
+ }
77
+
78
+ exports.success = init('success').log
79
+ if (NODE_ENV === 'test') {
80
+ exports.error = init('warn').warn
81
+ } else {
82
+ exports.error = init('error').error
83
+ }
84
+ exports.warn = init('warn').warn
@@ -0,0 +1,40 @@
1
+ 'use strict'
2
+
3
+ const {mapObject, actionMapper, requests} = require('./constants')
4
+
5
+ exports.getMapInstance = () => {
6
+ return mapObject
7
+ }
8
+
9
+ exports.get = (id, mapInstance, data = []) => {
10
+ // Create key if does not exist
11
+ let __data = mapInstance.get(id)
12
+
13
+ !__data && (
14
+ mapInstance.set(id, data),
15
+ __data = mapInstance.get(id)
16
+ )
17
+
18
+ return __data
19
+ }
20
+
21
+ exports.set = (id, mapInstance, data) => {
22
+ return mapInstance.set(id, data)
23
+ }
24
+
25
+ exports.remove = (id, mapInstance) => {
26
+ return mapInstance.delete(id)
27
+ }
28
+
29
+ exports.getDataWithAction = (id, mapInstance, action) => {
30
+ let data = this.get(id, mapInstance)
31
+ data = data[action]
32
+ return data
33
+ }
34
+
35
+ exports.resetMapInstance = mapInstance => {
36
+ this.set(actionMapper, mapInstance, [])
37
+ this.set(requests, mapInstance, [])
38
+ }
39
+
40
+ exports.delete = () => { }
@@ -0,0 +1,9 @@
1
+ 'use strict'
2
+
3
+ exports.getEntryObj = (fields, obj) => {
4
+ let entryObj = {}
5
+ fields.forEach(field => {
6
+ entryObj[field] = obj[field]
7
+ })
8
+ return entryObj
9
+ }
@@ -0,0 +1,95 @@
1
+ 'use strict'
2
+
3
+ // Dependencies
4
+ const {request} = require('https')
5
+ const {stringify, parse} = JSON
6
+
7
+ // Map helper
8
+ const {getMapInstance, getDataWithAction} = require('./map')
9
+
10
+ // constants
11
+ const {actions, nonWritableMethods} = require('./constants')
12
+
13
+ // Properties
14
+ const {DELETE_CT} = actions
15
+
16
+ module.exports = ({
17
+ hostname,
18
+ path,
19
+ headers,
20
+ method,
21
+ id,
22
+ action,
23
+ }) => {
24
+ let options = {
25
+ hostname,
26
+ path,
27
+ headers,
28
+ method,
29
+ id,
30
+ action,
31
+ }
32
+ return _data => {
33
+ // get data here using id and action
34
+ let data = getData(_data, id, action, method)
35
+ // Special handling for non writable methods
36
+ options = getNewOptions(options, data, action, method)
37
+
38
+ return new Promise((resolve, reject) => {
39
+ const req = request(options, res => {
40
+ let response = ''
41
+
42
+ res.on('data', _res => {
43
+ response += _res.toString()
44
+ })
45
+
46
+ res.on('end', () => {
47
+ try {
48
+ response = parse(response)
49
+ resolve(response)
50
+ } catch (err) {
51
+ reject('Error while parsing response!')
52
+ // throw new Error('Error while parsing response!');
53
+ }
54
+ })
55
+ })
56
+
57
+ req.on('error', err => {
58
+ reject(err)
59
+ })
60
+
61
+ !nonWritableMethods.includes(method) && req.write(data)
62
+ req.end()
63
+ })
64
+ }
65
+ }
66
+
67
+ function getData(_data, id, action, method) {
68
+ if (method === 'GET') return
69
+ // if (!nonWritableMethods.includes(method)) {
70
+ let mapInstance = getMapInstance()
71
+
72
+ let data = _data ? _data : getDataWithAction(id, mapInstance, action)
73
+ return stringify(data)
74
+ }
75
+
76
+ function getNewOptions(options, data, action, method) {
77
+ // Special handling for delete method
78
+ if (action === DELETE_CT) {
79
+ try {
80
+ data = parse(data)
81
+ } catch (err) {
82
+ throw 'Error while parsing data for delete operation'
83
+ }
84
+ options.path = `${options.path}?force=${data.content_type.force}`
85
+ }
86
+
87
+ if (!nonWritableMethods.includes(method)) {
88
+ options.headers['Content-Length'] = data.length
89
+ } else {
90
+ delete options.headers['Content-Type']
91
+ delete options.headers['Content-Length']
92
+ }
93
+
94
+ return options
95
+ }
@@ -0,0 +1,3 @@
1
+ 'use strict'
2
+
3
+ module.exports = promise => promise.then(res => [null, res]).catch(err => [err])