UNPKG

4.03 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3// quickly dump specific fields for users
4const { argv } = require('yargs')
5 .option('field', {
6 alias: 'f',
7 describe: 'fields to dump',
8 type: 'array',
9 })
10 .option('username', {
11 alias: 'u',
12 describe: 'custom field to treat as username',
13 default: null,
14 })
15 .option('output', {
16 alias: 'o',
17 describe: 'output for the data',
18 default: 'console',
19 choices: ['console', 'csv'],
20 })
21 .option('prefix', {
22 describe: 'prefix for launched users microservice',
23 })
24 .option('filter', {
25 describe: 'filter users - pass stringified JSON',
26 default: '{}',
27 })
28 .option('public', {
29 describe: 'list public users or not',
30 default: false,
31 })
32 .option('audience', {
33 describe: 'audience to fetch data from',
34 })
35 .option('criteria', {
36 alias: 's',
37 describe: 'sort by supplied field, defaults to original id',
38 })
39 .option('order', {
40 describe: 'sort order',
41 default: 'DESC',
42 choices: ['DESC', 'ASC'],
43 })
44 .option('separator', {
45 describe: 'separator for console output',
46 default: '\t',
47 })
48 .option('toDate', {
49 describe: 'transforms field to date',
50 type: 'array',
51 })
52 .option('dateFormat', {
53 describe: 'date transform format',
54 default: 'L',
55 })
56 .coerce({
57 filter: JSON.parse,
58 })
59 .demandOption(['field'], 'Please provide at least 1 field to dump')
60 .help('h');
61
62// deps
63const fs = require('fs');
64const Promise = require('bluebird');
65const AMQPTransport = require('@microfleet/transport-amqp');
66const csvWriter = require('csv-write-stream');
67const omit = require('lodash/omit');
68const pick = require('lodash/pick');
69const moment = require('moment');
70const conf = require('../lib/config');
71const { USERS_USERNAME_FIELD } = require('../lib/constants');
72
73const config = conf.get('/', { env: process.env.NODE_ENV });
74const amqpConfig = omit(config.amqp.transport, ['queue', 'neck', 'listen', 'onComplete']);
75const audience = argv.audience || config.jwt.defaultAudience;
76const prefix = argv.prefix || config.router.routes.prefix;
77const route = `${prefix}.list`;
78const iterator = {
79 offset: 0,
80 limit: 24,
81 audience,
82 filter: argv.filter,
83 public: argv.public,
84 order: argv.order,
85};
86
87// add sorting by this
88if (argv.criteria) iterator.criteria = argv.criteria;
89
90/**
91 * Get transport
92 */
93const getTransport = () => AMQPTransport
94 .connect({ ...amqpConfig, debug: false })
95 .disposer((amqp) => amqp.close());
96
97/**
98 * Output stream
99 */
100let output;
101const headers = ['id', 'username', ...argv.field];
102switch (argv.output) {
103 case 'console':
104 // so it's somewhat easier to read
105 output = csvWriter({ headers, separator: argv.separator });
106 output.pipe(process.stdout);
107 break;
108
109 case 'csv': {
110 const filename = `${process.cwd()}/dump-${Date.now()}.csv`;
111 process.stdout.write(`Writing to "${filename}"\n`);
112
113 output = csvWriter({ headers });
114 output.pipe(fs.createWriteStream(filename));
115 break;
116 }
117
118 default:
119 throw new Error('unknown output');
120}
121
122/**
123 * Writing user to output
124 */
125const writeUserToOutput = (user) => {
126 const attributes = user.metadata[audience];
127 const { id } = user;
128 const username = (argv.username && attributes[argv.username]) || attributes[USERS_USERNAME_FIELD];
129
130 if (argv.toDate) {
131 argv.toDate.forEach((fieldName) => {
132 const value = attributes[fieldName];
133 if (value) {
134 attributes[fieldName] = moment(value).format(argv.dateFormat);
135 }
136 });
137 }
138
139 output.write(Object.assign(pick(attributes, argv.field), { id, username }));
140};
141
142/**
143 * List users
144 */
145const listUsers = (amqp) => (
146 amqp
147 .publishAndWait(route, iterator, { timeout: 5000 })
148 .then((data) => {
149 data.users.forEach(writeUserToOutput);
150
151 // prepare for next iteration
152 if (data.page < data.pages) {
153 iterator.offset = data.cursor;
154 return listUsers(amqp);
155 }
156
157 return output.end();
158 })
159);
160
161Promise
162 .using(getTransport(), listUsers)
163 .catch((err) => setImmediate(() => { throw err; }));