Refactor API response structure to include success status and body for all endpoints, ensuring consistent response format across the application.
Some checks failed
platform/bro-js/challenge-pl/pipeline/head There was a failure building this commit

This commit is contained in:
Primakov Alexandr Alexandrovich 2025-11-04 11:18:13 +03:00
parent 67f29c103a
commit cde28b1dd8
9 changed files with 294 additions and 265 deletions

View File

@ -1,4 +1,7 @@
{ {
"ok": true, "success": true,
"userId": "user-frontend-001" "body": {
"ok": true,
"userId": "user-frontend-001"
}
} }

View File

@ -1,27 +1,30 @@
[ {
{ "success": true,
"id": "chain-frontend", "body": [
"_id": "chain-frontend", {
"name": "Frontend Basics", "id": "chain-frontend",
"createdAt": "2024-09-01T08:00:00.000Z", "_id": "chain-frontend",
"updatedAt": "2024-10-12T10:15:00.000Z", "name": "Frontend Basics",
"tasks": [ "createdAt": "2024-09-01T08:00:00.000Z",
{ "updatedAt": "2024-10-12T10:15:00.000Z",
"id": "task-html-intro", "tasks": [
"_id": "task-html-intro", {
"title": "HTML старт", "id": "task-html-intro",
"description": "# HTML старт\n\nСоздайте базовую HTML-страницу с заголовком и абзацем.", "_id": "task-html-intro",
"createdAt": "2024-09-01T08:05:00.000Z", "title": "HTML старт",
"updatedAt": "2024-09-10T12:00:00.000Z" "description": "# HTML старт\n\nСоздайте базовую HTML-страницу с заголовком и абзацем.",
}, "createdAt": "2024-09-01T08:05:00.000Z",
{ "updatedAt": "2024-09-10T12:00:00.000Z"
"id": "task-react-component", },
"_id": "task-react-component", {
"title": "React компонент", "id": "task-react-component",
"description": "# React компонент\n\nСоздайте компонент `StatCard` с пропсами `title` и `value`.", "_id": "task-react-component",
"createdAt": "2024-09-05T11:30:00.000Z", "title": "React компонент",
"updatedAt": "2024-10-01T09:45:00.000Z" "description": "# React компонент\n\nСоздайте компонент `StatCard` с пропсами `title` и `value`.",
} "createdAt": "2024-09-05T11:30:00.000Z",
] "updatedAt": "2024-10-01T09:45:00.000Z"
} }
] ]
}
]
}

View File

