tango-app-api-audio-analytics 1.0.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.cjs +41 -0
- package/API_EXAMPLES.md +310 -0
- package/COHORT_API.md +513 -0
- package/COHORT_API_EXAMPLES.sh +235 -0
- package/COHORT_API_IMPLEMENTATION.md +296 -0
- package/COHORT_API_QUICKSTART.md +387 -0
- package/index.js +6 -0
- package/package.json +36 -0
- package/src/controllers/audioAnalytics.controller.js +116 -0
- package/src/controllers/cohort.controller.js +357 -0
- package/src/controllers/cohortAnalytics.controller.js +46 -0
- package/src/controllers/conversationAnalytics.controller.js +92 -0
- package/src/dtos/audioAnalytics.dtos.js +537 -0
- package/src/middlewares/validation.middleware.js +624 -0
- package/src/routes/audioAnalytics.routes.js +18 -0
- package/src/services/cohort.service.js +332 -0
- package/src/validations/cohort.validation.js +113 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
# Cohort API - Quick Start Guide
|
|
2
|
+
|
|
3
|
+
## Project Structure
|
|
4
|
+
```
|
|
5
|
+
src/
|
|
6
|
+
├── controllers/
|
|
7
|
+
│ ├── cohortAnalytics.controller.js ✅ (7 endpoints)
|
|
8
|
+
│ └── conversationAnalytics.controller.js
|
|
9
|
+
├── services/
|
|
10
|
+
│ └── cohort.service.js ✅ (7 CRUD operations)
|
|
11
|
+
├── models/
|
|
12
|
+
│ └── (ready for database models)
|
|
13
|
+
├── routes/
|
|
14
|
+
│ └── audioAnalytics.routes.js ✅ (updated with 7 cohort routes)
|
|
15
|
+
├── middlewares/
|
|
16
|
+
│ └── validation.middleware.js ✅ (3 cohort validators added)
|
|
17
|
+
├── dtos/
|
|
18
|
+
│ └── audioAnalytics.dtos.js ✅ (17 DTO classes)
|
|
19
|
+
├── validations/
|
|
20
|
+
│ └── cohort.validation.js ✅ (Joi schemas)
|
|
21
|
+
└── utils/
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Required dependency (if not already installed)
|
|
28
|
+
npm install joi
|
|
29
|
+
|
|
30
|
+
# The tango-app-api-middleware is already available
|
|
31
|
+
const { insert, search, update, delete } = require('tango-app-api-middleware');
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Integration
|
|
35
|
+
|
|
36
|
+
### 1. Import Cohort Endpoints
|
|
37
|
+
Already integrated in `src/routes/audioAnalytics.routes.js`:
|
|
38
|
+
```javascript
|
|
39
|
+
const {
|
|
40
|
+
createCohort,
|
|
41
|
+
getCohort,
|
|
42
|
+
getCohortsByClient,
|
|
43
|
+
searchCohortsEndpoint,
|
|
44
|
+
updateCohort,
|
|
45
|
+
deleteCohort,
|
|
46
|
+
getCohortAnalyticsEndpoint
|
|
47
|
+
} = require('../controllers/cohort.controller');
|
|
48
|
+
|
|
49
|
+
const {
|
|
50
|
+
validateCohortCreation,
|
|
51
|
+
validateCohortUpdate,
|
|
52
|
+
validateCohortQuery
|
|
53
|
+
} = require('../middlewares/validation.middleware');
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 2. Available Routes
|
|
57
|
+
```javascript
|
|
58
|
+
// READ
|
|
59
|
+
GET /cohorts/:cohortId // Get single cohort
|
|
60
|
+
GET /cohorts/client/:clientId // Get by client (paginated)
|
|
61
|
+
GET /cohorts/:cohortId/analytics // Get analytics
|
|
62
|
+
|
|
63
|
+
// CREATE
|
|
64
|
+
POST /cohorts // Create new cohort
|
|
65
|
+
|
|
66
|
+
// SEARCH
|
|
67
|
+
POST /cohorts/search // Search with filters
|
|
68
|
+
|
|
69
|
+
// UPDATE
|
|
70
|
+
PUT /cohorts/:documentId // Update cohort
|
|
71
|
+
|
|
72
|
+
// DELETE
|
|
73
|
+
DELETE /cohorts/:documentId // Delete cohort
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Common Tasks
|
|
77
|
+
|
|
78
|
+
### Create a Cohort
|
|
79
|
+
```javascript
|
|
80
|
+
// Request
|
|
81
|
+
POST /cohorts
|
|
82
|
+
{
|
|
83
|
+
"clientId": "11",
|
|
84
|
+
"cohortId": "cohort_example_001",
|
|
85
|
+
"cohortName": "Example Cohort",
|
|
86
|
+
"cohortDescription": "A test cohort with metrics",
|
|
87
|
+
"metrics": [
|
|
88
|
+
{
|
|
89
|
+
"metricId": "metric_1",
|
|
90
|
+
"metricName": "Engagement",
|
|
91
|
+
"metricDescription": "Customer engagement level",
|
|
92
|
+
"hasContext": true,
|
|
93
|
+
"isNumneric": true,
|
|
94
|
+
"contexts": [
|
|
95
|
+
{
|
|
96
|
+
"contextName": "HIGH",
|
|
97
|
+
"priority": 2,
|
|
98
|
+
"description": "High engagement",
|
|
99
|
+
"minValue": 80,
|
|
100
|
+
"maxValue": 100
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Response (201 Created)
|
|
108
|
+
{
|
|
109
|
+
"status": "success",
|
|
110
|
+
"data": {
|
|
111
|
+
"documentId": "550e8400-e29b-41d4-a716-446655440000",
|
|
112
|
+
"clientId": "11",
|
|
113
|
+
"cohortId": "cohort_example_001",
|
|
114
|
+
...
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Get a Cohort
|
|
120
|
+
```javascript
|
|
121
|
+
GET /cohorts/cohort_example_001
|
|
122
|
+
|
|
123
|
+
// Response (200 OK)
|
|
124
|
+
{
|
|
125
|
+
"status": "success",
|
|
126
|
+
"data": { ...cohort document... }
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Search Cohorts
|
|
131
|
+
```javascript
|
|
132
|
+
POST /cohorts/search
|
|
133
|
+
{
|
|
134
|
+
"clientId": "11",
|
|
135
|
+
"cohortName": "Example",
|
|
136
|
+
"limit": 10,
|
|
137
|
+
"offset": 0
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Response (200 OK)
|
|
141
|
+
{
|
|
142
|
+
"status": "success",
|
|
143
|
+
"data": {
|
|
144
|
+
"cohorts": [...],
|
|
145
|
+
"total": 5,
|
|
146
|
+
"limit": 10,
|
|
147
|
+
"offset": 0
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Update a Cohort
|
|
153
|
+
```javascript
|
|
154
|
+
PUT /cohorts/550e8400-e29b-41d4-a716-446655440000
|
|
155
|
+
{
|
|
156
|
+
"cohortName": "Updated Cohort Name",
|
|
157
|
+
"metrics": [...]
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Response (200 OK)
|
|
161
|
+
{
|
|
162
|
+
"status": "success",
|
|
163
|
+
"data": { ...updated cohort... }
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Delete a Cohort
|
|
168
|
+
```javascript
|
|
169
|
+
DELETE /cohorts/550e8400-e29b-41d4-a716-446655440000
|
|
170
|
+
|
|
171
|
+
// Response (200 OK)
|
|
172
|
+
{
|
|
173
|
+
"status": "success",
|
|
174
|
+
"message": "Cohort deleted successfully"
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Get Analytics
|
|
179
|
+
```javascript
|
|
180
|
+
GET /cohorts/cohort_example_001/analytics
|
|
181
|
+
|
|
182
|
+
// Response (200 OK)
|
|
183
|
+
{
|
|
184
|
+
"status": "success",
|
|
185
|
+
"data": {
|
|
186
|
+
"cohortId": "cohort_example_001",
|
|
187
|
+
"metricsCount": 6,
|
|
188
|
+
"metrics": [
|
|
189
|
+
{
|
|
190
|
+
"metricId": "metric_1",
|
|
191
|
+
"metricName": "Engagement",
|
|
192
|
+
"contextsCount": 3
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Validation Reference
|
|
200
|
+
|
|
201
|
+
### Cohort ID Format
|
|
202
|
+
- Must be alphanumeric with hyphens and underscores
|
|
203
|
+
- Examples: `cohort_001`, `cohort-test`, `test123`
|
|
204
|
+
- Invalid: `cohort@001`, `cohort.test`, `Cohort_001` (uppercase)
|
|
205
|
+
|
|
206
|
+
### Cohort Name
|
|
207
|
+
- Minimum 3 characters, maximum 100 characters
|
|
208
|
+
- Any UTF-8 characters allowed
|
|
209
|
+
- Required field
|
|
210
|
+
|
|
211
|
+
### Cohort Description
|
|
212
|
+
- Minimum 10 characters, maximum 500 characters
|
|
213
|
+
- Any UTF-8 characters allowed
|
|
214
|
+
- Required field
|
|
215
|
+
|
|
216
|
+
### Metrics Array
|
|
217
|
+
- Minimum 1 metric, maximum 50 metrics
|
|
218
|
+
- Each metric requires: metricId, metricName, metricDescription, hasContext, isNumneric
|
|
219
|
+
- If hasContext is true, contexts array is required with at least 1 context
|
|
220
|
+
|
|
221
|
+
### Contexts
|
|
222
|
+
- contextName (required)
|
|
223
|
+
- priority: 0, 1, or 2 (required)
|
|
224
|
+
- description (required, any length)
|
|
225
|
+
- minValue, maxValue (optional, for numeric metrics)
|
|
226
|
+
|
|
227
|
+
## Error Handling
|
|
228
|
+
|
|
229
|
+
### Common Error Responses
|
|
230
|
+
|
|
231
|
+
**400 - Validation Error**
|
|
232
|
+
```json
|
|
233
|
+
{
|
|
234
|
+
"status": "error",
|
|
235
|
+
"message": "Validation failed",
|
|
236
|
+
"code": "VALIDATION_ERROR",
|
|
237
|
+
"details": [
|
|
238
|
+
"cohortName must be at least 3 characters",
|
|
239
|
+
"clientId is required"
|
|
240
|
+
],
|
|
241
|
+
"timestamp": "2024-01-15T10:30:00.000Z"
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**409 - Cohort Exists**
|
|
246
|
+
```json
|
|
247
|
+
{
|
|
248
|
+
"status": "error",
|
|
249
|
+
"message": "Cohort with ID 'cohort_001' already exists",
|
|
250
|
+
"code": "COHORT_EXISTS",
|
|
251
|
+
"timestamp": "2024-01-15T10:30:00.000Z"
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**404 - Not Found**
|
|
256
|
+
```json
|
|
257
|
+
{
|
|
258
|
+
"status": "error",
|
|
259
|
+
"message": "Cohort not found",
|
|
260
|
+
"code": "COHORT_NOT_FOUND",
|
|
261
|
+
"timestamp": "2024-01-15T10:30:00.000Z"
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Testing
|
|
266
|
+
|
|
267
|
+
### Using curl
|
|
268
|
+
```bash
|
|
269
|
+
# Create
|
|
270
|
+
curl -X POST http://localhost:3000/v3/audio-analitics/cohorts \
|
|
271
|
+
-H "Content-Type: application/json" \
|
|
272
|
+
-d @cohort_payload.json
|
|
273
|
+
|
|
274
|
+
# Get
|
|
275
|
+
curl http://localhost:3000/v3/audio-analitics/cohorts/cohort_001
|
|
276
|
+
|
|
277
|
+
# Search
|
|
278
|
+
curl -X POST http://localhost:3000/v3/audio-analitics/cohorts/search \
|
|
279
|
+
-H "Content-Type: application/json" \
|
|
280
|
+
-d '{"clientId":"11","limit":10}'
|
|
281
|
+
|
|
282
|
+
# Update
|
|
283
|
+
curl -X PUT http://localhost:3000/v3/audio-analitics/cohorts/DOC_ID \
|
|
284
|
+
-H "Content-Type: application/json" \
|
|
285
|
+
-d @update_payload.json
|
|
286
|
+
|
|
287
|
+
# Delete
|
|
288
|
+
curl -X DELETE http://localhost:3000/v3/audio-analitics/cohorts/DOC_ID
|
|
289
|
+
|
|
290
|
+
# Analytics
|
|
291
|
+
curl http://localhost:3000/v3/audio-analitics/cohorts/cohort_001/analytics
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Using Test Script
|
|
295
|
+
```bash
|
|
296
|
+
bash COHORT_API_EXAMPLES.sh
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## File Dependencies
|
|
300
|
+
|
|
301
|
+
| File | Depends On | Used By |
|
|
302
|
+
|------|-----------|---------|
|
|
303
|
+
| cohort.validation.js | joi | validation.middleware.js |
|
|
304
|
+
| cohort.service.js | tango-app-api-middleware | cohort.controller.js |
|
|
305
|
+
| cohort.controller.js | cohort.service.js, audioAnalytics.dtos.js | audioAnalytics.routes.js |
|
|
306
|
+
| validation.middleware.js | cohort.validation.js | audioAnalytics.routes.js |
|
|
307
|
+
| audioAnalytics.routes.js | cohort.controller.js, validation.middleware.js | app.js |
|
|
308
|
+
|
|
309
|
+
## Environmental Setup
|
|
310
|
+
|
|
311
|
+
### Required Environment Variables
|
|
312
|
+
```env
|
|
313
|
+
# Database (OpenSearch)
|
|
314
|
+
OPENSEARCH_HOST=localhost
|
|
315
|
+
OPENSEARCH_PORT=9200
|
|
316
|
+
OPENSEARCH_INDEX=tango-audio-cohort
|
|
317
|
+
|
|
318
|
+
# API
|
|
319
|
+
API_PORT=3000
|
|
320
|
+
NODE_ENV=development
|
|
321
|
+
|
|
322
|
+
# Logger
|
|
323
|
+
LOG_LEVEL=info
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Key Constants
|
|
327
|
+
|
|
328
|
+
```javascript
|
|
329
|
+
// Database
|
|
330
|
+
OPENSEARCH_INDEX = 'tango-audio-cohort'
|
|
331
|
+
|
|
332
|
+
// API Status Codes
|
|
333
|
+
201 = Created
|
|
334
|
+
200 = OK
|
|
335
|
+
400 = Bad Request
|
|
336
|
+
404 = Not Found
|
|
337
|
+
409 = Conflict
|
|
338
|
+
500 = Internal Server Error
|
|
339
|
+
|
|
340
|
+
// Error Codes
|
|
341
|
+
VALIDATION_ERROR // 400
|
|
342
|
+
COHORT_EXISTS // 409
|
|
343
|
+
COHORT_NOT_FOUND // 404
|
|
344
|
+
COHORT_CREATE_ERROR // 500
|
|
345
|
+
COHORT_SEARCH_ERROR // 500
|
|
346
|
+
INTERNAL_ERROR // 500
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Performance Considerations
|
|
350
|
+
|
|
351
|
+
### Pagination
|
|
352
|
+
- Default limit: 10 items
|
|
353
|
+
- Maximum items: 50
|
|
354
|
+
- Offset-based pagination supported
|
|
355
|
+
- Use for `/cohorts/client/:clientId` endpoint
|
|
356
|
+
|
|
357
|
+
### Search Optimization
|
|
358
|
+
- Fuzzy search enabled on cohortName (AUTO fuzziness)
|
|
359
|
+
- Bool query for multiple criteria
|
|
360
|
+
- Indexed by: clientId, cohortId, cohortName
|
|
361
|
+
|
|
362
|
+
### Caching Recommendations
|
|
363
|
+
- Cache GET /cohorts/:cohortId for 5 minutes
|
|
364
|
+
- Invalidate cache on PUT/DELETE
|
|
365
|
+
- Analytics endpoint can be cached for 1 hour
|
|
366
|
+
|
|
367
|
+
## Security Notes
|
|
368
|
+
|
|
369
|
+
- All inputs validated via Joi
|
|
370
|
+
- SQL injection not applicable (OpenSearch)
|
|
371
|
+
- Validate clientId against user permissions
|
|
372
|
+
- Use UUID for document IDs (not sequential)
|
|
373
|
+
- Hash sensitive data in metrics if needed
|
|
374
|
+
|
|
375
|
+
## Support Files
|
|
376
|
+
|
|
377
|
+
- **COHORT_API.md** - Full API documentation
|
|
378
|
+
- **COHORT_API_EXAMPLES.sh** - Bash testing script
|
|
379
|
+
- **COHORT_API_IMPLEMENTATION.md** - Implementation details
|
|
380
|
+
|
|
381
|
+
## Next Steps
|
|
382
|
+
|
|
383
|
+
1. ✅ Install dependencies (`npm install joi`)
|
|
384
|
+
2. ✅ Review COHORT_API.md for complete documentation
|
|
385
|
+
3. ✅ Run COHORT_API_EXAMPLES.sh to test endpoints
|
|
386
|
+
4. ✅ Integrate with conversation analytics
|
|
387
|
+
5. ✅ Deploy to production with proper environment configuration
|
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tango-app-api-audio-analytics",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "audioAnalytics",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"start": "nodemon --exec \"eslint --fix . && node app.js\""
|
|
9
|
+
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=18.10.0"
|
|
12
|
+
},
|
|
13
|
+
"author": "praveenraj",
|
|
14
|
+
"license": "ISC",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"aws-sdk": "^2.1693.0",
|
|
17
|
+
"body-parser": "^2.2.2",
|
|
18
|
+
"cors": "^2.8.6",
|
|
19
|
+
"dotenv": "^17.3.1",
|
|
20
|
+
"express": "^5.2.1",
|
|
21
|
+
"mongodb": "^6.21.0",
|
|
22
|
+
"nodemon": "^3.1.14",
|
|
23
|
+
"tango-api-schema": "^2.5.62",
|
|
24
|
+
"tango-app-api-middleware": "^3.6.18",
|
|
25
|
+
"winston": "^3.19.0",
|
|
26
|
+
"winston-daily-rotate-file": "^5.0.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"eslint": "^8.57.1",
|
|
30
|
+
"eslint-config-google": "^0.14.0",
|
|
31
|
+
"eslint-config-semistandard": "^17.0.0",
|
|
32
|
+
"eslint-config-standard": "^17.1.0",
|
|
33
|
+
"eslint-plugin-import": "^2.32.0",
|
|
34
|
+
"eslint-plugin-promise": "^6.6.0"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { insertWithId, logger } from 'tango-app-api-middleware';
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
|
|
4
|
+
const EXTERNAL_STREAM_API = 'http://172.236.179.51:8000/stream';
|
|
5
|
+
|
|
6
|
+
export async function createCohort( req, res ) {
|
|
7
|
+
try {
|
|
8
|
+
const inputData = req.body;
|
|
9
|
+
|
|
10
|
+
const cohortId = `cohort_${inputData.clientId}_${randomUUID()}`;
|
|
11
|
+
|
|
12
|
+
const metrics = ( inputData.metrics || [] ).map( ( metric ) => {
|
|
13
|
+
const hasContext = Array.isArray( metric.contexts ) && metric.contexts.length > 0;
|
|
14
|
+
return {
|
|
15
|
+
...metric,
|
|
16
|
+
metricId: randomUUID(),
|
|
17
|
+
hasContext,
|
|
18
|
+
isNumeric: metric.isNumeric ?? false,
|
|
19
|
+
};
|
|
20
|
+
} );
|
|
21
|
+
|
|
22
|
+
const cohortData = {
|
|
23
|
+
clientId: inputData.clientId,
|
|
24
|
+
cohortId,
|
|
25
|
+
cohortName: inputData.cohortName,
|
|
26
|
+
cohortDescription: inputData.cohortDescription,
|
|
27
|
+
metrics,
|
|
28
|
+
createdAt: new Date().toISOString(),
|
|
29
|
+
updatedAt: new Date().toISOString(),
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const result = await insertWithId( 'tango-audio-config', cohortId, cohortData );
|
|
33
|
+
logger.info( { result } );
|
|
34
|
+
|
|
35
|
+
if ( result && result.body && result.body.result === 'created' ) {
|
|
36
|
+
return res.sendSuccess( { result: 'Cohort created successfully', cohortId } );
|
|
37
|
+
} else {
|
|
38
|
+
return res.sendError( 'Failed to create cohort', 500 );
|
|
39
|
+
}
|
|
40
|
+
} catch ( error ) {
|
|
41
|
+
const err = error.message || 'Internal Server Error';
|
|
42
|
+
logger.error( { error: error, message: req.body, function: 'createCohort' } );
|
|
43
|
+
return res.sendError( err, 500 );
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const callExternalStreamAPI = async ( req, res ) => {
|
|
48
|
+
try {
|
|
49
|
+
const { prompt, storeId, productModule, country, region, fromDate, toDate, clusters } = req.body;
|
|
50
|
+
console.log( 'Received request for external stream API with parameters:', req.body );
|
|
51
|
+
// Validation for required parameters
|
|
52
|
+
if ( !prompt ) {
|
|
53
|
+
logger.warn( 'Stream API call attempted without prompt' );
|
|
54
|
+
return res.status( 400 ).json( {
|
|
55
|
+
status: 'error',
|
|
56
|
+
message: 'Missing required parameter: prompt',
|
|
57
|
+
code: 'MISSING_PROMPT',
|
|
58
|
+
timestamp: new Date().toISOString(),
|
|
59
|
+
} );
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Build query parameters
|
|
63
|
+
const queryParams = new URLSearchParams();
|
|
64
|
+
queryParams.append( 'prompt', prompt );
|
|
65
|
+
|
|
66
|
+
if ( storeId ) queryParams.append( 'storeId', storeId );
|
|
67
|
+
if ( productModule ) queryParams.append( 'productModule', productModule );
|
|
68
|
+
if ( country ) queryParams.append( 'country', country );
|
|
69
|
+
if ( region ) queryParams.append( 'region', region );
|
|
70
|
+
if ( fromDate ) queryParams.append( 'fromDate', fromDate );
|
|
71
|
+
if ( toDate ) queryParams.append( 'toDate', toDate );
|
|
72
|
+
if ( clusters ) queryParams.append( 'clusters', clusters );
|
|
73
|
+
|
|
74
|
+
const url = `${EXTERNAL_STREAM_API}?${queryParams.toString()}`;
|
|
75
|
+
|
|
76
|
+
logger.info( `Calling external stream API with URL: ${url}` );
|
|
77
|
+
|
|
78
|
+
// Make the external API call
|
|
79
|
+
const response = await fetch( url, {
|
|
80
|
+
method: 'GET',
|
|
81
|
+
headers: {
|
|
82
|
+
'Content-Type': 'application/json',
|
|
83
|
+
'Accept': 'application/json',
|
|
84
|
+
},
|
|
85
|
+
} );
|
|
86
|
+
|
|
87
|
+
if ( !response.ok ) {
|
|
88
|
+
logger.error( `External API error: ${response.status} ${response.statusText}` );
|
|
89
|
+
return res.status( response.status ).json( {
|
|
90
|
+
status: 'error',
|
|
91
|
+
message: `External API error: ${response.statusText}`,
|
|
92
|
+
code: 'EXTERNAL_API_ERROR',
|
|
93
|
+
timestamp: new Date().toISOString(),
|
|
94
|
+
} );
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const data = await response.json();
|
|
98
|
+
|
|
99
|
+
logger.info( `Successfully called external stream API` );
|
|
100
|
+
|
|
101
|
+
return res.status( 200 ).json( {
|
|
102
|
+
status: 'success',
|
|
103
|
+
data: data,
|
|
104
|
+
timestamp: new Date().toISOString(),
|
|
105
|
+
} );
|
|
106
|
+
} catch ( error ) {
|
|
107
|
+
logger.error( `Error calling external stream API: ${error.message}` );
|
|
108
|
+
return res.status( 500 ).json( {
|
|
109
|
+
status: 'error',
|
|
110
|
+
message: 'Internal server error while calling external API',
|
|
111
|
+
code: 'INTERNAL_ERROR',
|
|
112
|
+
error: error.message,
|
|
113
|
+
timestamp: new Date().toISOString(),
|
|
114
|
+
} );
|
|
115
|
+
}
|
|
116
|
+
};
|