{ "info": { "name": "Yimaru Dynamic Question Type Builder API", "_postman_id": "f0f9c795-09aa-4f5a-9cc0-1f2fcb0f1b01", "description": "Complete Postman collection for the dynamic question type builder feature, including catalog, validation, reusable type-definition CRUD, and question create/update using question_type_definition_id.", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "variable": [ { "key": "baseUrl", "value": "http://localhost:8080" }, { "key": "apiPrefix", "value": "/api/v1" }, { "key": "token", "value": "" }, { "key": "questionTypeDefinitionId", "value": "" }, { "key": "questionId", "value": "" } ], "auth": { "type": "bearer", "bearer": [ { "key": "token", "value": "{{token}}", "type": "string" } ] }, "item": [ { "name": "01 - Builder Component Catalog", "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}{{apiPrefix}}/questions/component-catalog", "host": [ "{{baseUrl}}" ], "path": [ "{{apiPrefix}}", "questions", "component-catalog" ] }, "description": "Returns supported stimulus and response component kinds for dynamic type definitions." }, "response": [] }, { "name": "02 - Validate Dynamic Type Definition", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": { "mode": "raw", "raw": "{\n \"stimulus_component_kinds\": [\"INSTRUCTION\", \"TEXT_PASSAGE\"],\n \"response_component_kinds\": [\"MULTIPLE_CHOICE\"]\n}" }, "url": { "raw": "{{baseUrl}}{{apiPrefix}}/questions/validate-question-type-definition", "host": [ "{{baseUrl}}" ], "path": [ "{{apiPrefix}}", "questions", "validate-question-type-definition" ] }, "description": "Validates a candidate dynamic question-type definition before saving." }, "response": [] }, { "name": "03 - Create Question Type Definition (MCQ Dynamic)", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": { "mode": "raw", "raw": "{\n \"key\": \"mcq_dynamic_vocab\",\n \"display_name\": \"MCQ Dynamic Vocabulary\",\n \"description\": \"Dynamic multiple-choice template for vocabulary checks.\",\n \"stimulus_component_kinds\": [\"INSTRUCTION\", \"TEXT_PASSAGE\"],\n \"response_component_kinds\": [\"MULTIPLE_CHOICE\"],\n \"status\": \"ACTIVE\"\n}" }, "url": { "raw": "{{baseUrl}}{{apiPrefix}}/questions/type-definitions", "host": [ "{{baseUrl}}" ], "path": [ "{{apiPrefix}}", "questions", "type-definitions" ] }, "description": "Creates a reusable dynamic question-type definition." }, "event": [ { "listen": "test", "script": { "exec": [ "pm.test('Status is 201', function () { pm.response.to.have.status(201); });", "var json = pm.response.json();", "if (json && json.data && json.data.id) {", " pm.collectionVariables.set('questionTypeDefinitionId', json.data.id);", "}" ], "type": "text/javascript" } } ], "response": [] }, { "name": "04 - List Question Type Definitions (Include System)", "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}{{apiPrefix}}/questions/type-definitions?include_system=true&status=ACTIVE", "host": [ "{{baseUrl}}" ], "path": [ "{{apiPrefix}}", "questions", "type-definitions" ], "query": [ { "key": "include_system", "value": "true" }, { "key": "status", "value": "ACTIVE" } ] }, "description": "Lists reusable dynamic definitions (system + custom)." }, "response": [] }, { "name": "05 - Get Question Type Definition By ID", "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}{{apiPrefix}}/questions/type-definitions/{{questionTypeDefinitionId}}", "host": [ "{{baseUrl}}" ], "path": [ "{{apiPrefix}}", "questions", "type-definitions", "{{questionTypeDefinitionId}}" ] }, "description": "Fetches one dynamic type-definition by ID." }, "response": [] }, { "name": "06 - Update Question Type Definition", "request": { "method": "PUT", "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": { "mode": "raw", "raw": "{\n \"display_name\": \"MCQ Dynamic Vocabulary (Updated)\",\n \"description\": \"Updated dynamic MCQ template.\",\n \"stimulus_component_kinds\": [\"INSTRUCTION\", \"TEXT_PASSAGE\", \"IMAGE\"],\n \"response_component_kinds\": [\"MULTIPLE_CHOICE\", \"ANSWER_TIMER\"],\n \"status\": \"ACTIVE\"\n}" }, "url": { "raw": "{{baseUrl}}{{apiPrefix}}/questions/type-definitions/{{questionTypeDefinitionId}}", "host": [ "{{baseUrl}}" ], "path": [ "{{apiPrefix}}", "questions", "type-definitions", "{{questionTypeDefinitionId}}" ] }, "description": "Updates dynamic definition (except key/system flag)." }, "response": [] }, { "name": "07 - Create Question Using question_type_definition_id", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": { "mode": "raw", "raw": "{\n \"question_text\": \"Choose the correct synonym for 'rapid'.\",\n \"question_type_definition_id\": {{questionTypeDefinitionId}},\n \"difficulty_level\": \"EASY\",\n \"points\": 1,\n \"status\": \"PUBLISHED\",\n \"options\": [\n { \"option_text\": \"Slow\", \"option_order\": 1, \"is_correct\": false },\n { \"option_text\": \"Quick\", \"option_order\": 2, \"is_correct\": true },\n { \"option_text\": \"Heavy\", \"option_order\": 3, \"is_correct\": false },\n { \"option_text\": \"Late\", \"option_order\": 4, \"is_correct\": false }\n ]\n}" }, "url": { "raw": "{{baseUrl}}{{apiPrefix}}/questions", "host": [ "{{baseUrl}}" ], "path": [ "{{apiPrefix}}", "questions" ] }, "description": "Creates a question by binding to a dynamic definition. Backend infers runtime question_type from the definition." }, "event": [ { "listen": "test", "script": { "exec": [ "pm.test('Status is 201', function () { pm.response.to.have.status(201); });", "var json = pm.response.json();", "if (json && json.data && json.data.id) {", " pm.collectionVariables.set('questionId', json.data.id);", "}" ], "type": "text/javascript" } } ], "response": [] }, { "name": "08 - Create Question (Explicit question_type + Definition)", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": { "mode": "raw", "raw": "{\n \"question_text\": \"Pick the antonym of 'expand'.\",\n \"question_type\": \"MCQ\",\n \"question_type_definition_id\": {{questionTypeDefinitionId}},\n \"difficulty_level\": \"MEDIUM\",\n \"points\": 2,\n \"options\": [\n { \"option_text\": \"Increase\", \"option_order\": 1, \"is_correct\": false },\n { \"option_text\": \"Contract\", \"option_order\": 2, \"is_correct\": true },\n { \"option_text\": \"Stretch\", \"option_order\": 3, \"is_correct\": false },\n { \"option_text\": \"Grow\", \"option_order\": 4, \"is_correct\": false }\n ]\n}" }, "url": { "raw": "{{baseUrl}}{{apiPrefix}}/questions", "host": [ "{{baseUrl}}" ], "path": [ "{{apiPrefix}}", "questions" ] }, "description": "Valid combination: explicit question_type must match the type inferred from the selected definition." }, "response": [] }, { "name": "09 - Update Question (Switch/Attach Definition)", "request": { "method": "PUT", "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": { "mode": "raw", "raw": "{\n \"question_type_definition_id\": {{questionTypeDefinitionId}},\n \"question_type\": \"MCQ\",\n \"question_text\": \"Choose the best definition of 'meticulous'.\",\n \"options\": [\n { \"option_text\": \"Careless\", \"option_order\": 1, \"is_correct\": false },\n { \"option_text\": \"Very careful and precise\", \"option_order\": 2, \"is_correct\": true },\n { \"option_text\": \"Quickly done\", \"option_order\": 3, \"is_correct\": false }\n ],\n \"status\": \"PUBLISHED\"\n}" }, "url": { "raw": "{{baseUrl}}{{apiPrefix}}/questions/{{questionId}}", "host": [ "{{baseUrl}}" ], "path": [ "{{apiPrefix}}", "questions", "{{questionId}}" ] }, "description": "Updates a question and links (or re-links) it to a dynamic definition." }, "response": [] }, { "name": "10 - Get Question By ID", "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}{{apiPrefix}}/questions/{{questionId}}", "host": [ "{{baseUrl}}" ], "path": [ "{{apiPrefix}}", "questions", "{{questionId}}" ] }, "description": "Returns question details (options/short_answers/audio fields as applicable)." }, "response": [] }, { "name": "11 - List Questions", "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}{{apiPrefix}}/questions?question_type=MCQ&limit=10&offset=0", "host": [ "{{baseUrl}}" ], "path": [ "{{apiPrefix}}", "questions" ], "query": [ { "key": "question_type", "value": "MCQ" }, { "key": "limit", "value": "10" }, { "key": "offset", "value": "0" } ] }, "description": "Lists questions filtered by runtime question_type." }, "response": [] }, { "name": "12 - Negative Test: Mismatched Type and Definition", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": { "mode": "raw", "raw": "{\n \"question_text\": \"This should fail.\",\n \"question_type\": \"AUDIO\",\n \"question_type_definition_id\": {{questionTypeDefinitionId}},\n \"options\": [\n { \"option_text\": \"A\", \"option_order\": 1, \"is_correct\": true }\n ]\n}" }, "url": { "raw": "{{baseUrl}}{{apiPrefix}}/questions", "host": [ "{{baseUrl}}" ], "path": [ "{{apiPrefix}}", "questions" ] }, "description": "Expected 400 because explicit question_type does not match inferred type from definition." }, "response": [] }, { "name": "13 - Delete Custom Question Type Definition", "request": { "method": "DELETE", "header": [], "url": { "raw": "{{baseUrl}}{{apiPrefix}}/questions/type-definitions/{{questionTypeDefinitionId}}", "host": [ "{{baseUrl}}" ], "path": [ "{{apiPrefix}}", "questions", "type-definitions", "{{questionTypeDefinitionId}}" ] }, "description": "Deletes a custom definition. System definitions cannot be deleted." }, "response": [] } ] }