@ -1,57 +1,60 @@
{ {
"queue-frontend-001": { "success": true,
"status": "completed", "body": {
"position": 0, "queue-frontend-001": {
"submission": { "status": "completed",
"_id": "submission-001", "position": 0,
"id": "submission-001", "submission": {
"user": "user-frontend-001", "_id": "submission-001",
"task": "task-html-intro", "id": "submission-001",
"result": "<html><head></head><body><h1>Hello</h1></body></html>", "user": "user-frontend-001",
"status": "needs_revision", "task": "task-html-intro",
"queueId": "queue-frontend-001", "result": "<html><head></head><body><h1>Hello</h1></body></html>",
"feedback": "Добавьте тег <title>", "status": "needs_revision",
"submittedAt": "2024-10-18T07:10:00.000Z", "queueId": "queue-frontend-001",
"checkedAt": "2024-10-18T07:10:15.000Z", "feedback": "Добавьте тег <title>",
"attemptNumber": 1 "submittedAt": "2024-10-18T07:10:00.000Z",
} "checkedAt": "2024-10-18T07:10:15.000Z",
}, "attemptNumber": 1
"queue-frontend-002": { }
"status": "completed", },
"position": 0, "queue-frontend-002": {
"submission": { "status": "completed",
"_id": "submission-002", "position": 0,
"id": "submission-002", "submission": {
"user": "user-frontend-001", "_id": "submission-002",
"task": "task-html-intro", "id": "submission-002",
"result": "<html><head><title>Home</title></head><body><h1>Hello</h1></body></html>", "user": "user-frontend-001",
"status": "accepted", "task": "task-html-intro",
"queueId": "queue-frontend-002", "result": "<html><head><title>Home</title></head><body><h1>Hello</h1></body></html>",
"feedback": "Отличная работа!", "status": "accepted",
"submittedAt": "2024-10-18T07:20:00.000Z", "queueId": "queue-frontend-002",
"checkedAt": "2024-10-18T07:20:05.000Z", "feedback": "Отличная работа!",
"attemptNumber": 2 "submittedAt": "2024-10-18T07:20:00.000Z",
} "checkedAt": "2024-10-18T07:20:05.000Z",
}, "attemptNumber": 2
"queue-frontend-003": { }
"status": "waiting", },
"position": 3 "queue-frontend-003": {
}, "status": "waiting",
"queue-react-001": { "position": 3
"status": "completed", },
"position": 0, "queue-react-001": {
"submission": { "status": "completed",
"_id": "submission-004", "position": 0,
"id": "submission-004", "submission": {
"user": "user-react-777", "_id": "submission-004",
"task": "task-react-component", "id": "submission-004",
"result": "export const StatCard = () => <div>Stat</div>;", "user": "user-react-777",
"status": "accepted", "task": "task-react-component",
"queueId": "queue-react-001", "result": "export const StatCard = () => <div>Stat</div>;",
"feedback": "Добавьте prop-types.", "status": "accepted",
"submittedAt": "2024-10-17T11:30:00.000Z", "queueId": "queue-react-001",
"checkedAt": "2024-10-17T11:30:07.000Z", "feedback": "Добавьте prop-types.",
"attemptNumber": 1 "submittedAt": "2024-10-17T11:30:00.000Z",
"checkedAt": "2024-10-17T11:30:07.000Z",
"attemptNumber": 1
}
} }
} }
} }

View File

@ -1,59 +1,62 @@
[ {
{ "success": true,
"_id": "submission-001", "body": [
"id": "submission-001", {
"user": { "_id": "submission-001",
"id": "user-frontend-001", "id": "submission-001",
"nickname": "frontend_ninja" "user": {
"id": "user-frontend-001",
"nickname": "frontend_ninja"
},
"task": {
"id": "task-html-intro",
"title": "HTML старт"
},
"result": "<html><head></head><body><h1>Hello</h1></body></html>",
"status": "needs_revision",
"queueId": "queue-frontend-001",
"feedback": "Добавьте тег <title>",
"submittedAt": "2024-10-18T07:10:00.000Z",
"checkedAt": "2024-10-18T07:10:15.000Z",
"attemptNumber": 1
}, },
"task": { {
"id": "task-html-intro", "_id": "submission-002",
"title": "HTML старт" "id": "submission-002",
"user": {
"id": "user-frontend-001",
"nickname": "frontend_ninja"
},
"task": {
"id": "task-html-intro",
"title": "HTML старт"
},
"result": "<html><head><title>Home</title></head><body><h1>Hello</h1></body></html>",
"status": "accepted",
"queueId": "queue-frontend-002",
"feedback": "Отличная работа!",
"submittedAt": "2024-10-18T07:20:00.000Z",
"checkedAt": "2024-10-18T07:20:05.000Z",
"attemptNumber": 2
}, },
"result": "<html><head></head><body><h1>Hello</h1></body></html>", {
"status": "needs_revision", "_id": "submission-004",
"queueId": "queue-frontend-001", "id": "submission-004",
"feedback": "Добавьте тег <title>", "user": {
"submittedAt": "2024-10-18T07:10:00.000Z", "id": "user-react-777",
"checkedAt": "2024-10-18T07:10:15.000Z", "nickname": "react_master"
"attemptNumber": 1 },
}, "task": {
{ "id": "task-react-component",
"_id": "submission-002", "title": "React компонент"
"id": "submission-002", },
"user": { "result": "export const StatCard = () => <div>Stat</div>;",
"id": "user-frontend-001", "status": "accepted",
"nickname": "frontend_ninja" "queueId": "queue-react-001",
}, "feedback": "Добавьте prop-types.",
"task": { "submittedAt": "2024-10-17T11:30:00.000Z",
"id": "task-html-intro", "checkedAt": "2024-10-17T11:30:07.000Z",
"title": "HTML старт" "attemptNumber": 1
}, }
"result": "<html><head><title>Home</title></head><body><h1>Hello</h1></body></html>", ]
"status": "accepted", }
"queueId": "queue-frontend-002",
"feedback": "Отличная работа!",
"submittedAt": "2024-10-18T07:20:00.000Z",
"checkedAt": "2024-10-18T07:20:05.000Z",
"attemptNumber": 2
},
{
"_id": "submission-004",
"id": "submission-004",
"user": {
"id": "user-react-777",
"nickname": "react_master"
},
"task": {
"id": "task-react-component",
"title": "React компонент"
},
"result": "export const StatCard = () => <div>Stat</div>;",
"status": "accepted",
"queueId": "queue-react-001",
"feedback": "Добавьте prop-types.",
"submittedAt": "2024-10-17T11:30:00.000Z",
"checkedAt": "2024-10-17T11:30:07.000Z",
"attemptNumber": 1
}
]

