tango-app-api-report 3.1.1 → 3.1.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
@@ -1,7 +1,8 @@
1
1
 
2
2
 
3
3
  import { reportRouter } from './src/routes/report.routes.js';
4
+ import { reportDocs } from './src/docs/report.docs.js';
4
5
 
5
- export { reportRouter };
6
+ export { reportRouter, reportDocs };
6
7
 
7
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-report",
3
- "version": "3.1.1",
3
+ "version": "3.1.2",
4
4
  "description": "report",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -15,14 +15,18 @@
15
15
  "dependencies": {
16
16
  "aws-sdk": "^2.1578.0",
17
17
  "cors": "^2.8.5",
18
+ "dayjs": "^1.11.11",
18
19
  "dotenv": "^16.4.5",
19
20
  "express": "^4.18.3",
20
21
  "express-fileupload": "^1.5.0",
21
22
  "handlebars": "^4.7.8",
23
+ "joi-to-swagger": "^6.2.0",
22
24
  "mongodb": "^6.5.0",
25
+ "nodemailer": "^6.9.13",
23
26
  "nodemon": "^3.1.0",
24
- "tango-api-schema": "^2.0.89",
25
- "tango-app-api-middleware": "^3.1.1",
27
+ "swagger-ui-express": "^5.0.0",
28
+ "tango-api-schema": "^2.0.111",
29
+ "tango-app-api-middleware": "^3.1.13",
26
30
  "winston": "^3.12.0",
27
31
  "winston-daily-rotate-file": "^5.0.0"
28
32
  },
@@ -1,7 +1,17 @@
1
- import { reportCreate, reportGet, reportGetSingle, reportUpdate } from '../service/report.service.js';
2
- import { insertOpenSearchData, logger } from 'tango-app-api-middleware';
3
- import { getUserNameEmailById } from '../service/user.service.js';
1
+ import { deleteOneReport, findOneReport, findReport, reportCreate, reportGet, reportGetSingle, reportUpdate } from '../service/report.service.js';
2
+ import { appConfig, fileUpload, getObject, getOpenSearchData, insertOpenSearchData, listFileByPath, logger, sendMessageToQueue } from 'tango-app-api-middleware';
3
+ import { findOneUser, getUserNameEmailById } from '../service/user.service.js';
4
+ import { updateOneBasePriceModel } from '../service/basePrice.service.js';
5
+ import { aggregateClient } from '../service/client.service.js';
6
+ import utc from 'dayjs/plugin/utc.js';
7
+ import timezone from 'dayjs/plugin/timezone.js';
8
+ import aws from 'aws-sdk';
9
+ import * as nodemailer from 'nodemailer';
10
+ import dayjs from 'dayjs';
4
11
 
12
+ // import basePricingModel from 'tango-api-schema/schema/basePricing.model.js';
13
+ dayjs.extend( utc );
14
+ dayjs.extend( timezone );
5
15
 
