closer-cli 2.48.0 → 2.48.2

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/index.js CHANGED
@@ -7,7 +7,7 @@ import yargs from 'yargs'
7
7
  import * as ImportData from './cmds/importData.js'
8
8
  import * as ImportMedia from './cmds/importMedia.js'
9
9
 
10
- console.log(chalk.bold(`\n šŸ‘œ Closer CLI (${pkg.version})\n`))
10
+ console.log(chalk.bold(`\nšŸ‘œ Closer CLI (${pkg.version})\n`))
11
11
 
12
12
  yargs(process.argv.slice(2))
13
13
  .scriptName('closer')
@@ -17,9 +17,9 @@ yargs(process.argv.slice(2))
17
17
  describe: 'Closer URL',
18
18
  demandOption: true
19
19
  })
20
- .option('s', {
21
- alias: 'secret',
22
- describe: 'Closer Secret',
20
+ .option('k', {
21
+ alias: 'secret-key',
22
+ describe: 'Closer Secret Key',
23
23
  demandOption: true
24
24
  })
25
25
  .command(ImportData.command, ImportData.desc, ImportData.builder, ImportData.handler)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "closer-cli",
3
- "version": "2.48.0",
3
+ "version": "2.48.2",
4
4
  "description": "",
5
5
  "exports": "./index.js",
6
6
  "type": "module",
@@ -12,20 +12,17 @@
12
12
  "author": "Francesco Pasqua <cesconix@me.com>",
13
13
  "license": "ISC",
14
14
  "dependencies": {
15
+ "axios": "^1.3.4",
15
16
  "chalk": "^5.1.2",
16
- "csv-parse": "^5.3.1",
17
- "csv-stringify": "^6.2.0",
18
17
  "del": "^7.0.0",
19
- "event-stream": "^4.0.1",
18
+ "form-data": "^4.0.0",
20
19
  "got": "^12.5.2",
21
- "mv": "^2.1.1",
22
20
  "ora": "^6.1.2",
23
21
  "prompts": "^2.4.2",
24
- "require-directory": "^2.1.1",
25
- "rimraf": "^3.0.2",
22
+ "qs": "^6.11.0",
26
23
  "slugify": "^1.6.3",
27
24
  "ws": "^8.0.0",
28
25
  "yargs": "^17.0.1"
29
26
  },
30
- "gitHead": "0e7b7c7d9698c08d8541710cb6e304ecc096ee6d"
27
+ "gitHead": "1fbbcab302b63f3123c234824fb8784cd7f286f2"
31
28
  }
@@ -1,341 +0,0 @@
1
- /* eslint-disable no-console */
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
- getCsvFilesFromDir,
15
- ALLOWED_CSV_FILES,
16
- getGoogleSheetCsvUrl,
17
- importCsvFromFile,
18
- validateCsvFromFile,
19
- refreshOrders,
20
- download,
21
- callBeforeDataImportProcess,
22
- callAfterDataImportProcess,
23
- getExecutionTime,
24
- getArchiveFolder,
25
- sendImportErrorMail,
26
- deleteRowsBeforeImportDate,
27
- readImportDir
28
- } from '../../helpers.js'
29
-
30
- const mv = promisify(move)
31
-
32
- export const command = 'multiple [options]'
33
-
34
- export const desc = 'Import different CSV files'
35
-
36
- export function builder(yargs) {
37
- yargs
38
- .usage(`Usage: ${chalk.cyan('$0 import:data multiple')} [options]`)
39
- .option('fromDir', {
40
- alias: 'd',
41
- describe: 'Directory path that contains files to import',
42
- type: 'string'
43
- })
44
- .option('fromGoogleDocId', {
45
- alias: 'g',
46
- describe: 'Google Doc ID that contains sheets to import',
47
- type: 'string'
48
- })
49
- .conflicts('fromDir', 'fromGoogleDocId')
50
- .check(argv => {
51
- if (!argv.fromDir && !argv.fromGoogleDocId) {
52
- throw new Error('Missing required argument: fromDir OR fromGoogleDocId')
53
- }
54
-
55
- if (argv.fromDir) {
56
- accessSync(argv.fromDir, constants.F_OK)
57
- }
58
-
59
- return true
60
- })
61
- }
62
-
63
- export async function handler(argv) {
64
- // eslint-disable-next-line no-param-reassign
65
- argv.out = argv.out || getArchiveFolder(argv.endpoint)
66
-
67
- const spinner = new Ora()
68
- const hrstart = process.hrtime()
69
-
70
- const importProcess = {
71
- importProcessStart: new Date(),
72
- importProcessEnd: null,
73
- time: null,
74
- endpoint: argv.endpoint,
75
- mode: null,
76
- name: argv.name,
77
- type: [],
78
- result: []
79
- }
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
-
85
- const interactive = !argv.nonInteractive
86
-
87
- const ws = {
88
- endpoint: argv.endpoint,
89
- secret: argv.secret
90
- }
91
-
92
- let csvChoices = []
93
-
94
- if (argv.fromDir) {
95
- const csvFilesFromDir = getCsvFilesFromDir(argv.fromDir)
96
-
97
- csvChoices = Object.keys(ALLOWED_CSV_FILES).map(csvType => ({
98
- title: ALLOWED_CSV_FILES[csvType],
99
- csvType,
100
- value: {
101
- file: path.resolve(argv.fromDir, ALLOWED_CSV_FILES[csvType]),
102
- csvType
103
- },
104
- disabled: !csvFilesFromDir.includes(ALLOWED_CSV_FILES[csvType])
105
- }))
106
- } else {
107
- csvChoices = Object.keys(ALLOWED_CSV_FILES).map(csvType => ({
108
- title: csvType,
109
- csvType,
110
- value: {
111
- url: getGoogleSheetCsvUrl(argv.fromGoogleDocId, csvType),
112
- csvType
113
- }
114
- }))
115
- }
116
-
117
- let files
118
- if (interactive && !argv.type) {
119
- const answers = await prompts(
120
- [
121
- {
122
- instructions: false,
123
- name: 'files',
124
- type: 'multiselect',
125
- message: argv.fromDir ? 'Select Files' : 'Select Sheets',
126
- warn: argv.fromDir && 'File not found',
127
- choices: csvChoices,
128
- min: 1
129
- }
130
- ],
131
- { onCancel: () => process.exit() }
132
- )
133
-
134
- files = answers.files
135
- } else {
136
- files = argv.type.map(type => {
137
- const choice = csvChoices.find(csvChoice => csvChoice.csvType === type)
138
- return choice.value
139
- })
140
- }
141
-
142
- importProcess.type = files.map(f => f.csvType)
143
-
144
- let mode
145
- if (interactive && !argv.mode) {
146
- const answers = await prompts(
147
- [
148
- {
149
- type: 'select',
150
- name: 'mode',
151
- message: 'Import Mode',
152
- choices: [
153
- {
154
- title: 'Incremental',
155
- value: 'incremental'
156
- },
157
- {
158
- title: 'Full',
159
- description: 'Before starting, all database records will be logically deleted',
160
- value: 'full'
161
- }
162
- ],
163
- hint: ' '
164
- }
165
- ],
166
- { onCancel: () => process.exit() }
167
- )
168
-
169
- mode = answers.mode
170
- } else {
171
- mode = argv.mode
172
- }
173
-
174
- importProcess.mode = mode
175
-
176
- const validations = []
177
- const csvReports = []
178
-
179
- const parsedFiles = []
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
-
189
- if (argv.fromGoogleDocId) {
190
- console.log('')
191
- spinner.start('Downloading Google sheets...')
192
- for (const item of files) {
193
- const filepath = await download(
194
- item.url,
195
- dirPathOriginal,
196
- `${importProcess.name}_${item.csvType}.csv`
197
- )
198
- parsedFiles.push({ file: filepath, csvType: item.csvType })
199
- }
200
- spinner.succeed('Google sheets downloaded')
201
- } else {
202
- for (const file of files) {
203
- const dst = path.resolve(dirPathOriginal, `${importProcess.name}_${file.csvType}.csv`)
204
- await mv(file.file, dst)
205
- parsedFiles.push({ file: dst, csvType: file.csvType })
206
- }
207
- }
208
-
209
- //
210
- // Validate CSV headers sequentially
211
- //
212
- for (const item of parsedFiles) {
213
- const validation = await validateCsvFromFile(
214
- item.file,
215
- item.csvType,
216
- ws,
217
- argv.csvDelimiter,
218
- importProcess
219
- )
220
- validations.push(validation)
221
- }
222
-
223
- const invalidFiles = validations.filter(v => v.error)
224
- if (invalidFiles.length > 0) {
225
- console.log('')
226
- console.log(invalidFiles.map(f => `ERROR => [${chalk.red(f.inputFile)}] ${f.error}`).join('\n'))
227
- process.exit(1)
228
- }
229
-
230
- if (interactive) {
231
- console.log('')
232
- const { confirmed } = await prompts(
233
- [
234
- {
235
- type: 'confirm',
236
- name: 'confirmed',
237
- message: 'Continue?',
238
- initial: false
239
- }
240
- ],
241
- { onCancel: () => process.exit() }
242
- )
243
-
244
- if (!confirmed) {
245
- process.exit()
246
- }
247
- }
248
-
249
- console.log('')
250
-
251
- //
252
- // Call ImportService.beforeImportProcess (import flag)
253
- //
254
- if (!argv.skipTasksBeforeImport) {
255
- try {
256
- await callBeforeDataImportProcess(spinner, ws, importProcess)
257
- } catch (e) {
258
- process.exit(1)
259
- }
260
- }
261
-
262
- //
263
- // Import CSV files sequentially
264
- //
265
- const partitionsForCsvType = new Map()
266
-
267
- for (const item of parsedFiles) {
268
- const csvReport = await importCsvFromFile({
269
- file: item.file,
270
- csvType: item.csvType,
271
- outputDir: dirPathTemp,
272
- spinner,
273
- ws,
274
- delimiter: argv.csvDelimiter,
275
- importProcess
276
- })
277
- csvReports.push(csvReport)
278
- if (csvReport.countFailed > 0 || csvReport.countSkipped > 0) {
279
- sendImportErrorMail(ws, importProcess, csvReport)
280
- }
281
- partitionsForCsvType.set(csvReport.csvType, Array.from(csvReport.partitions))
282
- }
283
-
284
- //
285
- // Import FULL?
286
- //
287
- if (mode === 'full') {
288
- await deleteRowsBeforeImportDate(
289
- importProcess,
290
- Object.fromEntries(partitionsForCsvType),
291
- spinner,
292
- ws
293
- )
294
- }
295
-
296
- //
297
- // Refresh Orders of type Draft, Template
298
- //
299
- // eslint-disable-next-line no-unused-vars
300
- const refreshedOrders = await refreshOrders(spinner, ws, importProcess)
301
-
302
- //
303
- // importProcess
304
- //
305
-
306
- importProcess.importProcessEnd = new Date()
307
- importProcess.time = getExecutionTime(hrstart)
308
-
309
- const importProcessRows = []
310
-
311
- csvReports.forEach(csvReport => {
312
- importProcessRows.push({
313
- type: 'import.csv',
314
- payload: csvReport
315
- })
316
- })
317
-
318
- importProcessRows.push({
319
- type: 'refreshed.orders',
320
- payload: refreshedOrders
321
- })
322
-
323
- importProcess.result = importProcessRows
324
-
325
- await callAfterDataImportProcess(spinner, ws, importProcess)
326
-
327
- await deleteAsync(path.resolve(dirPathTemp, '_processing'))
328
-
329
- const reportFiles = await readImportDir(dirPathTemp)
330
-
331
- for (const reportFile of reportFiles) {
332
- const { filename, size } = reportFile
333
- if ((filename.includes('skipped') || filename.includes('errors')) && size === 0) {
334
- await deleteAsync(path.resolve(dirPathTemp, filename))
335
- }
336
- }
337
-
338
- await mv(dirPathTemp, dirPathFinal)
339
-
340
- process.exit()
341
- }
@@ -1,281 +0,0 @@
1
- /* eslint-disable no-console */
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,
15
- importCsvFromFile,
16
- validateCsvFromFile,
17
- refreshOrders,
18
- download,
19
- callBeforeDataImportProcess,
20
- callAfterDataImportProcess,
21
- getExecutionTime,
22
- getArchiveFolder,
23
- sendImportErrorMail,
24
- deleteRowsBeforeImportDate,
25
- readImportDir
26
- } from '../../helpers.js'
27
-
28
- const mv = promisify(move)
29
-
30
- export const command = 'single [options]'
31
-
32
- export const desc = 'Import only one CSV file'
33
-
34
- export function builder(yargs) {
35
- yargs
36
- .usage(`Usage: ${chalk.cyan('$0 import:data single')} [options]`)
37
- .option('fromFile', {
38
- alias: 'f',
39
- describe: 'File to import',
40
- type: 'string'
41
- })
42
- .option('fromUrl', {
43
- alias: 'u',
44
- describe: 'Remote URL file to import',
45
- type: 'string'
46
- })
47
- .conflicts('fromFile', 'fromUrl')
48
- .check(argv => {
49
- if (!argv.fromFile && !argv.fromUrl) {
50
- throw new Error('Missing required argument: fromFile OR fromUrl')
51
- }
52
-
53
- if (argv.fromFile) {
54
- accessSync(argv.fromFile, constants.F_OK)
55
- }
56
-
57
- return true
58
- })
59
- }
60
-
61
- export async function handler(argv) {
62
- // eslint-disable-next-line no-param-reassign
63
- argv.out = argv.out || getArchiveFolder(argv.endpoint)
64
-
65
- const spinner = new Ora()
66
- const hrstart = process.hrtime()
67
-
68
- const importProcess = {
69
- importProcessStart: new Date(),
70
- importProcessEnd: null,
71
- time: null,
72
- endpoint: argv.endpoint,
73
- mode: null,
74
- name: argv.name,
75
- type: [],
76
- result: []
77
- }
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
-
85
- const ws = {
86
- endpoint: argv.endpoint,
87
- secret: argv.secret
88
- }
89
-
90
- let type
91
- if (interactive && !argv.type) {
92
- const answers = await prompts(
93
- [
94
- {
95
- instructions: false,
96
- name: 'type',
97
- type: 'select',
98
- message: 'Select Type',
99
- choices: Object.keys(ALLOWED_CSV_FILES).map(csvType => ({
100
- title: csvType,
101
- value: csvType
102
- }))
103
- }
104
- ],
105
- { onCancel: () => process.exit() }
106
- )
107
-
108
- type = answers.type
109
- } else {
110
- // eslint-disable-next-line prefer-destructuring
111
- type = argv.type[0]
112
- }
113
-
114
- importProcess.type = [type]
115
-
116
- let mode
117
- if (interactive && !argv.mode) {
118
- const answers = await prompts(
119
- [
120
- {
121
- type: 'select',
122
- name: 'mode',
123
- message: 'Import Mode',
124
- choices: [
125
- {
126
- title: 'Incremental',
127
- value: 'incremental'
128
- },
129
- {
130
- title: 'Full',
131
- description: 'Before starting, all database records will be logically deleted',
132
- value: 'full'
133
- }
134
- ],
135
- hint: ' '
136
- }
137
- ],
138
- { onCancel: () => process.exit() }
139
- )
140
-
141
- mode = answers.mode
142
- } else {
143
- mode = argv.mode
144
- }
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
-
156
- if (interactive) {
157
- console.log('')
158
- const { confirmed } = await prompts(
159
- [
160
- {
161
- type: 'confirm',
162
- name: 'confirmed',
163
- message: 'Continue?',
164
- initial: false
165
- }
166
- ],
167
- { onCancel: () => process.exit() }
168
- )
169
-
170
- if (!confirmed) {
171
- process.exit()
172
- }
173
-
174
- console.log('')
175
- }
176
-
177
- let filepath = null
178
-
179
- if (argv.fromUrl) {
180
- spinner.start('Downloading file...')
181
- filepath = await download(argv.fromUrl, dirPathOriginal, `${importProcess.name}_${type}.csv`)
182
- spinner.succeed('Downloaded file')
183
- }
184
-
185
- if (argv.fromFile) {
186
- filepath = path.resolve(dirPathOriginal, `${importProcess.name}_${type}.csv`)
187
- await mv(argv.fromFile, filepath)
188
- }
189
-
190
- const validation = await validateCsvFromFile(filepath, type, ws, argv.csvDelimiter, importProcess)
191
-
192
- if (validation.error) {
193
- console.log(`ERROR => [${chalk.red(validation.inputFile)}] ${validation.error}`)
194
- process.exit(1)
195
- }
196
-
197
- //
198
- // Call ImportService.beforeImportProcess (import flag)
199
- //
200
- if (!argv.skipTasksBeforeImport) {
201
- try {
202
- await callBeforeDataImportProcess(spinner, ws, importProcess)
203
- } catch (e) {
204
- process.exit(1)
205
- }
206
- }
207
-
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),
233
- spinner,
234
- ws
235
- )
236
- }
237
-
238
- //
239
- // Refresh Orders of type Draft, Template
240
- //
241
-
242
- const refreshedOrders = await refreshOrders(spinner, ws, importProcess)
243
-
244
- //
245
- // importProcess
246
- //
247
-
248
- importProcess.importProcessEnd = new Date()
249
- importProcess.time = getExecutionTime(hrstart)
250
-
251
- const importProcessRows = []
252
-
253
- importProcessRows.push({
254
- type: 'import.csv',
255
- payload: csvReport
256
- })
257
-
258
- importProcessRows.push({
259
- type: 'refreshed.orders',
260
- payload: refreshedOrders
261
- })
262
-
263
- importProcess.result = importProcessRows
264
-
265
- await callAfterDataImportProcess(spinner, ws, importProcess)
266
-
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)
279
-
280
- process.exit()
281
- }