View File

@ -1,4 +1,7 @@
{ {
"queueId": "queue-frontend-003", "success": true,
"submissionId": "submission-003" "body": {
"queueId": "queue-frontend-003",
"submissionId": "submission-003"
}
} }

View File

@ -1,20 +1,23 @@
{ {
"users": 128, "success": true,
"tasks": 34, "body": {
"chains": 5, "users": 128,
"submissions": { "tasks": 34,
"total": 540, "chains": 5,
"accepted": 312, "submissions": {
"rejected": 144, "total": 540,
"pending": 62, "accepted": 312,
"inProgress": 22 "rejected": 144,
}, "pending": 62,
"averageCheckTimeMs": 5600, "inProgress": 22
"queue": { },
"queueLength": 18, "averageCheckTimeMs": 5600,
"waiting": 12, "queue": {
"inProgress": 6, "queueLength": 18,
"maxConcurrency": 8, "waiting": 12,
"currentlyProcessing": 6 "inProgress": 6,
"maxConcurrency": 8,
"currentlyProcessing": 6
}
} }
} }

View File

@ -1,59 +1,62 @@
{ {
"user-frontend-001": { "success": true,
"totalTasksAttempted": 2, "body": {
"completedTasks": 1, "user-frontend-001": {
"inProgressTasks": 1, "totalTasksAttempted": 2,
"needsRevisionTasks": 0, "completedTasks": 1,
"totalSubmissions": 3, "inProgressTasks": 1,
"averageCheckTimeMs": 4200, "needsRevisionTasks": 0,
"taskStats": [ "totalSubmissions": 3,
{ "averageCheckTimeMs": 4200,
"taskId": "task-html-intro", "taskStats": [
"taskTitle": "HTML старт", {
"attempts": [ "taskId": "task-html-intro",
{ "taskTitle": "HTML старт",
"attemptNumber": 1, "attempts": [
"status": "needs_revision", {
"submittedAt": "2024-10-18T07:10:00.000Z", "attemptNumber": 1,
"checkedAt": "2024-10-18T07:10:15.000Z", "status": "needs_revision",
"feedback": "Добавьте тег <title>." "submittedAt": "2024-10-18T07:10:00.000Z",
}, "checkedAt": "2024-10-18T07:10:15.000Z",
{ "feedback": "Добавьте тег <title>."
"attemptNumber": 2, },
"status": "accepted", {
"submittedAt": "2024-10-18T07:20:00.000Z", "attemptNumber": 2,
"checkedAt": "2024-10-18T07:20:05.000Z", "status": "accepted",
"feedback": "Отличная работа!" "submittedAt": "2024-10-18T07:20:00.000Z",
} "checkedAt": "2024-10-18T07:20:05.000Z",
], "feedback": "Отличная работа!"
"totalAttempts": 2, }
"status": "completed", ],
"lastAttemptAt": "2024-10-18T07:20:00.000Z" "totalAttempts": 2,
}, "status": "completed",
{ "lastAttemptAt": "2024-10-18T07:20:00.000Z"
"taskId": "task-react-component", },
"taskTitle": "React компонент", {
"attempts": [ "taskId": "task-react-component",
{ "taskTitle": "React компонент",
"attemptNumber": 1, "attempts": [
"status": "pending", {
"submittedAt": "2024-10-19T09:05:00.000Z", "attemptNumber": 1,
"feedback": null "status": "pending",
} "submittedAt": "2024-10-19T09:05:00.000Z",
], "feedback": null
"totalAttempts": 1, }
"status": "pending", ],
"lastAttemptAt": "2024-10-19T09:05:00.000Z" "totalAttempts": 1,
} "status": "pending",
], "lastAttemptAt": "2024-10-19T09:05:00.000Z"
"chainStats": [ }
{ ],
"chainId": "chain-frontend", "chainStats": [
"chainName": "Frontend Basics", {
"totalTasks": 2, "chainId": "chain-frontend",
"completedTasks": 1, "chainName": "Frontend Basics",
"progress": 50 "totalTasks": 2,
} "completedTasks": 1,
] "progress": 50
}
]
}
} }
} }