6
16
  export async function createReport( req, res ) {
7
17
  try {
@@ -20,9 +30,10 @@ export async function createReport( req, res ) {
20
30
  logSubType: 'reportConfig',
21
31
  changes: [ `${req.body?.fileName}` ],
22
32
  eventType: 'create',
33
+ showTo: [ 'tango' ],
23
34
  };
24
35
 
25
- await insertOpenSearchData( 'tango-retail-activity-logs', logObj );
36
+ await insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
26
37
  if ( createReport ) {
27
38
  res.sendSuccess( { result: 'Created Successfully' } );
28
39
  }
@@ -62,6 +73,9 @@ export async function getSingleReport( req, res ) {
62
73
 
63
74
  export async function updateReport( req, res ) {
64
75
  try {
76
+ const previousReport = await reportGetSingle( {
77
+ _id: req.params?.id,
78
+ } );
65
79
  const updateAck = await reportUpdate( {
66
80
  _id: req.params?.id, fileType: req.body?.fileType, email: req.body?.email, clientId: req.body?.clientId,
67
81
  } );
@@ -77,9 +91,13 @@ export async function updateReport( req, res ) {
77
91
  logSubType: 'reportConfig',
78
92
  changes: [ `Report id ${req.params?.id}` ],
79
93
  eventType: 'update',
94
+ showTo: [ 'tango' ],
95
+ current: req.body,
96
+ previous: previousReport,
97
+
80
98
  };
81
99
 
82
- await insertOpenSearchData( 'tango-retail-activity-logs', logObj );
100
+ await insertOpenSearchData( appConfig.opensearch.activityLog, logObj );
83
101
  if ( updateAck ) {
84
102
  res.sendSuccess( { result: 'Updated Successfully' } );
85
103
  }
@@ -88,3 +106,493 @@ export async function updateReport( req, res ) {
88
106
  return res.sendError( error, 500 );
89
107
  }
90
108
  }
109
+
110
+ export async function deleteReport( req, res ) {
111
+ try {
112
+ const inputData = req.params;
113
+ await deleteOneReport( { _id: inputData.id } );
114
+ return res.sendSuccess( { result: 'Report has been deleted successfully' } );
115
+ } catch ( error ) {
116
+ return res.sendError( error, 500 );
117
+ }
118
+ }
119
+
120
+ export async function clientReportList( req, res ) {
121
+ try {
122
+ const inputData = req.body;
123
+ const limit = inputData.limit || 10;
124
+ const offset = inputData.offset? ( inputData.offset - 1 ) * inputData.limit : 0;
125
+ const daysDifference = Math.ceil( ( new Date( inputData.toDate ) - new Date( inputData.fromDate ) ) / ( 24 * 60 * 60 * 1000 ) );
126
+ const query = [
127
+ {
128
+ $match: {
129
+ $and: [
130
+ { 'reportConfigs.report': { $eq: true } },
131
+ { status: { $eq: 'active' } },
132
+ { clientId: { $in: inputData.clientId } },
133
+ ],
134
+
135
+ },
136
+ },
137
+ {
138
+ $project: {
139
+ clientId: 1,
140
+ tangoId: 1,
141
+ clientName: 1,
142
+ daysDifference: 1,
143
+ datesArray: {
144
+ $map: {
145
+ input: { $range: [ 0, { $toInt: daysDifference+1 } ] },
146
+ as: 'day',
147
+ in: { $add: [ new Date( inputData.fromDate ), { $multiply: [ '$$day', 1000 * 60 * 60 * 24 ] } ] },
148
+ },
149
+ },
150
+ },
151
+ },
152
+ {
153
+ $unwind: '$datesArray',
154
+ },
155
+ {
156
+ $sort: {
157
+ datesArray: -1,
158
+ },
159
+ },
160
+
161
+ {
162
+ $project: {
163
+ clientId: 1,
164
+ clientName: 1,
165
+ tangoId: 1,
166
+ fileDate: '$datesArray',
167
+ },
168
+ },
169
+ ];
170
+ if ( inputData.sortColumnName ) {
171
+ const sortBy = inputData.sortBy || -1;
172
+ const sortColumnName =inputData.sortColumnName == 'clientId'? 'tangoId': inputData.sortColumnName;
173
+ query.push(
174
+ {
175
+ $sort: { fileDate: -1 },
176
+ },
177
+ {
178
+ $sort: { [sortColumnName]: sortBy },
179
+ },
180
+
181
+ );
182
+ }
183
+
184
+
185
+ if ( inputData.searchValue ) {
186
+ query.push(
187
+ {
188
+ $match: {
189
+ $or: [
190
+ {
191
+ clientName: { $regex: inputData.searchValue, $options: 'i' },
192
+ },
193
+ {
194
+ clientId: { $regex: inputData.searchValue, $options: 'i' },
195
+
196
+ },
197
+ ],
198
+ },
199
+ },
200
+ );
201
+ }
202
+
203
+
204
+ const count = await aggregateClient( query );
205
+ if ( count?.length == 0 ) {
206
+ return res.sendError( 'No Data Found', 204 );
207
+ }
208
+
209
+ query.push(
210
+ { $skip: offset },
211
+ { $limit: limit },
212
+ );
213
+ const result = await aggregateClient( query );
214
+ res.sendSuccess( { result: result, count: count?.length } );
215
+ } catch ( error ) {
216
+ logger.info( { error: error, message: req.body, function: 'clientReportList' } );
217
+ return res.sendError( 'Internal Server Error', 500 );
218
+ }
219
+ }
220
+
221
+ export async function generateReport( req, res ) {
222
+ try {
223
+ const inputData = req.body;
224
+ const sqsProduceQueue = {
225
+ QueueUrl: `${appConfig.cloud.aws.sqs.url}${appConfig.cloud.aws.sqs.generateReport}`,
226
+ MessageBody: JSON.stringify( {
227
+ file_date: inputData.fileDate,
228
+ client_name: req.report?.reportConfigs?.reportName,
229
+ client_id: Number( inputData.clientId ),
230
+ message: 'good to trigger',
231
+ } ),
232
+ };
233
+ const sqsQueue = await sendMessageToQueue( sqsProduceQueue.QueueUrl, sqsProduceQueue.MessageBody );
234
+ if ( sqsQueue.MessageId ) {
235
+ const log = {
236
+ userName: req.user.userName,
237
+ Date: new Date(),
238
+ userId: req.user._id,
239
+ logType: 'report',
240
+ logSubType: 'generateReport',
241
+ logData: {
242
+ clientName: req.report?.clientName,
243
+ fileDate: inputData.fileDate,
244
+ reportName: req.report?.reportConfigs?.reportName,
245
+ clientId: inputData.ClientId,
246
+ sqsMessageId: sqsQueue.MessageId,
247
+ },
248
+ };
249
+ await insertOpenSearchData( 'tango-retail-activity-logs', log );
250
+
251
+ return res.sendSuccess( { result: 'Report has been Initiated Sucessfully' } );
252
+ }
253
+ } catch ( error ) {
254
+ const err = error.code || 'Internal Server Error';
255
+ logger.error( { error: error, message: req.body, function: 'generateReport' } );
256
+ return res.sendError( err, 500 );
257
+ }
258
+ }
259
+
260
+ export async function uploadManualReport( req, res ) {
261
+ try {
262
+ const inputData = req.body;
263
+ const name = inputData.fileName;
264
+ const regex = /^(\d{2}-\d{2}-\d{4})_(.*?)\.([a-zA-Z0-9]+)$/;
265
+ const matches = name.match( regex );
266
+ if ( matches ) {
267
+ const date = matches[1];
268
+ const filenameWithoutExtension = matches[2];
269
+ const extension = matches[3];
270
+ if ( inputData.fileDate !== date ) {
271
+ return res.sendError( 'File Date is Incorrect in the File Name', 400 );
272
+ }
273
+ const query = {
274
+ 'fileName': filenameWithoutExtension,
275
+ 'fileType': extension,
276
+ 'clientId': inputData.clientId,
277
+ };
278
+ const data = await findOneReport( query );
279
+ if ( !data ) {
280
+ return res.sendError( 'Report does not configured', 400 );
281
+ }
282
+ logger.info( { files: req.files } );
283
+ const uploadDataParams = {
284
+ Bucket: appConfig.cloud.aws.bucket.reportBucket,
285
+ Key: `${inputData.fileDate}/${req.report?.reportConfigs?.reportName}/`,
286
+ fileName: inputData.fileName,
287
+ body: req.files.files.data,
288
+ };
289
+ const result = await fileUpload( uploadDataParams );
290
+ logger.info( { result: result, value: 'file upload response' } );
291
+ if ( result.Error ) {
292
+ return res.sendError( 'File Upload Failed', 500 );
293
+ }
294
+ let user = await findOneUser( { _id: req.user._id, isActive: true } );
295
+ if ( !user ) {
296
+ return res.sendError( 'Forbidden', 403 );
297
+ }
298
+ const log = {
299
+ userName: user.userName,
300
+ Date: new Date(),
301
+ userId: req.user._id,
302
+ logType: 'report',
303
+ logSubType: 'uploadReport',
304
+ logData: {
305
+ fileDate: inputData.fileDate,
306
+ reportName: req.report?.reportConfigs?.reportName,
307
+ clientId: data.clientId,
308
+ },
309
+ };
310
+ logger.info( { log: log } );
311
+ // await insertOpenSearchData( 'tango-retail-activity-logs', log );
312
+ return res.sendSuccess( { result: 'uploaded Sucessfully' } );
313
+ } else {
314
+ return res.sendError( 'File Name is Incorrect', 400 );
315
+ }
316
+ } catch ( error ) {
317
+ logger.error( { error: error, message: req.body, function: 'uploadManualReport' } );
318
+ return res.sendError( error, 500 );
319
+ }
320
+ }
321
+
322
+ export async function getReportList( req, res ) {
323
+ try {
324
+ const inputData = req.query;
325
+ const fetchData = {
326
+ Bucket: appConfig.cloud.aws.bucket.reportBucket,
327
+ file_path: `${inputData.fileDate}/${req.report?.reportConfigs?.reportName}/`,
328
+ };
329
+ const folderPath = await listFileByPath( fetchData );
330
+
331
+ logger.info( { folderPath: folderPath, value: 'report list from bucket' } );
332
+ if ( folderPath?.data?.length == 0 ) {
333
+ return res.sendError( 'No Reports Found', 204 );
334
+ }
335
+ let temp =[];
336
+ for ( const item of folderPath.data ) {
337
+ temp.push( { Key: item.Key, LastModified: dayjs( item.LastModified ).tz( 'Asia/Kolkata' ).format( 'YYYY-MM-DD HH:mm:ss' ) } );
338
+ }
339
+ return res.sendSuccess( { result: temp } );
340
+ } catch ( error ) {
341
+ logger.error( { error: error, message: req.query, function: 'getReportList' } );
342
+ return res.sendError( error, 500 );
343
+ }
344
+ }
345
+
346
+ export async function dowloadReport( req, res ) {
347
+ try {
348
+ const inputData = req.query;
349
+ const params = {
350
+ Bucket: appConfig.cloud.aws.bucket.reportBucket,
351
+ Key: inputData.filePath,
352
+ };
353
+ const fileName = inputData.filePath.split( '/' );
354
+ const getReport = await getObject( params );
355
+ if ( getReport.code ) {
356
+ return res.sendError( getReport, 500 );
357
+ }
358
+ res.setHeader(
359
+ 'Content-disposition',
360
+ `attachment; filename=${fileName[2]}`,
361
+ );
362
+ res.setHeader(
363
+ 'Content-Type',
364
+ `${getReport.ContentType}`,
365
+ );
366
+ res.status( 200 ).send( getReport.Body );
367
+ } catch ( error ) {
368
+ logger.error( { error: error, message: req.query, function: 'dowloadReport' } );
369
+ return res.sendError( error, 500 );
370
+ }
371
+ }
372
+ export async function sendReport( req, res ) {
373
+ try {
374
+ const inputData = req.body;
375
+ const getReportList = await findReport( { clientId: inputData.clientId } );
376
+ let reportName = [];
377
+ let name = [];
378
+ let regionalKey = [];
379
+ let result = [];
380
+ let file = {};
381
+ let sendMailCount = 0;
382
+ let subject = 'Fwd: Tango Eye\'s Customer Intelligence Report - ' + inputData.fileDate;
383
+ let messageBody = `<br>Hi Team,<br><br>&nbsp;Please find attached Tango RetailEye report on customer footfall and shopping patterns for ${inputData.fileDate}.<br><br>&nbsp;Thanks <br>&nbsp;Team Tango`;
384
+ let UserattachmentDetails = [];
385
+ for ( const data of getReportList ) {
386
+ const fetchData = {
387
+ Bucket: appConfig.cloud.aws.bucket.reportBucket,
388
+ Key: `${inputData.fileDate}/${req.report?.reportConfigs?.reportName}/${inputData.fileDate}_${data.fileName}.${data.fileType}`,
389
+ };
390
+ reportName.push( `${inputData.fileDate}_${data.fileName}` );
391
+ name.push( `${data.fileName}` );
392
+ regionalKey.push( `${inputData.fileDate}_${data.fileName}.${data.fileType}` );
393
+ file = await getObject( fetchData );
394
+ if ( file.statusCode == 404 ) {
395
+ logger.info( 'File not available in bucket ' + `${JSON.stringify( file )}` );
396
+ sendMailCount += 1;
397
+ } else {
398
+ data.email.forEach( ( email ) => {
399
+ let UserIndex = UserattachmentDetails.findIndex( ( user ) => user.email == email );
400
+ if ( UserIndex == -1 ) {
401
+ UserattachmentDetails.push( {
402
+ email: email,
403
+ attachments: [
404
+ {
405
+ filename: `${inputData.fileDate}_${data.fileName}.${data.fileType}`,
406
+ content: file.Body,
407
+ contentType: file.ContentType,
408
+ },
409
+ ],
410
+ } );
411
+ } else {
412
+ UserattachmentDetails[UserIndex].attachments.push( {
413
+ filename: `${req.body.fileDate}_${data.fileName}.${data.fileType}`,
414
+ content: file.Body,
415
+ contentType: file.ContentType,
416
+ } );
417
+ }
418
+ } );
419
+ }
420
+ }
421
+ UserattachmentDetails.forEach( async ( userattach ) => {
422
+ await sendEmail( userattach.email, subject, messageBody, userattach.attachments, appConfig.cloud.aws.ses.reportEmail ).then( ( response ) => {
423
+ result.push( response );
424
+ } );
425
+ } );
426
+ if ( sendMailCount == getReportList.length ) {
427
+ return res.sendError( 'File not available in bucket', 204 );
428
+ }
429
+ let user = await findOneUser( { _id: req.user._id, isActive: true } );
430
+ if ( !user ) {
431
+ return res.sendError( 'Forbiddon to Access', 403 );
432
+ }
433
+ const sqsProduceQueue = {
434
+ QueueUrl: `${appConfig.cloud.aws.sqs.url}${appConfig.cloud.aws.sqs.sendReport}`,
435
+ MessageBody: JSON.stringify( {
436
+ file_date: inputData.fileDate,
437
+ client_name: req.report?.reportConfigs?.reportName,
438
+ client_id: Number( inputData.clientId ),
439
+ file_name: regionalKey,
440
+ report_name: name,
441
+ } ),
442
+ };
443
+ const sqsQueue = await sendMessageToQueue( sqsProduceQueue.QueueUrl, sqsProduceQueue.MessageBody );
444
+ if ( sqsQueue.MessageId ) {
445
+ logger.info( 'successfully Pushed to SQS ' + `${JSON.stringify( sqsQueue )}`, 'Send Report' );
446
+ const log = {
447
+ userName: user.userName,
448
+ Date: new Date(),
449
+ userId: user._id,
450
+ logType: 'report',
451
+ logSubType: 'sendReport',
452
+ logData: {
453
+ fileDate: inputData.fileDate,
454
+ reportName: req.report?.reportConfigs?.reportName,
455
+ clientId: getReportList[0].clientId,
456
+ },
457
+ };
458
+ logger.info( { log: log } );
459
+ // await insertOpenSearchData( 'tango-retail-activity-logs', log );
460
+ return res.sendSuccess( { result: 'Report has been Sent Successfully' } );
461
+ } else {
462
+ return res.sendError( ' Message does not Sent & Report has been Sent Successfully', 500 );
463
+ }
464
+ } catch ( error ) {
465
+ logger.error( { error: error, message: req.body, function: 'sendReport' } );
466
+
467
+ return res.sendError( error, 500 );
468
+ }
469
+ }
470
+
471
+ export async function getReportLog( req, res ) {
472
+ try {
473
+ const inputData = req.body;
474
+ const log = await getOpenSearchData( 'tango-retail-activity-logs',
475
+ {
476
+ 'size': 100,
477
+ 'query': {
478
+ 'bool': {
479
+ 'must': [
480
+ {
481
+ 'term': {
482
+ 'logType.keyword': 'report',
483
+ },
484
+ },
485
+ {
486
+ 'term': {
487
+ 'logData.fileDate.keyword': inputData.fileDate,
488
+ },
489
+ },
490
+
491
+ ],
492
+
493
+ },
494
+ },
495
+
496
+ } );
497
+ return res.sendSuccess( { result: log } );
498
+ } catch ( error ) {
499
+ logger.info( { error: error } );
500
+ return res.sendError( error, 500 );
501
+ }
502
+ }
503
+ export async function updateBasePrice( req, res ) {
504
+ try {
505
+ const inputData = req.body;
506
+ let count=0;
507
+ let increment = 0;
508
+ for ( let i =0; i< inputData.length; i++ ) {
509
+ increment = increment+1;
510
+ // await basePricingModel.create( {
511
+ // 'clientId': inputData[i].clientId,
512
+ // 'standard': [
513
+ // {
514
+ // 'productName': 'tangoTraffic',
515
+
516
+ // },
517
+
518
+ // ],
519
+ // 'step': [
520
+ // {
521
+ // 'productName': 'tangoTraffic',
522
+
523
+ // },
524
+
525
+ // ],
526
+ // } );
527
+ const query = {
528
+ 'clientId': inputData[i].clientId,
529
+ 'standard.productName': { $exists: true },
530
+ };
531
+ const record={
532
+ 'clientId': inputData[i].clientId,
533
+ 'standard.$[elem].basePrice': inputData[i].pricePerStore,
534
+ 'standard.$[elem].negotiatePrice': inputData[i].pricePerStore,
535
+ 'step.$[elem].basePrice': inputData[i].pricePerStore,
536
+ 'step.$[elem].negotiatePrice': inputData[i].pricePerStore,
537
+ 'step.$[elem].storeRange': '1-100',
538
+ };
539
+ const result = await updateOneBasePriceModel( query, record );
540
+ if ( result ) {
541
+ count= count+1;
542
+ }
543
+ console.log( result, '.data' );
544
+ }
545
+ if ( increment == inputData.length ) {
546
+ res.sendSuccess( { result: `The given ${count } recoeds updated` } );
547
+ }
548
+ } catch ( error ) {
549
+ logger.error( { error: error, message: req.params, function: 'updateBasePrice' } );
550
+ return res.sendError( error, 500 );
551
+ }
552
+ }
553
+
554
+ export async function sendEmail( toEmail, mailSubject, htmlBody, attachment, sourceEmail ) {
555
+ return new Promise( async ( resolve, reject ) => {
556
+ try {
557
+ const ses = new aws.SES( {
558
+ apiVersion: 'latest',
559
+ region: appConfig.cloud.aws.region,
560
+ // accessKeyId: process.env.AWS_ACCESS_KEY_ID_SES,
561
+ // secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY_SES,
562
+ } );
563
+
564
+ let Attachment = [];
565
+ if ( attachment ) {
566
+ Attachment = attachment;
567
+ }
568
+ try {
569
+ let transporter = nodemailer.createTransport( {
570
+ SES: { ses, aws },
571
+ } );
572
+ let mailOptions = {
573
+ from: sourceEmail,
574
+ to: toEmail,
575
+ subject: mailSubject,
576
+ html: htmlBody,
577
+ attachments: Attachment,
578
+ };
579
+ transporter.sendMail( mailOptions )
580
+ .then( ( data ) => {
581
+ resolve( data );
582
+ } )
583
+ .catch( ( err ) => {
584
+ logger.error( err, 'Node Mailer EXCEPTION' );
585
+ reject( err );
586
+ } );
587
+ } catch ( error ) {
588
+ console.error( 'Error sending email:', error );
589
+ throw error;
590
+ }
591
+ } catch ( error ) {
592
+ console.error( 'Error sending email:', error );
593
+ throw error;
594
+ }
595
+ } );
596
+ }
597
+
598
+
@@ -0,0 +1,227 @@
1
+ import j2s from 'joi-to-swagger';
2
+
3
+ import { clientListTableSchema, deleteReportSchemaParam, downloadReportSchema, generateReportSchema, getReportListSchema, getReportLogSchema, getReportSchemaParam, sendReportSchema, uploadManualReportSchema, uploadManualReportSchema2 } from '../dtos/report.dtos.js';
4
+
5
+ export const reportDocs = {
6
+
7
+ '/v3/report/client-list-table': {
8
+ post: {
9
+ tags: [ 'Report' ],
10
+ description: `Get list of client with date range`,
11
+ operationId: 'client-list-table',
12
+ parameters: {},
13
+ requestBody: {
14
+ content: {
15
+ 'application/json': {
16
+ schema: j2s( clientListTableSchema ).swagger,
17
+ },
18
+ },
19
+ },
20
+ responses: {
21
+ 200: { description: 'Get user permission successfully' },
22
+ 401: { description: 'Unauthorized User' },
23
+ 422: { description: 'Field Error' },
24
+ 500: { description: 'Server Error' },
25
+ 204: { description: 'Not Found' },
26
+ },
27
+ },
28
+ },
29
+
30
+
31
+ '/v3/report/generate-report': {
32
+ post: {
33
+ tags: [ 'Report' ],
34
+ description: `Push message to Qeueue to Generate Report Manually`,
35
+ operationId: 'generate-report',
36
+ parameters: {},
37
+ requestBody: {
38
+ content: {
39
+ 'application/json': {
40
+ schema: j2s( generateReportSchema ).swagger,
41
+ },
42
+ },
43
+ },
44
+ responses: {
45
+ 200: { description: 'Report Genrate Initiated Successfully' },
46
+ 401: { description: 'Unauthorized User' },
47
+ 422: { description: 'Field Error' },
48
+ 500: { description: 'Server Error' },
49
+ 204: { description: 'Not Found' },
50
+ },
51
+ },
52
+ },
53
+
54
+ '/v3/report/upload-manual-report': {
55
+ post: {
56
+ tags: [ 'Report' ],
57
+ description: `upload a Report`,
58
+ operationId: 'upload-manual-report',
59
+ parameters: {},
60
+ requestBody: {
61
+ content: {
62
+ 'multipart/form-data': {
63
+ schema: j2s( uploadManualReportSchema2 ).swagger,
64
+ },
65
+ 'application/json': {
66
+ schema: j2s( uploadManualReportSchema ).swagger,
67
+ },
68
+ },
69
+ },
70
+ responses: {
71
+ 200: { description: 'Report Uploaded Successfully' },
72
+ 401: { description: 'Unauthorized User' },
73
+ 422: { description: 'Field Error' },
74
+ 500: { description: 'Server Error' },
75
+ 204: { description: 'Not Found' },
76
+ },
77
+ },
78
+ },
79
+
80
+ '/v3/report/get-report-list': {
81
+ get: {
82
+ tags: [ 'Report' ],
83
+ description: 'Get List of Reports',
84
+ operationId: 'get-report-list',
85
+ parameters: [
86
+ {
87
+ in: 'query',
88
+ name: 'fileDate',
89
+ scema: j2s( getReportListSchema ).swagger,
90
+ require: true,
91
+ },
92
+ {
93
+ in: 'query',
94
+ name: 'clientId',
95
+ scema: j2s( getReportListSchema ).swagger,
96
+ require: true,
97
+ },
98
+ ],
99
+ responses: {
100
+ 200: { description: 'Received successfully' },
101
+ 401: { description: 'Unauthorized User' },
102
+ 422: { description: 'Field Error' },
103
+ 500: { description: 'Server Error' },
104
+ 204: { description: 'Not Found' },
105
+ },
106
+ },
107
+ },
108
+
109
+ '/v3/report/download-report': {
110
+ get: {
111
+ tags: [ 'Report' ],
112
+ description: 'Download a Report',
113
+ operationId: 'download-report',
114
+ parameters: [
115
+ {
116
+ in: 'query',
117
+ name: 'filePath',
118
+ scema: j2s( downloadReportSchema ).swagger,
119
+ require: true,
120
+ },
121
+ ],
122
+ responses: {
123
+ 200: { description: 'Report has been Successfully' },
124
+ 401: { description: 'Unauthorized User' },
125
+ 422: { description: 'Field Error' },
126
+ 500: { description: 'Server Error' },
127
+ 204: { description: 'Not Found' },
128
+ },
129
+ },
130
+ },
131
+
132
+ '/v3/report/send-report': {
133
+ post: {
134
+ tags: [ 'Report' ],
135
+ description: `send report a client`,
136
+ operationId: 'send-report',
137
+ parameters: {},
138
+ requestBody: {
139
+ content: {
140
+ 'application/json': {
141
+ schema: j2s( sendReportSchema ).swagger,
142
+ },
143
+ },
144
+ },
145
+ responses: {
146
+ 200: { description: 'Report has been Sent Successfully' },
147
+ 401: { description: 'Unauthorized User' },
148
+ 422: { description: 'Field Error' },
149
+ 500: { description: 'Server Error' },
150
+ 204: { description: 'Not Found' },
151
+ },
152
+ },
153
+ },
154
+
155
+ '/v3/report/get-report-log': {
156
+ post: {
157
+ tags: [ 'Report' ],
158
+ description: `get report log`,
159
+ operationId: 'get-report-log',
160
+ parameters: {},
161
+ requestBody: {
162
+ content: {
163
+ 'application/json': {
164
+ schema: j2s( getReportLogSchema ).swagger,
165
+ },
166
+ },
167
+ },
168
+ responses: {
169
+ 200: { description: 'Successful' },
170
+ 401: { description: 'Unauthorized User' },
171
+ 422: { description: 'Field Error' },
172
+ 500: { description: 'Server Error' },
173
+ 204: { description: 'Not Found' },
174
+ },
175
+ },
176
+ },
177
+
178
+ '/v3/report/delete-report/{id}': {
179
+ get: {
180
+ tags: [ 'Report' ],
181
+ description: 'delete report by _id',
182
+ operationId: 'delete-report/{id}',
183
+ parameters: [
184
+ {
185
+ in: 'path',
186
+ name: 'id',
187
+ required: true,
188
+ description: 'The _id of the report.',
189
+ scema: j2s( deleteReportSchemaParam ).swagger,
190
+ },
191
+ ],
192
+ responses: {
193
+ 200: { description: 'Success' },
194
+ 401: { description: 'Unauthorized User' },
195
+ 422: { description: 'Field Error' },
196
+ 500: { description: 'Server Error' },
197
+ 204: { description: 'Not Found' },
198
+ },
199
+ },
200
+ },
201
+
202
+ '/v3/report/get-client-reports/{id}': {
203
+ get: {
204
+ tags: [ 'Report' ],
205
+ description: 'Get list of reports by client wise',
206
+ operationId: 'get-client-reports/{id}',
207
+ parameters: [
208
+ {
209
+ in: 'path',
210
+ name: 'id',
211
+ required: true,
212
+ description: 'client Id',
213
+ scema: j2s( getReportSchemaParam ).swagger,
214
+ },
215
+ ],
216
+ responses: {
217
+ 200: { description: 'Success' },
218
+ 401: { description: 'Unauthorized User' },
219
+ 422: { description: 'Field Error' },
220
+ 500: { description: 'Server Error' },
221
+ 204: { description: 'Not Found' },
222
+ },
223
+ },
224
+ },
225
+
226
+ };
227
+
@@ -55,3 +55,96 @@ export const updateReportValid = {
55
55
  params: updateReportSchemaParam,
56
56
 
57
57
  };
58
+
59
+ export const deleteReportSchemaParam = joi.object( {
60
+ id: joi.string().required(),
61
+ } );
62
+
63
+ export const deleteReportValid = {
64
+ params: deleteReportSchemaParam,
65
+
66
+ };
67
+
68
+
69
+ export const clientListTableSchema = joi.object( {
70
+ fromDate: joi.string().required(),
71
+ toDate: joi.string().required(),
72
+ clientId: joi.array().required(),
73
+ limit: joi.number().optional(),
74
+ offset: joi.number().optional(),
75
+ searchValue: joi.string().optional(),
76
+ filterByType: joi.array().optional(),
77
+ sortColumnName: joi.string().optional(),
78
+ sortBy: joi.number().optional(),
79
+ } );
80
+
81
+ export const clientListTableValid = {
82
+ body: clientListTableSchema,
83
+
84
+ };
85
+
86
+ export const generateReportSchema = joi.object( {
87
+ fileDate: joi.string().required(),
88
+ clientId: joi.string().required(),
89
+ } );
90
+
91
+ export const generateReportValid = {
92
+ body: generateReportSchema,
93
+
94
+ };
95
+
96
+ export const uploadManualReportSchema = joi.object( {
97
+ fileDate: joi.string().required(),
98
+ clientId: joi.string().required(),
99
+ fileName: joi.string().required(),
100
+ file: joi.string().required(),
101
+ } );
102
+
103
+ export const uploadManualReportSchema2 = joi.object( {
104
+ file: joi.binary().required(),
105
+ } );
106
+
107
+ export const uploadManualReportValid = {
108
+ body: uploadManualReportSchema,
109
+ };
110
+
111
+ export const getReportListSchema = joi.object( {
112
+ fileDate: joi.string().required(),
113
+ clientId: joi.string().required(),
114
+ } );
115
+
116
+ export const getReportListValid = {
117
+ query: getReportListSchema,
118
+
119
+ };
120
+
121
+ export const downloadReportSchema = joi.object( {
122
+ filePath: joi.string().required(),
123
+ } );
124
+
125
+ export const downloadReportValid = {
126
+ query: downloadReportSchema,
127
+
128
+ };
129
+
130
+ export const sendReportSchema = joi.object( {
131
+ fileDate: joi.string().required(),
132
+ clientId: joi.string().required(),
133
+ } );
134
+
135
+ export const sendReportValid = {
136
+ body: sendReportSchema,
137
+
138
+ };
139
+
140
+ export const getReportLogSchema = joi.object( {
141
+ fileDate: joi.string().required(),
142
+ clientId: joi.string().required(),
143
+ } );
144
+
145
+ export const getReportLogValid = {
146
+ body: getReportLogSchema,
147
+
148
+ };
149
+
150
+
@@ -1,8 +1,9 @@
1
1
 
2
2
  import express from 'express';
3
3
  import { authorize, isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
4
- import { addReportValid, getReportValid, getSingleReportValid, updateReportValid } from '../dtos/report.dtos.js';
5
- import { createReport, getReport, getSingleReport, updateReport } from '../controllers/report.controllers.js';
4
+ import { addReportValid, clientListTableValid, deleteReportValid, downloadReportValid, generateReportValid, getReportListValid, getReportLogValid, getReportValid, getSingleReportValid, sendReportValid, updateReportValid } from '../dtos/report.dtos.js';
5
+ import { clientReportList, createReport, deleteReport, dowloadReport, generateReport, getReport, getReportList, getReportLog, getSingleReport, sendReport, updateBasePrice, updateReport, uploadManualReport } from '../controllers/report.controllers.js';
6
+ import { isFileExist, isFolderExist, isReportEnable } from '../validations/report.validations.js';
6
7
 
7
8
  export const reportRouter = express.Router();
8
9
 
@@ -15,10 +16,61 @@ reportRouter.get( '/get-client-reports/:id', isAllowedSessionHandler, authorize(
15
16
  reportRouter.get( '/get-report/:id', isAllowedSessionHandler, authorize( { userType: [ 'tango' ], access: [
16
17
  { featureName: 'settings', name: 'configuration', permissions: [ 'isView' ] },
17
18
  ] } ), validate( getSingleReportValid ), getSingleReport );
19
+
18
20
  reportRouter.put( '/update-report/:id', isAllowedSessionHandler, authorize( { userType: [ 'tango' ], access: [
19
21
  { featureName: 'settings', name: 'configuration', permissions: [ 'isEdit' ] },
20
22
  ] } ), validate( updateReportValid ), updateReport );
21
23
 
24
+ reportRouter.get( '/delete-report/:id', isAllowedSessionHandler, authorize( { userType: [ 'tango' ], access: [
25
+ { featureName: 'settings', name: 'configuration', permissions: [ 'isEdit' ] },
26
+ ] } ), validate( deleteReportValid ), deleteReport );
27
+
28
+
29
+ // admin report's overview
30
+ reportRouter.post( '/client-list-table', isAllowedSessionHandler,
31
+ authorize( { userType: [ 'tango' ], access: [
32
+ { featureName: 'settings', name: 'configuration', permissions: [ 'isView' ] },
33
+ ] } ),
34
+ validate( clientListTableValid ), clientReportList );
35
+
36
+ reportRouter.post( '/generate-report', isAllowedSessionHandler,
37
+ authorize( { userType: [ 'tango' ], access: [
38
+ { featureName: 'settings', name: 'configuration', permissions: [ 'isEdit' ] },
39
+ ] } ),
40
+ validate( generateReportValid ), isReportEnable, generateReport );
41
+
42
+ reportRouter.post( '/upload-manual-report', isAllowedSessionHandler,
43
+ authorize( { userType: [ 'tango' ], access: [
44
+ { featureName: 'settings', name: 'configuration', permissions: [ 'isEdit' ] },
45
+ ] } ),
46
+ isReportEnable, uploadManualReport );
47
+
48
+ reportRouter.get( '/get-report-list', isAllowedSessionHandler,
49
+ authorize( { userType: [ 'tango' ], access: [
50
+ { featureName: 'settings', name: 'configuration', permissions: [ 'isView' ] },
51
+ ] } ),
52
+ validate( getReportListValid ), isReportEnable, isFolderExist, getReportList );
53
+
54
+ reportRouter.get( '/download-report', isAllowedSessionHandler,
55
+ authorize( { userType: [ 'tango' ], access: [
56
+ { featureName: 'settings', name: 'configuration', permissions: [ 'isEdit' ] },
57
+ ] } ),
58
+ validate( downloadReportValid ), isFileExist, dowloadReport );
59
+
60
+ reportRouter.post( '/send-report', isAllowedSessionHandler,
61
+ authorize( { userType: [ 'tango' ], access: [
62
+ { featureName: 'settings', name: 'configuration', permissions: [ 'isEdit' ] },
63
+ ] } ),
64
+ validate( sendReportValid ), isReportEnable, sendReport );
65
+
66
+ reportRouter.post( '/get-report-log', isAllowedSessionHandler,
67
+ authorize( { userType: [ 'tango' ], access: [
68
+ { featureName: 'settings', name: 'configuration', permissions: [ 'isView' ] },
69
+ ] } ),
70
+ validate( getReportLogValid ), getReportLog );
71
+
72
+ reportRouter.post( '/invoice', updateBasePrice );
73
+
22
74
  export default reportRouter;
23
75
 
24
76
 
@@ -0,0 +1,6 @@
1
+ import basePricingModel from 'tango-api-schema/schema/basePricing.model.js';
2
+
3
+
4
+ export function updateOneBasePriceModel( query, record ) {
5
+ return basePricingModel.updateOne( query, { $set: record }, { arrayFilters: [ { 'elem.productName': 'tangoTraffic' } ], upsert: true } );
6
+ }
@@ -0,0 +1,10 @@
1
+ import clientModel from 'tango-api-schema/schema/client.model.js';
2
+
3
+
4
+ export function aggregateClient( query ) {
5
+ return clientModel.aggregate( query, { collation: { locale: 'en', strength: 2 } } );
6
+ }
7
+
8
+ export function findOneClient( query, fields ) {
9
+ return clientModel.findOne( query, fields );
10
+ }
@@ -17,7 +17,9 @@ export function reportGet( { clientId } ) {
17
17
  },
18
18
  {
19
19
  'fileName': 1,
20
- '_id': 1,
20
+ 'fileType': 1,
21
+ 'email': 1,
22
+ 'clientId': 1,
21
23
  } );
22
24
  }
23
25
 
@@ -37,3 +39,15 @@ export function reportUpdate( { _id, fileType, email, clientId } ) {
37
39
  },
38
40
  } );
39
41
  }
42
+
43
+ export function findOneReport( query ) {
44
+ return reportModel.findOne( query );
45
+ }
46
+
47
+ export function findReport( query, fields ) {
48
+ return reportModel.find( query, fields );
49
+ }
50
+
51
+ export function deleteOneReport( query ) {
52
+ return reportModel.deleteOne( query );
53
+ }
@@ -3,3 +3,7 @@ import userModel from 'tango-api-schema/schema/user.model.js';
3
3
  export function getUserNameEmailById( objectId ) {
4
4
  return userModel.findOne( { _id: objectId }, { userName: 1, email: 1, _id: 0 } );
5
5
  }
6
+
7
+ export async function findOneUser( query, fields ) {
8
+ return userModel.findOne( query, fields );
9
+ }
@@ -0,0 +1,57 @@
1
+ import { appConfig, checkFileExist, logger } from 'tango-app-api-middleware';
2
+ import { findOneClient } from '../service/client.service.js';
3
+
4
+
5
+ export async function isReportEnable( req, res, next ) {
6
+ try {
7
+ const inputData = req.method !== 'GET'? req.body: req.query;
8
+ const getReportName = await findOneClient( { 'clientId': inputData.clientId, 'status': 'active', 'reportConfigs.report': true }, { '_id': 0, 'reportConfigs.reportName': 1, 'clientName': 1 } );
9
+ if ( !getReportName ) {
10
+ return res.sendError( 'reportName is not there in the client', 204 );
11
+ }
12
+ req.report = getReportName;
13
+ return next();
14
+ } catch ( error ) {
15
+ logger.error( { error: error, message: req.body, function: 'isReportEnable' } );
16
+ return res.sendError( error, 500 );
17
+ }
18
+ }
19
+
20
+ export async function isFolderExist( req, res, next ) {
21
+ try {
22
+ const inputData = req.query;
23
+ const fetchData = {
24
+ Bucket: appConfig.cloud.aws.bucket.reportBucket,
25
+ Key: `${inputData.fileDate}/${req.report?.reportConfigs?.reportName}/`,
26
+ };
27
+ logger.info( { fetchData: fetchData, report: req.report?.reportConfigs?.reportName } );
28
+ const isExist = await checkFileExist( fetchData );
29
+ if ( isExist ) {
30
+ return next();
31
+ } else {
32
+ return res.sendError( 'Folder does not Exists', 204 );
33
+ }
34
+ } catch ( error ) {
35
+ logger.error( { error: error, message: req.query, function: 'isFolderExist' } );
36
+ return res.sendError( error, 500 );
37
+ }
38
+ }
39
+
40
+ export async function isFileExist( req, res, next ) {
41
+ try {
42
+ const inputData = req.query;
43
+ const fetchData = {
44
+ Bucket: appConfig.cloud.aws.bucket.reportBucket,
45
+ Key: inputData.Key,
46
+ };
47
+ const isExist = await checkFileExist( fetchData );
48
+ if ( isExist ) {
49
+ return next();
50
+ } else {
51
+ return res.sendError( 'Folder does not Exists', 204 );
52
+ }
53
+ } catch ( error ) {
54
+ logger.error( { error: error, message: req.query, function: 'isFolderExist' } );
55
+ return res.sendError( error, 500 );
56
+ }
57
+ }