closer-cli 2.33.0 → 2.37.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/.eslintrc.json +15 -0
- package/CHANGELOG.md +49 -0
- package/cmds/importData.js +26 -6
- package/cmds/importData_cmds/multiple.js +66 -27
- package/cmds/importData_cmds/single.js +99 -45
- package/cmds/importMedia.js +14 -10
- package/helpers.js +165 -119
- package/index.js +11 -15
- package/package.json +9 -5
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"rules": {
|
|
3
|
+
"semi": "off",
|
|
4
|
+
"comma-dangle": "off",
|
|
5
|
+
"arrow-parens": "off",
|
|
6
|
+
"no-use-before-define": "off",
|
|
7
|
+
"object-curly-newline": "off",
|
|
8
|
+
"import/prefer-default-export": "off",
|
|
9
|
+
"operator-linebreak": "off",
|
|
10
|
+
"import/extensions": "off"
|
|
11
|
+
},
|
|
12
|
+
"parserOptions": {
|
|
13
|
+
"ecmaVersion": 2020
|
|
14
|
+
}
|
|
15
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,55 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [2.37.0](https://code.hfarm.dev/closer/closer/compare/v2.36.1...v2.37.0) (2022-11-14)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **PJX-621:** Fine tuning report mail ([719ce9c](https://code.hfarm.dev/closer/closer/commits/719ce9cf5f6e78db4deeb452a0974ed4877c07dc))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## [2.36.1](https://code.hfarm.dev/closer/closer/compare/v2.36.0...v2.36.1) (2022-11-09)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* **PJX-619:** Import: link csv file inviati via mail non corrispondono ([af27d64](https://code.hfarm.dev/closer/closer/commits/af27d64b49145014d46afce1786ba72a3c78ebe3))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# [2.36.0](https://code.hfarm.dev/closer/closer/compare/v2.35.0...v2.36.0) (2022-11-09)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### Features
|
|
32
|
+
|
|
33
|
+
* **PJX-560:** AdminJS R&D ([dbfa216](https://code.hfarm.dev/closer/closer/commits/dbfa2165f4d05475c32b4e750bee205d77180643))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# [2.35.0](https://code.hfarm.dev/closer/closer/compare/v2.34.0...v2.35.0) (2022-11-04)
|
|
40
|
+
|
|
41
|
+
**Note:** Version bump only for package closer-cli
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# [2.34.0](https://code.hfarm.dev/closer/closer/compare/v2.33.2...v2.34.0) (2022-11-04)
|
|
48
|
+
|
|
49
|
+
**Note:** Version bump only for package closer-cli
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
6
55
|
# [2.33.0](https://code.hfarm.dev/closer/closer/compare/v2.32.3...v2.33.0) (2022-10-27)
|
|
7
56
|
|
|
8
57
|
|
package/cmds/importData.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import { ALLOWED_CSV_FILES, getHumanTimestamp } from '../helpers.js'
|
|
2
|
+
import * as ImportDataMultipleCmd from './importData_cmds/multiple.js'
|
|
3
|
+
import * as ImportDataSingleCmd from './importData_cmds/single.js'
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
export const command = 'import:data <command>'
|
|
6
|
+
export const desc = 'Import CSV data to Closer'
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
export function builder(yargs) {
|
|
7
9
|
yargs
|
|
8
|
-
.commandDir('importData_cmds')
|
|
10
|
+
// .commandDir('importData_cmds')
|
|
9
11
|
.option('type', {
|
|
10
12
|
alias: 't',
|
|
11
13
|
describe: 'CSV type to consider during import',
|
|
@@ -35,7 +37,25 @@ exports.builder = yargs => {
|
|
|
35
37
|
type: 'string',
|
|
36
38
|
default: ';'
|
|
37
39
|
})
|
|
40
|
+
.option('n', {
|
|
41
|
+
alias: 'name',
|
|
42
|
+
describe: 'Define import process name',
|
|
43
|
+
type: 'string',
|
|
44
|
+
default: getHumanTimestamp(new Date())
|
|
45
|
+
})
|
|
38
46
|
.implies('non-interactive', ['type', 'mode'])
|
|
47
|
+
.command(
|
|
48
|
+
ImportDataMultipleCmd.command,
|
|
49
|
+
ImportDataMultipleCmd.desc,
|
|
50
|
+
ImportDataMultipleCmd.builder,
|
|
51
|
+
ImportDataMultipleCmd.handler
|
|
52
|
+
)
|
|
53
|
+
.command(
|
|
54
|
+
ImportDataSingleCmd.command,
|
|
55
|
+
ImportDataSingleCmd.desc,
|
|
56
|
+
ImportDataSingleCmd.builder,
|
|
57
|
+
ImportDataSingleCmd.handler
|
|
58
|
+
)
|
|
39
59
|
}
|
|
40
60
|
|
|
41
|
-
|
|
61
|
+
export function handler() {}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
/* eslint-disable no-await-in-loop */
|
|
3
3
|
/* eslint-disable no-restricted-syntax */
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
import { accessSync, constants, existsSync, mkdirSync } from 'fs'
|
|
5
|
+
import path from 'path'
|
|
6
|
+
import chalk from 'chalk'
|
|
7
|
+
import prompts from 'prompts'
|
|
8
|
+
import Ora from 'ora'
|
|
9
|
+
import move from 'mv'
|
|
10
|
+
import { promisify } from 'util'
|
|
11
|
+
import { deleteAsync } from 'del'
|
|
12
|
+
|
|
13
|
+
import {
|
|
11
14
|
getCsvFilesFromDir,
|
|
12
15
|
ALLOWED_CSV_FILES,
|
|
13
16
|
getGoogleSheetCsvUrl,
|
|
@@ -19,18 +22,20 @@ const {
|
|
|
19
22
|
callAfterDataImportProcess,
|
|
20
23
|
getExecutionTime,
|
|
21
24
|
getArchiveFolder,
|
|
22
|
-
renameWithTimestamp,
|
|
23
25
|
sendImportErrorMail,
|
|
24
|
-
deleteRowsBeforeImportDate
|
|
25
|
-
|
|
26
|
+
deleteRowsBeforeImportDate,
|
|
27
|
+
readImportDir
|
|
28
|
+
} from '../../helpers.js'
|
|
29
|
+
|
|
30
|
+
const mv = promisify(move)
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
export const command = 'multiple [options]'
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
export const desc = 'Import different CSV files'
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
export function builder(yargs) {
|
|
32
37
|
yargs
|
|
33
|
-
.usage(`Usage: ${chalk.cyan('$0 import multiple')} [options]`)
|
|
38
|
+
.usage(`Usage: ${chalk.cyan('$0 import:data multiple')} [options]`)
|
|
34
39
|
.option('fromDir', {
|
|
35
40
|
alias: 'd',
|
|
36
41
|
describe: 'Directory path that contains files to import',
|
|
@@ -48,18 +53,20 @@ exports.builder = yargs => {
|
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
if (argv.fromDir) {
|
|
51
|
-
|
|
56
|
+
accessSync(argv.fromDir, constants.F_OK)
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
return true
|
|
55
60
|
})
|
|
56
61
|
}
|
|
57
62
|
|
|
58
|
-
|
|
63
|
+
export async function handler(argv) {
|
|
59
64
|
// eslint-disable-next-line no-param-reassign
|
|
60
65
|
argv.out = argv.out || getArchiveFolder(argv.endpoint)
|
|
61
66
|
|
|
67
|
+
const spinner = new Ora()
|
|
62
68
|
const hrstart = process.hrtime()
|
|
69
|
+
|
|
63
70
|
const importProcess = {
|
|
64
71
|
importProcessStart: new Date(),
|
|
65
72
|
importProcessEnd: null,
|
|
@@ -71,8 +78,17 @@ exports.handler = async argv => {
|
|
|
71
78
|
result: []
|
|
72
79
|
}
|
|
73
80
|
|
|
81
|
+
const dirPathTemp = path.resolve(argv.out, `${importProcess.name}_temp`)
|
|
82
|
+
const dirPathFinal = path.resolve(argv.out, importProcess.name)
|
|
83
|
+
const dirPathOriginal = path.resolve(dirPathTemp, '_original')
|
|
84
|
+
|
|
74
85
|
const interactive = !argv.nonInteractive
|
|
75
86
|
|
|
87
|
+
const ws = {
|
|
88
|
+
endpoint: argv.endpoint,
|
|
89
|
+
secret: argv.secret
|
|
90
|
+
}
|
|
91
|
+
|
|
76
92
|
let csvChoices = []
|
|
77
93
|
|
|
78
94
|
if (argv.fromDir) {
|
|
@@ -157,27 +173,39 @@ exports.handler = async argv => {
|
|
|
157
173
|
|
|
158
174
|
importProcess.mode = mode
|
|
159
175
|
|
|
160
|
-
const spinner = new Ora()
|
|
161
|
-
|
|
162
176
|
const validations = []
|
|
163
177
|
const csvReports = []
|
|
164
178
|
|
|
165
|
-
const ws = {
|
|
166
|
-
endpoint: argv.endpoint,
|
|
167
|
-
adminSecret: argv['admin-secret']
|
|
168
|
-
}
|
|
169
|
-
|
|
170
179
|
let parsedFiles = [...files]
|
|
171
180
|
|
|
181
|
+
if (!existsSync(dirPathFinal) && !existsSync(dirPathOriginal)) {
|
|
182
|
+
mkdirSync(dirPathOriginal, { recursive: true })
|
|
183
|
+
} else {
|
|
184
|
+
console.log('')
|
|
185
|
+
console.log(`ERROR => directory "${dirPathFinal}" already exists, change the import name.`)
|
|
186
|
+
process.exit(1)
|
|
187
|
+
}
|
|
188
|
+
|
|
172
189
|
if (argv.fromGoogleDocId) {
|
|
173
190
|
parsedFiles = []
|
|
174
191
|
console.log('')
|
|
175
192
|
spinner.start('Downloading Google sheets...')
|
|
176
193
|
for (const item of files) {
|
|
177
|
-
const filepath = await download(
|
|
194
|
+
const filepath = await download(
|
|
195
|
+
item.url,
|
|
196
|
+
dirPathOriginal,
|
|
197
|
+
`${importProcess.name}_${item.csvType}.csv`
|
|
198
|
+
)
|
|
178
199
|
parsedFiles.push({ file: filepath, csvType: item.csvType })
|
|
179
200
|
}
|
|
180
201
|
spinner.succeed('Google sheets downloaded')
|
|
202
|
+
} else {
|
|
203
|
+
for (const file of files) {
|
|
204
|
+
parsedFiles = []
|
|
205
|
+
const dst = path.resolve(dirPathOriginal, `${importProcess.name}_${file.csvType}.csv`)
|
|
206
|
+
await mv(file.file, dst)
|
|
207
|
+
parsedFiles.push({ file: dst, csvType: file.csvType })
|
|
208
|
+
}
|
|
181
209
|
}
|
|
182
210
|
|
|
183
211
|
//
|
|
@@ -242,7 +270,7 @@ exports.handler = async argv => {
|
|
|
242
270
|
const csvReport = await importCsvFromFile({
|
|
243
271
|
file: item.file,
|
|
244
272
|
csvType: item.csvType,
|
|
245
|
-
outputDir:
|
|
273
|
+
outputDir: dirPathTemp,
|
|
246
274
|
spinner,
|
|
247
275
|
ws,
|
|
248
276
|
delimiter: argv.csvDelimiter,
|
|
@@ -268,7 +296,7 @@ exports.handler = async argv => {
|
|
|
268
296
|
}
|
|
269
297
|
|
|
270
298
|
//
|
|
271
|
-
// Refresh Orders of type Draft
|
|
299
|
+
// Refresh Orders of type Draft, Template
|
|
272
300
|
//
|
|
273
301
|
// eslint-disable-next-line no-unused-vars
|
|
274
302
|
const refreshedOrders = await refreshOrders(spinner, ws, importProcess)
|
|
@@ -298,7 +326,18 @@ exports.handler = async argv => {
|
|
|
298
326
|
|
|
299
327
|
await callAfterDataImportProcess(spinner, ws, importProcess)
|
|
300
328
|
|
|
301
|
-
await
|
|
329
|
+
await deleteAsync(path.resolve(dirPathTemp, '_processing'))
|
|
330
|
+
|
|
331
|
+
const reportFiles = await readImportDir(dirPathTemp)
|
|
332
|
+
|
|
333
|
+
for (const reportFile of reportFiles) {
|
|
334
|
+
const { filename, size } = reportFile
|
|
335
|
+
if ((filename.includes('skipped') || filename.includes('errors')) && size === 0) {
|
|
336
|
+
await deleteAsync(path.resolve(dirPathTemp, filename))
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
await mv(dirPathTemp, dirPathFinal)
|
|
302
341
|
|
|
303
342
|
process.exit()
|
|
304
343
|
}
|
|
@@ -1,31 +1,39 @@
|
|
|
1
|
-
/* eslint-disable no-restricted-syntax */
|
|
2
1
|
/* eslint-disable no-console */
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
/* eslint-disable no-await-in-loop */
|
|
3
|
+
/* eslint-disable no-restricted-syntax */
|
|
4
|
+
import { accessSync, constants, existsSync, mkdirSync } from 'fs'
|
|
5
|
+
import path from 'path'
|
|
6
|
+
import chalk from 'chalk'
|
|
7
|
+
import prompts from 'prompts'
|
|
8
|
+
import Ora from 'ora'
|
|
9
|
+
import move from 'mv'
|
|
10
|
+
import { promisify } from 'util'
|
|
11
|
+
import { deleteAsync } from 'del'
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
ALLOWED_CSV_FILES,
|
|
9
15
|
importCsvFromFile,
|
|
10
|
-
|
|
16
|
+
validateCsvFromFile,
|
|
11
17
|
refreshOrders,
|
|
12
|
-
callAfterDataImportProcess,
|
|
13
|
-
callBeforeDataImportProcess,
|
|
14
|
-
ALLOWED_CSV_FILES,
|
|
15
18
|
download,
|
|
16
|
-
|
|
19
|
+
callBeforeDataImportProcess,
|
|
20
|
+
callAfterDataImportProcess,
|
|
21
|
+
getExecutionTime,
|
|
17
22
|
getArchiveFolder,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
sendImportErrorMail,
|
|
24
|
+
deleteRowsBeforeImportDate,
|
|
25
|
+
readImportDir
|
|
26
|
+
} from '../../helpers.js'
|
|
27
|
+
|
|
28
|
+
const mv = promisify(move)
|
|
21
29
|
|
|
22
|
-
|
|
30
|
+
export const command = 'single [options]'
|
|
23
31
|
|
|
24
|
-
|
|
32
|
+
export const desc = 'Import only one CSV file'
|
|
25
33
|
|
|
26
|
-
|
|
34
|
+
export function builder(yargs) {
|
|
27
35
|
yargs
|
|
28
|
-
.usage(`Usage: ${chalk.cyan('$0 import single')} [options]`)
|
|
36
|
+
.usage(`Usage: ${chalk.cyan('$0 import:data single')} [options]`)
|
|
29
37
|
.option('fromFile', {
|
|
30
38
|
alias: 'f',
|
|
31
39
|
describe: 'File to import',
|
|
@@ -43,14 +51,14 @@ exports.builder = yargs => {
|
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
if (argv.fromFile) {
|
|
46
|
-
|
|
54
|
+
accessSync(argv.fromFile, constants.F_OK)
|
|
47
55
|
}
|
|
48
56
|
|
|
49
57
|
return true
|
|
50
58
|
})
|
|
51
59
|
}
|
|
52
60
|
|
|
53
|
-
|
|
61
|
+
export async function handler(argv) {
|
|
54
62
|
// eslint-disable-next-line no-param-reassign
|
|
55
63
|
argv.out = argv.out || getArchiveFolder(argv.endpoint)
|
|
56
64
|
|
|
@@ -68,13 +76,17 @@ exports.handler = async argv => {
|
|
|
68
76
|
result: []
|
|
69
77
|
}
|
|
70
78
|
|
|
79
|
+
const dirPathTemp = path.resolve(argv.out, `${importProcess.name}_temp`)
|
|
80
|
+
const dirPathFinal = path.resolve(argv.out, importProcess.name)
|
|
81
|
+
const dirPathOriginal = path.resolve(dirPathTemp, '_original')
|
|
82
|
+
|
|
83
|
+
const interactive = !argv.nonInteractive
|
|
84
|
+
|
|
71
85
|
const ws = {
|
|
72
86
|
endpoint: argv.endpoint,
|
|
73
|
-
|
|
87
|
+
secret: argv.secret
|
|
74
88
|
}
|
|
75
89
|
|
|
76
|
-
const interactive = !argv.nonInteractive
|
|
77
|
-
|
|
78
90
|
let type
|
|
79
91
|
if (interactive && !argv.type) {
|
|
80
92
|
const answers = await prompts(
|
|
@@ -99,6 +111,8 @@ exports.handler = async argv => {
|
|
|
99
111
|
type = argv.type[0]
|
|
100
112
|
}
|
|
101
113
|
|
|
114
|
+
importProcess.type = [type]
|
|
115
|
+
|
|
102
116
|
let mode
|
|
103
117
|
if (interactive && !argv.mode) {
|
|
104
118
|
const answers = await prompts(
|
|
@@ -129,6 +143,16 @@ exports.handler = async argv => {
|
|
|
129
143
|
mode = argv.mode
|
|
130
144
|
}
|
|
131
145
|
|
|
146
|
+
importProcess.mode = mode
|
|
147
|
+
|
|
148
|
+
if (!existsSync(dirPathFinal) && !existsSync(dirPathOriginal)) {
|
|
149
|
+
mkdirSync(dirPathOriginal, { recursive: true })
|
|
150
|
+
} else {
|
|
151
|
+
console.log('')
|
|
152
|
+
console.log(`ERROR => directory "${dirPathFinal}" already exists, change the import name.`)
|
|
153
|
+
process.exit(1)
|
|
154
|
+
}
|
|
155
|
+
|
|
132
156
|
if (interactive) {
|
|
133
157
|
console.log('')
|
|
134
158
|
const { confirmed } = await prompts(
|
|
@@ -150,16 +174,19 @@ exports.handler = async argv => {
|
|
|
150
174
|
console.log('')
|
|
151
175
|
}
|
|
152
176
|
|
|
153
|
-
let
|
|
154
|
-
|
|
155
|
-
let filepath = argv.fromFile
|
|
177
|
+
let filepath = null
|
|
156
178
|
|
|
157
179
|
if (argv.fromUrl) {
|
|
158
180
|
spinner.start('Downloading file...')
|
|
159
|
-
filepath = await download(argv.fromUrl,
|
|
181
|
+
filepath = await download(argv.fromUrl, dirPathOriginal, `${importProcess.name}_${type}.csv`)
|
|
160
182
|
spinner.succeed('Downloaded file')
|
|
161
183
|
}
|
|
162
184
|
|
|
185
|
+
if (argv.fromFile) {
|
|
186
|
+
filepath = path.resolve(dirPathOriginal, `${importProcess.name}_${type}.csv`)
|
|
187
|
+
await mv(argv.fromFile, filepath)
|
|
188
|
+
}
|
|
189
|
+
|
|
163
190
|
const validation = await validateCsvFromFile(filepath, type, ws, argv.csvDelimiter, importProcess)
|
|
164
191
|
|
|
165
192
|
if (validation.error) {
|
|
@@ -167,9 +194,6 @@ exports.handler = async argv => {
|
|
|
167
194
|
process.exit(1)
|
|
168
195
|
}
|
|
169
196
|
|
|
170
|
-
importProcess.mode = mode
|
|
171
|
-
importProcess.type = [type]
|
|
172
|
-
|
|
173
197
|
//
|
|
174
198
|
// Call ImportService.beforeImportProcess (import flag)
|
|
175
199
|
//
|
|
@@ -181,25 +205,44 @@ exports.handler = async argv => {
|
|
|
181
205
|
}
|
|
182
206
|
}
|
|
183
207
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
208
|
+
const partitionsForCsvType = new Map()
|
|
209
|
+
|
|
210
|
+
const csvReport = await importCsvFromFile({
|
|
211
|
+
file: filepath,
|
|
212
|
+
csvType: type,
|
|
213
|
+
outputDir: dirPathTemp,
|
|
214
|
+
spinner,
|
|
215
|
+
ws,
|
|
216
|
+
delimiter: argv.csvDelimiter,
|
|
217
|
+
importProcess
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
if (csvReport.countFailed > 0 || csvReport.countSkipped > 0) {
|
|
221
|
+
sendImportErrorMail(ws, importProcess, csvReport)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
partitionsForCsvType.set(csvReport.csvType, Array.from(csvReport.partitions))
|
|
225
|
+
|
|
226
|
+
//
|
|
227
|
+
// Import FULL?
|
|
228
|
+
//
|
|
229
|
+
if (mode === 'full') {
|
|
230
|
+
await deleteRowsBeforeImportDate(
|
|
231
|
+
importProcess,
|
|
232
|
+
Object.fromEntries(partitionsForCsvType),
|
|
189
233
|
spinner,
|
|
190
|
-
ws
|
|
191
|
-
|
|
192
|
-
importProcess
|
|
193
|
-
})
|
|
194
|
-
if (csvReport.countFailed > 0 || csvReport.countSkipped > 0) {
|
|
195
|
-
sendImportErrorMail(ws, importProcess, csvReport)
|
|
196
|
-
}
|
|
234
|
+
ws
|
|
235
|
+
)
|
|
197
236
|
}
|
|
198
237
|
|
|
238
|
+
//
|
|
239
|
+
// Refresh Orders of type Draft, Template
|
|
240
|
+
//
|
|
241
|
+
|
|
199
242
|
const refreshedOrders = await refreshOrders(spinner, ws, importProcess)
|
|
200
243
|
|
|
201
244
|
//
|
|
202
|
-
//
|
|
245
|
+
// importProcess
|
|
203
246
|
//
|
|
204
247
|
|
|
205
248
|
importProcess.importProcessEnd = new Date()
|
|
@@ -221,7 +264,18 @@ exports.handler = async argv => {
|
|
|
221
264
|
|
|
222
265
|
await callAfterDataImportProcess(spinner, ws, importProcess)
|
|
223
266
|
|
|
224
|
-
await
|
|
267
|
+
await deleteAsync(path.resolve(dirPathTemp, '_processing'))
|
|
268
|
+
|
|
269
|
+
const reportFiles = await readImportDir(dirPathTemp)
|
|
270
|
+
|
|
271
|
+
for (const reportFile of reportFiles) {
|
|
272
|
+
const { filename, size } = reportFile
|
|
273
|
+
if ((filename.includes('skipped') || filename.includes('errors')) && size === 0) {
|
|
274
|
+
await deleteAsync(path.resolve(dirPathTemp, filename))
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
await mv(dirPathTemp, dirPathFinal)
|
|
225
279
|
|
|
226
280
|
process.exit()
|
|
227
281
|
}
|
package/cmds/importMedia.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import chalk from 'chalk'
|
|
2
|
+
import Ora from 'ora'
|
|
3
|
+
import {
|
|
4
4
|
importMedia,
|
|
5
5
|
flushMedia,
|
|
6
6
|
getExecutionTime,
|
|
7
7
|
callAfterMediaImportProcess
|
|
8
|
-
}
|
|
8
|
+
} from '../helpers.js'
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
export const command = 'import:media'
|
|
11
|
+
export const desc = 'Import media (images, 360, videos) to Closer'
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
export function builder(yargs) {
|
|
14
14
|
yargs
|
|
15
15
|
.usage(`Usage: ${chalk.cyan('$0 import:media')} [options]`)
|
|
16
16
|
|
|
@@ -25,7 +25,7 @@ exports.builder = yargs => {
|
|
|
25
25
|
})
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
export async function handler(argv) {
|
|
29
29
|
const spinner = new Ora()
|
|
30
30
|
const hrstart = process.hrtime()
|
|
31
31
|
|
|
@@ -41,11 +41,15 @@ exports.handler = async argv => {
|
|
|
41
41
|
|
|
42
42
|
const ws = {
|
|
43
43
|
endpoint: argv.endpoint,
|
|
44
|
-
|
|
44
|
+
secret: argv.secret
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
if (argv.flush) {
|
|
48
|
-
|
|
48
|
+
try {
|
|
49
|
+
await flushMedia(spinner, ws)
|
|
50
|
+
} catch (e) {
|
|
51
|
+
process.exit(1)
|
|
52
|
+
}
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
const result = await importMedia(argv.reprocess, spinner, ws)
|
package/helpers.js
CHANGED
|
@@ -1,27 +1,40 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
/* eslint-disable no-param-reassign */
|
|
3
3
|
/* eslint-disable implicit-arrow-linebreak */
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
4
|
+
import {
|
|
5
|
+
readdirSync,
|
|
6
|
+
existsSync,
|
|
7
|
+
mkdirSync,
|
|
8
|
+
copyFileSync,
|
|
9
|
+
createWriteStream,
|
|
10
|
+
createReadStream,
|
|
11
|
+
rename,
|
|
12
|
+
statSync
|
|
13
|
+
} from 'fs'
|
|
14
|
+
import path from 'path'
|
|
15
|
+
import WebSocket from 'ws'
|
|
16
|
+
import chalk from 'chalk'
|
|
17
|
+
import { parse } from 'csv-parse/sync'
|
|
18
|
+
import { stringify } from 'csv-stringify/sync'
|
|
19
|
+
import got from 'got'
|
|
20
|
+
import slugify from 'slugify'
|
|
21
|
+
import { deleteAsync } from 'del'
|
|
22
|
+
import eventStream from 'event-stream'
|
|
23
|
+
|
|
24
|
+
const { split, mapSync } = eventStream
|
|
25
|
+
const { stream } = got
|
|
26
|
+
|
|
27
|
+
export function createSocketConnection(endpoint, secret) {
|
|
16
28
|
const protocol = endpoint.includes('localhost') || endpoint.includes('127.0.0.1') ? 'ws' : 'wss'
|
|
17
|
-
const
|
|
18
|
-
|
|
29
|
+
const url = `${protocol}://${endpoint}/import`
|
|
30
|
+
const socket = new WebSocket(url, {
|
|
31
|
+
headers: { 'x-closer-secret': secret }
|
|
19
32
|
})
|
|
20
33
|
|
|
21
34
|
return socket
|
|
22
35
|
}
|
|
23
36
|
|
|
24
|
-
|
|
37
|
+
export const ALLOWED_CSV_FILES = {
|
|
25
38
|
product: 'products.csv',
|
|
26
39
|
localizedProduct: 'localizedProducts.csv',
|
|
27
40
|
localizedFilter: 'localizedFilters.csv',
|
|
@@ -40,15 +53,15 @@ exports.ALLOWED_CSV_FILES = {
|
|
|
40
53
|
team: 'teams.csv'
|
|
41
54
|
}
|
|
42
55
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
exports.getCsvFilesFromDir = getCsvFilesFromDir
|
|
56
|
+
export function getCsvFilesFromDir(dir) {
|
|
57
|
+
return readdirSync(dir).filter(file => path.extname(file).toLowerCase() === '.csv')
|
|
58
|
+
}
|
|
47
59
|
|
|
48
|
-
|
|
49
|
-
`https://docs.google.com/spreadsheets/d/${docId}/gviz/tq?tqx=out:csv&sheet=${sheet}`
|
|
60
|
+
export function getGoogleSheetCsvUrl(docId, sheet) {
|
|
61
|
+
return `https://docs.google.com/spreadsheets/d/${docId}/gviz/tq?tqx=out:csv&sheet=${sheet}`
|
|
62
|
+
}
|
|
50
63
|
|
|
51
|
-
|
|
64
|
+
export function importCsvFromFile({
|
|
52
65
|
file,
|
|
53
66
|
csvType,
|
|
54
67
|
outputDir,
|
|
@@ -56,27 +69,31 @@ exports.importCsvFromFile = ({
|
|
|
56
69
|
ws,
|
|
57
70
|
delimiter = ';',
|
|
58
71
|
importProcess
|
|
59
|
-
})
|
|
60
|
-
new Promise(resolve => {
|
|
61
|
-
const socket =
|
|
72
|
+
}) {
|
|
73
|
+
return new Promise(resolve => {
|
|
74
|
+
const socket = createSocketConnection(ws.endpoint, ws.secret)
|
|
62
75
|
|
|
63
76
|
const hrstart = process.hrtime()
|
|
64
77
|
const importFileStart = new Date()
|
|
65
78
|
|
|
66
79
|
let readStream = null
|
|
67
80
|
|
|
68
|
-
|
|
69
|
-
|
|
81
|
+
const dirPathProcessing = path.resolve(outputDir, '_processing')
|
|
82
|
+
|
|
83
|
+
if (!existsSync(dirPathProcessing)) {
|
|
84
|
+
mkdirSync(dirPathProcessing, { recursive: true })
|
|
70
85
|
}
|
|
71
86
|
|
|
72
87
|
const filename = path.basename(file)
|
|
73
|
-
|
|
88
|
+
const filepath = path.resolve(dirPathProcessing, filename)
|
|
74
89
|
|
|
75
|
-
|
|
76
|
-
const errorFileStream = fs.createWriteStream(errorCsvFile)
|
|
90
|
+
copyFileSync(file, filepath)
|
|
77
91
|
|
|
78
|
-
const
|
|
79
|
-
const
|
|
92
|
+
const errorCsvFile = path.resolve(outputDir, `${importProcess.name}_${csvType}_errors.csv`)
|
|
93
|
+
const errorFileStream = createWriteStream(errorCsvFile)
|
|
94
|
+
|
|
95
|
+
const skippedCsvFile = path.resolve(outputDir, `${importProcess.name}_${csvType}_skipped.csv`)
|
|
96
|
+
const skippedFileStream = createWriteStream(skippedCsvFile)
|
|
80
97
|
|
|
81
98
|
let header = ''
|
|
82
99
|
let lineNumber = 0
|
|
@@ -92,14 +109,14 @@ exports.importCsvFromFile = ({
|
|
|
92
109
|
|
|
93
110
|
countImported += 1
|
|
94
111
|
|
|
95
|
-
spinner.text =
|
|
112
|
+
spinner.text = printImportProcessResult({
|
|
96
113
|
csvType,
|
|
97
114
|
countImported,
|
|
98
115
|
countFailed,
|
|
99
116
|
countSkipped,
|
|
100
117
|
importFileStart,
|
|
101
118
|
importFileEnd: new Date(),
|
|
102
|
-
executionTime:
|
|
119
|
+
executionTime: getExecutionTime(hrstart),
|
|
103
120
|
name: filename
|
|
104
121
|
})
|
|
105
122
|
|
|
@@ -111,14 +128,14 @@ exports.importCsvFromFile = ({
|
|
|
111
128
|
|
|
112
129
|
countFailed += 1
|
|
113
130
|
|
|
114
|
-
spinner.text =
|
|
131
|
+
spinner.text = printImportProcessResult({
|
|
115
132
|
csvType,
|
|
116
133
|
countImported,
|
|
117
134
|
countFailed,
|
|
118
135
|
countSkipped,
|
|
119
136
|
importFileStart,
|
|
120
137
|
importFileEnd: new Date(),
|
|
121
|
-
executionTime:
|
|
138
|
+
executionTime: getExecutionTime(hrstart),
|
|
122
139
|
name: filename
|
|
123
140
|
})
|
|
124
141
|
|
|
@@ -137,14 +154,14 @@ exports.importCsvFromFile = ({
|
|
|
137
154
|
partitions.add(csvRow.Partition || '')
|
|
138
155
|
|
|
139
156
|
spinner.fail(
|
|
140
|
-
|
|
157
|
+
printImportProcessResult({
|
|
141
158
|
csvType,
|
|
142
159
|
countImported,
|
|
143
160
|
countFailed,
|
|
144
161
|
countSkipped,
|
|
145
162
|
importFileStart,
|
|
146
163
|
importFileEnd: new Date(),
|
|
147
|
-
executionTime:
|
|
164
|
+
executionTime: getExecutionTime(hrstart),
|
|
148
165
|
name: filename,
|
|
149
166
|
fatalError: errorMessage
|
|
150
167
|
})
|
|
@@ -155,7 +172,7 @@ exports.importCsvFromFile = ({
|
|
|
155
172
|
countImported,
|
|
156
173
|
countFailed,
|
|
157
174
|
countSkipped,
|
|
158
|
-
executionTime:
|
|
175
|
+
executionTime: getExecutionTime(hrstart),
|
|
159
176
|
importFileStart,
|
|
160
177
|
importFileEnd: new Date(),
|
|
161
178
|
inputFile: file,
|
|
@@ -178,6 +195,8 @@ exports.importCsvFromFile = ({
|
|
|
178
195
|
errorFileStream.end()
|
|
179
196
|
skippedFileStream.end()
|
|
180
197
|
|
|
198
|
+
deleteAsync(filepath)
|
|
199
|
+
|
|
181
200
|
resolve({
|
|
182
201
|
csvType,
|
|
183
202
|
countImported,
|
|
@@ -185,18 +204,17 @@ exports.importCsvFromFile = ({
|
|
|
185
204
|
countSkipped,
|
|
186
205
|
importFileStart,
|
|
187
206
|
importFileEnd: new Date(),
|
|
188
|
-
executionTime:
|
|
207
|
+
executionTime: getExecutionTime(hrstart),
|
|
189
208
|
inputFile: file,
|
|
190
209
|
partitions
|
|
191
210
|
})
|
|
192
211
|
}
|
|
193
212
|
|
|
194
213
|
socket.on('open', () => {
|
|
195
|
-
readStream =
|
|
196
|
-
.
|
|
197
|
-
.pipe(es.split())
|
|
214
|
+
readStream = createReadStream(file)
|
|
215
|
+
.pipe(split())
|
|
198
216
|
.pipe(
|
|
199
|
-
|
|
217
|
+
mapSync(line => {
|
|
200
218
|
readStream.pause()
|
|
201
219
|
|
|
202
220
|
if (lineNumber === 0) {
|
|
@@ -261,9 +279,10 @@ exports.importCsvFromFile = ({
|
|
|
261
279
|
importFatal(e.message)
|
|
262
280
|
})
|
|
263
281
|
})
|
|
282
|
+
}
|
|
264
283
|
|
|
265
|
-
|
|
266
|
-
const socket =
|
|
284
|
+
export function sendImportErrorMail(ws, importProcess, csvReport) {
|
|
285
|
+
const socket = createSocketConnection(ws.endpoint, ws.secret)
|
|
267
286
|
|
|
268
287
|
socket.on('open', () => {
|
|
269
288
|
socket.send(
|
|
@@ -275,9 +294,9 @@ exports.sendImportErrorMail = (ws, importProcess, csvReport) => {
|
|
|
275
294
|
})
|
|
276
295
|
}
|
|
277
296
|
|
|
278
|
-
|
|
279
|
-
new Promise(resolve => {
|
|
280
|
-
const socket =
|
|
297
|
+
export function validateCsvFromFile(file, csvType, ws, delimiter, importProcess) {
|
|
298
|
+
return new Promise(resolve => {
|
|
299
|
+
const socket = createSocketConnection(ws.endpoint, ws.secret)
|
|
281
300
|
const hrstart = process.hrtime()
|
|
282
301
|
|
|
283
302
|
let readStream = null
|
|
@@ -287,11 +306,10 @@ exports.validateCsvFromFile = (file, csvType, ws, delimiter = ';', importProcess
|
|
|
287
306
|
const filename = path.basename(file)
|
|
288
307
|
|
|
289
308
|
socket.on('open', () => {
|
|
290
|
-
readStream =
|
|
291
|
-
.
|
|
292
|
-
.pipe(es.split())
|
|
309
|
+
readStream = createReadStream(file)
|
|
310
|
+
.pipe(split())
|
|
293
311
|
.pipe(
|
|
294
|
-
|
|
312
|
+
mapSync(line => {
|
|
295
313
|
readStream.pause()
|
|
296
314
|
|
|
297
315
|
if (lineNumber === 0) {
|
|
@@ -303,7 +321,7 @@ exports.validateCsvFromFile = (file, csvType, ws, delimiter = ';', importProcess
|
|
|
303
321
|
if (line === '') {
|
|
304
322
|
resolve({
|
|
305
323
|
csvType,
|
|
306
|
-
executionTime:
|
|
324
|
+
executionTime: getExecutionTime(hrstart),
|
|
307
325
|
inputFile: filename
|
|
308
326
|
})
|
|
309
327
|
} else {
|
|
@@ -335,7 +353,7 @@ exports.validateCsvFromFile = (file, csvType, ws, delimiter = ';', importProcess
|
|
|
335
353
|
.on('error', e => {
|
|
336
354
|
resolve({
|
|
337
355
|
csvType,
|
|
338
|
-
executionTime:
|
|
356
|
+
executionTime: getExecutionTime(hrstart),
|
|
339
357
|
inputFile: filename,
|
|
340
358
|
error: e.message
|
|
341
359
|
})
|
|
@@ -348,7 +366,7 @@ exports.validateCsvFromFile = (file, csvType, ws, delimiter = ';', importProcess
|
|
|
348
366
|
case 'validateCsvHeaderResponse': {
|
|
349
367
|
const result = {
|
|
350
368
|
csvType,
|
|
351
|
-
executionTime:
|
|
369
|
+
executionTime: getExecutionTime(hrstart),
|
|
352
370
|
inputFile: filename
|
|
353
371
|
}
|
|
354
372
|
|
|
@@ -367,38 +385,32 @@ exports.validateCsvFromFile = (file, csvType, ws, delimiter = ';', importProcess
|
|
|
367
385
|
socket.on('error', e => {
|
|
368
386
|
resolve({
|
|
369
387
|
csvType,
|
|
370
|
-
executionTime:
|
|
388
|
+
executionTime: getExecutionTime(hrstart),
|
|
371
389
|
inputFile: file,
|
|
372
390
|
error: e.message
|
|
373
391
|
})
|
|
374
392
|
})
|
|
375
393
|
})
|
|
376
|
-
|
|
377
|
-
exports.importCsvFromUrl = async (url, csvType, outputDir, importProcess, spinner, ws) => {
|
|
378
|
-
spinner.start('Downloading file...')
|
|
379
|
-
const file = await this.download(url, outputDir, `${csvType}.csv`)
|
|
380
|
-
spinner.succeed('Downloaded file')
|
|
381
|
-
return this.importCsvFromFile({ file, csvType, outputDir, spinner, ws, importProcess })
|
|
382
394
|
}
|
|
383
395
|
|
|
384
|
-
|
|
396
|
+
export function getExecutionTime(importProcessStart) {
|
|
385
397
|
const hrend = process.hrtime(importProcessStart)
|
|
386
398
|
return ((hrend[0] * 1e9 + hrend[1]) / 1e9).toFixed(1)
|
|
387
399
|
}
|
|
388
400
|
|
|
389
|
-
|
|
401
|
+
export function printValidationProcessHeader() {
|
|
390
402
|
console.log('\n=== Validation Process Result ===\n')
|
|
391
403
|
}
|
|
392
404
|
|
|
393
|
-
|
|
405
|
+
export function printImportProcessHeader() {
|
|
394
406
|
console.log('\n=== Import Process Result ===\n')
|
|
395
407
|
}
|
|
396
408
|
|
|
397
|
-
|
|
409
|
+
export function printValidationProcessResult({
|
|
398
410
|
inputFile,
|
|
399
411
|
error
|
|
400
412
|
// eslint-disable-next-line consistent-return
|
|
401
|
-
})
|
|
413
|
+
}) {
|
|
402
414
|
let message = ''
|
|
403
415
|
const filename = path.basename(inputFile)
|
|
404
416
|
|
|
@@ -411,7 +423,7 @@ exports.printValidationProcessResult = ({
|
|
|
411
423
|
console.log(message)
|
|
412
424
|
}
|
|
413
425
|
|
|
414
|
-
|
|
426
|
+
export function printImportProcessResult({
|
|
415
427
|
countImported,
|
|
416
428
|
countFailed,
|
|
417
429
|
countSkipped,
|
|
@@ -419,7 +431,7 @@ exports.printImportProcessResult = ({
|
|
|
419
431
|
name,
|
|
420
432
|
fatalError
|
|
421
433
|
// eslint-disable-next-line consistent-return
|
|
422
|
-
})
|
|
434
|
+
}) {
|
|
423
435
|
let message = ''
|
|
424
436
|
|
|
425
437
|
if (fatalError) {
|
|
@@ -427,19 +439,19 @@ exports.printImportProcessResult = ({
|
|
|
427
439
|
return message
|
|
428
440
|
}
|
|
429
441
|
|
|
430
|
-
message = `[${
|
|
442
|
+
message = `[${colorByResult(
|
|
431
443
|
name,
|
|
432
444
|
countImported,
|
|
433
445
|
countFailed,
|
|
434
446
|
countSkipped
|
|
435
|
-
)}] imported ${
|
|
447
|
+
)}] imported ${colorGreaterThanZero(countImported, 'green')} items`
|
|
436
448
|
|
|
437
449
|
if (countFailed) {
|
|
438
|
-
message += `, failed ${
|
|
450
|
+
message += `, failed ${colorGreaterThanZero(countFailed, 'red')} items`
|
|
439
451
|
}
|
|
440
452
|
|
|
441
453
|
if (countSkipped) {
|
|
442
|
-
message += `, skipped ${
|
|
454
|
+
message += `, skipped ${colorGreaterThanZero(countSkipped, 'red')} items`
|
|
443
455
|
}
|
|
444
456
|
|
|
445
457
|
message += ` ${chalk.gray(`[${executionTime}s]`)}`
|
|
@@ -447,7 +459,7 @@ exports.printImportProcessResult = ({
|
|
|
447
459
|
return message
|
|
448
460
|
}
|
|
449
461
|
|
|
450
|
-
|
|
462
|
+
export function colorByResult(value, countImported, countFailed, countSkipped) {
|
|
451
463
|
let color = 'green'
|
|
452
464
|
|
|
453
465
|
if (countImported > 0 && (countFailed > 0 || countSkipped > 0)) {
|
|
@@ -461,7 +473,7 @@ exports.colorByResult = (value, countImported, countFailed, countSkipped) => {
|
|
|
461
473
|
return chalk[color](value)
|
|
462
474
|
}
|
|
463
475
|
|
|
464
|
-
|
|
476
|
+
export function colorGreaterThanZero(value, color) {
|
|
465
477
|
if (value > 0) {
|
|
466
478
|
return chalk[color](value)
|
|
467
479
|
}
|
|
@@ -469,11 +481,11 @@ exports.colorGreaterThanZero = (value, color) => {
|
|
|
469
481
|
return value
|
|
470
482
|
}
|
|
471
483
|
|
|
472
|
-
|
|
473
|
-
new Promise(resolve => {
|
|
484
|
+
export function refreshOrders(spinner, ws, importProcess) {
|
|
485
|
+
return new Promise(resolve => {
|
|
474
486
|
const hrstart = process.hrtime()
|
|
475
487
|
|
|
476
|
-
const socket =
|
|
488
|
+
const socket = createSocketConnection(ws.endpoint, ws.secret)
|
|
477
489
|
|
|
478
490
|
spinner.start('Refreshing orders (draft, template)...')
|
|
479
491
|
|
|
@@ -485,9 +497,9 @@ exports.refreshOrders = (spinner, ws, importProcess) =>
|
|
|
485
497
|
const { type, payload } = JSON.parse(packet)
|
|
486
498
|
switch (type) {
|
|
487
499
|
case 'refreshOrdersResponse': {
|
|
488
|
-
spinner.text =
|
|
500
|
+
spinner.text = printRefreshedOrders({
|
|
489
501
|
...payload,
|
|
490
|
-
executionTime:
|
|
502
|
+
executionTime: getExecutionTime(hrstart)
|
|
491
503
|
})
|
|
492
504
|
spinner.succeed()
|
|
493
505
|
resolve({
|
|
@@ -495,7 +507,7 @@ exports.refreshOrders = (spinner, ws, importProcess) =>
|
|
|
495
507
|
refreshed: payload.refreshed,
|
|
496
508
|
errored: payload.errored,
|
|
497
509
|
name: importProcess.name,
|
|
498
|
-
executionTime:
|
|
510
|
+
executionTime: getExecutionTime(hrstart)
|
|
499
511
|
})
|
|
500
512
|
break
|
|
501
513
|
}
|
|
@@ -508,13 +520,14 @@ exports.refreshOrders = (spinner, ws, importProcess) =>
|
|
|
508
520
|
resolve({ error: e.message })
|
|
509
521
|
})
|
|
510
522
|
})
|
|
523
|
+
}
|
|
511
524
|
|
|
512
525
|
// eslint-disable-next-line consistent-return
|
|
513
|
-
|
|
526
|
+
export function printRefreshedOrders({ refreshed, errored, executionTime }) {
|
|
514
527
|
let message = ''
|
|
515
528
|
|
|
516
|
-
const stringRefreshed =
|
|
517
|
-
const stringErrored =
|
|
529
|
+
const stringRefreshed = colorGreaterThanZero(refreshed.length, 'green')
|
|
530
|
+
const stringErrored = colorGreaterThanZero(errored.length, 'red')
|
|
518
531
|
|
|
519
532
|
message += `Refreshed ${stringRefreshed} orders (draft, template)`
|
|
520
533
|
|
|
@@ -527,9 +540,9 @@ exports.printRefreshedOrders = ({ refreshed, errored, executionTime }) => {
|
|
|
527
540
|
return message
|
|
528
541
|
}
|
|
529
542
|
|
|
530
|
-
|
|
531
|
-
new Promise(resolve => {
|
|
532
|
-
const socket =
|
|
543
|
+
export function deleteRowsBeforeImportDate(importProcess, partitionsForCsvType, spinner, ws) {
|
|
544
|
+
return new Promise(resolve => {
|
|
545
|
+
const socket = createSocketConnection(ws.endpoint, ws.secret)
|
|
533
546
|
|
|
534
547
|
spinner.start('Deleting old database records for chosen partitions...')
|
|
535
548
|
|
|
@@ -559,17 +572,18 @@ exports.deleteRowsBeforeImportDate = (importProcess, partitionsForCsvType, spinn
|
|
|
559
572
|
resolve({ error: e.message })
|
|
560
573
|
})
|
|
561
574
|
})
|
|
575
|
+
}
|
|
562
576
|
|
|
563
|
-
|
|
564
|
-
new Promise((resolve, reject) => {
|
|
577
|
+
export async function download(url, destPath, fileName) {
|
|
578
|
+
return new Promise((resolve, reject) => {
|
|
565
579
|
const filePath = path.resolve(destPath, fileName)
|
|
566
580
|
|
|
567
581
|
const dirname = path.dirname(filePath)
|
|
568
|
-
if (!
|
|
569
|
-
|
|
582
|
+
if (!existsSync(dirname)) {
|
|
583
|
+
mkdirSync(dirname, { recursive: true })
|
|
570
584
|
}
|
|
571
585
|
|
|
572
|
-
const downloadStream =
|
|
586
|
+
const downloadStream = stream(url)
|
|
573
587
|
const fileWriterStream = createWriteStream(filePath)
|
|
574
588
|
|
|
575
589
|
downloadStream
|
|
@@ -585,10 +599,11 @@ exports.download = async (url, destPath, fileName) =>
|
|
|
585
599
|
|
|
586
600
|
downloadStream.pipe(fileWriterStream)
|
|
587
601
|
})
|
|
602
|
+
}
|
|
588
603
|
|
|
589
|
-
|
|
590
|
-
new Promise((resolve, reject) => {
|
|
591
|
-
const socket =
|
|
604
|
+
export function callBeforeDataImportProcess(spinner, ws, importProcess) {
|
|
605
|
+
return new Promise((resolve, reject) => {
|
|
606
|
+
const socket = createSocketConnection(ws.endpoint, ws.secret)
|
|
592
607
|
|
|
593
608
|
spinner.start('Executing tasks before start...')
|
|
594
609
|
|
|
@@ -620,10 +635,11 @@ exports.callBeforeDataImportProcess = (spinner, ws, importProcess) =>
|
|
|
620
635
|
resolve({ error: e.message })
|
|
621
636
|
})
|
|
622
637
|
})
|
|
638
|
+
}
|
|
623
639
|
|
|
624
|
-
|
|
625
|
-
new Promise(resolve => {
|
|
626
|
-
const socket =
|
|
640
|
+
export function callAfterDataImportProcess(spinner, ws, importProcess) {
|
|
641
|
+
return new Promise(resolve => {
|
|
642
|
+
const socket = createSocketConnection(ws.endpoint, ws.secret)
|
|
627
643
|
|
|
628
644
|
spinner.start('Executing tasks after the end process...')
|
|
629
645
|
|
|
@@ -650,10 +666,11 @@ exports.callAfterDataImportProcess = (spinner, ws, importProcess) =>
|
|
|
650
666
|
resolve({ error: e.message })
|
|
651
667
|
})
|
|
652
668
|
})
|
|
669
|
+
}
|
|
653
670
|
|
|
654
|
-
|
|
655
|
-
new Promise(resolve => {
|
|
656
|
-
const socket =
|
|
671
|
+
export function callAfterMediaImportProcess(spinner, ws, report) {
|
|
672
|
+
return new Promise(resolve => {
|
|
673
|
+
const socket = createSocketConnection(ws.endpoint, ws.secret)
|
|
657
674
|
|
|
658
675
|
spinner.start('Executing tasks after the end process...')
|
|
659
676
|
|
|
@@ -678,11 +695,12 @@ exports.callAfterMediaImportProcess = (spinner, ws, report) =>
|
|
|
678
695
|
resolve({ error: e.message })
|
|
679
696
|
})
|
|
680
697
|
})
|
|
698
|
+
}
|
|
681
699
|
|
|
682
|
-
|
|
683
|
-
new Promise(resolve => {
|
|
700
|
+
export function importMedia(reprocess, spinner, ws) {
|
|
701
|
+
return new Promise(resolve => {
|
|
684
702
|
const hrstart = process.hrtime()
|
|
685
|
-
const socket =
|
|
703
|
+
const socket = createSocketConnection(ws.endpoint, ws.secret)
|
|
686
704
|
|
|
687
705
|
let countAssetImported = 0
|
|
688
706
|
let countAssetSkipped = 0
|
|
@@ -690,11 +708,11 @@ exports.importMedia = (reprocess, spinner, ws) =>
|
|
|
690
708
|
let countProductAssetImported = 0
|
|
691
709
|
|
|
692
710
|
const updateSpinnerText = () => {
|
|
693
|
-
spinner.text =
|
|
711
|
+
spinner.text = printImportProcessResult({
|
|
694
712
|
countImported: countAssetImported,
|
|
695
713
|
countSkipped: countAssetSkipped,
|
|
696
714
|
countFailed: countAssetFailed,
|
|
697
|
-
executionTime:
|
|
715
|
+
executionTime: getExecutionTime(hrstart),
|
|
698
716
|
name: 'Asset'
|
|
699
717
|
})
|
|
700
718
|
}
|
|
@@ -729,10 +747,10 @@ exports.importMedia = (reprocess, spinner, ws) =>
|
|
|
729
747
|
break
|
|
730
748
|
case 'import.images.productAssets.end':
|
|
731
749
|
spinner.succeed(
|
|
732
|
-
|
|
750
|
+
printImportProcessResult({
|
|
733
751
|
countImported: payload.count,
|
|
734
752
|
countFailed: 0,
|
|
735
|
-
executionTime:
|
|
753
|
+
executionTime: getExecutionTime(hrstart),
|
|
736
754
|
name: 'ProductAsset'
|
|
737
755
|
})
|
|
738
756
|
)
|
|
@@ -754,10 +772,11 @@ exports.importMedia = (reprocess, spinner, ws) =>
|
|
|
754
772
|
resolve({ error: e.message })
|
|
755
773
|
})
|
|
756
774
|
})
|
|
775
|
+
}
|
|
757
776
|
|
|
758
|
-
|
|
759
|
-
new Promise(resolve => {
|
|
760
|
-
const socket =
|
|
777
|
+
export function flushMedia(spinner, ws) {
|
|
778
|
+
return new Promise((resolve, reject) => {
|
|
779
|
+
const socket = createSocketConnection(ws.endpoint, ws.secret)
|
|
761
780
|
|
|
762
781
|
spinner.start('Flushing... ')
|
|
763
782
|
|
|
@@ -779,18 +798,22 @@ exports.flushMedia = (spinner, ws) =>
|
|
|
779
798
|
})
|
|
780
799
|
|
|
781
800
|
socket.on('error', e => {
|
|
782
|
-
|
|
801
|
+
spinner.fail(e.message)
|
|
802
|
+
reject(e)
|
|
783
803
|
})
|
|
784
804
|
})
|
|
805
|
+
}
|
|
785
806
|
|
|
786
|
-
|
|
807
|
+
export function getArchiveFolder(instanceEndpoint) {
|
|
787
808
|
const prefix = 'closer'
|
|
788
|
-
const instance = slugify(instanceEndpoint.split('/')[0], {
|
|
789
|
-
|
|
790
|
-
|
|
809
|
+
const instance = slugify(instanceEndpoint.split('/')[0], {
|
|
810
|
+
lower: true,
|
|
811
|
+
remove: /[*+~.()'"!:@]/g
|
|
812
|
+
})
|
|
813
|
+
return `${prefix}_${instance}`
|
|
791
814
|
}
|
|
792
815
|
|
|
793
|
-
|
|
816
|
+
export function renameWithTimestamp(dirPath) {
|
|
794
817
|
const ts = getTimestampForFilename()
|
|
795
818
|
|
|
796
819
|
const files = getCsvFilesFromDir(dirPath).map(filename => {
|
|
@@ -803,7 +826,7 @@ exports.renameWithTimestamp = dirPath => {
|
|
|
803
826
|
}
|
|
804
827
|
})
|
|
805
828
|
|
|
806
|
-
files.forEach(file =>
|
|
829
|
+
files.forEach(file => rename(file.oldPath, file.newPath, () => {}))
|
|
807
830
|
}
|
|
808
831
|
|
|
809
832
|
const getTimestampForFilename = () => {
|
|
@@ -813,3 +836,26 @@ const getTimestampForFilename = () => {
|
|
|
813
836
|
const timeString = isoTimeString.split(':').join('').split('.')[0]
|
|
814
837
|
return `${dateString}_${timeString}`.split(':').join('')
|
|
815
838
|
}
|
|
839
|
+
|
|
840
|
+
export function getHumanTimestamp(date) {
|
|
841
|
+
const [isoDateString, isoTimeString] = date.toISOString().split('T')
|
|
842
|
+
const dateString = isoDateString.split('-').join('')
|
|
843
|
+
const timeString = isoTimeString.split(':').join('').split('.')[0]
|
|
844
|
+
return `${dateString}_${timeString}`.split(':').join('')
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
export async function readImportDir(dirpath) {
|
|
848
|
+
const allFiles = readdirSync(dirpath)
|
|
849
|
+
const csvFiles = allFiles.filter(file => path.extname(file) === '.csv')
|
|
850
|
+
|
|
851
|
+
if (csvFiles.length === 0) {
|
|
852
|
+
return []
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
const files = csvFiles.map(file => {
|
|
856
|
+
const stat = statSync(path.resolve(dirpath, file))
|
|
857
|
+
return { filename: file, mtimeMS: stat.mtimeMs, mtime: stat.mtime, size: stat.size }
|
|
858
|
+
})
|
|
859
|
+
|
|
860
|
+
return files
|
|
861
|
+
}
|
package/index.js
CHANGED
|
@@ -1,32 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import chalk from 'chalk'
|
|
4
|
+
import pkg from './package.json' assert { type: 'json' }
|
|
5
|
+
import yargs from 'yargs'
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
import * as ImportData from './cmds/importData.js'
|
|
8
|
+
import * as ImportMedia from './cmds/importMedia.js'
|
|
8
9
|
|
|
9
|
-
console.log(chalk.bold(`\n 👜 Closer CLI (${
|
|
10
|
+
console.log(chalk.bold(`\n 👜 Closer CLI (${pkg.version})\n`))
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
yargs(process.argv.slice(2))
|
|
12
13
|
.scriptName('closer')
|
|
13
14
|
.usage(`Usage: ${chalk.cyan('$0')} <command> [options]`)
|
|
14
|
-
.commandDir('cmds')
|
|
15
15
|
.option('e', {
|
|
16
16
|
alias: 'endpoint',
|
|
17
17
|
describe: 'Closer URL',
|
|
18
18
|
demandOption: true
|
|
19
19
|
})
|
|
20
20
|
.option('s', {
|
|
21
|
-
alias: '
|
|
22
|
-
describe: '
|
|
23
|
-
demandOption: true
|
|
24
|
-
})
|
|
25
|
-
.option('n', {
|
|
26
|
-
alias: 'name',
|
|
27
|
-
describe: 'Attach string name label to import process',
|
|
28
|
-
type: 'string',
|
|
21
|
+
alias: 'secret',
|
|
22
|
+
describe: 'Closer Secret',
|
|
29
23
|
demandOption: true
|
|
30
24
|
})
|
|
25
|
+
.command(ImportData.command, ImportData.desc, ImportData.builder, ImportData.handler)
|
|
26
|
+
.command(ImportMedia.command, ImportMedia.desc, ImportMedia.builder, ImportMedia.handler)
|
|
31
27
|
.help()
|
|
32
28
|
.version().argv
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "closer-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.37.0",
|
|
4
4
|
"description": "",
|
|
5
|
-
"
|
|
5
|
+
"exports": "./index.js",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"bin": {
|
|
7
8
|
"closer": "./index.js"
|
|
8
9
|
},
|
|
@@ -11,14 +12,17 @@
|
|
|
11
12
|
"author": "Francesco Pasqua <cesconix@me.com>",
|
|
12
13
|
"license": "ISC",
|
|
13
14
|
"dependencies": {
|
|
14
|
-
"chalk": "^
|
|
15
|
+
"chalk": "^5.1.2",
|
|
15
16
|
"csv-parse": "^5.3.1",
|
|
16
17
|
"csv-stringify": "^6.2.0",
|
|
18
|
+
"del": "^7.0.0",
|
|
17
19
|
"event-stream": "^4.0.1",
|
|
18
|
-
"got": "^
|
|
19
|
-
"
|
|
20
|
+
"got": "^12.5.2",
|
|
21
|
+
"mv": "^2.1.1",
|
|
22
|
+
"ora": "^6.1.2",
|
|
20
23
|
"prompts": "^2.4.2",
|
|
21
24
|
"require-directory": "^2.1.1",
|
|
25
|
+
"rimraf": "^3.0.2",
|
|
22
26
|
"slugify": "^1.6.3",
|
|
23
27
|
"ws": "^8.0.0",
|
|
24
28
|
"yargs": "^17.0.1"
|