View File

@ -1,43 +1,46 @@
{ {
"user-frontend-001": [ "success": true,
{ "body": {
"_id": "submission-001", "user-frontend-001": [
"id": "submission-001", {
"user": "user-frontend-001", "_id": "submission-001",
"task": "task-html-intro", "id": "submission-001",
"result": "<html><head></head><body><h1>Hello</h1></body></html>", "user": "user-frontend-001",
"status": "needs_revision", "task": "task-html-intro",
"queueId": "queue-frontend-001", "result": "<html><head></head><body><h1>Hello</h1></body></html>",
"feedback": "Добавьте тег <title>", "status": "needs_revision",
"submittedAt": "2024-10-18T07:10:00.000Z", "queueId": "queue-frontend-001",
"checkedAt": "2024-10-18T07:10:15.000Z", "feedback": "Добавьте тег <title>",
"attemptNumber": 1 "submittedAt": "2024-10-18T07:10:00.000Z",
}, "checkedAt": "2024-10-18T07:10:15.000Z",
{ "attemptNumber": 1
"_id": "submission-002", },
"id": "submission-002", {
"user": "user-frontend-001", "_id": "submission-002",
"task": "task-html-intro", "id": "submission-002",
"result": "<html><head><title>Home</title></head><body><h1>Hello</h1></body></html>", "user": "user-frontend-001",
"status": "accepted", "task": "task-html-intro",
"queueId": "queue-frontend-002", "result": "<html><head><title>Home</title></head><body><h1>Hello</h1></body></html>",
"feedback": "Отличная работа!", "status": "accepted",
"submittedAt": "2024-10-18T07:20:00.000Z", "queueId": "queue-frontend-002",
"checkedAt": "2024-10-18T07:20:05.000Z", "feedback": "Отличная работа!",
"attemptNumber": 2 "submittedAt": "2024-10-18T07:20:00.000Z",
}, "checkedAt": "2024-10-18T07:20:05.000Z",
{ "attemptNumber": 2
"_id": "submission-003", },
"id": "submission-003", {
"user": "user-frontend-001", "_id": "submission-003",
"task": "task-react-component", "id": "submission-003",
"result": "export const StatCard = () => null;", "user": "user-frontend-001",
"status": "pending", "task": "task-react-component",
"queueId": "queue-frontend-003", "result": "export const StatCard = () => null;",
"feedback": null, "status": "pending",
"submittedAt": "2024-10-19T09:05:00.000Z", "queueId": "queue-frontend-003",
"checkedAt": null, "feedback": null,
"attemptNumber": 1 "submittedAt": "2024-10-19T09:05:00.000Z",
} "checkedAt": null,
] "attemptNumber": 1
}
]
}
} }

