Implement public FAQ read endpoints and admin CRUD with RBAC, persistence, and migrations, then regenerate Swagger and add a complete Postman collection so frontend/admin teams can integrate and validate the feature end-to-end. Co-authored-by: Cursor <cursoragent@cursor.com>
624 lines
16 KiB
JSON
624 lines
16 KiB
JSON
{
|
|
"info": {
|
|
"_postman_id": "8bbdb568-d64a-4f8e-8f89-0f57f6e7a65e",
|
|
"name": "FAQ Management - Complete Flow",
|
|
"description": "Complete collection for FAQ feature: public listing/detail and admin CRUD management with validation/error cases.",
|
|
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
|
},
|
|
"auth": {
|
|
"type": "bearer",
|
|
"bearer": [
|
|
{
|
|
"key": "token",
|
|
"value": "{{access_token}}",
|
|
"type": "string"
|
|
}
|
|
]
|
|
},
|
|
"variable": [
|
|
{
|
|
"key": "base_url",
|
|
"value": "http://localhost:8080"
|
|
},
|
|
{
|
|
"key": "access_token",
|
|
"value": ""
|
|
},
|
|
{
|
|
"key": "faq_id",
|
|
"value": ""
|
|
},
|
|
{
|
|
"key": "faq_id_inactive",
|
|
"value": ""
|
|
}
|
|
],
|
|
"item": [
|
|
{
|
|
"name": "01 - Admin FAQ CRUD",
|
|
"item": [
|
|
{
|
|
"name": "Create FAQ (ACTIVE)",
|
|
"event": [
|
|
{
|
|
"listen": "test",
|
|
"script": {
|
|
"exec": [
|
|
"pm.test(\"Status code is 201\", function () {",
|
|
" pm.response.to.have.status(201);",
|
|
"});",
|
|
"const body = pm.response.json();",
|
|
"pm.test(\"FAQ ID exists\", function () {",
|
|
" pm.expect(body.data.id).to.be.a(\"number\");",
|
|
"});",
|
|
"pm.collectionVariables.set(\"faq_id\", body.data.id);"
|
|
],
|
|
"type": "text/javascript"
|
|
}
|
|
}
|
|
],
|
|
"request": {
|
|
"method": "POST",
|
|
"header": [
|
|
{
|
|
"key": "Content-Type",
|
|
"value": "application/json",
|
|
"type": "text"
|
|
}
|
|
],
|
|
"body": {
|
|
"mode": "raw",
|
|
"raw": "{\n \"question\": \"How do I reset my password?\",\n \"answer\": \"Go to login and click 'Forgot Password'.\",\n \"category\": \"Account\",\n \"display_order\": 1,\n \"status\": \"ACTIVE\"\n}"
|
|
},
|
|
"url": {
|
|
"raw": "{{base_url}}/api/v1/admin/faqs",
|
|
"host": [
|
|
"{{base_url}}"
|
|
],
|
|
"path": [
|
|
"api",
|
|
"v1",
|
|
"admin",
|
|
"faqs"
|
|
]
|
|
}
|
|
},
|
|
"response": []
|
|
},
|
|
{
|
|
"name": "Create FAQ (INACTIVE)",
|
|
"event": [
|
|
{
|
|
"listen": "test",
|
|
"script": {
|
|
"exec": [
|
|
"pm.test(\"Status code is 201\", function () {",
|
|
" pm.response.to.have.status(201);",
|
|
"});",
|
|
"const body = pm.response.json();",
|
|
"pm.collectionVariables.set(\"faq_id_inactive\", body.data.id);"
|
|
],
|
|
"type": "text/javascript"
|
|
}
|
|
}
|
|
],
|
|
"request": {
|
|
"method": "POST",
|
|
"header": [
|
|
{
|
|
"key": "Content-Type",
|
|
"value": "application/json",
|
|
"type": "text"
|
|
}
|
|
],
|
|
"body": {
|
|
"mode": "raw",
|
|
"raw": "{\n \"question\": \"How can I change app language?\",\n \"answer\": \"Open settings and choose language preference.\",\n \"category\": \"General\",\n \"display_order\": 50,\n \"status\": \"INACTIVE\"\n}"
|
|
},
|
|
"url": {
|
|
"raw": "{{base_url}}/api/v1/admin/faqs",
|
|
"host": [
|
|
"{{base_url}}"
|
|
],
|
|
"path": [
|
|
"api",
|
|
"v1",
|
|
"admin",
|
|
"faqs"
|
|
]
|
|
}
|
|
},
|
|
"response": []
|
|
},
|
|
{
|
|
"name": "List FAQs (Admin - All)",
|
|
"request": {
|
|
"method": "GET",
|
|
"header": [],
|
|
"url": {
|
|
"raw": "{{base_url}}/api/v1/admin/faqs?limit=20&offset=0",
|
|
"host": [
|
|
"{{base_url}}"
|
|
],
|
|
"path": [
|
|
"api",
|
|
"v1",
|
|
"admin",
|
|
"faqs"
|
|
],
|
|
"query": [
|
|
{
|
|
"key": "limit",
|
|
"value": "20"
|
|
},
|
|
{
|
|
"key": "offset",
|
|
"value": "0"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"response": []
|
|
},
|
|
{
|
|
"name": "List FAQs (Admin - Filter ACTIVE)",
|
|
"request": {
|
|
"method": "GET",
|
|
"header": [],
|
|
"url": {
|
|
"raw": "{{base_url}}/api/v1/admin/faqs?status=ACTIVE&limit=20&offset=0",
|
|
"host": [
|
|
"{{base_url}}"
|
|
],
|
|
"path": [
|
|
"api",
|
|
"v1",
|
|
"admin",
|
|
"faqs"
|
|
],
|
|
"query": [
|
|
{
|
|
"key": "status",
|
|
"value": "ACTIVE"
|
|
},
|
|
{
|
|
"key": "limit",
|
|
"value": "20"
|
|
},
|
|
{
|
|
"key": "offset",
|
|
"value": "0"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"response": []
|
|
},
|
|
{
|
|
"name": "Get FAQ By ID (Admin)",
|
|
"request": {
|
|
"method": "GET",
|
|
"header": [],
|
|
"url": {
|
|
"raw": "{{base_url}}/api/v1/admin/faqs/{{faq_id}}",
|
|
"host": [
|
|
"{{base_url}}"
|
|
],
|
|
"path": [
|
|
"api",
|
|
"v1",
|
|
"admin",
|
|
"faqs",
|
|
"{{faq_id}}"
|
|
]
|
|
}
|
|
},
|
|
"response": []
|
|
},
|
|
{
|
|
"name": "Update FAQ (Admin)",
|
|
"event": [
|
|
{
|
|
"listen": "test",
|
|
"script": {
|
|
"exec": [
|
|
"pm.test(\"Status code is 200\", function () {",
|
|
" pm.response.to.have.status(200);",
|
|
"});"
|
|
],
|
|
"type": "text/javascript"
|
|
}
|
|
}
|
|
],
|
|
"request": {
|
|
"method": "PUT",
|
|
"header": [
|
|
{
|
|
"key": "Content-Type",
|
|
"value": "application/json",
|
|
"type": "text"
|
|
}
|
|
],
|
|
"body": {
|
|
"mode": "raw",
|
|
"raw": "{\n \"question\": \"How do I reset my account password?\",\n \"answer\": \"Tap 'Forgot Password' on login and follow OTP steps.\",\n \"category\": \"Account\",\n \"display_order\": 2,\n \"status\": \"ACTIVE\"\n}"
|
|
},
|
|
"url": {
|
|
"raw": "{{base_url}}/api/v1/admin/faqs/{{faq_id}}",
|
|
"host": [
|
|
"{{base_url}}"
|
|
],
|
|
"path": [
|
|
"api",
|
|
"v1",
|
|
"admin",
|
|
"faqs",
|
|
"{{faq_id}}"
|
|
]
|
|
}
|
|
},
|
|
"response": []
|
|
},
|
|
{
|
|
"name": "Archive FAQ (Set INACTIVE)",
|
|
"request": {
|
|
"method": "PUT",
|
|
"header": [
|
|
{
|
|
"key": "Content-Type",
|
|
"value": "application/json",
|
|
"type": "text"
|
|
}
|
|
],
|
|
"body": {
|
|
"mode": "raw",
|
|
"raw": "{\n \"status\": \"INACTIVE\"\n}"
|
|
},
|
|
"url": {
|
|
"raw": "{{base_url}}/api/v1/admin/faqs/{{faq_id}}",
|
|
"host": [
|
|
"{{base_url}}"
|
|
],
|
|
"path": [
|
|
"api",
|
|
"v1",
|
|
"admin",
|
|
"faqs",
|
|
"{{faq_id}}"
|
|
]
|
|
}
|
|
},
|
|
"response": []
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"name": "02 - Public FAQ Endpoints",
|
|
"item": [
|
|
{
|
|
"name": "List Public FAQs (Only ACTIVE)",
|
|
"request": {
|
|
"auth": {
|
|
"type": "noauth"
|
|
},
|
|
"method": "GET",
|
|
"header": [],
|
|
"url": {
|
|
"raw": "{{base_url}}/api/v1/faqs?limit=20&offset=0",
|
|
"host": [
|
|
"{{base_url}}"
|
|
],
|
|
"path": [
|
|
"api",
|
|
"v1",
|
|
"faqs"
|
|
],
|
|
"query": [
|
|
{
|
|
"key": "limit",
|
|
"value": "20"
|
|
},
|
|
{
|
|
"key": "offset",
|
|
"value": "0"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"response": []
|
|
},
|
|
{
|
|
"name": "List Public FAQs by Category",
|
|
"request": {
|
|
"auth": {
|
|
"type": "noauth"
|
|
},
|
|
"method": "GET",
|
|
"header": [],
|
|
"url": {
|
|
"raw": "{{base_url}}/api/v1/faqs?category=Account&limit=20&offset=0",
|
|
"host": [
|
|
"{{base_url}}"
|
|
],
|
|
"path": [
|
|
"api",
|
|
"v1",
|
|
"faqs"
|
|
],
|
|
"query": [
|
|
{
|
|
"key": "category",
|
|
"value": "Account"
|
|
},
|
|
{
|
|
"key": "limit",
|
|
"value": "20"
|
|
},
|
|
{
|
|
"key": "offset",
|
|
"value": "0"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"response": []
|
|
},
|
|
{
|
|
"name": "Get Public FAQ By ID (ACTIVE only)",
|
|
"request": {
|
|
"auth": {
|
|
"type": "noauth"
|
|
},
|
|
"method": "GET",
|
|
"header": [],
|
|
"url": {
|
|
"raw": "{{base_url}}/api/v1/faqs/{{faq_id}}",
|
|
"host": [
|
|
"{{base_url}}"
|
|
],
|
|
"path": [
|
|
"api",
|
|
"v1",
|
|
"faqs",
|
|
"{{faq_id}}"
|
|
]
|
|
}
|
|
},
|
|
"response": []
|
|
},
|
|
{
|
|
"name": "Get Public FAQ By ID (INACTIVE should be 404)",
|
|
"event": [
|
|
{
|
|
"listen": "test",
|
|
"script": {
|
|
"exec": [
|
|
"pm.test(\"Status code is 404\", function () {",
|
|
" pm.response.to.have.status(404);",
|
|
"});"
|
|
],
|
|
"type": "text/javascript"
|
|
}
|
|
}
|
|
],
|
|
"request": {
|
|
"auth": {
|
|
"type": "noauth"
|
|
},
|
|
"method": "GET",
|
|
"header": [],
|
|
"url": {
|
|
"raw": "{{base_url}}/api/v1/faqs/{{faq_id_inactive}}",
|
|
"host": [
|
|
"{{base_url}}"
|
|
],
|
|
"path": [
|
|
"api",
|
|
"v1",
|
|
"faqs",
|
|
"{{faq_id_inactive}}"
|
|
]
|
|
}
|
|
},
|
|
"response": []
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"name": "03 - Validation & Auth Errors",
|
|
"item": [
|
|
{
|
|
"name": "Create FAQ Missing Question - Expect 400",
|
|
"event": [
|
|
{
|
|
"listen": "test",
|
|
"script": {
|
|
"exec": [
|
|
"pm.test(\"Status code is 400\", function () {",
|
|
" pm.response.to.have.status(400);",
|
|
"});"
|
|
],
|
|
"type": "text/javascript"
|
|
}
|
|
}
|
|
],
|
|
"request": {
|
|
"method": "POST",
|
|
"header": [
|
|
{
|
|
"key": "Content-Type",
|
|
"value": "application/json",
|
|
"type": "text"
|
|
}
|
|
],
|
|
"body": {
|
|
"mode": "raw",
|
|
"raw": "{\n \"answer\": \"Missing question should fail\",\n \"status\": \"ACTIVE\"\n}"
|
|
},
|
|
"url": {
|
|
"raw": "{{base_url}}/api/v1/admin/faqs",
|
|
"host": [
|
|
"{{base_url}}"
|
|
],
|
|
"path": [
|
|
"api",
|
|
"v1",
|
|
"admin",
|
|
"faqs"
|
|
]
|
|
}
|
|
},
|
|
"response": []
|
|
},
|
|
{
|
|
"name": "Create FAQ Invalid Status - Expect 400",
|
|
"event": [
|
|
{
|
|
"listen": "test",
|
|
"script": {
|
|
"exec": [
|
|
"pm.test(\"Status code is 400\", function () {",
|
|
" pm.response.to.have.status(400);",
|
|
"});"
|
|
],
|
|
"type": "text/javascript"
|
|
}
|
|
}
|
|
],
|
|
"request": {
|
|
"method": "POST",
|
|
"header": [
|
|
{
|
|
"key": "Content-Type",
|
|
"value": "application/json",
|
|
"type": "text"
|
|
}
|
|
],
|
|
"body": {
|
|
"mode": "raw",
|
|
"raw": "{\n \"question\": \"Sample question\",\n \"answer\": \"Sample answer\",\n \"status\": \"PUBLISHED\"\n}"
|
|
},
|
|
"url": {
|
|
"raw": "{{base_url}}/api/v1/admin/faqs",
|
|
"host": [
|
|
"{{base_url}}"
|
|
],
|
|
"path": [
|
|
"api",
|
|
"v1",
|
|
"admin",
|
|
"faqs"
|
|
]
|
|
}
|
|
},
|
|
"response": []
|
|
},
|
|
{
|
|
"name": "List Admin FAQs Without Auth - Expect 401/403",
|
|
"request": {
|
|
"auth": {
|
|
"type": "noauth"
|
|
},
|
|
"method": "GET",
|
|
"header": [],
|
|
"url": {
|
|
"raw": "{{base_url}}/api/v1/admin/faqs",
|
|
"host": [
|
|
"{{base_url}}"
|
|
],
|
|
"path": [
|
|
"api",
|
|
"v1",
|
|
"admin",
|
|
"faqs"
|
|
]
|
|
}
|
|
},
|
|
"response": []
|
|
},
|
|
{
|
|
"name": "Get Missing FAQ (Admin) - Expect 404",
|
|
"event": [
|
|
{
|
|
"listen": "test",
|
|
"script": {
|
|
"exec": [
|
|
"pm.test(\"Status code is 404\", function () {",
|
|
" pm.response.to.have.status(404);",
|
|
"});"
|
|
],
|
|
"type": "text/javascript"
|
|
}
|
|
}
|
|
],
|
|
"request": {
|
|
"method": "GET",
|
|
"header": [],
|
|
"url": {
|
|
"raw": "{{base_url}}/api/v1/admin/faqs/99999999",
|
|
"host": [
|
|
"{{base_url}}"
|
|
],
|
|
"path": [
|
|
"api",
|
|
"v1",
|
|
"admin",
|
|
"faqs",
|
|
"99999999"
|
|
]
|
|
}
|
|
},
|
|
"response": []
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"name": "04 - Cleanup",
|
|
"item": [
|
|
{
|
|
"name": "Delete FAQ (ACTIVE/UPDATED)",
|
|
"request": {
|
|
"method": "DELETE",
|
|
"header": [],
|
|
"url": {
|
|
"raw": "{{base_url}}/api/v1/admin/faqs/{{faq_id}}",
|
|
"host": [
|
|
"{{base_url}}"
|
|
],
|
|
"path": [
|
|
"api",
|
|
"v1",
|
|
"admin",
|
|
"faqs",
|
|
"{{faq_id}}"
|
|
]
|
|
}
|
|
},
|
|
"response": []
|
|
},
|
|
{
|
|
"name": "Delete FAQ (INACTIVE)",
|
|
"request": {
|
|
"method": "DELETE",
|
|
"header": [],
|
|
"url": {
|
|
"raw": "{{base_url}}/api/v1/admin/faqs/{{faq_id_inactive}}",
|
|
"host": [
|
|
"{{base_url}}"
|
|
],
|
|
"path": [
|
|
"api",
|
|
"v1",
|
|
"admin",
|
|
"faqs",
|
|
"{{faq_id_inactive}}"
|
|
]
|
|
}
|
|
},
|
|
"response": []
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|