{
  "info": {
    "name": "FeedSystem API (All-in-One)",
    "_postman_id": "1a41aef7-6f8a-4bc4-8f55-0c6b12b930d2",
    "description": "All endpoints in one collection.\n\nSuggested run order:\n1) Account/Register\n2) Account/Login (auto-saves jwt_token)\n3) Account/Find By Username (auto-saves accountId)\n4) Social/* (optional)\n5) Video/Publish (auto-saves videoId)\n6) Like/* (optional)\n7) Feed/* (optional; copy next fields for pagination)\n\nNotes:\n- JWT protected endpoints require Authorization: Bearer {{jwt_token}}.\n- Pagination vars:\n  - /feed/listLatest: response.next_time (unix seconds) -> feedLatestTime\n  - /feed/listLikesCount: response.next_likes_count_before/next_id_before -> feedLikesCountBefore/feedLikesIDBefore",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "const ensure = (k, v) => {",
          "  const cur = pm.collectionVariables.get(k);",
          "  if (cur === undefined || cur === null || cur === \"\") pm.collectionVariables.set(k, v);",
          "};",
          "ensure(\"host\", \"http://localhost:8080\");",
          "ensure(\"username\", `user_${Date.now()}`);",
          "ensure(\"password\", \"pass123\");",
          "ensure(\"newPassword\", \"newpass456\");",
          "ensure(\"newUsername\", `user_renamed_${Date.now()}`);",
          "ensure(\"jwt_token\", \"\");",
          "ensure(\"accountId\", \"1\");",
          "ensure(\"vloggerId\", \"1\");",
          "ensure(\"authorId\", \"1\");",
          "ensure(\"videoId\", \"1\");",
          "ensure(\"publishedVideoId\", \"\");",
          "ensure(\"feedLimit\", \"10\");",
          "ensure(\"feedLatestTime\", \"0\");",
          "ensure(\"feedLikesCountBefore\", \"0\");",
          "ensure(\"feedLikesIDBefore\", \"0\");",
          "ensure(\"commentContent\", `comment_${Date.now()}`);",
          "ensure(\"commentId\", \"1\");"
        ]
      }
    }
  ],
  "item": [
    {
      "name": "Account",
      "item": [
        {
          "name": "Register Account",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"username\": \"{{username}}\",\n  \"password\": \"{{password}}\"\n}"
            },
            "url": {
              "raw": "{{host}}/account/register",
              "host": ["{{host}}"],
              "path": ["account", "register"]
            }
          }
        },
        {
          "name": "Login (save jwt_token)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try { json = pm.response.json(); } catch (e) {}",
                  "if (json && json.token) pm.collectionVariables.set(\"jwt_token\", json.token);",
                  "pm.test(\"Login returns token\", function () {",
                  "  pm.expect(json).to.have.property(\"token\");",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"username\": \"{{username}}\",\n  \"password\": \"{{password}}\"\n}"
            },
            "url": {
              "raw": "{{host}}/account/login",
              "host": ["{{host}}"],
              "path": ["account", "login"]
            }
          }
        },
        {
          "name": "Find By Username (save accountId)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try { json = pm.response.json(); } catch (e) {}",
                  "if (json && json.id) pm.collectionVariables.set(\"accountId\", String(json.id));",
                  "if (json && json.id) pm.collectionVariables.set(\"vloggerId\", String(json.id));",
                  "pm.test(\"FindByUsername returns id\", function () {",
                  "  pm.expect(json).to.have.property(\"id\");",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"username\": \"{{username}}\"\n}"
            },
            "url": {
              "raw": "{{host}}/account/findByUsername",
              "host": ["{{host}}"],
              "path": ["account", "findByUsername"]
            }
          }
        },
        {
          "name": "Find By ID",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"id\": {{accountId}}\n}"
            },
            "url": {
              "raw": "{{host}}/account/findByID",
              "host": ["{{host}}"],
              "path": ["account", "findByID"]
            }
          }
        },
        {
          "name": "Change Password",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"username\": \"{{username}}\",\n  \"old_password\": \"{{password}}\",\n  \"new_password\": \"{{newPassword}}\"\n}"
            },
            "url": {
              "raw": "{{host}}/account/changePassword",
              "host": ["{{host}}"],
              "path": ["account", "changePassword"]
            }
          }
        },
        {
          "name": "Rename (JWT protected)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try { json = pm.response.json(); } catch (e) {}",
                  "if (json && json.token) pm.collectionVariables.set(\"jwt_token\", json.token);",
                  "pm.test(\"Rename returns token\", function () {",
                  "  pm.expect(json).to.have.property(\"token\");",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Authorization", "value": "Bearer {{jwt_token}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"new_username\": \"{{newUsername}}\"\n}"
            },
            "url": {
              "raw": "{{host}}/account/rename",
              "host": ["{{host}}"],
              "path": ["account", "rename"]
            }
          }
        },
        {
          "name": "Logout (JWT protected)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Authorization", "value": "Bearer {{jwt_token}}" }
            ],
            "body": { "mode": "raw", "raw": "{}" },
            "url": {
              "raw": "{{host}}/account/logout",
              "host": ["{{host}}"],
              "path": ["account", "logout"]
            }
          }
        }
      ]
    },
    {
      "name": "Social",
      "item": [
        {
          "name": "Follow (JWT protected)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try { json = pm.response.json(); } catch (e) {}",
                  "pm.test(\"Follow returns message\", function () {",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Authorization", "value": "Bearer {{jwt_token}}" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"vlogger_id\": {{vloggerId}}\n}" },
            "url": {
              "raw": "{{host}}/social/follow",
              "host": ["{{host}}"],
              "path": ["social", "follow"]
            }
          }
        },
        {
          "name": "Get All Followers (JWT protected)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try { json = pm.response.json(); } catch (e) {}",
                  "pm.test(\"GetAllFollowers returns array\", function () {",
                  "  pm.expect(json).to.have.property(\"followers\");",
                  "  pm.expect(json.followers).to.be.an(\"array\");",
                  "});",
                  "pm.test(\"Followers contains current account\", function () {",
                  "  const accountId = pm.collectionVariables.get(\"accountId\");",
                  "  const ids = (json.followers || []).map(x => String(x && x.id));",
                  "  pm.expect(ids).to.include(accountId);",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Authorization", "value": "Bearer {{jwt_token}}" }
            ],
            "body": { "mode": "raw", "raw": "{}" },
            "url": {
              "raw": "{{host}}/social/getAllFollowers",
              "host": ["{{host}}"],
              "path": ["social", "getAllFollowers"]
            }
          }
        },
        {
          "name": "Get All Vloggers (JWT protected)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try { json = pm.response.json(); } catch (e) {}",
                  "pm.test(\"GetAllVloggers returns array\", function () {",
                  "  pm.expect(json).to.have.property(\"vloggers\");",
                  "  pm.expect(json.vloggers).to.be.an(\"array\");",
                  "});",
                  "pm.test(\"Vloggers contains current account\", function () {",
                  "  const accountId = pm.collectionVariables.get(\"accountId\");",
                  "  const ids = (json.vloggers || []).map(x => String(x && x.id));",
                  "  pm.expect(ids).to.include(accountId);",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Authorization", "value": "Bearer {{jwt_token}}" }
            ],
            "body": { "mode": "raw", "raw": "{}" },
            "url": {
              "raw": "{{host}}/social/getAllVloggers",
              "host": ["{{host}}"],
              "path": ["social", "getAllVloggers"]
            }
          }
        },
        {
          "name": "Unfollow (JWT protected)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try { json = pm.response.json(); } catch (e) {}",
                  "pm.test(\"Unfollow returns message\", function () {",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Authorization", "value": "Bearer {{jwt_token}}" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"vlogger_id\": {{vloggerId}}\n}" },
            "url": {
              "raw": "{{host}}/social/unfollow",
              "host": ["{{host}}"],
              "path": ["social", "unfollow"]
            }
          }
        }
      ]
    },
    {
      "name": "Video",
      "item": [
        {
          "name": "Publish Video (JWT protected) (save videoId)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try { json = pm.response.json(); } catch (e) {}",
                  "if (json && json.id) pm.collectionVariables.set(\"videoId\", String(json.id));",
                  "if (json && json.id) pm.collectionVariables.set(\"publishedVideoId\", String(json.id));",
                  "if (json && json.author_id) pm.collectionVariables.set(\"authorId\", String(json.author_id));",
                  "pm.test(\"Publish returns id\", function () {",
                  "  pm.expect(json).to.have.property(\"id\");",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Authorization", "value": "Bearer {{jwt_token}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"title\": \"demo video\",\n  \"description\": \"manual request\",\n  \"play_url\": \"http://example.com/demo.mp4\",\n  \"cover_url\": \"http://example.com/demo.jpg\"\n}"
            },
            "url": {
              "raw": "{{host}}/video/publish",
              "host": ["{{host}}"],
              "path": ["video", "publish"]
            }
          }
        },
        {
          "name": "Get Video Detail",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"id\": {{videoId}}\n}" },
            "url": {
              "raw": "{{host}}/video/getDetail",
              "host": ["{{host}}"],
              "path": ["video", "getDetail"]
            }
          }
        },
        {
          "name": "List Videos By Author ID",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"author_id\": {{authorId}}\n}" },
            "url": {
              "raw": "{{host}}/video/listByAuthorID",
              "host": ["{{host}}"],
              "path": ["video", "listByAuthorID"]
            }
          }
        }
      ]
    },
    {
      "name": "Like",
      "item": [
        {
          "name": "Get Likes Count",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"video_id\": {{videoId}}\n}" },
            "url": {
              "raw": "{{host}}/like/getLikesCount",
              "host": ["{{host}}"],
              "path": ["like", "getLikesCount"]
            }
          }
        },
        {
          "name": "Is Liked (JWT protected)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Authorization", "value": "Bearer {{jwt_token}}" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"video_id\": {{videoId}}\n}" },
            "url": {
              "raw": "{{host}}/like/isLiked",
              "host": ["{{host}}"],
              "path": ["like", "isLiked"]
            }
          }
        },
        {
          "name": "Like Video (JWT protected)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Authorization", "value": "Bearer {{jwt_token}}" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"video_id\": {{videoId}}\n}" },
            "url": {
              "raw": "{{host}}/like/like",
              "host": ["{{host}}"],
              "path": ["like", "like"]
            }
          }
        },
        {
          "name": "Unlike Video (JWT protected)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Authorization", "value": "Bearer {{jwt_token}}" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"video_id\": {{videoId}}\n}" },
            "url": {
              "raw": "{{host}}/like/unlike",
              "host": ["{{host}}"],
              "path": ["like", "unlike"]
            }
          }
        }
      ]
    },
    {
      "name": "Comment",
      "item": [
        {
          "name": "Publish Comment (JWT protected) (save commentId)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try { json = pm.response.json(); } catch (e) {}",
                  "if (json && json.id) pm.collectionVariables.set(\"commentId\", String(json.id));",
                  "pm.test(\"Publish comment returns id\", function () {",
                  "  pm.expect(json).to.have.property(\"id\");",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Authorization", "value": "Bearer {{jwt_token}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"video_id\": {{videoId}},\n  \"content\": \"{{commentContent}}\"\n}"
            },
            "url": {
              "raw": "{{host}}/comment/publish",
              "host": ["{{host}}"],
              "path": ["comment", "publish"]
            }
          }
        },
        {
          "name": "List All Comments",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"video_id\": {{videoId}}\n}" },
            "url": {
              "raw": "{{host}}/comment/listAll",
              "host": ["{{host}}"],
              "path": ["comment", "listAll"]
            }
          }
        },
        {
          "name": "Delete Comment (JWT protected)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Authorization", "value": "Bearer {{jwt_token}}" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"comment_id\": {{commentId}}\n}" },
            "url": {
              "raw": "{{host}}/comment/delete",
              "host": ["{{host}}"],
              "path": ["comment", "delete"]
            }
          }
        }
      ]
    },
    {
      "name": "Feed",
      "item": [
        {
          "name": "List By Following (JWT protected)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try { json = pm.response.json(); } catch (e) {}",
                  "pm.test(\"ListByFollowing returns shape\", function () {",
                  "  pm.expect(json).to.have.property(\"video_list\");",
                  "  pm.expect(json.video_list).to.be.an(\"array\");",
                  "  pm.expect(json).to.have.property(\"next_time\");",
                  "  pm.expect(json).to.have.property(\"has_more\");",
                  "});",
                  "pm.test(\"ListByFollowing contains published video (if any)\", function () {",
                  "  const publishedVideoId = pm.collectionVariables.get(\"publishedVideoId\");",
                  "  if (!publishedVideoId) return;",
                  "  const ids = (json.video_list || []).map(x => String(x && x.id));",
                  "  pm.expect(ids).to.include(String(publishedVideoId));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Authorization", "value": "Bearer {{jwt_token}}" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"limit\": {{feedLimit}}\n}" },
            "url": {
              "raw": "{{host}}/feed/listByFollowing",
              "host": ["{{host}}"],
              "path": ["feed", "listByFollowing"]
            }
          }
        },
        {
          "name": "List Latest Feed (save feedLatestTime)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try { json = pm.response.json(); } catch (e) {}",
                  "if (json && typeof json.next_time !== \"undefined\") pm.collectionVariables.set(\"feedLatestTime\", String(json.next_time));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"limit\": {{feedLimit}},\n  \"latest_time\": {{feedLatestTime}}\n}"
            },
            "url": {
              "raw": "{{host}}/feed/listLatest",
              "host": ["{{host}}"],
              "path": ["feed", "listLatest"]
            }
          }
        },
        {
          "name": "List Feed By Likes Count (save feedLikesCountBefore)",
          "event": [
            {
              "listen": "test",
              "script": {
                  "type": "text/javascript",
                  "exec": [
                  "let json = {};",
                  "try { json = pm.response.json(); } catch (e) {}",
                  "if (json && typeof json.next_likes_count_before !== \"undefined\") pm.collectionVariables.set(\"feedLikesCountBefore\", String(json.next_likes_count_before));",
                  "if (json && typeof json.next_id_before !== \"undefined\") pm.collectionVariables.set(\"feedLikesIDBefore\", String(json.next_id_before));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"limit\": {{feedLimit}},\n  \"likes_count_before\": {{feedLikesCountBefore}},\n  \"id_before\": {{feedLikesIDBefore}}\n}"
            },
            "url": {
              "raw": "{{host}}/feed/listLikesCount",
              "host": ["{{host}}"],
              "path": ["feed", "listLikesCount"]
            }
          }
        }
      ]
    }
  ],
  "variable": [
    { "key": "host", "value": "http://localhost:8080" },
    { "key": "username", "value": "" },
    { "key": "password", "value": "" },
    { "key": "newPassword", "value": "" },
    { "key": "newUsername", "value": "" },
    { "key": "jwt_token", "value": "" },
    { "key": "accountId", "value": "" },
    { "key": "vloggerId", "value": "" },
    { "key": "authorId", "value": "" },
    { "key": "videoId", "value": "" },
    { "key": "publishedVideoId", "value": "" },
    { "key": "feedLimit", "value": "10" },
    { "key": "feedLatestTime", "value": "0" },
    { "key": "feedLikesCountBefore", "value": "0" },
    { "key": "feedLikesIDBefore", "value": "0" },
    { "key": "commentContent", "value": "" },
    { "key": "commentId", "value": "" }
  ]
}