View File

@ -32,16 +32,18 @@ router.get('/challenge/chains', (req, res) => {
}) })
router.get('/challenge/chain/:id', (req, res) => { router.get('/challenge/chain/:id', (req, res) => {
const chains = readJson('chains.json') const data = readJson('chains.json')
const chains = data.body || data
const chain = chains.find((item) => item.id === req.params.id || item._id === req.params.id) const chain = chains.find((item) => item.id === req.params.id || item._id === req.params.id)
if (!chain) { if (!chain) {
return sendNotFound(res, `Цепочка ${req.params.id} не найдена`) return sendNotFound(res, `Цепочка ${req.params.id} не найдена`)
} }
return res.json(chain) return res.json({ success: true, body: chain })
}) })
router.get('/challenge/task/:id', (req, res) => { router.get('/challenge/task/:id', (req, res) => {
const chains = readJson('chains.json') const data = readJson('chains.json')
const chains = data.body || data
const task = chains const task = chains
.flatMap((chain) => chain.tasks || []) .flatMap((chain) => chain.tasks || [])
.find((item) => item.id === req.params.id || item._id === req.params.id) .find((item) => item.id === req.params.id || item._id === req.params.id)
@ -50,7 +52,7 @@ router.get('/challenge/task/:id', (req, res) => {
return sendNotFound(res, `Задание ${req.params.id} не найдено`) return sendNotFound(res, `Задание ${req.params.id} не найдено`)
} }
return res.json(task) return res.json({ success: true, body: task })
}) })
router.post('/challenge/submit', (req, res) => { router.post('/challenge/submit', (req, res) => {
@ -59,31 +61,34 @@ router.post('/challenge/submit', (req, res) => {
}) })
router.get('/challenge/check-status/:queueId', (req, res) => { router.get('/challenge/check-status/:queueId', (req, res) => {
const statuses = readJson('queue-status.json') const data = readJson('queue-status.json')
const statuses = data.body || data
const status = statuses[req.params.queueId] const status = statuses[req.params.queueId]
if (!status) { if (!status) {
return sendNotFound(res, `Статус очереди ${req.params.queueId} не найден`) return sendNotFound(res, `Статус очереди ${req.params.queueId} не найден`)
} }
return res.json(status) return res.json({ success: true, body: status })
}) })
router.get('/challenge/user/:userId/stats', (req, res) => { router.get('/challenge/user/:userId/stats', (req, res) => {
const statsMap = readJson('user-stats.json') const data = readJson('user-stats.json')
const statsMap = data.body || data
const stats = statsMap[req.params.userId] const stats = statsMap[req.params.userId]
if (!stats) { if (!stats) {
return sendNotFound(res, `Статистика пользователя ${req.params.userId} не найдена`) return sendNotFound(res, `Статистика пользователя ${req.params.userId} не найдена`)
} }
return res.json(stats) return res.json({ success: true, body: stats })
}) })
router.get('/challenge/user/:userId/submissions', (req, res) => { router.get('/challenge/user/:userId/submissions', (req, res) => {
const submissionsMap = readJson('user-submissions.json') const data = readJson('user-submissions.json')
const submissionsMap = data.body || data
const submissions = submissionsMap[req.params.userId] || [] const submissions = submissionsMap[req.params.userId] || []
return res.json(submissions) return res.json({ success: true, body: submissions })
}) })
router.get('/challenge/stats', (req, res) => { router.get('/challenge/stats', (req, res) => {