tango-app-api-audit 1.0.4 → 1.0.6

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
@@ -4,8 +4,10 @@ import { auditRouter } from './src/routes/audit.routes.js';
4
4
  import { auditMetricsRouter } from './src/routes/auditMetrics.routes.js';
5
5
  import { auditMetricsDocs } from './src/docs/auditMetrics.docs.js';
6
6
  import { auditDocs } from './src/docs/audit.docs.js';
7
+ import clientRouter from './src/routes/client.routes.js';
8
+ import { clientDocs } from './src/docs/client.docs.js';
7
9
 
8
10
 
9
- export { auditRouter, auditMetricsRouter, auditMetricsDocs, auditDocs };
11
+ export { auditRouter, auditMetricsRouter, auditMetricsDocs, auditDocs, clientRouter, clientDocs };
10
12
 
11
13
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-audit",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "audit & audit metrics apis",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -1,12 +1,12 @@
1
- import { chunkArray, getJsonFileData, getUTC, listFileByPath, listFileWithoutLimit, signedUrl } from 'tango-app-api-middleware';
2
- import { aggregateUserAudit, createUserAudit } from '../service/userAudit.service.js';
1
+ import { chunkArray, getJsonFileData, getOpenSearchData, getUTC, insertOpenSearchData, listFileByPath, listFileWithoutLimit, signedUrl } from 'tango-app-api-middleware';
2
+ import { aggregateUserAudit, createUserAudit, findOneUserAudit, updateOneUserAudit } from '../service/userAudit.service.js';
3
3
  import { aggregateAssignAudit, updateOneAssignAudit } from '../service/assignAudit.service.js';
4
4
  import { findOneUser } from '../service/user.service.js';
5
5
  import dayjs from 'dayjs';
6
6
  import { aggregateClient } from '../service/client.service.js';
7
7
  import { logger } from 'tango-app-api-middleware';
8
8
  import { sqsReceive } from 'tango-app-api-middleware/src/utils/aws/sqs.js';
9
- import { createStoreAudit } from '../service/storeAudit.service.js';
9
+ import { createStoreAudit, updateOneStoreAudit } from '../service/storeAudit.service.js';
10
10
 
11
11
  export async function getAuditFile( req, res ) {
12
12
  try {
@@ -742,3 +742,139 @@ export async function getQueueDetailList( req, res ) {
742
742
  return res.sendError( error );
743
743
  }
744
744
  }
745
+
746
+ export async function saveDraft( req, res ) {
747
+ try {
748
+ const openSearch = JSON.parse( process.env.OPENSEARCH );
749
+ const inputData = req.body;
750
+ inputData.userId = req.user._id;
751
+ const getUserAuditData = await findOneUserAudit( { _id: inputData.auditId } );
752
+ if ( !getUserAuditData ) {
753
+ return res.sendError( 'No Data Found', 204 );
754
+ }
755
+ if ( getUserAuditData.auditStatus == 'skipped' ) {
756
+ return res.sendError( 'File Assigned to Someone else', 203 );
757
+ }
758
+
759
+ const userQuery = {
760
+ _id: inputData.auditId,
761
+ };
762
+ let userRecord ={
763
+ isDraft: true,
764
+ auditStatus: 'drafted',
765
+ };
766
+ const storeQuery ={
767
+ storeId: inputData.storeId,
768
+ fileDate: inputData.fileDate,
769
+ };
770
+
771
+ let storeRecord ={
772
+ status: 'drafted',
773
+ };
774
+
775
+ if ( getUserAuditData?.startTime ) {
776
+ const isoDate = getUserAuditData.startTime;
777
+ const currentTime = dayjs();
778
+ const isoDateTime = dayjs( isoDate );
779
+ const timeDifferenceInMinutes = currentTime.diff( isoDateTime, 'seconds' );
780
+
781
+ userRecord.timeSpent=timeDifferenceInMinutes;
782
+
783
+ storeRecord.timeSpent = timeDifferenceInMinutes;
784
+ }
785
+
786
+ if ( inputData.userCommands ) {
787
+ userRecord.userCommands = inputData.userCommands;
788
+ const logData = {
789
+ userId: req.user._id,
790
+ userName: req.user.userName,
791
+ logType: 'audit',
792
+ logSubType: 'auditDraft',
793
+ logData: {
794
+ auditType: inputData.auditType,
795
+ storeId: inputData.storeId,
796
+ clientName: inputData.queueName,
797
+ beforeCount: inputData.totalCount,
798
+ afterCount: inputData.customerCount,
799
+ auditId: inputData.auditId,
800
+ },
801
+ };
802
+ await insertOpenSearchData( openSearch.activityLog, logData );
803
+ logger.info( { logData: logData } ); // need to insert to os
804
+ }
805
+ await insertOpenSearchData( openSearch.auditLog, inputData );
806
+ await updateOneUserAudit( userQuery, userRecord );
807
+ await updateOneStoreAudit( storeQuery, storeRecord );
808
+ return res.sendSuccess( { result: 'The file has been drafted successfully' } );
809
+ } catch ( error ) {
810
+ const err = error.message || 'Internal Server Error';
811
+ logger.error( { error: error, message: req.body, function: 'saveDraft' } );
812
+ return res.sendError( err, 500 );
813
+ }
814
+ }
815
+
816
+ export async function getDraftedData( req, res ) {
817
+ try {
818
+ const openSearch = JSON.parse( process.env.OPENSEARCH );
819
+ const inputData = req.query;
820
+ logger.info( { inputData: inputData } );
821
+ const getLog = await getOpenSearchData( openSearch.auditLog,
822
+ {
823
+ 'size': 1,
824
+ 'query': {
825
+ 'bool': {
826
+ 'must': [
827
+ {
828
+ 'term': {
829
+ 'fileDate.keyword': inputData.fileDate,
830
+ },
831
+ },
832
+ {
833
+ 'term': {
834
+ 'storeId.keyword': inputData.storeId,
835
+ },
836
+ },
837
+ {
838
+ 'term': {
839
+ 'userId.keyword': req.user._id,
840
+ },
841
+ },
842
+
843
+ ],
844
+
845
+ },
846
+ },
847
+
848
+ 'sort': [
849
+ {
850
+ 'timestamp': {
851
+ 'order': 'desc',
852
+ },
853
+ },
854
+ ],
855
+
856
+ } );
857
+ const result = getLog?.body?.hits?.hits;
858
+ if ( result.length ==0 ) {
859
+ return res.sendError( 'No Data Found', 500 );
860
+ }
861
+ return res.sendSuccess( { result: result } );
862
+ } catch ( error ) {
863
+ const err = error.message || 'Internal Server Error';
864
+ logger.error( { error: error, message: req.query, function: 'getDraftedData' } );
865
+ return res.sendError( err, 500 );
866
+ }
867
+ }
868
+
869
+ export async function save( req, res ) {
870
+ try {
871
+ const inputData = req.body;
872
+ return res.sendSuccess( { result: inputData } );
873
+ } catch ( error ) {
874
+ const err= error.message || 'Internal Server Error';
875
+ logger.error( { error: error, message: req.body, function: 'save' } );
876
+ return res.sendError( err );
877
+ }
878
+ }
879
+
880
+
@@ -245,8 +245,13 @@ export async function clientMetrics( req, res ) {
245
245
  let filter = [
246
246
  { fileDateISO: { $gte: dateRange.start } },
247
247
  { fileDateISO: { $lte: dateRange.end } },
248
- { clientId: { $in: inputData.filterByClient } },
248
+
249
249
  ];
250
+ if ( inputData?.filterByClient?.length> 0 ) {
251
+ filter.push(
252
+ { clientId: { $in: inputData.filterByClient } },
253
+ );
254
+ }
250
255
  const query = [
251
256
  {
252
257
  $match: {
@@ -374,10 +379,11 @@ export async function clientMetrics( req, res ) {
374
379
  query.push(
375
380
  {
376
381
  $project: {
382
+ '_id': 0,
377
383
  'File Date': '$fileDate',
378
384
  'Client Name': '$clientName',
379
385
  'Client Id': '$clientId',
380
- 'Completed Percentage': '$completionPercentage' + ' %',
386
+ 'Completed Percentage': '$completionPercentage',
381
387
  'Installed Stores': '$installedStore',
382
388
  'Audit Stores': '$totalFilesCount',
383
389
  'Inprogress Stores': '$inprogressStoresCount',
@@ -386,9 +392,6 @@ export async function clientMetrics( req, res ) {
386
392
  'Status': '$clientStatus',
387
393
  },
388
394
  },
389
- {
390
-
391
- },
392
395
  );
393
396
  }
394
397
 
@@ -0,0 +1,35 @@
1
+ import { logger } from 'tango-app-api-middleware';
2
+ import { aggregateClient } from '../service/client.service.js';
3
+
4
+ export async function clients( req, res ) {
5
+ try {
6
+ const inputData = req.query;
7
+ const query =[
8
+ {
9
+ $match: {
10
+ 'status': { $in: [ 'active', 'hold' ] },
11
+ 'auditConfigs.audit': true,
12
+ },
13
+ },
14
+ ];
15
+
16
+ const count = await aggregateClient( query );
17
+ if ( count.length == 0 ) {
18
+ return res.sendError( 'No Data Found', 204 );
19
+ }
20
+ if ( inputData.limit ) {
21
+ const offset = inputData.offset ? ( inputData.offset - 1 ) * inputData.limit : 0;
22
+ query.push(
23
+ { $skip: offset },
24
+ { $limit: limit },
25
+ );
26
+ }
27
+ const getAuditClients = await aggregateClient( query );
28
+
29
+ return res.sendSuccess( { result: getAuditClients, count: count.length } );
30
+ } catch ( error ) {
31
+ const err = error.message || 'Internal Server Error';
32
+ logger.info( { error: error, messgae: req.query, function: 'clients' } );
33
+ return res.sendError( err, 500 );
34
+ }
35
+ }
@@ -1,4 +1,4 @@
1
- import { getFileSchema } from '../dtos/audit.dtos.js';
1
+ import { getDraftedDataSchema, getFileSchema, saveDraftSchema, saveSchema } from '../dtos/audit.dtos.js';
2
2
  import j2s from 'joi-to-swagger';
3
3
 
4
4
  export const auditDocs = {
@@ -28,7 +28,79 @@ export const auditDocs = {
28
28
  },
29
29
  ],
30
30
  responses: {
31
- 200: { description: 'Successfully' },
31
+ 200: { description: 'Successful' },
32
+ 401: { description: 'Unauthorized User' },
33
+ 422: { description: 'Field Error' },
34
+ 500: { description: 'Server Error' },
35
+ 204: { description: 'Not Found' },
36
+ },
37
+ },
38
+ },
39
+ '/v3/audit/save-draft': {
40
+ post: {
41
+ tags: [ 'Audit' ],
42
+ description: `save audited file. which is save in the mongo db`,
43
+ operationId: 'save-draft',
44
+ parameters: {},
45
+ requestBody: {
46
+ content: {
47
+ 'application/json': {
48
+ schema: j2s( saveDraftSchema ).swagger,
49
+ },
50
+ },
51
+ },
52
+ responses: {
53
+ 200: { description: 'Successful' },
54
+ 401: { description: 'Unauthorized User' },
55
+ 422: { description: 'Field Error' },
56
+ 500: { description: 'Server Error' },
57
+ 204: { description: 'Not Found' },
58
+ },
59
+ },
60
+ },
61
+ '/v3/audit/get-drafted-data': {
62
+ get: {
63
+ tags: [ 'Audit' ],
64
+ description: 'Get a saved file via DB',
65
+ operationId: 'get-drafted-data',
66
+ parameters: [
67
+ {
68
+ in: 'query',
69
+ name: 'fileDate',
70
+ scema: j2s( getDraftedDataSchema ).swagger,
71
+ require: true,
72
+ },
73
+ {
74
+ in: 'query',
75
+ name: 'storeId',
76
+ scema: j2s( getDraftedDataSchema ).swagger,
77
+ require: false,
78
+ },
79
+ ],
80
+ responses: {
81
+ 200: { description: 'Successful' },
82
+ 401: { description: 'Unauthorized User' },
83
+ 422: { description: 'Field Error' },
84
+ 500: { description: 'Server Error' },
85
+ 204: { description: 'Not Found' },
86
+ },
87
+ },
88
+ },
89
+ '/v3/audit/save': {
90
+ post: {
91
+ tags: [ 'Audit' ],
92
+ description: `submit the audit file. which is store in the bucket in the json format`,
93
+ operationId: 'save',
94
+ parameters: {},
95
+ requestBody: {
96
+ content: {
97
+ 'application/json': {
98
+ schema: j2s( saveSchema ).swagger,
99
+ },
100
+ },
101
+ },
102
+ responses: {
103
+ 200: { description: 'The Audited file has been submited successfully ' },
32
104
  401: { description: 'Unauthorized User' },
33
105
  422: { description: 'Field Error' },
34
106
  500: { description: 'Server Error' },
@@ -0,0 +1,33 @@
1
+ import j2s from 'joi-to-swagger';
2
+ import { clientSchema } from '../dtos/client.dtos.js';
3
+
4
+ export const clientDocs = {
5
+ '/v3/audit/client/clients': {
6
+ get: {
7
+ tags: [ 'Audit/Client' ],
8
+ description: 'Get client list which is enable for audit',
9
+ operationId: 'clients',
10
+ parameters: [
11
+ {
12
+ in: 'query',
13
+ name: 'limit',
14
+ scema: j2s( clientSchema ).swagger,
15
+ require: false,
16
+ },
17
+ {
18
+ in: 'query',
19
+ name: 'offset',
20
+ scema: j2s( clientSchema ).swagger,
21
+ require: false,
22
+ },
23
+ ],
24
+ responses: {
25
+ 200: { description: 'Successful' },
26
+ 401: { description: 'Unauthorized User' },
27
+ 422: { description: 'Field Error' },
28
+ 500: { description: 'Server Error' },
29
+ 204: { description: 'Not Found' },
30
+ },
31
+ },
32
+ },
33
+ };
@@ -13,6 +13,53 @@ export const getFileValid = {
13
13
  query: getFileSchema,
14
14
  };
15
15
 
16
+ export const saveDraftSchema = joi.object(
17
+ {
18
+ userCommands: joi.string().optional(),
19
+ storeId: joi.string().required(),
20
+ auditId: joi.string().required(),
21
+ auditType: joi.string().required(),
22
+ fileDate: joi.string().required(),
23
+ queueName: joi.string().required(),
24
+ totalCount: joi.number().required(),
25
+ junkCount: joi.number().optional(),
26
+ junk: joi.array().optional(),
27
+ employeeCount: joi.number().optional(),
28
+ employee: joi.array().optional(),
29
+ customerCount: joi.number().optional(),
30
+ customer: joi.array().optional(),
31
+ retagCount: joi.number().optional(),
32
+ retagImage: joi.array().optional(),
33
+ },
34
+ );
35
+
36
+ export const saveDraftValid = {
37
+ body: saveDraftSchema,
38
+ };
39
+
40
+ export const getDraftedDataSchema = joi.object(
41
+ {
42
+ storeId: joi.string().required(),
43
+ fileDate: joi.string().required(),
44
+ },
45
+ );
46
+
47
+ export const getDraftedDataValid = {
48
+ query: getDraftedDataSchema,
49
+ };
50
+
51
+ export const saveSchema = joi.object(
52
+ {
53
+ auditId: joi.string().required(),
54
+ storeId: joi.string().required(),
55
+ fileDate: joi.string().required(),
56
+ },
57
+ );
58
+
59
+ export const saveValid = {
60
+ body: saveSchema,
61
+ };
62
+
16
63
  export const getQueueDetailListSchema = joi.object(
17
64
  {
18
65
  searchValue: joi.string().optional(),
@@ -47,7 +47,7 @@ export const clientMetricsSchema = joi.object(
47
47
  toDate: joi.string().required(),
48
48
  searchValue: joi.string().optional(),
49
49
  filterByClient: joi.array().required(),
50
- status: joi.array().optional(),
50
+ filterByStatus: joi.array().optional(),
51
51
  sortColumnName: joi.string().optional(),
52
52
  sortBy: joi.number().optional(),
53
53
  limit: joi.number().optional(),
@@ -0,0 +1,13 @@
1
+ import joi from 'joi';
2
+
3
+
4
+ export const clientSchema = joi.object(
5
+ {
6
+ limit: joi.number().optional(),
7
+ offset: joi.number().optional(),
8
+ },
9
+ );
10
+
11
+ export const clientValid = {
12
+ query: clientSchema,
13
+ };
@@ -1,12 +1,15 @@
1
1
  import express from 'express';
2
- import { isExistsQueue } from '../validation/audit.validation.js';
2
+ import { isExistsQueue, validateUserAudit } from '../validation/audit.validation.js';
3
3
  import { isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
4
- import { getAuditFile, getQueueDetailList } from '../controllers/audit.controllers.js';
5
- import { getFileValid, getQueueDetailListValid } from '../dtos/audit.dtos.js';
4
+ import { getAuditFile, getDraftedData, getQueueDetailList, save, saveDraft } from '../controllers/audit.controllers.js';
5
+ import { getDraftedDataValid, getFileValid, getQueueDetailListValid, saveDraftValid, saveValid } from '../dtos/audit.dtos.js';
6
6
 
7
7
  export const auditRouter = express.Router();
8
8
 
9
- auditRouter.get( '/get-file', validate( getFileValid ), isAllowedSessionHandler, isExistsQueue, getAuditFile );
10
- auditRouter.get( '/get-queue-detail-list', validate( getQueueDetailListValid ), isAllowedSessionHandler, getQueueDetailList );
9
+ auditRouter.get( '/get-file', isAllowedSessionHandler, validate( getFileValid ), isExistsQueue, getAuditFile );
10
+ auditRouter.post( '/save-draft', isAllowedSessionHandler, validate( saveDraftValid ), saveDraft );
11
+ auditRouter.get( '/get-drafted-data', isAllowedSessionHandler, validate( getDraftedDataValid ), getDraftedData );
12
+ auditRouter.post( '/save', isAllowedSessionHandler, validate( saveValid ), validateUserAudit, save );
13
+ auditRouter.get( '/get-queue-detail-list', isAllowedSessionHandler, validate( getQueueDetailListValid ), getQueueDetailList );
11
14
 
12
15
  export default auditRouter;
@@ -0,0 +1,10 @@
1
+ import { Router } from 'express';
2
+ import { isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
3
+ import { clients } from '../controllers/client.controllers.js';
4
+ import { clientValid } from '../dtos/client.dtos.js';
5
+
6
+ export const clientRouter = Router();
7
+
8
+ clientRouter.get( '/clients', isAllowedSessionHandler, validate( clientValid ), clients );
9
+
10
+ export default clientRouter;
@@ -4,5 +4,5 @@ export function aggregateAssignAudit( query ) {
4
4
  return assignAuditModel.aggregate( query, { collation: { locale: 'en', strength: 2 } } );
5
5
  }
6
6
  export function updateOneAssignAudit( query, record ) {
7
- return assignAuditModel.updateOne( query, record );
7
+ return assignAuditModel.updateOne( query, { $set: record } );
8
8
  }
@@ -4,3 +4,7 @@ import clientModel from 'tango-api-schema/schema/client.model.js';
4
4
  export function aggregateClient( query ) {
5
5
  return clientModel.aggregate( query, { collation: { locale: 'en', strength: 2 } } );
6
6
  }
7
+
8
+ export function findClient( query, fields, sort={ createdAt: -1 } ) {
9
+ return clientModel.find( query, fields ).sort( sort );
10
+ }
@@ -1,7 +1,7 @@
1
1
  import storeAuditModel from 'tango-api-schema/schema/storeAudit.model.js';
2
2
 
3
3
  export function updateOneStoreAudit( query, record ) {
4
- return storeAuditModel.updateOne( query, record );
4
+ return storeAuditModel.updateOne( query, { $set: record } );
5
5
  }
6
6
 
7
7
  export function createStoreAudit( record ) {
@@ -6,9 +6,13 @@ export function aggregateUserAudit( query ) {
6
6
  }
7
7
 
8
8
  export function updateOneUserAudit( query, record ) {
9
- return userAuditModel.updateOne( query, record );
9
+ return userAuditModel.updateOne( query, { $set: record } );
10
10
  }
11
11
 
12
12
  export function createUserAudit( record ) {
13
13
  return userAuditModel.create( record );
14
14
  }
15
+
16
+ export function findOneUserAudit( query, fields ) {
17
+ return userAuditModel.findOne( query, fields );
18
+ }
@@ -1,4 +1,5 @@
1
- import { getQueueUrl, logger } from 'tango-app-api-middleware';
1
+ import { checkFileExist, getQueueUrl, logger } from 'tango-app-api-middleware';
2
+ import { findOneUserAudit } from '../service/userAudit.service.js';
2
3
 
3
4
  export async function isExistsQueue( req, res, next ) {
4
5
  try {
@@ -14,3 +15,44 @@ export async function isExistsQueue( req, res, next ) {
14
15
  return res.sendError( error, 500 );
15
16
  }
16
17
  }
18
+
19
+ export async function validateUserAudit( req, res, next ) {
20
+ try {
21
+ const inputData = req.body;
22
+ const userAuditDetails = findOneUserAudit( { _id: inputData.auditId } );
23
+ if ( !userAuditDetails ) {
24
+ return res.sendError( 'No Data Available' );
25
+ } else if ( userAuditDetails && userAuditDetails.auditStatus == 'skipped' ) {
26
+ return res.sendError( 'The Audit file Assigned to some one else', 203 );
27
+ }
28
+ req.body.userAudit = userAuditDetails;
29
+ next();
30
+ } catch ( error ) {
31
+ const err = error.message || 'Internal Server Error';
32
+ logger.error( { error: error, message: req.query, function: 'validateUserAudit' } );
33
+ return res.sendError( err, 500 );
34
+ }
35
+ }
36
+
37
+ export async function isMasterJsonExists( req, res, next ) {
38
+ try {
39
+ const bucket = JSON.parse( process.env.BUCKET );
40
+ const inputData = req.body;
41
+ params={
42
+ Bucket: bucket.masterJson,
43
+ Key: `${inputData.storeId}/${inputData.fileDate}/${bucket.masterJsonFile}`,
44
+ };
45
+ const isExist = await checkFileExist( params );
46
+ if ( isExist ) {
47
+ next();
48
+ } else {
49
+ return res.sendError( `NO_MASTER_JSON_AVAILABLE : ${inputData.storeId}/${inputData.fileDate}/${bucketName.masterJsonFile}`, 400 );
50
+ }
51
+ } catch ( error ) {
52
+ const err = error.message || 'Internal Server Error';
53
+ logger.error( { error: error, message: req.query, function: 'isMasterJsonExists' } );
54
+ return res.sendError( err, 500 );
55
+ }
56
+ }
57
+
58
+