diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..aff60c9 --- /dev/null +++ b/.env.sample @@ -0,0 +1,75 @@ +NODE_ENV=local +TESTING=true +########################################################## +API_VERSION=0.1.000 +########################################################## + +PORT=2345 +THIS_BASE_URL=http://localhost:2345 +SERVICE_NAME=charcoal +USER_ACCESS_TOKEN_SECRET= +CIPHER_SALT= +HTTP_LOGGING=false +MAX_UPLOAD_FILE_SIZE=104857600 # 100MB +########################################################## + +#Database connection details +DB_DIALECT=mysql +DB_USER_NAME= +DB_USER_PASSWORD= +DB_NAME=worflow_db +DB_HOST=localhost +DB_PORT=5432 + +########################################################## + +# Bot API details + +BOT_API_URL=http://localhost:2323/api/v1 +BOT_API_KEY=123456 + +########################################################## + +# File storage Storage related details (e.g. aws s3 bucket) + +# STORAGE_BUCKET= or +# STORAGE_BUCKET_ACCESS_KEY_ID= +# STORAGE_BUCKET_ACCESS_KEY_SECRET= + +########################################################## + +# OpenTelemetry related details + +ENABLE_TELEMETRY=true +# ENABLE_TELEMETRY_TRACES=true +# ENABLE_TELEMETRY_METRICS=false + +# For any signal type +# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 # for gRPC +# or for HTTP -> http://localhost:4318 + +# Tracing +# OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4317 # for gRPC +# or for HTTP -> http://localhost:4318/v1/traces +# or custom endpoint -> https:///v1/traces/ + +# Metrics +# OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://localhost:4317 # for gRPC +# or for HTTP http://localhost:4318/v1/metrics +# or custom endpoint -> https:///v1/metrics/ + +# Logs +# OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://localhost:4317 # for gRPC +# or for HTTP http://localhost:4318/v1/logs +# or custom endpoint -> https:///v1/logs/ + +# OTEL_EXPORTER_OTLP_HEADERS= # for custom headers + +# OTEL_EXPORTER_OTLP_TIMEOUT=10s # for timeout + +# OTEL_EXPORTER_OTLP_PROTOCOL=grpc +# grpc to use OTLP/gRPC +# http/protobuf to use OTLP/HTTP + protobuf +# http/json to use OTLP/HTTP + JSON + +########################################################## diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..e15d0f7 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,6 @@ +node_modules +dist +src/**/*.json +src/**/*.zip +src/assets +src/tests diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..03991d1 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,167 @@ +{ + "env": { + "node": true, + "commonjs": true, + "es2022": true + }, + "extends": [ + "eslint:recommended", + "@typescript-eslint/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 2020, + "sourceType": "module", + "project": "./tsconfig.json" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "semi": [ "error", "always"], + "no-extra-semi": "error", + "no-console": "error", + "eqeqeq": [ + "warn", + "always", + { + "null": "ignore" + } + ], + "init-declarations": [ + "warn", + "always" + ], + "eol-last": [ + "warn", + "always" + ], + "block-spacing": [ + "warn", + "always" + ], + "object-curly-spacing": [ + "warn", + "always" + ], + "space-infix-ops": "warn", + "key-spacing": [ + "warn", + { + "singleLine": { + "beforeColon": false, + "afterColon": true + }, + "multiLine": { + "beforeColon": true, + "afterColon": true, + "align": "colon" + } + } + ], + "keyword-spacing": [ + "error", + { + "overrides": { + "if": { + "after": true + }, + "for": { + "after": true + }, + "while": { + "after": true + } + } + } + ], + "indent": ["warn", 4, { + "SwitchCase": 1, + "ignoredNodes": [ + "FunctionExpression > .params[decorators.length > 0]", + "FunctionExpression > .params > :matches(Decorator, :not(:first-child))", + "ClassBody.body > PropertyDefinition[decorators.length > 0] > .key" + ] + }], + "func-call-spacing": [ + "warn", + "never" + ], + "lines-between-class-members": [ + "warn", + "always" + ], + "linebreak-style": [ + "warn", + "unix" + ], + "max-len": [ + "warn", + { + "code": 150, + "tabWidth": 4, + "ignoreTrailingComments": true, + "ignoreTemplateLiterals": true, + "ignoreStrings": true + } + ], + "newline-per-chained-call": [ + "warn", + { + "ignoreChainWithDepth": 5 + } + ], + "max-statements-per-line": [ + "error", + { + "max": 1 + } + ], + "lines-around-comment": [ + "warn", + { + "beforeBlockComment": true, + "beforeLineComment": false + } + ], + "no-multi-assign": "error", + "no-multiple-empty-lines": [ + "warn", + { + "max": 1, + "maxEOF": 1 + } + ], + "no-tabs": "error", + "no-trailing-spaces": [ + "warn", + { + "skipBlankLines": true + } + ], + "padded-blocks": [ + "warn", + { + "classes": "always" + } + ], + "no-empty-function": "off", + "@typescript-eslint/no-empty-function": [ + "error", + { + "allow": [ + "private-constructors" + ] + } + ], + "@typescript-eslint/explicit-module-boundary-types": [ + "off" + ], + "@typescript-eslint/no-explicit-any": [ + "off" + ], + "no-var": [ + "off" + ] + } +} \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..565b46a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +*.js text eol=lf +*.jsx text eol=lf +*.json text eol=lf +*.ts text eol=lf + \ No newline at end of file diff --git a/.gitignore b/.gitignore index b96db17..5e45350 100644 --- a/.gitignore +++ b/.gitignore @@ -128,4 +128,6 @@ dist .yarn/build-state.yml .yarn/install-state.gz .pnp.* -tempDownloads/ + +seed.data/default.users.seed.json +seed.data/internal.clients.seed.json diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..d40df14 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,16 @@ +node_modules/ +dist/ +build/ +coverage/ +*.min.js +*.min.css +package-lock.json +yarn.lock +.git/ +.vscode/ +.idea/ +*.log +.env +.env.local +.env.production +.env.development \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..c96e2d4 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "tabWidth": 4, + "semi": true, + "singleQuote": true, + "trailingComma": "es5", + "printWidth": 150, + "useTabs": false, + "endOfLine": "lf" +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 3a5eec0..9e1616b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,10 +12,29 @@ "skipFiles": [ "/**" ], - "program": "${workspaceFolder}//dist//index.js", + "program": "${workspaceFolder}/src/index.ts", "preLaunchTask": "tsc: build - tsconfig.json", - } + }, + // { + // "args": [ + // "-u", + // "tdd", + // "--timeout", + // "999999", + // "--colors", + // "${workspaceFolder}/dist/tests/api-tests/tests" + // ], + // "internalConsoleOptions": "openOnSessionStart", + // "name": "Mocha Tests", + // "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", + // "preLaunchTask": "tsc: build - tsconfig.json", + // "request": "launch", + // "skipFiles": [ + // "/**" + // ], + // "type": "node" + // }, ] } \ No newline at end of file diff --git a/Postman/form-service.postman_collection.json b/Postman/form-service.postman_collection.json index bf3be87..6082420 100644 --- a/Postman/form-service.postman_collection.json +++ b/Postman/form-service.postman_collection.json @@ -1,10 +1,10 @@ { "info": { - "_postman_id": "bc9d63d1-c937-475d-acda-8aa4d240fbaa", + "_postman_id": "303c4b99-04f5-484e-a92b-79abd1651bbc", "name": "form-service", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "24347822", - "_collection_link": "https://inflectionzone-order-management-typescript.postman.co/workspace/assessment~6c10fd4f-9447-4878-a4d7-88df8016665b/collection/24347822-bc9d63d1-c937-475d-acda-8aa4d240fbaa?action=share&source=collection_link&creator=24347822" + "_exporter_id": "37785554", + "_collection_link": "https://sense-software.postman.co/workspace/sense-software-Workspace~4bf846a4-41d5-49ae-b537-9dcb3386e0ba/collection/37785554-303c4b99-04f5-484e-a92b-79abd1651bbc?action=share&source=collection_link&creator=37785554" }, "item": [ { @@ -134,23 +134,18 @@ " pm.expect(jsonRes.Status).to.eql('Success');\r", "});\r", "\r", - "pm.test(\"User is updated\", function () {\r", + "pm.test(\"User Updated successful\", function () {\r", " var jsonRes = pm.response.json();\r", + " pm.expect(jsonRes.Data).to.have.property('id');\r", " pm.expect(jsonRes.Data).to.have.property('FirstName');\r", - " pm.expect(jsonRes.Data.FirstName).equals('FirstName');\r", " pm.expect(jsonRes.Data).to.have.property('LastName');\r", - " pm.expect(jsonRes.Data.LastName).equals('LastName');\r", " pm.expect(jsonRes.Data).to.have.property('CountryCode');\r", - " pm.expect(jsonRes.Data.CountryCode).equals('123456');\r", " pm.expect(jsonRes.Data).to.have.property('Phone');\r", - " pm.expect(jsonRes.Data.Phone).equals('Phone');\r", " pm.expect(jsonRes.Data).to.have.property('Email');\r", - " pm.expect(jsonRes.Data.Email).equals('email@gmail.com');\r", " pm.expect(jsonRes.Data).to.have.property('Username');\r", - " pm.expect(jsonRes.Data.Username).equals('Username');\r", " pm.expect(jsonRes.Data).to.have.property('Password');\r", - " pm.expect(jsonRes.Data.Password).equals('lakalakalakalaka');\r", "});\r", + "\r", "" ], "type": "text/javascript", @@ -163,7 +158,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"FirstName\": \"FirstName\",\r\n \"LastName\": \"LastName\",\r\n \"CountryCode\": 123456,\r\n \"Phone\": \"Phone\",\r\n \"Email\": \"email@gmail.com\",\r\n \"Username\": \"Username\",\r\n \"Password\": \"lakalakalakalaka\"\r\n}", + "raw": "{\r\n \"FirstName\": \"Updated FirstName\",\r\n \"LastName\": \"Updated LastName\",\r\n \"CountryCode\": 987654,\r\n \"Phone\": \"0987654321\",\r\n \"Email\": \"updatedemail@gmail.com\",\r\n \"Username\": \"UpdatedUsername\",\r\n \"Password\": \"UpdatedPassword\"\r\n}", "options": { "raw": { "language": "json" @@ -196,10 +191,18 @@ " pm.expect(jsonRes.Status).to.eql('Success');\r", "});\r", "\r", - "pm.test(\"User records are returned\", function () {\r", + "pm.test(\"User search working\", function () {\r", " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data.Items.length).greaterThan(0);\r", - "});" + " pm.expect(jsonRes.Data).to.have.property('TotalCount');\r", + " pm.expect(jsonRes.Data).to.have.property('RetrievedCount');\r", + " pm.expect(jsonRes.Data).to.have.property('PageIndex');\r", + " pm.expect(jsonRes.Data).to.have.property('ItemsPerPage');\r", + " pm.expect(jsonRes.Data).to.have.property('Order');\r", + " pm.expect(jsonRes.Data).to.have.property('OrderBy');\r", + " pm.expect(jsonRes.Data).to.have.property('Items');\r", + "});\r", + "\r", + "" ], "type": "text/javascript", "packages": {} @@ -210,23 +213,13 @@ "method": "GET", "header": [], "url": { - "raw": "{{BASE_URL}}/users/search?username=Username&password=Password", + "raw": "{{BASE_URL}}/users/search", "host": [ "{{BASE_URL}}" ], "path": [ "users", "search" - ], - "query": [ - { - "key": "username", - "value": "Username" - }, - { - "key": "password", - "value": "Password" - } ] } }, @@ -245,10 +238,12 @@ " pm.expect(jsonRes.Status).to.eql('Success');\r", "});\r", "\r", - "pm.test(\"User is deleted\", function () {\r", + "pm.test(\"User deleted successful\", function () {\r", " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data).equals(true);\r", - "});" + " pm.expect(jsonRes.Data).to.be.true;\r", + "});\r", + "\r", + "" ], "type": "text/javascript", "packages": {} @@ -270,6 +265,948 @@ } }, "response": [] + }, + { + "name": "Get All Users", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/users", + "host": [ + "{{BASE_URL}}" + ], + "path": [ + "users" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Field Operations", + "item": [ + { + "name": "Logical Operations", + "item": [ + { + "name": "Create Logical Operation", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "try {", + " var jsonRes = pm.response.json();", + " pm.environment.set(\"LOGICAL_OPERATION_ID\", jsonRes.Data.id);", + "}", + "catch (error) {", + " console.log(error.message);", + "}", + "", + "pm.test(\"Request is successful\", function () {", + " pm.response.to.have.status(201);", + " var jsonRes = pm.response.json();", + " pm.expect(jsonRes.Status).to.eql('Success');", + "});", + "", + "pm.test(\"Logical Operation is created\", function () {", + " var jsonRes = pm.response.json();", + " pm.expect(jsonRes.Data).to.have.property('id');", + " pm.expect(jsonRes.Data).to.have.property('Type');", + " pm.expect(jsonRes.Data).to.have.property('Operator');", + " pm.expect(jsonRes.Data).to.have.property('Operands');", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"Name\": \"Age Range Check\",\n \"Description\": \"Check if age is between 18 and 65\",\n \"Operator\": \"Between\",\n \"Operands\": \"[{\\\"Type\\\": \\\"FieldReference\\\", \\\"DataType\\\": \\\"Integer\\\", \\\"FieldId\\\": \\\"{{FIELD_ID}}\\\"}, {\\\"Type\\\": \\\"Constant\\\", \\\"DataType\\\": \\\"Integer\\\", \\\"Value\\\": 18}, {\\\"Type\\\": \\\"Constant\\\", \\\"DataType\\\": \\\"Integer\\\", \\\"Value\\\": 65}]\"\n}" + }, + "url": { + "raw": "{{BASE_URL}}/field-logical-operations", + "host": ["{{BASE_URL}}"], + "path": ["field-logical-operations"] + } + }, + "response": [] + }, + { + "name": "Get Logical Operation by ID", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-logical-operations/{{LOGICAL_OPERATION_ID}}", + "host": ["{{BASE_URL}}"], + "path": ["field-logical-operations", "{{LOGICAL_OPERATION_ID}}"] + } + }, + "response": [] + }, + { + "name": "Update Logical Operation", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"Name\": \"Updated Age Range Check\",\n \"Description\": \"Updated description for age range validation\",\n \"Operator\": \"GreaterThanOrEqual\",\n \"Operands\": \"[{\\\"Type\\\": \\\"FieldReference\\\", \\\"DataType\\\": \\\"Integer\\\", \\\"FieldId\\\": \\\"{{FIELD_ID}}\\\"}, {\\\"Type\\\": \\\"Constant\\\", \\\"DataType\\\": \\\"Integer\\\", \\\"Value\\\": 21}]\"\n}" + }, + "url": { + "raw": "{{BASE_URL}}/field-logical-operations/{{LOGICAL_OPERATION_ID}}", + "host": ["{{BASE_URL}}"], + "path": ["field-logical-operations", "{{LOGICAL_OPERATION_ID}}"] + } + }, + "response": [] + }, + { + "name": "Search Logical Operations", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-logical-operations/search?operator=Between&type=Logical", + "host": ["{{BASE_URL}}"], + "path": ["field-logical-operations", "search"], + "query": [ + { + "key": "operator", + "value": "Between" + }, + { + "key": "type", + "value": "Logical" + } + ] + } + }, + "response": [] + }, + { + "name": "Delete Logical Operation", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-logical-operations/{{LOGICAL_OPERATION_ID}}", + "host": ["{{BASE_URL}}"], + "path": ["field-logical-operations", "{{LOGICAL_OPERATION_ID}}"] + } + }, + "response": [] + } + ] + }, + { + "name": "Mathematical Operations", + "item": [ + { + "name": "Create Mathematical Operation", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "try {", + " var jsonRes = pm.response.json();", + " pm.environment.set(\"MATHEMATICAL_OPERATION_ID\", jsonRes.Data.id);", + "}", + "catch (error) {", + " console.log(error.message);", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"Name\": \"BMI Calculation\",\n \"Description\": \"Calculate BMI from weight and height\",\n \"Operator\": \"Divide\",\n \"Operands\": \"[{\\\"Type\\\": \\\"FieldReference\\\", \\\"DataType\\\": \\\"Float\\\", \\\"FieldId\\\": \\\"{{WEIGHT_FIELD_ID}}\\\"}, {\\\"Type\\\": \\\"FieldReference\\\", \\\"DataType\\\": \\\"Float\\\", \\\"FieldId\\\": \\\"{{HEIGHT_FIELD_ID}}\\\"}]\"\n}" + }, + "url": { + "raw": "{{BASE_URL}}/field-mathematical-operations", + "host": ["{{BASE_URL}}"], + "path": ["field-mathematical-operations"] + } + }, + "response": [] + }, + { + "name": "Get Mathematical Operation by ID", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-mathematical-operations/{{MATHEMATICAL_OPERATION_ID}}", + "host": ["{{BASE_URL}}"], + "path": ["field-mathematical-operations", "{{MATHEMATICAL_OPERATION_ID}}"] + } + }, + "response": [] + }, + { + "name": "Search Mathematical Operations", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-mathematical-operations/search?operator=Add&type=Mathematical", + "host": ["{{BASE_URL}}"], + "path": ["field-mathematical-operations", "search"], + "query": [ + { + "key": "operator", + "value": "Add" + }, + { + "key": "type", + "value": "Mathematical" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Function Expression Operations", + "item": [ + { + "name": "Create Function Expression Operation", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "try {", + " var jsonRes = pm.response.json();", + " pm.environment.set(\"FUNCTION_EXPRESSION_OPERATION_ID\", jsonRes.Data.id);", + "}", + "catch (error) {", + " console.log(error.message);", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"Name\": \"BMI Calculator\",\n \"Description\": \"Calculate BMI using weight and height\",\n \"Expression\": \"weight / (height / 100) ^ 2\",\n \"Variables\": \"{\\\"weight\\\": {\\\"Type\\\": \\\"FieldReference\\\", \\\"DataType\\\": \\\"Float\\\", \\\"FieldId\\\": \\\"{{WEIGHT_FIELD_ID}}\\\"}, \\\"height\\\": {\\\"Type\\\": \\\"FieldReference\\\", \\\"DataType\\\": \\\"Float\\\", \\\"FieldId\\\": \\\"{{HEIGHT_FIELD_ID}}\\\"}}\"\n}" + }, + "url": { + "raw": "{{BASE_URL}}/field-function-expression-operations", + "host": ["{{BASE_URL}}"], + "path": ["field-function-expression-operations"] + } + }, + "response": [] + }, + { + "name": "Create Risk Score Calculator", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"Name\": \"Risk Score Calculator\",\n \"Description\": \"Calculate risk score based on age, BMI and blood pressure\",\n \"Expression\": \"baseRisk + (age * 0.5) + (bloodPressure > 140 ? 20 : 0) + (bmi > 30 ? 15 : 0)\",\n \"Variables\": \"{\\\"baseRisk\\\": {\\\"Type\\\": \\\"Constant\\\", \\\"DataType\\\": \\\"Float\\\", \\\"Value\\\": 10}, \\\"age\\\": {\\\"Type\\\": \\\"FieldReference\\\", \\\"DataType\\\": \\\"Integer\\\", \\\"FieldId\\\": \\\"{{AGE_FIELD_ID}}\\\"}, \\\"bloodPressure\\\": {\\\"Type\\\": \\\"FieldReference\\\", \\\"DataType\\\": \\\"Float\\\", \\\"FieldId\\\": \\\"{{BP_FIELD_ID}}\\\"}, \\\"bmi\\\": {\\\"Type\\\": \\\"FieldReference\\\", \\\"DataType\\\": \\\"Float\\\", \\\"FieldId\\\": \\\"{{BMI_FIELD_ID}}\\\"}}\"\n}" + }, + "url": { + "raw": "{{BASE_URL}}/field-function-expression-operations", + "host": ["{{BASE_URL}}"], + "path": ["field-function-expression-operations"] + } + }, + "response": [] + }, + { + "name": "Search Function Expression Operations", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-function-expression-operations/search?type=FunctionExpression", + "host": ["{{BASE_URL}}"], + "path": ["field-function-expression-operations", "search"], + "query": [ + { + "key": "type", + "value": "FunctionExpression" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Composition Operations", + "item": [ + { + "name": "Create Composition Operation", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "try {", + " var jsonRes = pm.response.json();", + " pm.environment.set(\"COMPOSITION_OPERATION_ID\", jsonRes.Data.id);", + "}", + "catch (error) {", + " console.log(error.message);", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"Name\": \"Complex Validation\",\n \"Description\": \"AND combination of multiple validation rules\",\n \"Operator\": \"And\",\n \"Children\": \"[\\\"{{LOGICAL_OPERATION_ID}}\\\", \\\"{{MATHEMATICAL_OPERATION_ID}}\\\"]\"\n}" + }, + "url": { + "raw": "{{BASE_URL}}/field-composition-operations", + "host": ["{{BASE_URL}}"], + "path": ["field-composition-operations"] + } + }, + "response": [] + }, + { + "name": "Search Composition Operations", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-composition-operations/search?operator=And&type=Composition", + "host": ["{{BASE_URL}}"], + "path": ["field-composition-operations", "search"], + "query": [ + { + "key": "operator", + "value": "And" + }, + { + "key": "type", + "value": "Composition" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Iterate Operations", + "item": [ + { + "name": "Create Iterate Operation", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "try {", + " var jsonRes = pm.response.json();", + " pm.environment.set(\"ITERATE_OPERATION_ID\", jsonRes.Data.id);", + "}", + "catch (error) {", + " console.log(error.message);", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"Name\": \"Array Score Validation\",\n \"Description\": \"Check if all scores in array are above threshold\",\n \"ItemAlias\": \"score\",\n \"OperationId\": \"{{LOGICAL_OPERATION_ID}}\",\n \"ArrayOperand\": \"{\\\"Type\\\": \\\"FieldReference\\\", \\\"DataType\\\": \\\"Array\\\", \\\"FieldId\\\": \\\"{{SCORES_ARRAY_FIELD_ID}}\\\"}\"\n}" + }, + "url": { + "raw": "{{BASE_URL}}/field-iterate-operations", + "host": ["{{BASE_URL}}"], + "path": ["field-iterate-operations"] + } + }, + "response": [] + }, + { + "name": "Search Iterate Operations", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-iterate-operations/search?type=Iterate", + "host": ["{{BASE_URL}}"], + "path": ["field-iterate-operations", "search"], + "query": [ + { + "key": "type", + "value": "Iterate" + } + ] + } + }, + "response": [] + } + ] + } + ] + }, + { + "name": "Field Logic", + "item": [ + { + "name": "Skip Logic", + "item": [ + { + "name": "Create Skip Logic", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "try {", + " var jsonRes = pm.response.json();", + " pm.environment.set(\"SKIP_LOGIC_ID\", jsonRes.Data.id);", + "}", + "catch (error) {", + " console.log(error.message);", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"FieldId\": \"{{FIELD_ID}}\",\n \"Enabled\": true,\n \"DefaultSkip\": false\n}" + }, + "url": { + "raw": "{{BASE_URL}}/field-skip-logic", + "host": ["{{BASE_URL}}"], + "path": ["field-skip-logic"] + } + }, + "response": [] + }, + { + "name": "Get Skip Logic by ID", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-skip-logic/{{SKIP_LOGIC_ID}}", + "host": ["{{BASE_URL}}"], + "path": ["field-skip-logic", "{{SKIP_LOGIC_ID}}"] + } + }, + "response": [] + }, + { + "name": "Update Skip Logic", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"Enabled\": false,\n \"DefaultSkip\": true\n}" + }, + "url": { + "raw": "{{BASE_URL}}/field-skip-logic/{{SKIP_LOGIC_ID}}", + "host": ["{{BASE_URL}}"], + "path": ["field-skip-logic", "{{SKIP_LOGIC_ID}}"] + } + }, + "response": [] + }, + { + "name": "Search Skip Logic", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-skip-logic/search?enabled=true&defaultSkip=false", + "host": ["{{BASE_URL}}"], + "path": ["field-skip-logic", "search"], + "query": [ + { + "key": "enabled", + "value": "true" + }, + { + "key": "defaultSkip", + "value": "false" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Validation Logic", + "item": [ + { + "name": "Create Validation Logic", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "try {", + " var jsonRes = pm.response.json();", + " pm.environment.set(\"VALIDATION_LOGIC_ID\", jsonRes.Data.id);", + "}", + "catch (error) {", + " console.log(error.message);", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"FieldId\": \"{{FIELD_ID}}\",\n \"Enabled\": true\n}" + }, + "url": { + "raw": "{{BASE_URL}}/field-validation-logic", + "host": ["{{BASE_URL}}"], + "path": ["field-validation-logic"] + } + }, + "response": [] + }, + { + "name": "Search Validation Logic", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-validation-logic/search?enabled=true", + "host": ["{{BASE_URL}}"], + "path": ["field-validation-logic", "search"], + "query": [ + { + "key": "enabled", + "value": "true" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Calculation Logic", + "item": [ + { + "name": "Create Calculation Logic", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "try {", + " var jsonRes = pm.response.json();", + " pm.environment.set(\"CALCULATION_LOGIC_ID\", jsonRes.Data.id);", + "}", + "catch (error) {", + " console.log(error.message);", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"FieldId\": \"{{FIELD_ID}}\",\n \"Enabled\": true,\n \"FallbackValue\": \"0\"\n}" + }, + "url": { + "raw": "{{BASE_URL}}/field-calculation-logic", + "host": ["{{BASE_URL}}"], + "path": ["field-calculation-logic"] + } + }, + "response": [] + }, + { + "name": "Search Calculation Logic", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-calculation-logic/search?enabled=true", + "host": ["{{BASE_URL}}"], + "path": ["field-calculation-logic", "search"], + "query": [ + { + "key": "enabled", + "value": "true" + } + ] + } + }, + "response": [] + } + ] + } + ] + }, + { + "name": "Field Rules", + "item": [ + { + "name": "Skip Rules", + "item": [ + { + "name": "Create Skip Rule", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "try {", + " var jsonRes = pm.response.json();", + " pm.environment.set(\"SKIP_RULE_ID\", jsonRes.Data.id);", + "}", + "catch (error) {", + " console.log(error.message);", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"Name\": \"Adult Skip Rule\",\n \"Description\": \"Skip guardian fields for adults\",\n \"OperationId\": \"{{LOGICAL_OPERATION_ID}}\",\n \"SkipWhenTrue\": true,\n \"LogicId\": \"{{SKIP_LOGIC_ID}}\"\n}" + }, + "url": { + "raw": "{{BASE_URL}}/field-skip-rules", + "host": ["{{BASE_URL}}"], + "path": ["field-skip-rules"] + } + }, + "response": [] + }, + { + "name": "Get Skip Rule by ID", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-skip-rules/{{SKIP_RULE_ID}}", + "host": ["{{BASE_URL}}"], + "path": ["field-skip-rules", "{{SKIP_RULE_ID}}"] + } + }, + "response": [] + }, + { + "name": "Update Skip Rule", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"Name\": \"Updated Adult Skip Rule\",\n \"Description\": \"Updated skip rule for adult users\",\n \"SkipWhenTrue\": false\n}" + }, + "url": { + "raw": "{{BASE_URL}}/field-skip-rules/{{SKIP_RULE_ID}}", + "host": ["{{BASE_URL}}"], + "path": ["field-skip-rules", "{{SKIP_RULE_ID}}"] + } + }, + "response": [] + }, + { + "name": "Search Skip Rules", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-skip-rules/search?name=Adult&logicId={{SKIP_LOGIC_ID}}", + "host": ["{{BASE_URL}}"], + "path": ["field-skip-rules", "search"], + "query": [ + { + "key": "name", + "value": "Adult" + }, + { + "key": "logicId", + "value": "{{SKIP_LOGIC_ID}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Delete Skip Rule", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-skip-rules/{{SKIP_RULE_ID}}", + "host": ["{{BASE_URL}}"], + "path": ["field-skip-rules", "{{SKIP_RULE_ID}}"] + } + }, + "response": [] + } + ] + }, + { + "name": "Validation Rules", + "item": [ + { + "name": "Create Validation Rule", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "try {", + " var jsonRes = pm.response.json();", + " pm.environment.set(\"VALIDATION_RULE_ID\", jsonRes.Data.id);", + "}", + "catch (error) {", + " console.log(error.message);", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"Name\": \"Age Validation Rule\",\n \"Description\": \"Validate age is between 0 and 150\",\n \"OperationId\": \"{{LOGICAL_OPERATION_ID}}\",\n \"ErrorWhenFalse\": true,\n \"ErrorMessage\": \"Age must be between 0 and 150\",\n \"LogicId\": \"{{VALIDATION_LOGIC_ID}}\"\n}" + }, + "url": { + "raw": "{{BASE_URL}}/field-validation-rules", + "host": ["{{BASE_URL}}"], + "path": ["field-validation-rules"] + } + }, + "response": [] + }, + { + "name": "Get Validation Rule by ID", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-validation-rules/{{VALIDATION_RULE_ID}}", + "host": ["{{BASE_URL}}"], + "path": ["field-validation-rules", "{{VALIDATION_RULE_ID}}"] + } + }, + "response": [] + }, + { + "name": "Search Validation Rules", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-validation-rules/search?name=Age&errorWhenFalse=true", + "host": ["{{BASE_URL}}"], + "path": ["field-validation-rules", "search"], + "query": [ + { + "key": "name", + "value": "Age" + }, + { + "key": "errorWhenFalse", + "value": "true" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Calculation Rules", + "item": [ + { + "name": "Create Calculation Rule", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "try {", + " var jsonRes = pm.response.json();", + " pm.environment.set(\"CALCULATION_RULE_ID\", jsonRes.Data.id);", + "}", + "catch (error) {", + " console.log(error.message);", + "}" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"Name\": \"BMI Calculation Rule\",\n \"Description\": \"Calculate BMI based on weight and height\",\n \"OperationId\": \"{{FUNCTION_EXPRESSION_OPERATION_ID}}\",\n \"ConditionForOperationId\": \"{{LOGICAL_OPERATION_ID}}\",\n \"LogicId\": \"{{CALCULATION_LOGIC_ID}}\"\n}" + }, + "url": { + "raw": "{{BASE_URL}}/field-calculation-rules", + "host": ["{{BASE_URL}}"], + "path": ["field-calculation-rules"] + } + }, + "response": [] + }, + { + "name": "Search Calculation Rules", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/field-calculation-rules/search?name=BMI", + "host": ["{{BASE_URL}}"], + "path": ["field-calculation-rules", "search"], + "query": [ + { + "key": "name", + "value": "BMI" + } + ] + } + }, + "response": [] + } + ] } ] }, @@ -300,15 +1237,20 @@ "pm.test(\"Template is created\", function () {\r", " var jsonRes = pm.response.json();\r", " pm.expect(jsonRes.Data).to.have.property('id');\r", - " pm.expect(jsonRes.Data).to.have.property('Title');\r", + " pm.expect(jsonRes.Data).to.have.property('TemplateName');\r", " pm.expect(jsonRes.Data).to.have.property('Description');\r", " pm.expect(jsonRes.Data).to.have.property('CurrentVersion');\r", - " pm.expect(jsonRes.Data).to.have.property('Type');\r", - " pm.expect(jsonRes.Data).to.have.property('ItemsPerPage');\r", - " pm.expect(jsonRes.Data).to.have.property('DisplayCode');\r", - " pm.expect(jsonRes.Data).to.have.property('OwnerUserId');\r", - " pm.expect(jsonRes.Data).to.have.property('RootSectionId');\r", - " pm.expect(jsonRes.Data).to.have.property('DefaultSectionNumbering'); \r", + " pm.expect(jsonRes.Data).to.have.property('Tags');\r", + " pm.expect(jsonRes.Data).to.have.property('CreatedBy');\r", + " pm.expect(jsonRes.Data).to.have.property('ApprovalStatus');\r", + " pm.expect(jsonRes.Data).to.have.property('Visibility');\r", + " pm.expect(jsonRes.Data).to.have.property('StatusDescription');\r", + " pm.expect(jsonRes.Data).to.have.property('OwnerId');\r", + " pm.expect(jsonRes.Data).to.have.property('OwnerName');\r", + " pm.expect(jsonRes.Data).to.have.property('PublishedBy');\r", + " pm.expect(jsonRes.Data).to.have.property('PublishedDate');\r", + " pm.expect(jsonRes.Data).to.have.property('MachineReadableFileName');\r", + " \r", "});" ], "type": "text/javascript", @@ -321,7 +1263,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"Title\": \"Assessment First\",\r\n \"Description\": \"This is description\",\r\n \"CurrentVersion\": 1,\r\n \"Type\": \"Questionnaire\",\r\n // \"DisplayCode\": \"xyz1234\",\r\n \"OwnerUserId\": \"16377833-8e6f-41b4-944a-98a91815a4d5\",\r\n // \"RootSectionId\": \"9618c6a8-0555-4a14-95ec-1946ec09c8e0\",\r\n \"DefaultSectionNumbering\": true,\r\n \"ItemsPerPage\":\"OneQuestion\"\r\n}", + "raw": "{\r\n \"TemplateName\": \"Patient Survey Form\",\r\n \"Description\": \"A comprehensive patient intake survey\",\r\n \"CurrentVersion\": \"1.0\",\r\n \"Tags\": \"Patient, Survey, Medical\",\r\n \"CreatedBy\": \"{{USER_ID}}\",\r\n \"ApprovalStatus\": \"Pending\",\r\n \"Visibility\": \"Public\",\r\n \"StatusDescription\": \"New template for patient intake\",\r\n \"OwnerId\": \"{{USER_ID}}\",\r\n \"OwnerName\": \"Test User\",\r\n \"PublishedBy\": \"{{USER_ID}}\",\r\n \"PublishedDate\": \"2024-01-01T00:00:00.000Z\",\r\n \"MachineReadableFileName\": \"patient-survey-v1.json\"\r\n}", "options": { "raw": { "language": "json" @@ -353,19 +1295,25 @@ " pm.expect(jsonRes.Status).to.eql('Success');\r", "});\r", "\r", - "pm.test(\"User retrived successful\", function () {\r", + "pm.test(\"Template retrived successful\", function () {\r", " var jsonRes = pm.response.json();\r", " pm.expect(jsonRes.Data).to.have.property('id');\r", - " pm.expect(jsonRes.Data).to.have.property('Title');\r", + " pm.expect(jsonRes.Data).to.have.property('TemplateName');\r", " pm.expect(jsonRes.Data).to.have.property('Description');\r", " pm.expect(jsonRes.Data).to.have.property('CurrentVersion');\r", - " pm.expect(jsonRes.Data).to.have.property('Type');\r", - " pm.expect(jsonRes.Data).to.have.property('ItemsPerPage');\r", - " pm.expect(jsonRes.Data).to.have.property('DisplayCode');\r", - " pm.expect(jsonRes.Data).to.have.property('OwnerUserId');\r", - " pm.expect(jsonRes.Data).to.have.property('RootSectionId');\r", - " pm.expect(jsonRes.Data).to.have.property('DefaultSectionNumbering');\r", - "});" + " pm.expect(jsonRes.Data).to.have.property('Tags');\r", + " pm.expect(jsonRes.Data).to.have.property('CreatedBy');\r", + " pm.expect(jsonRes.Data).to.have.property('ApprovalStatus');\r", + " pm.expect(jsonRes.Data).to.have.property('Visibility');\r", + " pm.expect(jsonRes.Data).to.have.property('StatusDescription');\r", + " pm.expect(jsonRes.Data).to.have.property('OwnerId');\r", + " pm.expect(jsonRes.Data).to.have.property('OwnerName');\r", + " pm.expect(jsonRes.Data).to.have.property('PublishedBy');\r", + " pm.expect(jsonRes.Data).to.have.property('PublishedDate');\r", + " pm.expect(jsonRes.Data).to.have.property('MachineReadableFileName');\r", + "});\r", + "\r", + "" ], "type": "text/javascript", "packages": {} @@ -401,1060 +1349,25 @@ " pm.expect(jsonRes.Status).to.eql('Success');\r", "});\r", "\r", - "pm.test(\"User is updated\", function () {\r", + "pm.test(\"Template Updated successful\", function () {\r", " var jsonRes = pm.response.json();\r", " pm.expect(jsonRes.Data).to.have.property('id');\r", - " pm.expect(jsonRes.Data.id).equals('b78158bd-7496-48f1-b625-2c489b9c65a9');\r", - " pm.expect(jsonRes.Data).to.have.property('Title');\r", - " pm.expect(jsonRes.Data.Title).equals('Work Load Capacity of company');\r", + " pm.expect(jsonRes.Data).to.have.property('TemplateName');\r", " pm.expect(jsonRes.Data).to.have.property('Description');\r", - " pm.expect(jsonRes.Data.Description).equals('This is description');\r", " pm.expect(jsonRes.Data).to.have.property('CurrentVersion');\r", - " pm.expect(jsonRes.Data.CurrentVersion).equals(1);\r", - " pm.expect(jsonRes.Data).to.have.property('Type');\r", - " pm.expect(jsonRes.Data.Type).equals('Questionnaire');\r", - " pm.expect(jsonRes.Data).to.have.property('ItemsPerPage');\r", - " pm.expect(jsonRes.Data.ItemsPerPage).equals('OneQuestion');\r", - " pm.expect(jsonRes.Data).to.have.property('DisplayCode');\r", - " pm.expect(jsonRes.Data.DisplayCode).equals('ASSESS_TEMP_#ps09rsfHz4JMmXftn');\r", - " pm.expect(jsonRes.Data).to.have.property('OwnerUserId');\r", - " pm.expect(jsonRes.Data.OwnerUserId).equals('16377833-8e6f-41b4-944a-98a91815a4d5');\r", - " pm.expect(jsonRes.Data).to.have.property('RootSectionId');\r", - " pm.expect(jsonRes.Data.RootSectionId).equals('d04ca675-d4eb-4c6a-9820-6ba1cce3e625');\r", - " pm.expect(jsonRes.Data).to.have.property('DefaultSectionNumbering');\r", - " pm.expect(jsonRes.Data.DefaultSectionNumbering).equals(true);\r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"Title\": \"Work Load Capacity of company\",\r\n \"Description\": \"This is description\",\r\n \"CurrentVersion\": 1,\r\n \"Type\": \"Questionnaire\",\r\n \"DisplayCode\": \"xyz1234asssssdfghj\",\r\n \"OwnerUserId\": \"16377833-8e6f-41b4-944a-98a91815a4d5\",\r\n \"RootSectionId\": \"d04ca675-d4eb-4c6a-9820-6ba1cce3e625\",\r\n \"DefaultSectionNumbering\": true,\r\n \"ItemsPerPage\":\"OneQuestion\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{BASE_URL}}/form-templates/{{TEMPLATE_ID}}", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "form-templates", - "{{TEMPLATE_ID}}" - ] - } - }, - "response": [] - }, - { - "name": "Generic search", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Request is successfull\", function () {\r", - " pm.response.to.have.status(200);\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Status).to.eql('Success');\r", - "});\r", - "\r", - "pm.test(\"Template retrived successful\", function () {\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data.Items.length).greaterThan(0);\r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{BASE_URL}}/form-templates/search?id=210939f1-c6c4-4274-843d-72f44f77da65", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "form-templates", - "search" - ], - "query": [ - { - "key": "title", - "value": "What is your full name", - "disabled": true - }, - { - "key": "description", - "value": "about testing the assessment", - "disabled": true - }, - { - "key": "id", - "value": "210939f1-c6c4-4274-843d-72f44f77da65" - } - ] - } - }, - "response": [] - }, - { - "name": "Get Details by template id", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{BASE_URL}}/form-templates/d71cc1a7-abdb-4196-bfef-19ba4e7cd1be/details", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "form-templates", - "d71cc1a7-abdb-4196-bfef-19ba4e7cd1be", - "details" - ] - } - }, - "response": [] - }, - { - "name": "Delete form template record", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Request is successfull\", function () {\r", - " pm.response.to.have.status(200);\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Status).to.eql('Success');\r", - "});\r", - "\r", - "pm.test(\"Template is deleted\", function () {\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data).equals(true);\r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "DELETE", - "header": [], - "url": { - "raw": "{{BASE_URL}}/form-templates/db5a31aa-9908-4413-9c74-f9929d41a861", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "form-templates", - "db5a31aa-9908-4413-9c74-f9929d41a861" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Form section", - "item": [ - { - "name": "Create new form section", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "try {\r", - " var jsonRes = pm.response.json();\r", - " pm.environment.set(\"SECTION_ID\", jsonRes.Data.id);\r", - "}\r", - "catch (error) {\r", - " console.log(error.message);\r", - "}\r", - "\r", - "pm.test(\"Request is successfull\", function () {\r", - " pm.response.to.have.status(201);\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Status).to.eql('Success');\r", - "});\r", - "\r", - "pm.test(\"User is created\", function () {\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data).to.have.property('id');\r", - " pm.expect(jsonRes.Data).to.have.property('ParentFormTemplateId');\r", - " pm.expect(jsonRes.Data).to.have.property('SectionIdentifier');\r", - " pm.expect(jsonRes.Data).to.have.property('Title');\r", - " pm.expect(jsonRes.Data).to.have.property('Description');\r", - " pm.expect(jsonRes.Data).to.have.property('DisplayCode');\r", - " pm.expect(jsonRes.Data).to.have.property('Sequence');\r", - " pm.expect(jsonRes.Data).to.have.property('ParentSectionId');\r", - " \r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// {\r\n// \"ParentFormTemplateId\": \"64888ed6-7452-48ad-8f4e-48448f21259f\", //Form template id\r\n// \"SectionIdentifier\": \"SectionIdentifier\",\r\n// \"Title\": \"Title.................\",\r\n// \"Description\": \"Description\",\r\n// // \"DisplayCode\": \"DisplayCode\",\r\n// \"Sequence\": \"Sequence three\",\r\n// \"ParentSectionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\" //any uuid value\r\n// }\r\n{\r\n \"ParentFormTemplateId\": \"b78158bd-7496-48f1-b625-2c489b9c65a9\",\r\n // \"SectionIdentifier\": \"new form section 1\"\r\n // \"Title\": \"New form section \",\r\n // \"Description\": \"sf esdrg\",\r\n // \"Sequence\":\"abc\"\r\n \"ParentSectionId\": \"d04ca675-d4eb-4c6a-9820-6ba1cce3e625\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{BASE_URL}}/form-sections", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "form-sections" - ] - } - }, - "response": [] - }, - { - "name": "Get form section by id", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Request is successfull\", function () {\r", - " pm.response.to.have.status(200);\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Status).to.eql('Success');\r", - "});\r", - "\r", - "pm.test(\"User is created\", function () {\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data).to.have.property('id');\r", - " pm.expect(jsonRes.Data).to.have.property('ParentFormTemplateId');\r", - " pm.expect(jsonRes.Data).to.have.property('SectionIdentifier');\r", - " pm.expect(jsonRes.Data).to.have.property('Title');\r", - " pm.expect(jsonRes.Data).to.have.property('Description');\r", - " pm.expect(jsonRes.Data).to.have.property('DisplayCode');\r", - " pm.expect(jsonRes.Data).to.have.property('Sequence');\r", - " pm.expect(jsonRes.Data).to.have.property('ParentSectionId');\r", - " \r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{BASE_URL}}/form-sections/{{SECTION_ID}}", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "form-sections", - "{{SECTION_ID}}" - ] - } - }, - "response": [] - }, - { - "name": "Update form section", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Request is successfull\", function () {\r", - " pm.response.to.have.status(200);\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Status).to.eql('Success');\r", - "});\r", - "\r", - "pm.test(\"User is updated\", function () {\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data).to.have.property('id');\r", - " pm.expect(jsonRes.Data.id).equals('ed5f241d-c8db-4483-bc9d-78077607f012');\r", - " pm.expect(jsonRes.Data).to.have.property('Title');\r", - " pm.expect(jsonRes.Data.Title).equals('Work Load Capacity of company');\r", - " pm.expect(jsonRes.Data).to.have.property('Description');\r", - " pm.expect(jsonRes.Data.Description).equals('This is description');\r", - " pm.expect(jsonRes.Data).to.have.property('CurrentVersion');\r", - " pm.expect(jsonRes.Data.CurrentVersion).equals(1);\r", - " pm.expect(jsonRes.Data).to.have.property('Type');\r", - " pm.expect(jsonRes.Data.Type).equals('Questionnaire');\r", - " pm.expect(jsonRes.Data).to.have.property('ItemsPerPage');\r", - " pm.expect(jsonRes.Data.ItemsPerPage).equals('OneQuestion');\r", - " pm.expect(jsonRes.Data).to.have.property('DisplayCode');\r", - " pm.expect(jsonRes.Data.DisplayCode).equals('ASSESS_TEMP_#ps09rsfHz4JMmXftn');\r", - " pm.expect(jsonRes.Data).to.have.property('OwnerUserId');\r", - " pm.expect(jsonRes.Data.OwnerUserId).equals('16377833-8e6f-41b4-944a-98a91815a4d5');\r", - " pm.expect(jsonRes.Data).to.have.property('RootSectionId');\r", - " pm.expect(jsonRes.Data.RootSectionId).equals('d04ca675-d4eb-4c6a-9820-6ba1cce3e625');\r", - " pm.expect(jsonRes.Data).to.have.property('DefaultSectionNumbering');\r", - " pm.expect(jsonRes.Data.DefaultSectionNumbering).equals(true);\r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "// {\r\n// // \"TemplateId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\",\r\n// \"SectionIdentifier\": \"SectionIdentifier\",\r\n// \"Title\": \"Title section \",\r\n// \"Description\": \"Description\",\r\n// \"DisplayCode\": \"DisplayCode\",\r\n// \"Sequence\": \"Sequence three\",\r\n// \"ParentSectionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\"\r\n// }\r\n{\r\n \"SectionIdentifier\": \"new form section\",\r\n \"Title\": \"first section\",\r\n \"Description\": \"description\",\r\n // \"Sequence\": \"1\",\r\n \"ParentSectionId\": \"6eb7e68d-608b-4076-8a9d-bbc467ab9211\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{BASE_URL}}/form-sections/{{SECTION_ID}}", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "form-sections", - "{{SECTION_ID}}" - ] - } - }, - "response": [] - }, - { - "name": "Generic search", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Request is successfull\", function () {\r", - " pm.response.to.have.status(200);\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Status).to.eql('Success');\r", - "});\r", - "\r", - "pm.test(\"User records are returned\", function () {\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data.Items.length).greaterThan(0);\r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{BASE_URL}}/form-sections/search?parentFormTemplateId=51688651-458d-4b8f-922e-28ce072cfa15", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "form-sections", - "search" - ], - "query": [ - { - "key": "parentFormTemplateId", - "value": "51688651-458d-4b8f-922e-28ce072cfa15" - } - ] - } - }, - "response": [] - }, - { - "name": "Delete the form section record", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Request is successfull\", function () {\r", - " pm.response.to.have.status(200);\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Status).to.eql('Success');\r", - "});\r", - "\r", - "pm.test(\"Section is deleted\", function () {\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data).equals(true);\r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "DELETE", - "header": [], - "url": { - "raw": "{{BASE_URL}}/form-sections/a3fd3b20-473c-4b26-9bd7-b4791c9010d7", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "form-sections", - "a3fd3b20-473c-4b26-9bd7-b4791c9010d7" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Question", - "item": [ - { - "name": "Create a new question", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "try {\r", - " var jsonRes = pm.response.json();\r", - " pm.environment.set(\"QUESTION_ID\", jsonRes.Data.id);\r", - "}\r", - "catch (error) {\r", - " console.log(error.message);\r", - "}\r", - "\r", - "pm.test(\"Request is successfull\", function () {\r", - " pm.response.to.have.status(201);\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Status).to.eql('Success');\r", - "});\r", - "\r", - "pm.test(\"Question is created\", function () {\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data).to.have.property('id');\r", - " pm.expect(jsonRes.Data).to.have.property('ParentTemplateId');\r", - " pm.expect(jsonRes.Data).to.have.property('ParentSectionId');\r", - " pm.expect(jsonRes.Data).to.have.property('Title');\r", - " pm.expect(jsonRes.Data).to.have.property('Description');\r", - " pm.expect(jsonRes.Data).to.have.property('DisplayCode');\r", - " pm.expect(jsonRes.Data).to.have.property('ResponseType');\r", - " pm.expect(jsonRes.Data).to.have.property('Score');\r", - " pm.expect(jsonRes.Data).to.have.property('CorrectAnswer');\r", - " pm.expect(jsonRes.Data).to.have.property('Hint');\r", - " pm.expect(jsonRes.Data).to.have.property('Options');\r", - " pm.expect(jsonRes.Data).to.have.property('QuestionImageUrl');\r", - " pm.expect(jsonRes.Data).to.have.property('RangeMin');\r", - " pm.expect(jsonRes.Data).to.have.property('RangeMax');\r", - " \r", - "});\r", - "var template = `\r", - "\r", - "\r", - "\r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - " \r", - "
FieldValue
Status{{response.Status}}
Message{{response.Message}}
HttpCode{{response.HttpCode}}
Request Method{{response.Request.Method}}
Request Host{{response.Request.Host}}
Request Body - ParentTemplateId{{response.Request.Body.ParentTemplateId}}
Request Body - ParentSectionId{{response.Request.Body.ParentSectionId}}
Request Body - ResponseType{{response.Request.Body.ResponseType}}
Request Body - Options{{response.Request.Body.Options}}
Request Url{{response.Request.Url}}
Request Params{{response.Request.Params}}
Data ID{{response.Data.id}}
Data Title{{response.Data.Title}}
Data Description{{response.Data.Description}}
Data DisplayCode{{response.Data.DisplayCode}}
Data ResponseType{{response.Data.ResponseType}}
Data Score{{response.Data.Score}}
Data Sequence{{response.Data.Sequence}}
Data CorrectAnswer{{response.Data.CorrectAnswer}}
Data Hint{{response.Data.Hint}}
Data Options{{response.Data.Options}}
Data QuestionImageUrl{{response.Data.QuestionImageUrl}}
Data RangeMin{{response.Data.RangeMin}}
Data RangeMax{{response.Data.RangeMax}}
Data ParentFormSection ID{{response.Data.ParentFormSection.id}}
Data ParentFormSection SectionIdentifier{{response.Data.ParentFormSection.SectionIdentifier}}
Data ParentFormSection Title{{response.Data.ParentFormSection.Title}}
Data ParentFormSection Description{{response.Data.ParentFormSection.Description}}
Data ParentFormSection DisplayCode{{response.Data.ParentFormSection.DisplayCode}}
Data ParentFormSection Sequence{{response.Data.ParentFormSection.Sequence}}
Data ParentFormSection ParentSectionId{{response.Data.ParentFormSection.ParentSectionId}}
Data ParentFormSection CreatedAt{{response.Data.ParentFormSection.CreatedAt}}
Data ParentFormTemplate ID{{response.Data.ParentFormTemplate.id}}
Data ParentFormTemplate Title{{response.Data.ParentFormTemplate.Title}}
Data ParentFormTemplate Description{{response.Data.ParentFormTemplate.Description}}
Data ParentFormTemplate Type{{response.Data.ParentFormTemplate.Type}}
Data ParentFormTemplate DisplayCode{{response.Data.ParentFormTemplate.DisplayCode}}
Data ParentFormTemplate OwnerUserId{{response.Data.ParentFormTemplate.OwnerUserId}}
Data ParentFormTemplate RootSectionId{{response.Data.ParentFormTemplate.RootSectionId}}
Data ParentFormTemplate DefaultSectionNumbering{{response.Data.ParentFormTemplate.DefaultSectionNumbering}}
Data ParentFormTemplate CreatedAt{{response.Data.ParentFormTemplate.CreatedAt}}
Data CreatedAt{{response.Data.CreatedAt}}
Data UpdatedAt{{response.Data.UpdatedAt}}
\r", - "`;\r", - "\r", - "function constructVisualizerPayload() {\r", - " return {response: pm.response.json()}\r", - "}\r", - "\r", - "pm.visualizer.set(template, constructVisualizerPayload());" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "// {\r\n// \"ParentTemplateId\": \"d9e0416b-b142-4492-a522-a7fc57f9c224\", //Form Template id\r\n// \"ParentSectionId\": \"36869bba-551f-4aaf-a150-ccafd9567254\", //Form Section Id\r\n// \"Title\": \"Question Two\",\r\n// \"Description\": \"Question Two description\",\r\n// \"DisplayCode\": \"Question Two Display code\",\r\n// \"ResponseType\": \"Integer\",\r\n// \"Score\": 11,\r\n// \"CorrectAnswer\": \"Question Two correct answer\",\r\n// \"Hint\": \"Question Two hint\",\r\n// \"Options\":\"option1,option2\",\r\n// \"QuestionImageUrl\":\"this.is.image\",\r\n// \"RangeMin\":2,\r\n// \"RangeMax\":4\r\n// },\r\n// {\r\n// \"ParentTemplateId\": \"d9e0416b-b142-4492-a522-a7fc57f9c224\",\r\n// \"ParentSectionId\": \"36869bba-551f-4aaf-a150-ccafd9567254\",\r\n// \"Title\": \"range ........!\",\r\n// \"Description\": \"Birth Day\",\r\n// \"ResponseType\": \"Boolean\",\r\n// \"Score\": 5,\r\n// // \"DisplayCode\": \"2b3b3ea7-d55f-46fb-901f-380a92be0059\",\r\n// \"CorrectAnswer\": \"1234\",\r\n// \"Hint\": \"date\",\r\n// \"QuestionImageUrl\": \"a\",\r\n// \"RangeMin\": 1,\r\n// \"RangeMax\": 2,\r\n// \"Options\": \"option1, option2\"\r\n// }\r\n// {\r\n// \"ParentTemplateId\": \"5777e0a8-6ba7-4313-ba4a-ff9b5bdb3d4a\",\r\n// \"ParentSectionId\": \"a1b31378-59b0-4ca8-8d54-323798bf924e\",\r\n// \"Title\": \"qqqqqqqqqqqqqqqq\",\r\n// \"Description\": \"qqqqqqqqq\",\r\n// \"ResponseType\": \"Text\",\r\n// \"Score\": 1,\r\n// \"CorrectAnswer\": \"qqqqqqqqqqqqqqq\",\r\n// \"Hint\": \"qqqqqqqqqqqqq\",\r\n// \"QuestionImageUrl\": \"qqqqqqqqqqqq\",\r\n// \"RangeMin\": null,\r\n// \"RangeMax\": null,\r\n// \"Options\": [\r\n// \"option1\",\r\n// \"option2\"\r\n// ]\r\n// }\r\n// {\r\n// \"ParentTemplateId\": \"5777e0a8-6ba7-4313-ba4a-ff9b5bdb3d4a\",\r\n// \"ParentSectionId\": \"a1b31378-59b0-4ca8-8d54-323798bf924e\",\r\n// \"Title\": \"qqqqqqqqqqqqqqqq\",\r\n// \"Description\": \"qqqqqqqqqq\",\r\n// \"ResponseType\": \"SinglehoiceSelection\",\r\n// \"Score\": 1,\r\n// \"CorrectAnswer\": \"qqqqqqqqqqqqqqq\",\r\n// \"Hint\": \"qqqqqqqqqqqqq\",\r\n// \"QuestionImageUrl\": \"qqqqqqqqqqqq\",\r\n// \"RangeMin\": null,\r\n// \"RangeMax\": null,\r\n// \"Options\": [\r\n// {\r\n// \"Text\": \"qqqqq\",\r\n// \"Sequence\": 1\r\n// },\r\n// {\r\n// \"Text\": \"qqqq\",\r\n// \"Sequence\": 2\r\n// },\r\n// {\r\n// \"Text\": \"qq\",\r\n// \"Sequence\": 3\r\n// }\r\n// ]\r\n// }\r\n{\r\n \"ParentTemplateId\": \"4fabef36-5992-4f9c-aa7b-2539ae24aa7e\",\r\n \"ParentSectionId\": \"b8243963-7999-4623-b357-d1d16549d619\",\r\n \"ResponseType\": \"SingleChoiceSelection\",\r\n \"Options\": [\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"1\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n },\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"2\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n },\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"3\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n },\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"1\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n },\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"2\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n },\r\n {\r\n \"Text\": \"qqqqq\",\r\n \"Sequence\": \"3\",\r\n \"ImageUrl\": \"asdasfdsf\"\r\n }\r\n ]\r\n}\r\n\r\n\r\n\r\n{\r\n \"Title\": \"What is your full name\",\r\n \"Description\": \"description\",\r\n \"ResponseType\": \"SingleChoiceSelection\",\r\n \"Options\": [\r\n {\r\n \"Text\": \"sdfg\",\r\n \"Sequence\": \"a1\",\r\n \"ImageUrl\": \"wererterytryur\"\r\n },\r\n {\r\n \"Text\": \"sdagdf\",\r\n \"Sequence\": \"a2\",\r\n \"ImageUrl\": \"asdf\"\r\n },\r\n {\r\n \"Text\": \"dzfg\",\r\n \"Sequence\": \"a3\",\r\n \"ImageUrl\": \"zsdfg\"\r\n },\r\n {\r\n \"Text\": \"zdfgdfg\",\r\n \"Sequence\": \"a4\",\r\n \"ImageUrl\": \"dfg\"\r\n }\r\n ]\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{BASE_URL}}/questions", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "questions" - ] - } - }, - "response": [] - }, - { - "name": "Get question by id", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Request is successfull\", function () {\r", - " pm.response.to.have.status(201);\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Status).to.eql('Success');\r", - "});\r", - "\r", - "pm.test(\"Question is created\", function () {\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data).to.have.property('id');\r", - " pm.expect(jsonRes.Data).to.have.property('ParentTemplateId');\r", - " pm.expect(jsonRes.Data).to.have.property('ParentSectionId');\r", - " pm.expect(jsonRes.Data).to.have.property('Title');\r", - " pm.expect(jsonRes.Data).to.have.property('Description');\r", - " pm.expect(jsonRes.Data).to.have.property('DisplayCode');\r", - " pm.expect(jsonRes.Data).to.have.property('ResponseType');\r", - " pm.expect(jsonRes.Data).to.have.property('Score');\r", - " pm.expect(jsonRes.Data).to.have.property('CorrectAnswer');\r", - " pm.expect(jsonRes.Data).to.have.property('Hint');\r", - " pm.expect(jsonRes.Data).to.have.property('Options');\r", - " pm.expect(jsonRes.Data).to.have.property('QuestionImageUrl');\r", - " pm.expect(jsonRes.Data).to.have.property('RangeMin');\r", - " pm.expect(jsonRes.Data).to.have.property('RangeMax');\r", - " \r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "packages": {}, - "type": "text/javascript" - } - } - ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{BASE_URL}}/questions/{{QUESTION_ID}}", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "questions", - "{{QUESTION_ID}}" - ] - } - }, - "response": [] - }, - { - "name": "Update a question record", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Request is successfull\", function () {\r", - " pm.response.to.have.status(200);\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Status).to.eql('Success');\r", - "});\r", - "\r", - "pm.test(\"User is updated\", function () {\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data).to.have.property('id');\r", - " pm.expect(jsonRes.Data.id).equals('006330fd-c768-4034-90a0-3aa8ce3475f4');\r", - " pm.expect(jsonRes.Data).to.have.property('ParentTemplateId');\r", - " pm.expect(jsonRes.Data.ParentTemplateId).equals('163bac66-4af0-40dc-a09f-bfbe866e7e89');\r", - " pm.expect(jsonRes.Data).to.have.property('ParentSectionId');\r", - " pm.expect(jsonRes.Data.ParentSectionId).equals('c99e1620-df7a-45ce-b374-325002706f5f');\r", - " pm.expect(jsonRes.Data).to.have.property('Title');\r", - " pm.expect(jsonRes.Data.Title).equals('Enter respiratory rate');\r", - " pm.expect(jsonRes.Data).to.have.property('Description');\r", - " pm.expect(jsonRes.Data.Description).equals('description');\r", - " pm.expect(jsonRes.Data).to.have.property('DisplayCode');\r", - " pm.expect(jsonRes.Data.DisplayCode).equals('QUESTION_#RTx8BEH6Zsv8t2V');\r", - " pm.expect(jsonRes.Data).to.have.property('ResponseType');\r", - " pm.expect(jsonRes.Data.ResponseType).equals('MultiChoiceSelection');\r", - " pm.expect(jsonRes.Data).to.have.property('Score');\r", - " pm.expect(jsonRes.Data.Score).equals(1);\r", - " pm.expect(jsonRes.Data).to.have.property('Sequence');\r", - " pm.expect(jsonRes.Data.Sequence).equals('A2');\r", - " pm.expect(jsonRes.Data).to.have.property('CorrectAnswer');\r", - " pm.expect(jsonRes.Data.CorrectAnswer).equals('A');\r", - " pm.expect(jsonRes.Data).to.have.property('CorrectAnswer');\r", - " pm.expect(jsonRes.Data.CorrectAnswer).equals('CorrectAnswer');\r", - " pm.expect(jsonRes.Data).to.have.property('Hint');\r", - " pm.expect(jsonRes.Data.Hint).equals('Hint');\r", - " pm.expect(jsonRes.Data).to.have.property('Options');\r", - " pm.expect(jsonRes.Data.Options).equals('[\"HTML,CSS,JAVASCRIPT,JAVA,PYTHON,C,C++\"]');\r", - " pm.expect(jsonRes.Data).to.have.property('QuestionImageUrl');\r", - " pm.expect(jsonRes.Data.QuestionImageUrl).equals('QuestionImageUrl');\r", - " pm.expect(jsonRes.Data).to.have.property('RangeMin');\r", - " pm.expect(jsonRes.Data.RangeMin).equals(1);\r", - " pm.expect(jsonRes.Data).to.have.property('RangeMax');\r", - " pm.expect(jsonRes.Data.RangeMax).equals(2);\r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"Title\": \"Current (referral) cardiac diagnosis/diagnoses?\",\r\n \"ResponseType\": \"MultiChoiceSelection\",\r\n \"RangeMin\": 1,\r\n \"RangeMax\": 2,\r\n \"Options\": [\r\n \"Acute Coronary Syndrome - STEMI,Acute Coronary Syndrome - NSTEMI,Acute Coronary Syndrome - Unstable Angina,Stable Coronary Artery Disease or Stable Angina,Heart Failure,Atrial Fibrillation/Flutter/SVT,Peripheral Arterial Disease,Percutaneous Cardiac Intervention (TAVI/ASD Repair/Valvotomies),Heart Valve replacement,High cardiovascular risk patient (High CVD risk score, Diabetes, Metabolic syndrome, Chronic Kidney Disease, Multiple risk factors),Other\"\r\n ]\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{BASE_URL}}/questions/{{QUESTION_ID}}", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "questions", - "{{QUESTION_ID}}" - ] - } - }, - "response": [] - }, - { - "name": "Generic search", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Request is successfull\", function () {\r", - " pm.response.to.have.status(200);\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Status).to.eql('Success');\r", - "});\r", - "\r", - "pm.test(\"User records are returned\", function () {\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data.Items.length).greaterThan(0);\r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{BASE_URL}}/questions/search?parentTemplateId=210939f1-c6c4-4274-843d-72f44f77da65", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "questions", - "search" - ], - "query": [ - { - "key": "parentTemplateId", - "value": "210939f1-c6c4-4274-843d-72f44f77da65" - } - ] - } - }, - "response": [] - }, - { - "name": "Delete a question record", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Request is successfull\", function () {\r", - " pm.response.to.have.status(200);\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Status).to.eql('Success');\r", - "});\r", - "\r", - "pm.test(\"Question is deleted\", function () {\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data).equals(true);\r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "DELETE", - "header": [], - "url": { - "raw": "{{BASE_URL}}/questions/{{QUESTION_ID}}", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "questions", - "{{QUESTION_ID}}" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Form Submission", - "item": [ - { - "name": "Create new form", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "try {\r", - " var jsonRes = pm.response.json();\r", - " pm.environment.set(\"SUBMISSION_ID\", jsonRes.Data.id);\r", - "}\r", - "catch (error) {\r", - " console.log(error.message);\r", - "}\r", - "\r", - "pm.test(\"Request is successfull\", function () {\r", - " pm.response.to.have.status(201);\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Status).to.eql('Success');\r", - "});\r", - "\r", - "pm.test(\"Question is created\", function () {\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data).to.have.property('id');\r", - " pm.expect(jsonRes.Data).to.have.property('ParentFormTemplateId');\r", - " pm.expect(jsonRes.Data).to.have.property('FormUrl');\r", - " pm.expect(jsonRes.Data).to.have.property('SubmissionTimestamp');\r", - " \r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"FormTemplateId\":\"163bac66-4af0-40dc-a09f-bfbe866e7e89\" //Form Template Id\r\n // \"FormUrl\": \"This is Third form\",\r\n // \"AnsweredByUserId\": \"c9e5b365-a0ff-4f73-b168-f3a4263b777e\", //Submitter / User Id\r\n // \"Status\": \"InProgress\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{BASE_URL}}/form-submissions", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "form-submissions" - ] - } - }, - "response": [] - }, - { - "name": "Get form by id", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{BASE_URL}}/form-submissions/{{SUBMISSION_ID}}", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "form-submissions", - "{{SUBMISSION_ID}}" - ] - } - }, - "response": [] - }, - { - "name": "Update a form record", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Request is successfull\", function () {\r", - " pm.response.to.have.status(200);\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Status).to.eql('Success');\r", + " pm.expect(jsonRes.Data).to.have.property('Tags');\r", + " pm.expect(jsonRes.Data).to.have.property('CreatedBy');\r", + " pm.expect(jsonRes.Data).to.have.property('ApprovalStatus');\r", + " pm.expect(jsonRes.Data).to.have.property('Visibility');\r", + " pm.expect(jsonRes.Data).to.have.property('StatusDescription');\r", + " pm.expect(jsonRes.Data).to.have.property('OwnerId');\r", + " pm.expect(jsonRes.Data).to.have.property('OwnerName');\r", + " pm.expect(jsonRes.Data).to.have.property('PublishedBy');\r", + " pm.expect(jsonRes.Data).to.have.property('PublishedDate');\r", + " pm.expect(jsonRes.Data).to.have.property('MachineReadableFileName');\r", "});\r", "\r", - "pm.test(\"User is updated\", function () {\r", - " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data).to.have.property('id');\r", - " pm.expect(jsonRes.Data.id).equals('03ad39c3-d959-4843-bb08-122b06ed37a1');\r", - " pm.expect(jsonRes.Data).to.have.property('FormTemplateId');\r", - " pm.expect(jsonRes.Data.FormTemplateId).equals('163bac66-4af0-40dc-a09f-bfbe866e7e89');\r", - " pm.expect(jsonRes.Data).to.have.property('FormUrl');\r", - " pm.expect(jsonRes.Data.FormUrl).equals('FormUrl');\r", - " pm.expect(jsonRes.Data).to.have.property('AnsweredByUserId');\r", - " pm.expect(jsonRes.Data.AnsweredByUserId).equals('03ad39c3-d959-4843-bb08-122b06ed37a1');\r", - " pm.expect(jsonRes.Data).to.have.property('Status');\r", - " pm.expect(jsonRes.Data.Status).equals('LinkShared');\r", - " pm.expect(jsonRes.Data).to.have.property('SubmissionTimestamp');\r", - " pm.expect(jsonRes.Data.SubmissionTimestamp).equals('2024-09-12 12:36:51.712');\r", - "\r", - "});" + "" ], "type": "text/javascript", "packages": {} @@ -1466,59 +1379,21 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"FormTemplateId\":\"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\",\r\n \"FormUrl\": \"This is Third form\",\r\n \"AnsweredByUserId\": \"05b04a84-ce2d-4c5a-9528-4cc168e0ad0a\",\r\n \"Status\": \"LinkShared\"\r\n}", + "raw": "{\r\n \"TemplateName\": \"Updated Patient Survey Form\",\r\n \"Description\": \"An updated comprehensive patient intake survey\",\r\n \"CurrentVersion\": \"1.1\",\r\n \"Tags\": \"Patient, Survey, Medical, Updated\",\r\n \"CreatedBy\": \"{{USER_ID}}\",\r\n \"ApprovalStatus\": \"Approved\",\r\n \"Visibility\": \"Public\",\r\n \"StatusDescription\": \"Updated template for patient intake\",\r\n \"OwnerId\": \"{{USER_ID}}\",\r\n \"OwnerName\": \"Updated Test User\",\r\n \"PublishedBy\": \"{{USER_ID}}\",\r\n \"PublishedDate\": \"2024-01-02T00:00:00.000Z\",\r\n \"MachineReadableFileName\": \"patient-survey-v1-1.json\"\r\n}", "options": { "raw": { "language": "json" } - } - }, - "url": { - "raw": "{{BASE_URL}}/form-submissions/{{SUBMISSION_ID}}", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "form-submissions", - "{{SUBMISSION_ID}}" - ] - } - }, - "response": [] - }, - { - "name": "get submission by template id", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{BASE_URL}}/form-submissions/by-template/5777e0a8-6ba7-4313-ba4a-ff9b5bdb3d4a", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "form-submissions", - "by-template", - "5777e0a8-6ba7-4313-ba4a-ff9b5bdb3d4a" - ] - } - }, - "response": [] - }, - { - "name": "Submit the instance of template", - "request": { - "method": "POST", - "header": [], + } + }, "url": { - "raw": "{{BASE_URL}}/form-submissions/c17f6654-4ca3-47a1-8761-986f4f23d95e/submit", + "raw": "{{BASE_URL}}/form-templates/{{TEMPLATE_ID}}", "host": [ "{{BASE_URL}}" ], "path": [ - "form-submissions", - "c17f6654-4ca3-47a1-8761-986f4f23d95e", - "submit" + "form-templates", + "{{TEMPLATE_ID}}" ] } }, @@ -1537,10 +1412,18 @@ " pm.expect(jsonRes.Status).to.eql('Success');\r", "});\r", "\r", - "pm.test(\"User records are returned\", function () {\r", + "pm.test(\"Template search working\", function () {\r", " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data.Items.length).greaterThan(0);\r", - "});" + " pm.expect(jsonRes.Data).to.have.property('TotalCount');\r", + " pm.expect(jsonRes.Data).to.have.property('RetrievedCount');\r", + " pm.expect(jsonRes.Data).to.have.property('PageIndex');\r", + " pm.expect(jsonRes.Data).to.have.property('ItemsPerPage');\r", + " pm.expect(jsonRes.Data).to.have.property('Order');\r", + " pm.expect(jsonRes.Data).to.have.property('OrderBy');\r", + " pm.expect(jsonRes.Data).to.have.property('Items');\r", + "});\r", + "\r", + "" ], "type": "text/javascript", "packages": {} @@ -1551,26 +1434,39 @@ "method": "GET", "header": [], "url": { - "raw": "{{BASE_URL}}/form-submissions/search?id=b0ba4554-ffc0-496f-9985-45325e37de09", + "raw": "{{BASE_URL}}/form-templates/search", "host": [ "{{BASE_URL}}" ], "path": [ - "form-submissions", + "form-templates", "search" + ] + } + }, + "response": [] + }, + { + "name": "Get Details by template id", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/form-templates/{{TEMPLATE_ID}}/details", + "host": [ + "{{BASE_URL}}" ], - "query": [ - { - "key": "id", - "value": "b0ba4554-ffc0-496f-9985-45325e37de09" - } + "path": [ + "form-templates", + "{{TEMPLATE_ID}}", + "details" ] } }, "response": [] }, { - "name": "Delete a form record", + "name": "Delete form template record", "event": [ { "listen": "test", @@ -1582,10 +1478,12 @@ " pm.expect(jsonRes.Status).to.eql('Success');\r", "});\r", "\r", - "pm.test(\"Form Submission is deleted\", function () {\r", + "pm.test(\"Template deleted successful\", function () {\r", " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data).equals(true);\r", - "});" + " pm.expect(jsonRes.Data).to.be.true;\r", + "});\r", + "\r", + "" ], "type": "text/javascript", "packages": {} @@ -1596,107 +1494,70 @@ "method": "DELETE", "header": [], "url": { - "raw": "{{BASE_URL}}/form-submissions/{{SUBMISSION_ID}}", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "form-submissions", - "{{SUBMISSION_ID}}" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "User Login Session", - "item": [ - { - "name": "Create a new session", - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"UserId\": \"b2e2baf0-fc4f-47ec-a01a-20bcd18c2c9b\",\r\n //User Id\r\n \"IsActiveSession\": true,\r\n \"ValidTill\": \"2025-10-30T16:30:00Z\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{BASE_URL}}/user-login-sessions", + "raw": "{{BASE_URL}}/form-templates/{{TEMPLATE_ID}}", "host": [ "{{BASE_URL}}" ], "path": [ - "user-login-sessions" + "form-templates", + "{{TEMPLATE_ID}}" ] } }, "response": [] }, { - "name": "Get a login session by id", + "name": "Get Form Submissions", "request": { "method": "GET", "header": [], "url": { - "raw": "{{BASE_URL}}/user-login-sessions/6aad120e-4122-4358-b4c5-cffb6fdbb9d3", + "raw": "{{BASE_URL}}/form-templates/{{TEMPLATE_ID}}/form-submissions", "host": [ "{{BASE_URL}}" ], "path": [ - "user-login-sessions", - "6aad120e-4122-4358-b4c5-cffb6fdbb9d3" + "form-templates", + "{{TEMPLATE_ID}}", + "form-submissions" ] } }, "response": [] }, { - "name": "Update a session", + "name": "Export Form Template", "request": { - "method": "PUT", + "method": "GET", "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"UserId\": \"05b04a84-ce2d-4c5a-9528-4cc168e0ad0a\",\r\n \"IsActiveSession\": false,\r\n \"ValidTill\": \"2025-10-30T16:30:00Z\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, "url": { - "raw": "{{BASE_URL}}/user-login-sessions/6aad120e-4122-4358-b4c5-cffb6fdbb9d3", + "raw": "{{BASE_URL}}/form-templates/{{TEMPLATE_ID}}/export", "host": [ "{{BASE_URL}}" ], "path": [ - "user-login-sessions", - "6aad120e-4122-4358-b4c5-cffb6fdbb9d3" + "form-templates", + "{{TEMPLATE_ID}}", + "export" ] } }, "response": [] }, { - "name": "Delete a existing session", + "name": "Preview Form Template", "request": { - "method": "DELETE", + "method": "GET", "header": [], "url": { - "raw": "{{BASE_URL}}/user-login-sessions/6aad120e-4122-4358-b4c5-cffb6fdbb9d3", + "raw": "{{BASE_URL}}/form-templates/{{TEMPLATE_ID}}/preview", "host": [ "{{BASE_URL}}" ], "path": [ - "user-login-sessions", - "6aad120e-4122-4358-b4c5-cffb6fdbb9d3" + "form-templates", + "{{TEMPLATE_ID}}", + "preview" ] } }, @@ -1705,10 +1566,10 @@ ] }, { - "name": "Question Response", + "name": "Form section", "item": [ { - "name": "Create a new response", + "name": "Create new form section", "event": [ { "listen": "test", @@ -1716,7 +1577,7 @@ "exec": [ "try {\r", " var jsonRes = pm.response.json();\r", - " pm.environment.set(\"RESPONSE_ID\", jsonRes.Data.id);\r", + " pm.environment.set(\"SECTION_ID\", jsonRes.Data.id);\r", "}\r", "catch (error) {\r", " console.log(error.message);\r", @@ -1728,13 +1589,16 @@ " pm.expect(jsonRes.Status).to.eql('Success');\r", "});\r", "\r", - "pm.test(\"Question is created\", function () {\r", + "pm.test(\"Section is created\", function () {\r", " var jsonRes = pm.response.json();\r", " pm.expect(jsonRes.Data).to.have.property('id');\r", - " pm.expect(jsonRes.Data).to.have.property('ParentFormTemplateId');\r", - " pm.expect(jsonRes.Data).to.have.property('FormUrl');\r", - " pm.expect(jsonRes.Data).to.have.property('SubmissionTimestamp');\r", - " \r", + " pm.expect(jsonRes.Data).to.have.property('Title');\r", + " pm.expect(jsonRes.Data).to.have.property('Description');\r", + " pm.expect(jsonRes.Data).to.have.property('DisplayCode');\r", + " pm.expect(jsonRes.Data).to.have.property('Sequence');\r", + " pm.expect(jsonRes.Data).to.have.property('ParentSectionId');\r", + " pm.expect(jsonRes.Data).to.have.property('ParentTemplateId');\r", + " \r", "});" ], "type": "text/javascript", @@ -1747,7 +1611,7 @@ "header": [], "body": { "mode": "raw", - "raw": "// {\r\n// \"FormSubmissionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\",\r\n// \"QuestionId\": \"1ce7590c-b37d-4668-b324-d20f0a86c923\",\r\n// \"ResponseType\": \"Text\",\r\n// \"IntegerValue\": 1,\r\n// \"FloatValue\": 1.2,\r\n// \"BooleanValue\": true,\r\n// \"DateTimeValue\": \"2024-03-08 18:25:30.843\",\r\n// \"Url\": \"https://meet.google.com/zrb-tyni-rdz\",\r\n// \"FileResourceId\": \"0c9ea637-cb8d-468e-b4c7-303d2604c1b5\",\r\n// \"TextValue\": \"hjsj1234-erhh233\",\r\n// \"SubmissionTimestamp\": \"2024-03-08 18:25:30.843\",\r\n// \"LastSaveTimestamp\": \"2024-03-08 18:25:30.843\"\r\n// }\r\n// {\r\n// \"FormSubmissionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\",\r\n// \"QuestionId\": \"1ce7590c-b37d-4668-b324-d20f0a86c923\",\r\n// \"ResponseType\": \"Text\",\r\n// // \"IntegerValue\": 1,\r\n// // \"FloatValue\": 1.2,\r\n// // \"BooleanValue\": true,\r\n// // \"DateTimeValue\": \"2024-03-08 18:25:30.843\",\r\n// // \"Url\": \"https://meet.google.com/zrb-tyni-rdz\",\r\n// // \"FileResourceId\": \"0c9ea637-cb8d-468e-b4c7-303d2604c1b5\",\r\n// \"TextValue\": \"hjsj1234-erhh233\",\r\n// \"SubmissionTimestamp\": \"2024-03-08 18:25:30.843\",\r\n// \"LastSaveTimestamp\": \"2024-03-08 18:25:30.843\"\r\n// }\r\n// {\r\n// \"FormSubmissionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\",\r\n// \"QuestionId\": \"1ce7590c-b37d-4668-b324-d20f0a86c923\",\r\n// \"ResponseType\": \"Text\",\r\n// \"TextValue\": \"hjsj1234-erhh233\",\r\n// \"SubmissionTimestamp\": \"2024-03-08 18:25:30.843\",\r\n// \"LastSaveTimestamp\": \"2024-03-08 18:25:30.843\"\r\n// }\r\n// \"FormSubmissionId\": \"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6\",\r\n// \"QuestionId\": \"1ce7590c-b37d-4668-b324-d20f0a86c923\",\r\n// \"ResponseType\": \"Text\",\r\n// \"TextValue\": \"hjsj1234-erhh233\",\r\n// }\r\n// {\r\n// \"FormSubmissionId\": \"533c53a0-c7bd-45a8-a557-441dab0ca650\", //Form submission id\r\n// \"Data\": [\r\n// {\r\n// \"id\": \"dce55a42-f0c4-49ed-b84c-d34893397f4a\",\r\n// \"value\": \"Answer\"\r\n// },\r\n// {\r\n// \"id\": \"dce55a42-f0c4-49ed-b84c-d34893397f4a\",\r\n// \"value\": \"Answer\"\r\n// }\r\n// ]\r\n// }\r\n// {\r\n// \"FormSubmissionId\": \"533c53a0-c7bd-45a8-a557-441dab0ca650\", //Form submission id\r\n// // \"Data\": [\r\n// // {\r\n// // \"id\": \"dce55a42-f0c4-49ed-b84c-d34893397f4a\",\r\n// // \"value\": \"Answer\"\r\n// // },\r\n// // {\r\n// // \"id\": \"dce55a42-f0c4-49ed-b84c-d34893397f4a\",\r\n// // \"value\": \"Answer\"\r\n// // }\r\n// // ]\r\n// \"Data\":{\r\n// \"dce55a42-f0c4-49ed-b84c-d34893397f4a\":45646\r\n// // \"dce55a42-f0c4-49ed-b84c-d34893397f5a\":\"Answer\"\r\n// }\r\n// }\r\n// {\r\n// \"Data\": {\r\n// \"3fcfd54d-b91e-4a3b-8867-92799cddc99e\": \"Prashant Pandurang Kharade\",\r\n// \"c522ff62-7767-4d93-803b-14c935c3d96f\": \"kharade\",\r\n// \"7716400e-240f-475e-bfc5-ee2b4afec14c\": \"pk\",\r\n// \"d397554c-3ac5-4a25-8824-92bffb33e644\": \"Kiran\",\r\n// \"541217fa-46a8-42f4-9151-1c9c66166cb3\": \"kharade\"\r\n// },\r\n// \"FormSubmissionId\": \"a4728261-1b9a-47a4-aabe-7a3437429639\"\r\n// }\r\n\r\n{\r\n \"Data\": {\r\n \"2255a13c-f4a3-4bcf-9125-e947adc262af\": \"Answer of first question\",\r\n // \"f242a2f0-ab75-4423-bb3d-b08f496a66b9\": 123456,\r\n // \"39b788f6-fbb2-4b77-bf38-d9c2b779bb26\": 6,\r\n // \"d397554c-3ac5-4a25-8824-92bffb33e644\": [\"Prashant\", \"Pandurang\", \"Kharade\"],\r\n \"02f21e5d-787f-409b-8456-f2ed691bc2b1\": \"true\"\r\n },\r\n \"FormSubmissionId\": \"8df8d6a5-5f35-46d8-8cae-4838a7a43d41\"\r\n}", + "raw": "{\r\n \"Title\": \"Personal Information\",\r\n \"Description\": \"Basic personal details section\",\r\n \"DisplayCode\": \"PERSONAL_INFO\",\r\n \"Sequence\": 1,\r\n \"ParentSectionId\": null,\r\n \"ParentTemplateId\": \"{{TEMPLATE_ID}}\"\r\n}", "options": { "raw": { "language": "json" @@ -1755,38 +1619,66 @@ } }, "url": { - "raw": "{{BASE_URL}}/question-responses/save", + "raw": "{{BASE_URL}}/form-sections", "host": [ "{{BASE_URL}}" ], "path": [ - "question-responses", - "save" + "form-sections" ] } }, "response": [] }, { - "name": "Get response by id", + "name": "Get form section by id", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Request is successfull\", function () {\r", + " pm.response.to.have.status(200);\r", + " var jsonRes = pm.response.json();\r", + " pm.expect(jsonRes.Status).to.eql('Success');\r", + "});\r", + "\r", + "pm.test(\"Section retrived successful\", function () {\r", + " var jsonRes = pm.response.json();\r", + " pm.expect(jsonRes.Data).to.have.property('id');\r", + " pm.expect(jsonRes.Data).to.have.property('Title');\r", + " pm.expect(jsonRes.Data).to.have.property('Description');\r", + " pm.expect(jsonRes.Data).to.have.property('DisplayCode');\r", + " pm.expect(jsonRes.Data).to.have.property('Sequence');\r", + " pm.expect(jsonRes.Data).to.have.property('ParentSectionId');\r", + " pm.expect(jsonRes.Data).to.have.property('ParentTemplateId');\r", + "});\r", + "\r", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], "request": { "method": "GET", "header": [], "url": { - "raw": "{{BASE_URL}}/question-responses/{RESPONSE_ID}}", + "raw": "{{BASE_URL}}/form-sections/{{SECTION_ID}}", "host": [ "{{BASE_URL}}" ], "path": [ - "question-responses", - "{RESPONSE_ID}}" + "form-sections", + "{{SECTION_ID}}" ] } }, "response": [] }, { - "name": "Update a existing response", + "name": "Update form section", "event": [ { "listen": "test", @@ -1798,35 +1690,18 @@ " pm.expect(jsonRes.Status).to.eql('Success');\r", "});\r", "\r", - "pm.test(\"User is updated\", function () {\r", + "pm.test(\"Section Updated successful\", function () {\r", " var jsonRes = pm.response.json();\r", " pm.expect(jsonRes.Data).to.have.property('id');\r", - " pm.expect(jsonRes.Data.id).equals('03ad39c3-d959-4843-bb08-122b06ed37a1');\r", - " pm.expect(jsonRes.Data).to.have.property('FormSubmissionId');\r", - " pm.expect(jsonRes.Data.FormSubmissionId).equals('163bac66-4af0-40dc-a09f-bfbe866e7e89');\r", - " pm.expect(jsonRes.Data).to.have.property('QuestionId');\r", - " pm.expect(jsonRes.Data.QuestionId).equals('6eddc7f6-bfae-4ee9-914e-2f48cfe3c825');\r", - " pm.expect(jsonRes.Data).to.have.property('ResponseType');\r", - " pm.expect(jsonRes.Data.ResponseType).equals('Text');\r", - " pm.expect(jsonRes.Data).to.have.property('IntegerValue');\r", - " pm.expect(jsonRes.Data.IntegerValue).equals(1);\r", - " pm.expect(jsonRes.Data).to.have.property('FloatValue');\r", - " pm.expect(jsonRes.Data.FloatValue).equals(1.2);\r", - " pm.expect(jsonRes.Data).to.have.property('BooleanValue');\r", - " pm.expect(jsonRes.Data.BooleanValue).equals(true);\r", - " pm.expect(jsonRes.Data).to.have.property('DateTimeValue');\r", - " pm.expect(jsonRes.Data.DateTimeValue).equals('2024-09-12 12:36:51.712');\r", - " pm.expect(jsonRes.Data).to.have.property('Url');\r", - " pm.expect(jsonRes.Data.Url).equals('url');\r", - " pm.expect(jsonRes.Data).to.have.property('FileResourceId');\r", - " pm.expect(jsonRes.Data.FileResourceId).equals('6eddc7f6-bfae-4ee9-914e-2f48cfe3c825');\r", - " pm.expect(jsonRes.Data).to.have.property('TextValue');\r", - " pm.expect(jsonRes.Data.TextValue).equals('text value');\r", - " pm.expect(jsonRes.Data).to.have.property('SubmissionTimestamp');\r", - " pm.expect(jsonRes.Data.SubmissionTimestamp).equals('2024-09-12 12:36:51.712');\r", - " pm.expect(jsonRes.Data).to.have.property('LastSaveTimestamp');\r", - " pm.expect(jsonRes.Data.LastSaveTimestamp).equals('2024-09-12 12:36:51.712');\r", - "});" + " pm.expect(jsonRes.Data).to.have.property('Title');\r", + " pm.expect(jsonRes.Data).to.have.property('Description');\r", + " pm.expect(jsonRes.Data).to.have.property('DisplayCode');\r", + " pm.expect(jsonRes.Data).to.have.property('Sequence');\r", + " pm.expect(jsonRes.Data).to.have.property('ParentSectionId');\r", + " pm.expect(jsonRes.Data).to.have.property('ParentTemplateId');\r", + "});\r", + "\r", + "" ], "type": "text/javascript", "packages": {} @@ -1838,7 +1713,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"ResponseType\": \"Boolean\",\r\n \"IntegerValue\": 1,\r\n \"FloatValue\": 1.2,\r\n \"BooleanValue\": true,\r\n \"DateTimeValue\": \"2024-03-08 18:25:30.843\",\r\n \"Url\": \"https://meet.google.com/zrb-tyni-rdz\",\r\n \"FileResourceId\": \"0c9ea637-cb8d-468e-b4c7-303d2604c1b5\",\r\n \"TextValue\": \"hjsj1234-erhh233\"\r\n}", + "raw": "{\r\n \"Title\": \"Updated Personal Information\",\r\n \"Description\": \"Updated basic personal details section\",\r\n \"DisplayCode\": \"PERSONAL_INFO_V2\",\r\n \"Sequence\": 1,\r\n \"ParentSectionId\": null,\r\n \"ParentTemplateId\": \"{{TEMPLATE_ID}}\"\r\n}", "options": { "raw": { "language": "json" @@ -1846,58 +1721,67 @@ } }, "url": { - "raw": "{{BASE_URL}}/question-responses/{{RESPONSE_ID}}", + "raw": "{{BASE_URL}}/form-sections/{{SECTION_ID}}", "host": [ "{{BASE_URL}}" ], "path": [ - "question-responses", - "{{RESPONSE_ID}}" + "form-sections", + "{{SECTION_ID}}" ] } }, "response": [] }, { - "name": "Export Responses in CSV", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{BASE_URL}}/question-responses/exportcsv/export", - "host": [ - "{{BASE_URL}}" - ], - "path": [ - "question-responses", - "exportcsv", - "export" - ] + "name": "Generic search", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Request is successfull\", function () {\r", + " pm.response.to.have.status(200);\r", + " var jsonRes = pm.response.json();\r", + " pm.expect(jsonRes.Status).to.eql('Success');\r", + "});\r", + "\r", + "pm.test(\"Section search working\", function () {\r", + " var jsonRes = pm.response.json();\r", + " pm.expect(jsonRes.Data).to.have.property('TotalCount');\r", + " pm.expect(jsonRes.Data).to.have.property('RetrievedCount');\r", + " pm.expect(jsonRes.Data).to.have.property('PageIndex');\r", + " pm.expect(jsonRes.Data).to.have.property('ItemsPerPage');\r", + " pm.expect(jsonRes.Data).to.have.property('Order');\r", + " pm.expect(jsonRes.Data).to.have.property('OrderBy');\r", + " pm.expect(jsonRes.Data).to.have.property('Items');\r", + "});\r", + "\r", + "" + ], + "type": "text/javascript", + "packages": {} + } } - }, - "response": [] - }, - { - "name": "Export responses in PDF", + ], "request": { "method": "GET", "header": [], "url": { - "raw": "{{BASE_URL}}/question-responses/exportpdf/export", + "raw": "{{BASE_URL}}/form-sections/search", "host": [ "{{BASE_URL}}" ], "path": [ - "question-responses", - "exportpdf", - "export" + "form-sections", + "search" ] } }, "response": [] }, { - "name": "Generic search", + "name": "Delete the form section record", "event": [ { "listen": "test", @@ -1909,10 +1793,12 @@ " pm.expect(jsonRes.Status).to.eql('Success');\r", "});\r", "\r", - "pm.test(\"User records are returned\", function () {\r", + "pm.test(\"Section deleted successful\", function () {\r", " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data.Items.length).greaterThan(0);\r", - "});" + " pm.expect(jsonRes.Data).to.be.true;\r", + "});\r", + "\r", + "" ], "type": "text/javascript", "packages": {} @@ -1920,53 +1806,90 @@ } ], "request": { - "method": "GET", + "method": "DELETE", "header": [], "url": { - "raw": "{{BASE_URL}}/question-responses/search?responseType=Text", + "raw": "{{BASE_URL}}/form-sections/{{SECTION_ID}}", "host": [ "{{BASE_URL}}" ], "path": [ - "question-responses", - "search" - ], - "query": [ - { - "key": "formSubmissionId", - "value": "a8b15839-1592-43b7-b586-89765cf99222", - "disabled": true - }, - { - "key": "questionId", - "value": "b56027ee-bdba-4182-bed6-d544a5bcc8e4", - "disabled": true - }, - { - "key": "responseType", - "value": "Text" - } + "form-sections", + "{{SECTION_ID}}" ] } }, "response": [] }, { - "name": "Delete a response", + "name": "Get form section by template id", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{BASE_URL}}/form-sections/by-template/{{TEMPLATE_ID}}", + "host": [ + "{{BASE_URL}}" + ], + "path": [ + "form-sections", + "by-template", + "{{TEMPLATE_ID}}" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Question", + "item": [ + { + "name": "Create a new question", "event": [ { "listen": "test", "script": { "exec": [ + "try {\r", + " var jsonRes = pm.response.json();\r", + " pm.environment.set(\"FIELD_ID\", jsonRes.Data.id);\r", + "}\r", + "catch (error) {\r", + " console.log(error.message);\r", + "}\r", + "\r", "pm.test(\"Request is successfull\", function () {\r", - " pm.response.to.have.status(200);\r", + " pm.response.to.have.status(201);\r", " var jsonRes = pm.response.json();\r", " pm.expect(jsonRes.Status).to.eql('Success');\r", "});\r", "\r", - "pm.test(\"Response is deleted\", function () {\r", + "pm.test(\"Question is created\", function () {\r", " var jsonRes = pm.response.json();\r", - " pm.expect(jsonRes.Data).equals(true);\r", + " pm.expect(jsonRes.Data).to.have.property('id');\r", + " pm.expect(jsonRes.Data).to.have.property('ParentTemplateId');\r", + " pm.expect(jsonRes.Data).to.have.property('ParentSectionId');\r", + " pm.expect(jsonRes.Data).to.have.property('Title');\r", + " pm.expect(jsonRes.Data).to.have.property('Description');\r", + " pm.expect(jsonRes.Data).to.have.property('DisplayCode');\r", + " pm.expect(jsonRes.Data).to.have.property('ResponseType');\r", + " pm.expect(jsonRes.Data).to.have.property('Score');\r", + " pm.expect(jsonRes.Data).to.have.property('Sequence');\r", + " pm.expect(jsonRes.Data).to.have.property('CorrectAnswer');\r", + " pm.expect(jsonRes.Data).to.have.property('IsRequired');\r", + " pm.expect(jsonRes.Data).to.have.property('Hint');\r", + " pm.expect(jsonRes.Data).to.have.property('Options');\r", + " pm.expect(jsonRes.Data).to.have.property('ImageResourceId');\r", + " pm.expect(jsonRes.Data).to.have.property('RangeMin');\r", + " pm.expect(jsonRes.Data).to.have.property('RangeMax');\r", + " pm.expect(jsonRes.Data).to.have.property('DefaultExpectedUnit');\r", + " pm.expect(jsonRes.Data).to.have.property('PageBreakAfter');\r", + " pm.expect(jsonRes.Data).to.have.property('SkipLogicId');\r", + " pm.expect(jsonRes.Data).to.have.property('CalculateLogicId');\r", + " pm.expect(jsonRes.Data).to.have.property('ValidateLogicId');\r", + " \r", "});" ], "type": "text/javascript", @@ -1975,16 +1898,24 @@ } ], "request": { - "method": "DELETE", + "method": "POST", "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"ParentTemplateId\": \"{{TEMPLATE_ID}}\",\r\n \"ParentSectionId\": \"{{SECTION_ID}}\",\r\n \"Title\": \"What is your age?\",\r\n \"Description\": \"Please enter your age in years\",\r\n \"DisplayCode\": \"AGE_QUESTION\",\r\n \"ResponseType\": \"Number\",\r\n \"Score\": 0,\r\n \"Sequence\": 1,\r\n \"CorrectAnswer\": null,\r\n \"IsRequired\": true,\r\n \"Hint\": \"Enter a number between 0 and 150\",\r\n \"Options\": [],\r\n \"ImageResourceId\": null,\r\n \"RangeMin\": 0,\r\n \"RangeMax\": 150,\r\n \"DefaultExpectedUnit\": \"years\",\r\n \"PageBreakAfter\": false,\r\n \"SkipLogicId\": null,\r\n \"CalculateLogicId\": null,\r\n \"ValidateLogicId\": null\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, "url": { - "raw": "{{BASE_URL}}/question-responses/{{RESPONSE_ID}}", + "raw": "{{BASE_URL}}/form-fields", "host": [ "{{BASE_URL}}" ], "path": [ - "question-responses", - "{{RESPONSE_ID}}" + "form-fields" ] } }, diff --git a/Postman/forms-service.postman_environment.json b/Postman/forms-service.postman_environment.json new file mode 100644 index 0000000..b762eed --- /dev/null +++ b/Postman/forms-service.postman_environment.json @@ -0,0 +1,75 @@ +{ + "id": "c625adeb-50e0-4ee6-832b-152b32530ce2", + "name": "forms-service", + "values": [ + { + "key": "BASE_URL", + "value": "http://localhost:5555/api/v1", + "type": "default", + "enabled": true + }, + { + "key": "USER_ID", + "value": "", + "type": "any", + "enabled": true + }, + { + "key": "TEMPLATE_ID", + "value": "", + "type": "any", + "enabled": true + }, + { + "key": "SECTION_ID", + "value": "", + "type": "any", + "enabled": true + }, + { + "key": "QUESTION_ID", + "value": "", + "type": "any", + "enabled": true + }, + { + "key": "SUBMISSION_ID", + "value": "", + "type": "any", + "enabled": true + }, + { + "key": "RESPONSE_ID", + "value": "", + "type": "any", + "enabled": true + }, + { + "key": "PARENT_SECTION_ID", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "SUBMISSION_KEY", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "x-api-key", + "value": "T26BP24-MRGMRYE-JB90S0V-NC93PY0", + "type": "default", + "enabled": true + }, + { + "key": "Authorization ", + "value": "", + "type": "default", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2025-06-21T12:41:29.517Z", + "_postman_exported_using": "Postman/11.50.6" +} \ No newline at end of file diff --git a/README.md b/README.md index 28b22be..41c4b8b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,149 @@ -# forms-service -A generic API service to manage forms data, build, publish and share forms. +# Form Service + +> A comprehensive Node.js microservice for dynamic form building, management, and processing with advanced skip logic and conditional field operations. + +## 🚀 Quick Start + +```bash +# Clone the repository +git clone https://github.com/your-org/form-service.git +cd form-service + +# Install dependencies +npm install + +# Start the service +npm run dev +``` + +The service will be available at `http://localhost:3000` + +## 📚 Documentation + +**📖 [View Full Documentation](http://localhost:3000/api/docs)** + +Once the service is running, you can access the complete documentation at: + +- **Local Development**: +- **Production**: + +### Documentation Features + +- **Interactive API Reference**: Test endpoints directly from the docs +- **Code Examples**: Ready-to-use code snippets in multiple languages +- **Visual Diagrams**: Mermaid diagrams for complex concepts +- **Search Functionality**: Find what you need quickly +- **Responsive Design**: Works on desktop and mobile + +## ✨ Key Features + +- **Dynamic Form Builder**: Create complex forms with conditional logic +- **Skip Logic Engine**: Advanced field visibility and skip conditions +- **Validation Rules**: Comprehensive field validation with custom rules +- **Calculation Logic**: Mathematical and logical operations on form fields +- **Field Operations**: Composition, iteration, and function expressions +- **RESTful API**: Complete CRUD operations for all entities +- **Multi-database Support**: PostgreSQL, MySQL, and SQLite +- **TypeScript**: Full type safety and IntelliSense support + +## 🛠️ Development + +### Prerequisites + +- Node.js 18+ +- PostgreSQL/MySQL/SQLite +- npm or yarn + +### Local Development + +```bash +# Install dependencies +npm install + +# Run in development mode +npm run dev + +# Run tests +npm test + +# Build for production +npm run build +``` + +### API Testing + +The project includes Bruno API collections for testing: + +- **Bruno Collection**: `bruno/form-service/` +- **Postman Collection**: `Postman/form-service.postman_collection.json` + +## 📖 Documentation Sections + +### Getting Started + +- [Quick Start Guide](http://localhost:3000/api/docs/#/about/quickstart) - Get up and running in 5 minutes +- [Installation Guide](http://localhost:3000/api/docs/#/about/installation) - Detailed installation instructions +- [Configuration](http://localhost:3000/api/docs/#/about/configuration) - Environment and service configuration + +### API Reference + +- [REST API Overview](http://localhost:3000/api/docs/#/api/overview) - Complete API documentation +- [Form Templates](http://localhost:3000/api/docs/#/api/form-templates) - Form template management +- [Form Submissions](http://localhost:3000/api/docs/#/api/form-submissions) - Data collection and processing +- [Field Logic](http://localhost:3000/api/docs/#/api/field-logic) - Skip logic and validation + +### Core Concepts + +- [Skip Logic](http://localhost:3000/api/docs/#/guides/skip-logic) - Conditional field visibility +- [Validation Rules](http://localhost:3000/api/docs/#/guides/validation-rules) - Field validation +- [Calculation Logic](http://localhost:3000/api/docs/#/guides/calculation-logic) - Mathematical operations + +### Examples + +- [Basic Form Creation](http://localhost:3000/api/docs/#/examples/basic-form) - Simple form example +- [Conditional Fields](http://localhost:3000/api/docs/#/examples/conditional-fields) - Skip logic examples +- [API Integration](http://localhost:3000/api/docs/#/examples/api-integration-examples) - Client integration + +## 🔧 API Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| `GET` | `/api/health` | Health check | +| `GET` | `/api/docs` | Documentation | +| `POST` | `/api/forms/templates` | Create form template | +| `GET` | `/api/forms/templates` | List form templates | +| `POST` | `/api/forms/submissions` | Submit form data | +| `GET` | `/api/forms/submissions` | Get form submissions | + +## 🏗️ Architecture + +The service follows clean architecture principles with: + +- **Domain Layer**: Core business logic and entities +- **Application Layer**: Use cases and application services +- **Infrastructure Layer**: Database, external services, and frameworks +- **Presentation Layer**: API controllers and validators + +## 🤝 Contributing + +We welcome contributions! Please see our [Contributing Guide](http://localhost:3000/api/docs/#/about/contributing) for details. + +## 📄 License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## 🆘 Support + +- **Documentation**: [http://localhost:3000/api/docs](http://localhost:3000/api/docs) +- **Issues**: [GitHub Issues](https://github.com/your-org/form-service/issues) +- **Discussions**: [GitHub Discussions](https://github.com/your-org/form-service/discussions) +- **Email**: + +--- + + diff --git a/bruno/form-service/Favorite Template/Create favorite template.bru b/bruno/form-service/Favorite Template/Create favorite template.bru new file mode 100644 index 0000000..9093d9f --- /dev/null +++ b/bruno/form-service/Favorite Template/Create favorite template.bru @@ -0,0 +1,53 @@ +meta { + name: Create Favorite Template + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/favorite-templates + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "UserId": "{{USER_ID}}", + "TemplateId": "{{TEMPLATE_ID}}" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("FAVORITE_TEMPLATE_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Favorite Template is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('UserId'); + expect(jsonRes.Data.UserId).equals('{{USER_ID}}'); + expect(jsonRes.Data).to.have.property('FormTemplateId'); + expect(jsonRes.Data.FormTemplateId).equals('{{FORM_TEMPLATE_ID}}'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Favorite Template/Delete favorite template.bru b/bruno/form-service/Favorite Template/Delete favorite template.bru new file mode 100644 index 0000000..ef26949 --- /dev/null +++ b/bruno/form-service/Favorite Template/Delete favorite template.bru @@ -0,0 +1,30 @@ +meta { + name: Delete Favorite Template + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/favorite-templates/{{FAVORITE_TEMPLATE_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Favorite Template is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Favorite Template/Get favorite template by id.bru b/bruno/form-service/Favorite Template/Get favorite template by id.bru new file mode 100644 index 0000000..76f3c8f --- /dev/null +++ b/bruno/form-service/Favorite Template/Get favorite template by id.bru @@ -0,0 +1,35 @@ +meta { + name: Get Favorite Template by ID + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/favorite-templates/{{FAVORITE_TEMPLATE_ID}} + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Favorite Template has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{FAVORITE_TEMPLATE_ID}}'); + expect(jsonRes.Data).to.have.property('UserId'); + expect(jsonRes.Data).to.have.property('FormTemplateId'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Favorite Template/Search favorite templates.bru b/bruno/form-service/Favorite Template/Search favorite templates.bru new file mode 100644 index 0000000..b7de356 --- /dev/null +++ b/bruno/form-service/Favorite Template/Search favorite templates.bru @@ -0,0 +1,60 @@ +meta { + name: Search Favorite Templates + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/favorite-templates/search?pageIndex=0&itemsPerPage=10&orderBy=CreatedAt&userId={{USER_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + userId: {{USER_ID}} + ~Order: DESC + ~IsActive: true +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Favorite Templates search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Favorite Templates have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('UserId'); + expect(item).to.have.property('FormTemplateId'); + expect(item).to.have.property('IsActive'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Favorite Template/Update favorite template.bru b/bruno/form-service/Favorite Template/Update favorite template.bru new file mode 100644 index 0000000..7d114c5 --- /dev/null +++ b/bruno/form-service/Favorite Template/Update favorite template.bru @@ -0,0 +1,45 @@ +meta { + name: Update Favorite Template + type: http + seq: 2 +} + +put { + url: {{BASE_URL}}/favorite-templates/{{FAVORITE_TEMPLATE_ID}} + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "IsActive": false + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Favorite Template is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{FAVORITE_TEMPLATE_ID}}'); + expect(jsonRes.Data).to.have.property('UserId'); + expect(jsonRes.Data.UserId).equals('{{USER_ID}}'); + expect(jsonRes.Data).to.have.property('FormTemplateId'); + expect(jsonRes.Data.FormTemplateId).equals('{{FORM_TEMPLATE_ID}}'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(false); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Favorite Template/folder.bru b/bruno/form-service/Favorite Template/folder.bru new file mode 100644 index 0000000..e7cb457 --- /dev/null +++ b/bruno/form-service/Favorite Template/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Favorite Template + seq: 10 +} diff --git a/bruno/form-service/Field Logic/Calculation Logic/Create calculation logic.bru b/bruno/form-service/Field Logic/Calculation Logic/Create calculation logic.bru new file mode 100644 index 0000000..aabfa32 --- /dev/null +++ b/bruno/form-service/Field Logic/Calculation Logic/Create calculation logic.bru @@ -0,0 +1,57 @@ +meta { + name: Create Calculation Logic + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-calculation-logic + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "FieldId": "{{FORM_FIELD_ID}}", + "Enabled": true, + "FallbackValue": "0" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("CALCULATION_LOGIC_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Calculation Logic is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Calculation'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data.Enabled).equals(true); + expect(jsonRes.Data).to.have.property('FallbackValue'); + expect(jsonRes.Data.FallbackValue).equals('0'); + expect(jsonRes.Data).to.have.property('Rules'); + expect(jsonRes.Data.Rules).to.be.an('array'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Logic/Calculation Logic/Delete calculation logic.bru b/bruno/form-service/Field Logic/Calculation Logic/Delete calculation logic.bru new file mode 100644 index 0000000..8f2e73f --- /dev/null +++ b/bruno/form-service/Field Logic/Calculation Logic/Delete calculation logic.bru @@ -0,0 +1,30 @@ +meta { + name: Delete Calculation Logic + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-calculation-logic/{{CALCULATION_LOGIC_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Calculation Logic is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Logic/Calculation Logic/Get calculation logic by id.bru b/bruno/form-service/Field Logic/Calculation Logic/Get calculation logic by id.bru new file mode 100644 index 0000000..1d6eeb4 --- /dev/null +++ b/bruno/form-service/Field Logic/Calculation Logic/Get calculation logic by id.bru @@ -0,0 +1,38 @@ +meta { + name: Get Calculation Logic by ID + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/field-calculation-logic/{{CALCULATION_LOGIC_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Calculation Logic has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Calculation'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data).to.have.property('DefaultSkip'); + expect(jsonRes.Data).to.have.property('FallbackValue'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Logic/Calculation Logic/Search calculation logic.bru b/bruno/form-service/Field Logic/Calculation Logic/Search calculation logic.bru new file mode 100644 index 0000000..4c834d1 --- /dev/null +++ b/bruno/form-service/Field Logic/Calculation Logic/Search calculation logic.bru @@ -0,0 +1,62 @@ +meta { + name: Search Calculation Logic + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-calculation-logic/search?enabled=true&type=Calculation&orderBy=CreatedAt&order=DESC + body: none + auth: none +} + +params:query { + enabled: true + type: Calculation + orderBy: CreatedAt + order: DESC + ~pageIndex: 0 + ~itemsPerPage: 10 +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Search results have correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Search results contain calculation logic", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var firstItem = jsonRes.Data.Items[0]; + expect(firstItem).to.have.property('id'); + expect(firstItem).to.have.property('Type'); + expect(firstItem.Type).equals('Calculation'); + expect(firstItem).to.have.property('FieldId'); + expect(firstItem).to.have.property('Enabled'); + expect(firstItem).to.have.property('DefaultSkip'); + expect(firstItem).to.have.property('FallbackValue'); + expect(firstItem).to.have.property('CreatedAt'); + expect(firstItem).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Logic/Calculation Logic/Update calculation logic.bru b/bruno/form-service/Field Logic/Calculation Logic/Update calculation logic.bru new file mode 100644 index 0000000..294699b --- /dev/null +++ b/bruno/form-service/Field Logic/Calculation Logic/Update calculation logic.bru @@ -0,0 +1,51 @@ +meta { + name: Update Calculation Logic + type: http + seq: 3 +} + +put { + url: {{BASE_URL}}/field-calculation-logic/{{CALCULATION_LOGIC_ID}} + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Type": "Calculation", + "FieldId": "{{FORM_FIELD_ID}}", + "Enabled": false, + "FallbackValue": "100" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Calculation Logic is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Calculation'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data.FieldId).equals('5b1410d5-47dd-424b-ac85-336e8d2a4d36'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data.Enabled).equals(false); + expect(jsonRes.Data).to.have.property('DefaultSkip'); + expect(jsonRes.Data.DefaultSkip).equals(true); + expect(jsonRes.Data).to.have.property('FallbackValue'); + expect(jsonRes.Data.FallbackValue).equals('100'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Logic/Calculation Logic/folder.bru b/bruno/form-service/Field Logic/Calculation Logic/folder.bru new file mode 100644 index 0000000..f4cdd67 --- /dev/null +++ b/bruno/form-service/Field Logic/Calculation Logic/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Calculation Logic + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Logic/README.md b/bruno/form-service/Field Logic/README.md new file mode 100644 index 0000000..e902ed9 --- /dev/null +++ b/bruno/form-service/Field Logic/README.md @@ -0,0 +1,61 @@ +# Field Logic API Documentation + +This section contains API requests for managing field logic in the form service. Field logic determines the behavior of form fields based on business rules. + +## Logic Types + +### 1. Validation Logic +Controls field validation rules and error handling. + +**Request Body Structure:** +```json +{ + "FieldId": "{{FORM_FIELD_ID}}", + "Enabled": true +} +``` + +**Response Includes:** +- `Type`: Always "Validation" (auto-set by service) +- `Rules`: Array of validation rules +- Field metadata + +### 2. Calculation Logic +Controls field value calculations based on other fields. + +**Request Body Structure:** +```json +{ + "FieldId": "{{FORM_FIELD_ID}}", + "Enabled": true, + "FallbackValue": "0" +} +``` + +**Response Includes:** +- `Type`: Always "Calculation" (auto-set by service) +- `Rules`: Array of calculation rules +- `FallbackValue`: Default value when calculation fails + +### 3. Skip Logic +Controls field visibility based on conditions. + +**Request Body Structure:** +```json +{ + "FieldId": "{{FORM_FIELD_ID}}", + "Enabled": true, + "DefaultSkip": false +} +``` + +**Response Includes:** +- `Type`: Always "Skip" (auto-set by service) +- `Rules`: Array of skip rules +- `DefaultSkip`: Default skip behavior + +## Important Notes + +- **Type Field**: The `Type` field is automatically set by the service and should NOT be included in create/update requests +- **Rules Association**: Rules are managed separately and associated with logic entities via the `LogicId` field +- **Environment Variables**: Use environment variables for field IDs to maintain consistency across requests \ No newline at end of file diff --git a/bruno/form-service/Field Logic/Skip Logic/Create skip logic.bru b/bruno/form-service/Field Logic/Skip Logic/Create skip logic.bru new file mode 100644 index 0000000..70c530a --- /dev/null +++ b/bruno/form-service/Field Logic/Skip Logic/Create skip logic.bru @@ -0,0 +1,57 @@ +meta { + name: Create Skip Logic + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-skip-logic + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "FieldId": "{{FORM_FIELD_ID}}", + "Enabled": true, + "DefaultSkip": false + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("SKIP_LOGIC_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Skip Logic is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Skip'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data.Enabled).equals(true); + expect(jsonRes.Data).to.have.property('DefaultSkip'); + expect(jsonRes.Data.DefaultSkip).equals(false); + expect(jsonRes.Data).to.have.property('Rules'); + expect(jsonRes.Data.Rules).to.be.an('array'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Logic/Skip Logic/Delete skip logic.bru b/bruno/form-service/Field Logic/Skip Logic/Delete skip logic.bru new file mode 100644 index 0000000..7c93435 --- /dev/null +++ b/bruno/form-service/Field Logic/Skip Logic/Delete skip logic.bru @@ -0,0 +1,30 @@ +meta { + name: Delete Skip Logic + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-skip-logic/{{SKIP_LOGIC_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Skip Logic is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Logic/Skip Logic/Get skip logic by id.bru b/bruno/form-service/Field Logic/Skip Logic/Get skip logic by id.bru new file mode 100644 index 0000000..5c7778e --- /dev/null +++ b/bruno/form-service/Field Logic/Skip Logic/Get skip logic by id.bru @@ -0,0 +1,36 @@ +meta { + name: Get Skip Logic by ID + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/field-skip-logic/{{SKIP_LOGIC_ID}} + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Skip Logic has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Skip'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data).to.have.property('DefaultSkip'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Logic/Skip Logic/Search skip logic.bru b/bruno/form-service/Field Logic/Skip Logic/Search skip logic.bru new file mode 100644 index 0000000..5e10a4a --- /dev/null +++ b/bruno/form-service/Field Logic/Skip Logic/Search skip logic.bru @@ -0,0 +1,61 @@ +meta { + name: Search Skip Logic + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-skip-logic/search?type=Skip&enabled=true&PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +params:query { + type: Skip + enabled: true + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Search results have correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Search results contain skip logic", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var firstItem = jsonRes.Data.Items[0]; + expect(firstItem).to.have.property('id'); + expect(firstItem).to.have.property('Type'); + expect(firstItem.Type).equals('Skip'); + expect(firstItem).to.have.property('FieldId'); + expect(firstItem).to.have.property('Enabled'); + expect(firstItem).to.have.property('DefaultSkip'); + expect(firstItem).to.have.property('CreatedAt'); + expect(firstItem).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Logic/Skip Logic/Update skip logic.bru b/bruno/form-service/Field Logic/Skip Logic/Update skip logic.bru new file mode 100644 index 0000000..395b28b --- /dev/null +++ b/bruno/form-service/Field Logic/Skip Logic/Update skip logic.bru @@ -0,0 +1,49 @@ +meta { + name: Update Skip Logic + type: http + seq: 3 +} + +put { + url: {{BASE_URL}}/field-skip-logic/{{SKIP_LOGIC_ID}} + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Type": "Skip", + "FieldId": "5b1410d5-47dd-424b-ac85-336e8d2a4d36", + "Enabled": false, + "DefaultSkip": true + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Skip Logic is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Skip'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data.FieldId).equals('5b1410d5-47dd-424b-ac85-336e8d2a4d36'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data.Enabled).equals(false); + expect(jsonRes.Data).to.have.property('DefaultSkip'); + expect(jsonRes.Data.DefaultSkip).equals(true); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Logic/Skip Logic/folder.bru b/bruno/form-service/Field Logic/Skip Logic/folder.bru new file mode 100644 index 0000000..1220479 --- /dev/null +++ b/bruno/form-service/Field Logic/Skip Logic/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Skip Logic + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Logic/Validation Logic/Create validation logic.bru b/bruno/form-service/Field Logic/Validation Logic/Create validation logic.bru new file mode 100644 index 0000000..c5d2c1d --- /dev/null +++ b/bruno/form-service/Field Logic/Validation Logic/Create validation logic.bru @@ -0,0 +1,54 @@ +meta { + name: Create Validation Logic + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-validation-logic + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "FieldId": "c88c6869-2ee7-4888-8b5e-3002ed9ec24b", + "Enabled": true + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("VALIDATION_LOGIC_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Validation Logic is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Validation'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data.Enabled).equals(true); + expect(jsonRes.Data).to.have.property('Rules'); + expect(jsonRes.Data.Rules).to.be.an('array'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Logic/Validation Logic/Delete validation logic.bru b/bruno/form-service/Field Logic/Validation Logic/Delete validation logic.bru new file mode 100644 index 0000000..f6862e4 --- /dev/null +++ b/bruno/form-service/Field Logic/Validation Logic/Delete validation logic.bru @@ -0,0 +1,30 @@ +meta { + name: Delete Validation Logic + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-validation-logic/{{VALIDATION_LOGIC_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Validation Logic is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Logic/Validation Logic/Get validation logic by id.bru b/bruno/form-service/Field Logic/Validation Logic/Get validation logic by id.bru new file mode 100644 index 0000000..8955f32 --- /dev/null +++ b/bruno/form-service/Field Logic/Validation Logic/Get validation logic by id.bru @@ -0,0 +1,37 @@ +meta { + name: Get Validation Logic by ID + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/field-validation-logic/bf348643-c23d-499a-afd3-617782d06e21 + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +tests { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Validation Logic has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Validation'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data).to.have.property('DefaultSkip'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Logic/Validation Logic/Search validation logic.bru b/bruno/form-service/Field Logic/Validation Logic/Search validation logic.bru new file mode 100644 index 0000000..b0f8e79 --- /dev/null +++ b/bruno/form-service/Field Logic/Validation Logic/Search validation logic.bru @@ -0,0 +1,61 @@ +meta { + name: Search Validation Logic + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-validation-logic/search?pageIndex=0&itemsPerPage=10&orderBy=CreatedAt&order=DESC + body: none + auth: none +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~type: Validation + ~enabled: true +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Search results have correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Search results contain validation logic", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var firstItem = jsonRes.Data.Items[0]; + expect(firstItem).to.have.property('id'); + expect(firstItem).to.have.property('Type'); + expect(firstItem.Type).equals('Validation'); + expect(firstItem).to.have.property('FieldId'); + expect(firstItem).to.have.property('Enabled'); + expect(firstItem).to.have.property('DefaultSkip'); + expect(firstItem).to.have.property('CreatedAt'); + expect(firstItem).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Logic/Validation Logic/Update validation logic.bru b/bruno/form-service/Field Logic/Validation Logic/Update validation logic.bru new file mode 100644 index 0000000..27f8a5f --- /dev/null +++ b/bruno/form-service/Field Logic/Validation Logic/Update validation logic.bru @@ -0,0 +1,46 @@ +meta { + name: Update Validation Logic + type: http + seq: 3 +} + +put { + url: {{BASE_URL}}/field-validation-logic/{{VALIDATION_LOGIC_ID}} + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "FieldId": "{{FORM_FIELD_ID}}", + "Enabled": false + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Validation Logic is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Validation'); + expect(jsonRes.Data).to.have.property('FieldId'); + expect(jsonRes.Data).to.have.property('Enabled'); + expect(jsonRes.Data.Enabled).equals(false); + expect(jsonRes.Data).to.have.property('Rules'); + expect(jsonRes.Data.Rules).to.be.an('array'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Logic/Validation Logic/folder.bru b/bruno/form-service/Field Logic/Validation Logic/folder.bru new file mode 100644 index 0000000..16b5331 --- /dev/null +++ b/bruno/form-service/Field Logic/Validation Logic/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Validation Logic + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Logic/folder.bru b/bruno/form-service/Field Logic/folder.bru new file mode 100644 index 0000000..5fd2350 --- /dev/null +++ b/bruno/form-service/Field Logic/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Field Logic + seq: 6 +} diff --git a/bruno/form-service/Field Operation/Composition Operations/Create composition operation.bru b/bruno/form-service/Field Operation/Composition Operations/Create composition operation.bru new file mode 100644 index 0000000..6a1f5e6 --- /dev/null +++ b/bruno/form-service/Field Operation/Composition Operations/Create composition operation.bru @@ -0,0 +1,59 @@ +meta { + name: Create Composition Operation + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-composition-operations + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + // { + // "Name": "Age and Email Validation", + // "Description": "Check if user is adult AND has valid email", + // "Operator": "And", + // "Children": "[\"{{LOGICAL_OPERATION_ID}}\", \"{{EMAIL_VALIDATION_OPERATION_ID}}\"]" + // } + {"Name":"Test rule 1 - Composite validation","Description":"Test rule 1","Operator":"Or","Children":"[]"} +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("COMPOSITION_OPERATION_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Composition Operation is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Age and Email Validation'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Check if user is adult AND has valid email'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Composition'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data.Operator).equals('And'); + expect(jsonRes.Data).to.have.property('Children'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Operation/Composition Operations/Delete composition operation.bru b/bruno/form-service/Field Operation/Composition Operations/Delete composition operation.bru new file mode 100644 index 0000000..98bed7c --- /dev/null +++ b/bruno/form-service/Field Operation/Composition Operations/Delete composition operation.bru @@ -0,0 +1,30 @@ +meta { + name: Delete Composition Operation + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-composition-operations/{{COMPOSITION_OPERATION_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Composition Operation is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Operation/Composition Operations/Get composition operation by id.bru b/bruno/form-service/Field Operation/Composition Operations/Get composition operation by id.bru new file mode 100644 index 0000000..943ddb5 --- /dev/null +++ b/bruno/form-service/Field Operation/Composition Operations/Get composition operation by id.bru @@ -0,0 +1,37 @@ +meta { + name: Get Composition Operation by ID + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/field-composition-operations/{{COMPOSITION_OPERATION_ID}} + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Composition Operation is retrieved with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Composition'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data).to.have.property('Operands'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Composition Operations/Search composition operations.bru b/bruno/form-service/Field Operation/Composition Operations/Search composition operations.bru new file mode 100644 index 0000000..81c33c1 --- /dev/null +++ b/bruno/form-service/Field Operation/Composition Operations/Search composition operations.bru @@ -0,0 +1,63 @@ +meta { + name: Search Composition Operations + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-composition-operations/search?PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: descending + name: Multi-Condition + operator: And + type: Composition +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Composition Operations search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Composition Operations have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('Name'); + expect(item).to.have.property('Description'); + expect(item).to.have.property('Type'); + expect(item.Type).equals('Composition'); + expect(item).to.have.property('Operator'); + expect(item).to.have.property('Operands'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Operation/Composition Operations/Update composition operation.bru b/bruno/form-service/Field Operation/Composition Operations/Update composition operation.bru new file mode 100644 index 0000000..7142281 --- /dev/null +++ b/bruno/form-service/Field Operation/Composition Operations/Update composition operation.bru @@ -0,0 +1,52 @@ +meta { + name: Update Composition Operation + type: http + seq: 3 +} + +put { + url: {{BASE_URL}}/field-composition-operations/{{COMPOSITION_OPERATION_ID}} + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Updated Multi-Condition Check", + "Description": "Updated description for multi-condition check using OR composition", + "Type": "Composition", + "Operator": "Or", + "Operands": "[{\"id\": \"updatedCondition1\", \"type\": \"boolean\"}, {\"id\": \"updatedCondition2\", \"type\": \"boolean\"}]" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Composition Operation is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Multi-Condition Check'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated description for multi-condition check using OR composition'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Composition'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data.Operator).equals('Or'); + expect(jsonRes.Data).to.have.property('Operands'); + expect(jsonRes.Data.Operands).equals('[{"id": "updatedCondition1", "type": "boolean"}, {"id": "updatedCondition2", "type": "boolean"}]'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Composition Operations/folder.bru b/bruno/form-service/Field Operation/Composition Operations/folder.bru new file mode 100644 index 0000000..84cbd73 --- /dev/null +++ b/bruno/form-service/Field Operation/Composition Operations/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Composition Operations + seq: 2 +} diff --git a/bruno/form-service/Field Operation/Function Expression Operations/Create function expression operation.bru b/bruno/form-service/Field Operation/Function Expression Operations/Create function expression operation.bru new file mode 100644 index 0000000..45ce9f8 --- /dev/null +++ b/bruno/form-service/Field Operation/Function Expression Operations/Create function expression operation.bru @@ -0,0 +1,90 @@ +meta { + name: Create Function Expression Operation + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-function-expression-operations + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "BMI Calculation Function", + "Description": "Calculate BMI using weight and height fields", + "Expression": "weight / (height / 100) ^ 2", + "Variables": "{\"weight\": {\"Type\": \"FieldReference\", \"DataType\": \"Float\", \"FieldId\": \"{{WEIGHT_FIELD_ID}}\"}, \"height\": {\"Type\": \"FieldReference\", \"DataType\": \"Float\", \"FieldId\": \"{{HEIGHT_FIELD_ID}}\"}}" + } + + { + FieldId: 'monthly-payment', + Name: 'monthly-payment', + Label: 'Monthly Payment', + ResponseType: FieldResponseType.Float, + Required: false, + CalculateLogic: + { + id: 'payment-calculation', + Type: LogicType.Calculation, + FieldId: 'monthly-payment', + Enabled: true, + Rules: [ + { + id: 'calculate-payment', + Name: 'Calculate monthly payment', + Operation: { + id: 'payment-formula', + Type: OperationType.FunctionExpression, + Expression: 'P * (r / 1200) / (1 - pow(1 + (r / 1200), -n))', + Variables: { + P: { Type: OperandType.FieldReference, DataType: OperandDataType.Float, FieldId: 'loan-amount' }, + r: { Type: OperandType.FieldReference, DataType: OperandDataType.Float, FieldId: 'interest-rate' }, + n: { Type: OperandType.FieldReference, DataType: OperandDataType.Integer, FieldId: 'loan-term' } + } + } + } + ] + } + + }, +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("FUNCTION_EXPRESSION_OPERATION_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Function Expression Operation is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('BMI Calculation Function'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Calculate BMI using weight and height fields'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('FunctionExpression'); + expect(jsonRes.Data).to.have.property('Expression'); + expect(jsonRes.Data.Expression).equals('weight / (height / 100) ^ 2'); + expect(jsonRes.Data).to.have.property('Variables'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Operation/Function Expression Operations/Delete function expression operation.bru b/bruno/form-service/Field Operation/Function Expression Operations/Delete function expression operation.bru new file mode 100644 index 0000000..2a1f13b --- /dev/null +++ b/bruno/form-service/Field Operation/Function Expression Operations/Delete function expression operation.bru @@ -0,0 +1,30 @@ +meta { + name: Delete Function Expression Operation + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-function-expression-operations/{{FUNCTION_EXPRESSION_OPERATION_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Function Expression Operation is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Operation/Function Expression Operations/Get function expression operation by id.bru b/bruno/form-service/Field Operation/Function Expression Operations/Get function expression operation by id.bru new file mode 100644 index 0000000..0e45803 --- /dev/null +++ b/bruno/form-service/Field Operation/Function Expression Operations/Get function expression operation by id.bru @@ -0,0 +1,38 @@ +meta { + name: Get Function Expression Operation by ID + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/field-function-expression-operations/{{FUNCTION_EXPRESSION_OPERATION_ID}} + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Function Expression Operation is retrieved with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('FunctionExpression'); + expect(jsonRes.Data).to.have.property('Expression'); + expect(jsonRes.Data).to.have.property('Variables'); + expect(jsonRes.Data).to.have.property('ResultDataType'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Function Expression Operations/Search function expression operations.bru b/bruno/form-service/Field Operation/Function Expression Operations/Search function expression operations.bru new file mode 100644 index 0000000..4fae63c --- /dev/null +++ b/bruno/form-service/Field Operation/Function Expression Operations/Search function expression operations.bru @@ -0,0 +1,65 @@ +meta { + name: Search Function Expression Operations + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-function-expression-operations/search?PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: descending + name: Custom + expression: amount * taxRate + resultDataType: number + type: FunctionExpression +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Function Expression Operations search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Function Expression Operations have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('Name'); + expect(item).to.have.property('Description'); + expect(item).to.have.property('Type'); + expect(item.Type).equals('FunctionExpression'); + expect(item).to.have.property('Expression'); + expect(item).to.have.property('Variables'); + expect(item).to.have.property('ResultDataType'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Operation/Function Expression Operations/Update function expression operation.bru b/bruno/form-service/Field Operation/Function Expression Operations/Update function expression operation.bru new file mode 100644 index 0000000..2c7adce --- /dev/null +++ b/bruno/form-service/Field Operation/Function Expression Operations/Update function expression operation.bru @@ -0,0 +1,50 @@ +meta { + name: Update Function Expression Operation + type: http + seq: 3 +} + +put { + url: {{BASE_URL}}/field-function-expression-operations/{{FUNCTION_EXPRESSION_OPERATION_ID}} + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Updated BMI Calculation Function", + "Description": "Updated BMI calculation with additional validation", + "Expression": "weight / (height / 100) ^ 2", + "Variables": "{\"weight\": {\"Type\": \"FieldReference\", \"DataType\": \"Float\", \"FieldId\": \"{{WEIGHT_FIELD_ID}}\"}, \"height\": {\"Type\": \"FieldReference\", \"DataType\": \"Float\", \"FieldId\": \"{{HEIGHT_FIELD_ID}}\"}}" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Function Expression Operation is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated BMI Calculation Function'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated BMI calculation with additional validation'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('FunctionExpression'); + expect(jsonRes.Data).to.have.property('Expression'); + expect(jsonRes.Data.Expression).equals('weight / (height / 100) ^ 2'); + expect(jsonRes.Data).to.have.property('Variables'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Function Expression Operations/folder.bru b/bruno/form-service/Field Operation/Function Expression Operations/folder.bru new file mode 100644 index 0000000..c8c8258 --- /dev/null +++ b/bruno/form-service/Field Operation/Function Expression Operations/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Function Expression Operations + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Iterate Operations/Create iterate operation.bru b/bruno/form-service/Field Operation/Iterate Operations/Create iterate operation.bru new file mode 100644 index 0000000..9c667c0 --- /dev/null +++ b/bruno/form-service/Field Operation/Iterate Operations/Create iterate operation.bru @@ -0,0 +1,61 @@ +meta { + name: Create Iterate Operation + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-iterate-operations + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Score Validation Operation", + "Description": "Check if all scores in array are above threshold", + "ItemAlias": "score", + "OperationId": "{{LOGICAL_OPERATION_ID}}", + "ArrayOperand": "{\"Type\": \"FieldReference\", \"DataType\": \"Array\", \"FieldId\": \"{{SCORES_FIELD_ID}}\"}" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("ITERATE_OPERATION_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Iterate Operation is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Score Validation Operation'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Check if all scores in array are above threshold'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Iterate'); + expect(jsonRes.Data).to.have.property('ItemAlias'); + expect(jsonRes.Data.ItemAlias).equals('score'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('ArrayOperand'); + expect(jsonRes.Data).to.have.property('Operation'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Iterate Operations/Delete iterate operation.bru b/bruno/form-service/Field Operation/Iterate Operations/Delete iterate operation.bru new file mode 100644 index 0000000..6c57f8f --- /dev/null +++ b/bruno/form-service/Field Operation/Iterate Operations/Delete iterate operation.bru @@ -0,0 +1,30 @@ +meta { + name: Delete Iterate Operation + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-iterate-operations/{{ITERATE_OPERATION_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Iterate Operation is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Operation/Iterate Operations/Get iterate operation by id.bru b/bruno/form-service/Field Operation/Iterate Operations/Get iterate operation by id.bru new file mode 100644 index 0000000..959fbee --- /dev/null +++ b/bruno/form-service/Field Operation/Iterate Operations/Get iterate operation by id.bru @@ -0,0 +1,40 @@ +meta { + name: Get Iterate Operation by ID + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/field-iterate-operations/{{ITERATE_OPERATION_ID}} + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Iterate Operation has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{ITERATE_OPERATION_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Iterate'); + expect(jsonRes.Data).to.have.property('CollectionField'); + expect(jsonRes.Data).to.have.property('ResultField'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('FilterExpression'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Iterate Operations/Search iterate operations.bru b/bruno/form-service/Field Operation/Iterate Operations/Search iterate operations.bru new file mode 100644 index 0000000..be9e67f --- /dev/null +++ b/bruno/form-service/Field Operation/Iterate Operations/Search iterate operations.bru @@ -0,0 +1,65 @@ +meta { + name: Search Iterate Operations + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-iterate-operations/search?PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: descending + name: Array + collectionField: items + resultField: total +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Iterate Operations search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Iterate Operations have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('Name'); + expect(item).to.have.property('Description'); + expect(item).to.have.property('Type'); + expect(item.Type).equals('Iterate'); + expect(item).to.have.property('CollectionField'); + expect(item).to.have.property('ResultField'); + expect(item).to.have.property('OperationId'); + expect(item).to.have.property('FilterExpression'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Operation/Iterate Operations/Update iterate operation.bru b/bruno/form-service/Field Operation/Iterate Operations/Update iterate operation.bru new file mode 100644 index 0000000..2b57619 --- /dev/null +++ b/bruno/form-service/Field Operation/Iterate Operations/Update iterate operation.bru @@ -0,0 +1,59 @@ +meta { + name: Update Iterate Operation + type: http + seq: 2 +} + +put { + url: {{BASE_URL}}/field-iterate-operations/{{ITERATE_OPERATION_ID}} + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Updated Array Sum Operation", + "Description": "Updated description for array sum operation", + "Type": "Iterate", + "CollectionField": "products", + "ResultField": "sum", + "OperationId": "{{MATHEMATICAL_OPERATION_ID}}", + "FilterExpression": "product.price > 10" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Iterate Operation is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{ITERATE_OPERATION_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Array Sum Operation'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated description for array sum operation'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Iterate'); + expect(jsonRes.Data).to.have.property('CollectionField'); + expect(jsonRes.Data.CollectionField).equals('products'); + expect(jsonRes.Data).to.have.property('ResultField'); + expect(jsonRes.Data.ResultField).equals('sum'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data.OperationId).equals('5b1410d5-47dd-424b-ac85-336e8d2a4d36'); + expect(jsonRes.Data).to.have.property('FilterExpression'); + expect(jsonRes.Data.FilterExpression).equals('product.price > 10'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Iterate Operations/folder.bru b/bruno/form-service/Field Operation/Iterate Operations/folder.bru new file mode 100644 index 0000000..11df3fe --- /dev/null +++ b/bruno/form-service/Field Operation/Iterate Operations/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Iterate Operations + seq: 4 +} diff --git a/bruno/form-service/Field Operation/Logical Operations/Create logical operation.bru b/bruno/form-service/Field Operation/Logical Operations/Create logical operation.bru new file mode 100644 index 0000000..1afe31f --- /dev/null +++ b/bruno/form-service/Field Operation/Logical Operations/Create logical operation.bru @@ -0,0 +1,64 @@ +meta { + name: Create Logical Operation + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-logical-operations + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + // { + // "Name": "email validation operation", + // "Description": "Check whether email is correct ", + // "Operator": "Contains", + // "Operands": "[{\"Type\": \"FieldReference\", \"DataType\": \"Text\", \"FieldId\": \"{{BASE_AGE_FIELD_ID}}\"}, {\"Type\": \"Constant\", \"DataType\": \"Text\", \"Value\": \"gmail.com\"}]" + // } + { + "Name": "What is your age? validation operation", + "Description": "Check whether what is your age? is correct", + "Operator": "LessThan", + "Operands": "[{\"Type\":\"FieldReference\",\"DataType\":\"Integer\",\"FieldId\":\"FORMFIELD_#ST2HI3vYucYe2y\"},{\"Type\":\"Constant\",\"DataType\":\"Integer\",\"Value\":\"20\"}]" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("LOGICAL_OPERATION_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Logical Operation is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Age Validation Operation'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Check if age is greater than or equal to 18'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Logical'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data.Operator).equals('GreaterThanOrEqual'); + expect(jsonRes.Data).to.have.property('Operands'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Operation/Logical Operations/Delete logical operation.bru b/bruno/form-service/Field Operation/Logical Operations/Delete logical operation.bru new file mode 100644 index 0000000..413e500 --- /dev/null +++ b/bruno/form-service/Field Operation/Logical Operations/Delete logical operation.bru @@ -0,0 +1,30 @@ +meta { + name: Delete Logical Operation + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-logical-operations/{{LOGICAL_OPERATION_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Logical Operation is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Operation/Logical Operations/Get logical operation by id.bru b/bruno/form-service/Field Operation/Logical Operations/Get logical operation by id.bru new file mode 100644 index 0000000..242b1ab --- /dev/null +++ b/bruno/form-service/Field Operation/Logical Operations/Get logical operation by id.bru @@ -0,0 +1,38 @@ +meta { + name: Get Logical Operation by ID + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/field-logical-operations/{{LOGICAL_OPERATION_ID}} + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Logical Operation has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{LOGICAL_OPERATION_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Logical'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data).to.have.property('Operands'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Logical Operations/Search logical operations.bru b/bruno/form-service/Field Operation/Logical Operations/Search logical operations.bru new file mode 100644 index 0000000..3ca11e7 --- /dev/null +++ b/bruno/form-service/Field Operation/Logical Operations/Search logical operations.bru @@ -0,0 +1,63 @@ +meta { + name: Search Logical Operations + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-logical-operations/search?PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: descending + name: Age + operator: GreaterThan + type: Logical +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Logical Operations search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Logical Operations have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('Name'); + expect(item).to.have.property('Description'); + expect(item).to.have.property('Type'); + expect(item.Type).equals('Logical'); + expect(item).to.have.property('Operator'); + expect(item).to.have.property('Operands'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Operation/Logical Operations/Update logical operation.bru b/bruno/form-service/Field Operation/Logical Operations/Update logical operation.bru new file mode 100644 index 0000000..4d7a6c5 --- /dev/null +++ b/bruno/form-service/Field Operation/Logical Operations/Update logical operation.bru @@ -0,0 +1,53 @@ +meta { + name: Update Logical Operation + type: http + seq: 2 +} + +put { + url: {{BASE_URL}}/field-logical-operations/{{LOGICAL_OPERATION_ID}} + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Updated Age Check Operation", + "Description": "Updated description for age check operation", + "Type": "Logical", + "Operator": "GreaterThanOrEqual", + "Operands": "[{\"type\":\"field\",\"value\":\"age\"},{\"type\":\"number\",\"value\":21}]" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Logical Operation is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{LOGICAL_OPERATION_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Age Check Operation'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated description for age check operation'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Logical'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data.Operator).equals('GreaterThanOrEqual'); + expect(jsonRes.Data).to.have.property('Operands'); + expect(jsonRes.Data.Operands).equals('[{"type":"field","value":"age"},{"type":"number","value":21}]'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Logical Operations/folder.bru b/bruno/form-service/Field Operation/Logical Operations/folder.bru new file mode 100644 index 0000000..9e8e6c0 --- /dev/null +++ b/bruno/form-service/Field Operation/Logical Operations/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Logical Operations + seq: 5 +} diff --git a/bruno/form-service/Field Operation/Mathematical Operations/Create mathematical operation.bru b/bruno/form-service/Field Operation/Mathematical Operations/Create mathematical operation.bru new file mode 100644 index 0000000..d8107d3 --- /dev/null +++ b/bruno/form-service/Field Operation/Mathematical Operations/Create mathematical operation.bru @@ -0,0 +1,58 @@ +meta { + name: Create Mathematical Operation + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-mathematical-operations + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Age check operation", + "Description": "Calculate age is greater that 18 person is adult", + "Operator": "GreaterThan", + "Operands": "[{\"Type\": \"FieldReference\", \"DataType\": \"Float\", \"FieldId\": \"{{BASE_AGE_FIELD_ID}}\"}]" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("MATHEMATICAL_OPERATION_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Mathematical Operation is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Price Calculation Operation'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Calculate total price including tax'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Mathematical'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data.Operator).equals('Add'); + expect(jsonRes.Data).to.have.property('Operands'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Operation/Mathematical Operations/Delete mathematical operation.bru b/bruno/form-service/Field Operation/Mathematical Operations/Delete mathematical operation.bru new file mode 100644 index 0000000..7c3431f --- /dev/null +++ b/bruno/form-service/Field Operation/Mathematical Operations/Delete mathematical operation.bru @@ -0,0 +1,29 @@ +meta { + name: Delete Mathematical Operation + type: http + seq: 4 +} + +delete { + url: {{BASE_URL}}/field-mathematical-operations/{{MATHEMATICAL_OPERATION_ID}} + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Mathematical Operation is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Mathematical Operations/Get mathematical operation by id.bru b/bruno/form-service/Field Operation/Mathematical Operations/Get mathematical operation by id.bru new file mode 100644 index 0000000..2fcdda1 --- /dev/null +++ b/bruno/form-service/Field Operation/Mathematical Operations/Get mathematical operation by id.bru @@ -0,0 +1,39 @@ +meta { + name: Get Mathematical Operation by ID + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/field-mathematical-operations/{{MATHEMATICAL_OPERATION_ID}} + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Mathematical Operation has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{MATHEMATICAL_OPERATION_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Mathematical'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data).to.have.property('Operands'); + expect(jsonRes.Data).to.have.property('ResultDataType'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Mathematical Operations/Search mathematical operations.bru b/bruno/form-service/Field Operation/Mathematical Operations/Search mathematical operations.bru new file mode 100644 index 0000000..5797251 --- /dev/null +++ b/bruno/form-service/Field Operation/Mathematical Operations/Search mathematical operations.bru @@ -0,0 +1,65 @@ +meta { + name: Search Mathematical Operations + type: http + seq: 5 +} + +get { + url: {{BASE_URL}}/field-mathematical-operations/search?PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: descending + name: Addition + operator: Add + resultDataType: Float + type: Mathematical +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Mathematical Operations search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Mathematical Operations have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('Name'); + expect(item).to.have.property('Description'); + expect(item).to.have.property('Type'); + expect(item.Type).equals('Mathematical'); + expect(item).to.have.property('Operator'); + expect(item).to.have.property('Operands'); + expect(item).to.have.property('ResultDataType'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Operation/Mathematical Operations/Update mathematical operation.bru b/bruno/form-service/Field Operation/Mathematical Operations/Update mathematical operation.bru new file mode 100644 index 0000000..3a1b089 --- /dev/null +++ b/bruno/form-service/Field Operation/Mathematical Operations/Update mathematical operation.bru @@ -0,0 +1,56 @@ +meta { + name: Update Mathematical Operation + type: http + seq: 2 +} + +put { + url: {{BASE_URL}}/field-mathematical-operations/{{MATHEMATICAL_OPERATION_ID}} + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Updated Addition Operation", + "Description": "Updated description for addition operation", + "Type": "Mathematical", + "Operator": "Subtract", + "Operands": "[{\"type\":\"number\",\"value\":10},{\"type\":\"number\",\"value\":4}]", + "ResultDataType": "Integer" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Mathematical Operation is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{MATHEMATICAL_OPERATION_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Addition Operation'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated description for addition operation'); + expect(jsonRes.Data).to.have.property('Type'); + expect(jsonRes.Data.Type).equals('Mathematical'); + expect(jsonRes.Data).to.have.property('Operator'); + expect(jsonRes.Data.Operator).equals('Subtract'); + expect(jsonRes.Data).to.have.property('Operands'); + expect(jsonRes.Data.Operands).equals('[{"type":"number","value":10},{"type":"number","value":4}]'); + expect(jsonRes.Data).to.have.property('ResultDataType'); + expect(jsonRes.Data.ResultDataType).equals('Integer'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Operation/Mathematical Operations/folder.bru b/bruno/form-service/Field Operation/Mathematical Operations/folder.bru new file mode 100644 index 0000000..be321bc --- /dev/null +++ b/bruno/form-service/Field Operation/Mathematical Operations/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Mathematical Operations + seq: 3 +} diff --git a/bruno/form-service/Field Operation/README.md b/bruno/form-service/Field Operation/README.md new file mode 100644 index 0000000..4a9e0ee --- /dev/null +++ b/bruno/form-service/Field Operation/README.md @@ -0,0 +1,101 @@ +# Field Operations API Documentation + +This section contains API requests for managing field operations in the form service. Operations define mathematical, logical, and compositional expressions used in field logic. + +## Operation Types + +### 1. Logical Operations +Perform logical comparisons and boolean operations. + +**Request Body Structure:** +```json +{ + "Name": "Age Validation Operation", + "Description": "Check if age is greater than or equal to 18", + "Operator": "GreaterThanOrEqual", + "Operands": "[{\"Type\": \"FieldReference\", \"DataType\": \"Integer\", \"FieldId\": \"{{AGE_FIELD_ID}}\", \"FieldCode\": \"AGE_FIELD\"}, {\"Type\": \"Constant\", \"DataType\": \"Integer\", \"Value\": 18, \"FieldCode\": \"AGE_FIELD\"}]" +} +``` + +**Available Operators:** Equal, NotEqual, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual, In, NotIn, Contains, DoesNotContain, Between, IsTrue, IsFalse, Exists + +### 2. Mathematical Operations +Perform mathematical calculations. + +**Request Body Structure:** +```json +{ + "Name": "Price Calculation Operation", + "Description": "Calculate total price including tax", + "Operator": "Add", + "Operands": "[{\"Type\": \"FieldReference\", \"DataType\": \"Float\", \"FieldId\": \"{{BASE_PRICE_FIELD_ID}}\", \"FieldCode\": \"BASE_PRICE\"}, {\"Type\": \"FieldReference\", \"DataType\": \"Float\", \"FieldId\": \"{{TAX_AMOUNT_FIELD_ID}}\", \"FieldCode\": \"TAX_AMOUNT\"}]" +} +``` + +**Available Operators:** Add, Subtract, Multiply, Divide, Modulo, Power + +### 3. Function Expression Operations +Use mathematical expressions with variables. + +**Request Body Structure:** +```json +{ + "Name": "BMI Calculation Function", + "Description": "Calculate BMI using weight and height fields", + "Expression": "weight / (height / 100) ^ 2", + "Variables": "{\"weight\": {\"Type\": \"FieldReference\", \"DataType\": \"Float\", \"FieldId\": \"{{WEIGHT_FIELD_ID}}\", \"FieldCode\": \"WEIGHT\"}, \"height\": {\"Type\": \"FieldReference\", \"DataType\": \"Float\", \"FieldId\": \"{{HEIGHT_FIELD_ID}}\", \"FieldCode\": \"HEIGHT\"}}" +} +``` + +### 4. Composition Operations +Combine multiple operations using logical operators. + +**Request Body Structure:** +```json +{ + "Name": "Age and Email Validation", + "Description": "Check if user is adult AND has valid email", + "Operator": "And", + "Children": "[\"{{LOGICAL_OPERATION_ID}}\", \"{{EMAIL_VALIDATION_OPERATION_ID}}\"]" +} +``` + +**Available Operators:** And, Or, Xor, Not + +### 5. Iterate Operations +Perform operations over array values. + +**Request Body Structure:** +```json +{ + "Name": "Score Validation Operation", + "Description": "Check if all scores in array are above threshold", + "ItemAlias": "score", + "OperationId": "{{LOGICAL_OPERATION_ID}}", + "ArrayOperand": "{\"Type\": \"FieldReference\", \"DataType\": \"Array\", \"FieldId\": \"{{SCORES_FIELD_ID}}\", \"FieldCode\": \"SCORES\"}" +} +``` + +## Operand Structure + +Operands follow a standardized structure: + +```json +{ + "Type": "FieldReference|Constant|Function|Mathematical", + "DataType": "Float|Integer|Boolean|Text|DateTime|Location|Array|Object|Date", + "Value": "any", // For Constant operands + "FieldId": "uuid", // For FieldReference operands + "FieldCode": "string", // Display code for the field (optional) + "FunctionName": "string", // For Function operands + "FunctionArgs": [] // For Function operands +} +``` + +## Important Notes + +- **Type Field**: The operation `Type` is automatically set by the service based on the endpoint +- **Operands**: Must be properly formatted JSON strings with correct operand structure +- **Variables**: For function expressions, variables must map to valid operand objects +- **Children**: For composition operations, children are arrays of operation IDs (JSON string format) +- **Environment Variables**: Use environment variables for field and operation IDs to maintain consistency \ No newline at end of file diff --git a/bruno/form-service/Field Operation/folder.bru b/bruno/form-service/Field Operation/folder.bru new file mode 100644 index 0000000..8c3b6d3 --- /dev/null +++ b/bruno/form-service/Field Operation/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Field Operation + seq: 7 +} diff --git a/bruno/form-service/Field Rules/Calculation Rules/Create calculation rule.bru b/bruno/form-service/Field Rules/Calculation Rules/Create calculation rule.bru new file mode 100644 index 0000000..bc1b52a --- /dev/null +++ b/bruno/form-service/Field Rules/Calculation Rules/Create calculation rule.bru @@ -0,0 +1,78 @@ +meta { + name: Create Calculation Rule + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-calculation-rules + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + // { + // "Name": "BMI Calculation Rule", + // "Description": "Calculate BMI using weight and height fields", + // "Priority": 1, + // "IsActive": true, + // "OperationType": "FunctionExpression", + // "ConditionForOperationId": "{{LOGICAL_OPERATION_ID}}", + // "OperationId": "{{FUNCTION_EXPRESSION_OPERATION_ID}}", + // "LogicId": "{{CALCULATION_LOGIC_ID}}", + // "FallbackRuleId": "{{FALLBACK_RULE_ID}}" + // } + { + "Name": "Total Price Calculation", + "Description": "Field Calculation-rule Description", + "OperationType": "FunctionExpression", + // "ConditionForOperationId": "47e0e1fd-d5e1-4eba-b5fa-31edb8fb24f9", + "BaseOperationId": "47e0e1fd-d5e1-4eba-b5fa-31edb8fb24f9", + "LogicId": "e08d47f4-eb09-4bda-a6db-a7501ce16b07", + "FallbackRuleId": "{{FALLBACK_RULE_ID}}" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("CALCULATION_RULE_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } +} + +tests { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Calculation Rule is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('BMI Calculation Rule'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Calculate BMI using weight and height fields'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data.Priority).equals(1); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('OperationType'); + expect(jsonRes.Data.OperationType).equals('FunctionExpression'); + expect(jsonRes.Data).to.have.property('ConditionForOperationId'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Rules/Calculation Rules/Delete calculation rule.bru b/bruno/form-service/Field Rules/Calculation Rules/Delete calculation rule.bru new file mode 100644 index 0000000..ea61bde --- /dev/null +++ b/bruno/form-service/Field Rules/Calculation Rules/Delete calculation rule.bru @@ -0,0 +1,30 @@ +meta { + name: Delete Calculation Rule + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-calculation-rules/{{CALCULATION_RULE_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Calculation Rule is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Rules/Calculation Rules/Get calculation rule by id.bru b/bruno/form-service/Field Rules/Calculation Rules/Get calculation rule by id.bru new file mode 100644 index 0000000..28e181b --- /dev/null +++ b/bruno/form-service/Field Rules/Calculation Rules/Get calculation rule by id.bru @@ -0,0 +1,39 @@ +meta { + name: Get Calculation Rule by ID + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/field-calculation-rules/{{CALCULATION_RULE_ID}} + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Calculation Rule has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data).to.have.property('ConditionForOperationId'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('FallbackRuleId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Rules/Calculation Rules/Search calculation rules.bru b/bruno/form-service/Field Rules/Calculation Rules/Search calculation rules.bru new file mode 100644 index 0000000..66e686b --- /dev/null +++ b/bruno/form-service/Field Rules/Calculation Rules/Search calculation rules.bru @@ -0,0 +1,68 @@ +meta { + name: Search Calculation Rules + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-calculation-rules/search?PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~name: Price + ~isActive: true + ~operationType: FunctionExpression +} + +script:post-response { +} + +tests { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Search results have correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Search results contain calculation rules", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var firstItem = jsonRes.Data.Items[0]; + expect(firstItem).to.have.property('id'); + expect(firstItem).to.have.property('Name'); + expect(firstItem).to.have.property('Description'); + expect(firstItem).to.have.property('Priority'); + expect(firstItem).to.have.property('IsActive'); + expect(firstItem).to.have.property('OperationType'); + expect(firstItem).to.have.property('ConditionForOperationId'); + expect(firstItem).to.have.property('OperationId'); + expect(firstItem).to.have.property('LogicId'); + expect(firstItem).to.have.property('CreatedAt'); + expect(firstItem).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Rules/Calculation Rules/Update calculation rule.bru b/bruno/form-service/Field Rules/Calculation Rules/Update calculation rule.bru new file mode 100644 index 0000000..4ae071e --- /dev/null +++ b/bruno/form-service/Field Rules/Calculation Rules/Update calculation rule.bru @@ -0,0 +1,61 @@ +meta { + name: Update Calculation Rule + type: http + seq: 3 +} + +put { + url: {{BASE_URL}}/field-calculation-rules/{{CALCULATION_RULE_ID}} + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Updated Total Price Calculation", + "Description": "Updated calculation for total price", + "Priority": 2, + "IsActive": false, + "OperationType": "Mathematical", + "ConditionForOperationId": "7c1410d5-47dd-424b-ac85-336e8d2a4d37", + "OperationId": "7c1410d5-47dd-424b-ac85-336e8d2a4d37", + "LogicId": "15af8cd4-828d-48af-9d21-f4630083f487" + } +} + +script:post-response { +} + +tests { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Calculation Rule is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Total Price Calculation'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated calculation for total price'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data.Priority).equals(2); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(false); + expect(jsonRes.Data).to.have.property('OperationType'); + expect(jsonRes.Data.OperationType).equals('Mathematical'); + expect(jsonRes.Data).to.have.property('ConditionForOperationId'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Rules/Calculation Rules/folder.bru b/bruno/form-service/Field Rules/Calculation Rules/folder.bru new file mode 100644 index 0000000..8a72244 --- /dev/null +++ b/bruno/form-service/Field Rules/Calculation Rules/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Calculation Rules + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Rules/Fallback Rules/Complete fallback rule workflow.bru b/bruno/form-service/Field Rules/Fallback Rules/Complete fallback rule workflow.bru new file mode 100644 index 0000000..f14f4b8 --- /dev/null +++ b/bruno/form-service/Field Rules/Fallback Rules/Complete fallback rule workflow.bru @@ -0,0 +1,169 @@ +meta { + name: Complete Fallback Rule Workflow + type: http + seq: 6 +} + +get { + url: {{BASE_URL}}/form-templates/{{TEMPLATE_ID}}/details + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:pre-request { + console.log("Starting Complete Fallback Rule Workflow Test"); + console.log("Template ID:", bru.getEnvVar("TEMPLATE_ID")); + console.log("Fallback Rule ID:", bru.getEnvVar("FALLBACK_RULE_ID")); +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes).to.have.property('FormSections'); + }); + + test("Template structure is valid", function () { + var jsonRes = res.getBody(); + expect(jsonRes.FormSections).to.be.an('array'); + + if (jsonRes.FormSections.length > 0) { + jsonRes.FormSections.forEach(function(section, sectionIndex) { + expect(section).to.have.property('id'); + expect(section).to.have.property('FormFields'); + expect(section.FormFields).to.be.an('array'); + + console.log(`Section ${sectionIndex + 1}: ${section.Title || 'Untitled'} (${section.FormFields.length} fields)`); + + if (section.FormFields.length > 0) { + section.FormFields.forEach(function(field, fieldIndex) { + console.log(` Field ${fieldIndex + 1}: ${field.Title || field.DisplayCode || 'Untitled'}`); + + // Check for Skip Logic with fallback rules + if (field.SkipLogic && field.SkipLogic.Rules) { + console.log(` Skip Logic: ${field.SkipLogic.Rules.length} rules`); + field.SkipLogic.Rules.forEach(function(rule, ruleIndex) { + console.log(` Rule ${ruleIndex + 1}: ${rule.Name}`); + if (rule.FallbackRule) { + console.log(` Fallback Rule: ${rule.FallbackRule.Action} - ${rule.FallbackRule.ActionMessage}`); + } + }); + } + + // Check for Calculate Logic with fallback rules + if (field.CalculateLogic && field.CalculateLogic.Rules) { + console.log(` Calculate Logic: ${field.CalculateLogic.Rules.length} rules`); + field.CalculateLogic.Rules.forEach(function(rule, ruleIndex) { + console.log(` Rule ${ruleIndex + 1}: ${rule.Name}`); + if (rule.FallbackRule) { + console.log(` Fallback Rule: ${rule.FallbackRule.Action} - ${rule.FallbackRule.ActionMessage}`); + } + }); + } + + // Check for Validate Logic with fallback rules + if (field.ValidateLogic && field.ValidateLogic.Rules) { + console.log(` Validate Logic: ${field.ValidateLogic.Rules.length} rules`); + field.ValidateLogic.Rules.forEach(function(rule, ruleIndex) { + console.log(` Rule ${ruleIndex + 1}: ${rule.Name}`); + if (rule.FallbackRule) { + console.log(` Fallback Rule: ${rule.FallbackRule.Action} - ${rule.FallbackRule.ActionMessage}`); + } + }); + } + }); + } + }); + } + }); + + test("Fallback rules are properly integrated", function () { + var jsonRes = res.getBody(); + var fallbackRuleCount = 0; + var ruleTypesWithFallback = []; + + if (jsonRes.FormSections && jsonRes.FormSections.length > 0) { + jsonRes.FormSections.forEach(function(section) { + if (section.FormFields && section.FormFields.length > 0) { + section.FormFields.forEach(function(field) { + // Check all rule types + var ruleTypes = [ + { name: 'SkipLogic', rules: field.SkipLogic?.Rules }, + { name: 'CalculateLogic', rules: field.CalculateLogic?.Rules }, + { name: 'ValidateLogic', rules: field.ValidateLogic?.Rules } + ]; + + ruleTypes.forEach(function(ruleType) { + if (ruleType.rules && ruleType.rules.length > 0) { + ruleType.rules.forEach(function(rule) { + if (rule.FallbackRule) { + fallbackRuleCount++; + if (!ruleTypesWithFallback.includes(ruleType.name)) { + ruleTypesWithFallback.push(ruleType.name); + } + + // Validate fallback rule structure + expect(rule.FallbackRule).to.have.property('id'); + expect(rule.FallbackRule).to.have.property('Action'); + expect(rule.FallbackRule).to.have.property('CreatedAt'); + + // Validate action properties + if (rule.FallbackRule.ActionMessage) { + expect(rule.FallbackRule).to.have.property('ActionMessage'); + } + if (rule.FallbackRule.ActionParameters) { + expect(rule.FallbackRule).to.have.property('ActionParameters'); + } + } + }); + } + }); + }); + } + }); + } + + console.log(`Total fallback rules found: ${fallbackRuleCount}`); + console.log(`Rule types with fallback rules: ${ruleTypesWithFallback.join(', ')}`); + + // This test will pass even if no fallback rules are found, as it's just demonstrating the structure + expect(fallbackRuleCount).to.be.a('number'); + }); + + test("Fallback rule actions are valid", function () { + var jsonRes = res.getBody(); + var validActions = ['SET_DEFAULT', 'SHOW_MESSAGE', 'SKIP_FIELD', 'RETRY', 'CLEAR_FIELD', 'DISABLE_FIELD']; + var foundActions = []; + + if (jsonRes.FormSections && jsonRes.FormSections.length > 0) { + jsonRes.FormSections.forEach(function(section) { + if (section.FormFields && section.FormFields.length > 0) { + section.FormFields.forEach(function(field) { + var ruleTypes = ['SkipLogic', 'CalculateLogic', 'ValidateLogic']; + + ruleTypes.forEach(function(ruleType) { + if (field[ruleType] && field[ruleType].Rules) { + field[ruleType].Rules.forEach(function(rule) { + if (rule.FallbackRule && rule.FallbackRule.Action) { + expect(validActions).to.include(rule.FallbackRule.Action); + if (!foundActions.includes(rule.FallbackRule.Action)) { + foundActions.push(rule.FallbackRule.Action); + } + } + }); + } + }); + }); + } + }); + } + + console.log(`Found fallback actions: ${foundActions.join(', ')}`); + }); +} diff --git a/bruno/form-service/Field Rules/Fallback Rules/Create fallback rule.bru b/bruno/form-service/Field Rules/Fallback Rules/Create fallback rule.bru new file mode 100644 index 0000000..4ee7e0a --- /dev/null +++ b/bruno/form-service/Field Rules/Fallback Rules/Create fallback rule.bru @@ -0,0 +1,70 @@ +meta { + name: Create Fallback Rule + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-fallback-rules + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Default Value Fallback", + "Description": "Set default value when validation fails", + "Priority": 1, + "IsActive": true, + "OperationType": "Logical", + "Action": "SET_DEFAULT", + "ActionMessage": "Setting default value due to validation failure", + "ActionParameters": {"timeout": 3, "maxRetries": 5000} + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("FALLBACK_RULE_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } +} + +tests { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Fallback Rule is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Default Value Fallback'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Set default value when validation fails'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data.Priority).equals(1); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('OperationType'); + expect(jsonRes.Data.OperationType).equals('Logical'); + expect(jsonRes.Data).to.have.property('Action'); + expect(jsonRes.Data.Action).equals('SET_DEFAULT'); + expect(jsonRes.Data).to.have.property('ActionMessage'); + expect(jsonRes.Data.ActionMessage).equals('Setting default value due to validation failure'); + expect(jsonRes.Data).to.have.property('ActionParameters'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Rules/Fallback Rules/Delete fallback rule.bru b/bruno/form-service/Field Rules/Fallback Rules/Delete fallback rule.bru new file mode 100644 index 0000000..8b71a26 --- /dev/null +++ b/bruno/form-service/Field Rules/Fallback Rules/Delete fallback rule.bru @@ -0,0 +1,30 @@ +meta { + name: Delete Fallback Rule + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-fallback-rules/{{FALLBACK_RULE_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Fallback Rule is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Rules/Fallback Rules/Get fallback rule by id.bru b/bruno/form-service/Field Rules/Fallback Rules/Get fallback rule by id.bru new file mode 100644 index 0000000..870ffa3 --- /dev/null +++ b/bruno/form-service/Field Rules/Fallback Rules/Get fallback rule by id.bru @@ -0,0 +1,39 @@ +meta { + name: Get Fallback Rule by ID + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/field-fallback-rules/{{FALLBACK_RULE_ID}} + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Fallback Rule has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data).to.have.property('OperationType'); + expect(jsonRes.Data).to.have.property('Action'); + expect(jsonRes.Data).to.have.property('ActionMessage'); + expect(jsonRes.Data).to.have.property('ActionParameters'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Rules/Fallback Rules/README.md b/bruno/form-service/Field Rules/Fallback Rules/README.md new file mode 100644 index 0000000..b32f2a4 --- /dev/null +++ b/bruno/form-service/Field Rules/Fallback Rules/README.md @@ -0,0 +1,97 @@ +# Fallback Rules API + +This folder contains Bruno API requests for managing Fallback Rules in the Forms Service. + +## Overview + +Fallback Rules are used to define fallback actions when primary rules (validation, calculation, or skip) fail or need to execute alternative behaviors. + +## Available Requests + +### 1. Create Fallback Rule +- **File**: `Create fallback rule.bru` +- **Method**: POST +- **Endpoint**: `/field-fallback-rules` +- **Description**: Creates a new fallback rule with action-related properties + +### 2. Get Fallback Rule by ID +- **File**: `Get fallback rule by id.bru` +- **Method**: GET +- **Endpoint**: `/field-fallback-rules/{id}` +- **Description**: Retrieves a specific fallback rule by its ID + +### 3. Search Fallback Rules +- **File**: `Search fallback rules.bru` +- **Method**: GET +- **Endpoint**: `/field-fallback-rules` +- **Description**: Searches and filters fallback rules with pagination + +### 4. Update Fallback Rule +- **File**: `Update fallback rule.bru` +- **Method**: PUT +- **Endpoint**: `/field-fallback-rules/{id}` +- **Description**: Updates an existing fallback rule + +### 5. Delete Fallback Rule +- **File**: `Delete fallback rule.bru` +- **Method**: DELETE +- **Endpoint**: `/field-fallback-rules/{id}` +- **Description**: Soft deletes a fallback rule + +## Fallback Rule Properties + +### Core Properties +- `Name`: Name of the fallback rule +- `Description`: Description of the fallback rule +- `Priority`: Priority level for execution order +- `IsActive`: Whether the rule is active +- `OperationType`: Type of operation (Logical, Mathematical, etc.) + +### Action Properties + +- `Action`: Type of action to take (e.g., 'SET_DEFAULT', 'SHOW_MESSAGE', 'SKIP_FIELD', 'RETRY') +- `ActionMessage`: User-friendly message for the action +- `ActionParameters`: Additional parameters for complex actions (JSON) + +## Usage Examples + +### Creating a Fallback Rule +```json +{ + "Name": "Default Value Fallback", + "Description": "Set default value when validation fails", + "Priority": 1, + "IsActive": true, + "OperationType": "Logical", + "Action": "SET_DEFAULT", + "ActionMessage": "Setting default value due to validation failure", + "ActionParameters": "{\"retryCount\": 3, \"timeout\": 5000}" +} +``` + +### Common Actions +- `SET_DEFAULT`: Set a default value +- `SHOW_MESSAGE`: Display a message to the user +- `SKIP_FIELD`: Skip the current field +- `RETRY`: Retry the operation +- `CLEAR_FIELD`: Clear the field value +- `DISABLE_FIELD`: Disable the field + +## Environment Variables + +Make sure the following environment variables are set: +- `BASE_URL`: Base URL for the API +- `INTERNAL_API_KEY`: Internal API key for authentication +- `JWT_TOKEN`: JWT token for authorization +- `FALLBACK_RULE_ID`: ID of the fallback rule (set after creation) +- `LOGICAL_OPERATION_ID`: ID of a logical operation for testing + +## Testing + +Each request includes comprehensive tests to verify: +- HTTP status codes +- Response structure +- Data properties +- Business logic validation + +Run the requests in sequence to test the complete CRUD lifecycle. diff --git a/bruno/form-service/Field Rules/Fallback Rules/Search fallback rules.bru b/bruno/form-service/Field Rules/Fallback Rules/Search fallback rules.bru new file mode 100644 index 0000000..af6edb3 --- /dev/null +++ b/bruno/form-service/Field Rules/Fallback Rules/Search fallback rules.bru @@ -0,0 +1,67 @@ +meta { + name: Search Fallback Rules + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/field-fallback-rules + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +params:query { + pageIndex: 0 + limit: 10 + orderBy: CreatedAt + order: DESC + Name: + Action: + IsActive: +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Search results have correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Fallback Rule items have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('Name'); + expect(item).to.have.property('Description'); + expect(item).to.have.property('Priority'); + expect(item).to.have.property('IsActive'); + expect(item).to.have.property('OperationType'); + expect(item).to.have.property('Action'); + expect(item).to.have.property('ActionMessage'); + expect(item).to.have.property('ActionParameters'); + expect(item).to.have.property('ExecutionOrder'); + expect(item).to.have.property('StopOnSuccess'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Rules/Fallback Rules/Update fallback rule.bru b/bruno/form-service/Field Rules/Fallback Rules/Update fallback rule.bru new file mode 100644 index 0000000..1a9f699 --- /dev/null +++ b/bruno/form-service/Field Rules/Fallback Rules/Update fallback rule.bru @@ -0,0 +1,63 @@ +meta { + name: Update Fallback Rule + type: http + seq: 4 +} + +put { + url: {{BASE_URL}}/field-fallback-rules/{{FALLBACK_RULE_ID}} + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Updated Default Value Fallback", + "Description": "Updated description for fallback rule", + "Priority": 2, + "IsActive": true, + "OperationType": "Logical", + "Action": "SHOW_MESSAGE", + "ActionMessage": "Updated message for fallback action", + "ActionParameters": "{\"retryCount\": 5, \"timeout\": 10000}" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Fallback Rule is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Default Value Fallback'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated description for fallback rule'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data.Priority).equals(2); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('OperationType'); + expect(jsonRes.Data.OperationType).equals('Logical'); + expect(jsonRes.Data).to.have.property('Action'); + expect(jsonRes.Data.Action).equals('SHOW_MESSAGE'); + expect(jsonRes.Data).to.have.property('ActionMessage'); + expect(jsonRes.Data.ActionMessage).equals('Updated message for fallback action'); + expect(jsonRes.Data).to.have.property('ActionParameters'); + expect(jsonRes.Data).to.have.property('ExecutionOrder'); + expect(jsonRes.Data.ExecutionOrder).equals(2); + expect(jsonRes.Data).to.have.property('StopOnSuccess'); + expect(jsonRes.Data.StopOnSuccess).equals(false); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Rules/Fallback Rules/folder.bru b/bruno/form-service/Field Rules/Fallback Rules/folder.bru new file mode 100644 index 0000000..0dfa1b8 --- /dev/null +++ b/bruno/form-service/Field Rules/Fallback Rules/folder.bru @@ -0,0 +1,5 @@ +meta { + name: Fallback Rules + type: folder + seq: 4 +} diff --git a/bruno/form-service/Field Rules/README.md b/bruno/form-service/Field Rules/README.md new file mode 100644 index 0000000..0509670 --- /dev/null +++ b/bruno/form-service/Field Rules/README.md @@ -0,0 +1,94 @@ +# Field Rules API Documentation + +This section contains API requests for managing field rules in the form service. Rules define specific business logic conditions and their associated operations. + +## Rule Types + +### 1. Validation Rules +Define validation conditions and error messages for fields. + +**Request Body Structure:** +```json +{ + "Name": "Age Validation Rule", + "Description": "Validate that age is between 0 and 150", + "Priority": 1, + "IsActive": true, + "OperationId": "{{LOGICAL_OPERATION_ID}}", + "ErrorWhenFalse": true, + "ErrorMessage": "Age must be between 0 and 150 years", + "LogicId": "{{VALIDATION_LOGIC_ID}}" +} +``` + +**Key Properties:** +- `ErrorWhenFalse`: Show error when operation returns false (typical) +- `ErrorMessage`: Custom error message to display + +### 2. Calculation Rules +Define calculation conditions and operations for computed fields. + +**Request Body Structure:** +```json +{ + "Name": "BMI Calculation Rule", + "Description": "Calculate BMI using weight and height fields", + "Priority": 1, + "IsActive": true, + "ConditionForOperationId": "{{LOGICAL_OPERATION_ID}}", + "OperationId": "{{FUNCTION_EXPRESSION_OPERATION_ID}}", + "LogicId": "{{CALCULATION_LOGIC_ID}}" +} +``` + +**Key Properties:** +- `ConditionForOperationId`: Optional condition that must be true for calculation to execute +- `OperationId`: The operation that performs the actual calculation + +### 3. Skip Rules +Define conditions for skipping/hiding fields. + +**Request Body Structure:** +```json +{ + "Name": "Adult Check Skip Rule", + "Description": "Skip guardian fields if user is 18 or older", + "Priority": 1, + "IsActive": true, + "OperationId": "{{LOGICAL_OPERATION_ID}}", + "SkipWhenTrue": true, + "LogicId": "{{SKIP_LOGIC_ID}}" +} +``` + +**Key Properties:** +- `SkipWhenTrue`: Skip field when operation returns true +- `Priority`: Execution order (lower numbers execute first) + +## Common Properties + +All rules share these common properties: + +- **Name**: Human-readable rule name +- **Description**: Detailed description of rule purpose +- **Priority**: Execution order (0 = highest priority) +- **IsActive**: Whether the rule is currently active +- **OperationId**: Reference to the operation that defines the rule logic +- **LogicId**: Reference to the parent logic entity + +## Rule Execution Flow + +1. **Priority Order**: Rules execute in priority order (0 first, then 1, 2, etc.) +2. **Condition Check**: For calculation rules, condition is checked first +3. **Operation Execution**: The referenced operation is executed +4. **Result Processing**: Result is processed based on rule type: + - **Validation**: Error shown if `ErrorWhenFalse` and result is false + - **Calculation**: Field value is set to operation result + - **Skip**: Field is hidden/shown based on `SkipWhenTrue` and result + +## Important Notes + +- **Operation References**: All rules must reference valid operations via `OperationId` +- **Logic Association**: Rules are associated with logic entities via `LogicId` +- **Priority System**: Use priority to control execution order when multiple rules exist +- **Environment Variables**: Use environment variables for operation and logic IDs to maintain consistency \ No newline at end of file diff --git a/bruno/form-service/Field Rules/Skip Rules/Create skip rule.bru b/bruno/form-service/Field Rules/Skip Rules/Create skip rule.bru new file mode 100644 index 0000000..9831548 --- /dev/null +++ b/bruno/form-service/Field Rules/Skip Rules/Create skip rule.bru @@ -0,0 +1,70 @@ +meta { + name: Create Skip Rule + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-skip-rules + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Adult Check Skip Rule", + "Description": "Skip guardian fields if user is 18 or older", + "Priority": 1, + "IsActive": true, + "OperationType": "Logical", + "OperationId": "{{LOGICAL_OPERATION_ID}}", + "SkipWhenTrue": true, + "LogicId": "{{SKIP_LOGIC_ID}}", + "FallbackRuleId": "{{FALLBACK_RULE_ID}}" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("SKIP_RULE_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } +} + +tests { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Skip Rule is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Adult Check Skip Rule'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Skip guardian fields if user is 18 or older'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data.Priority).equals(1); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('OperationType'); + expect(jsonRes.Data.OperationType).equals('Logical'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('SkipWhenTrue'); + expect(jsonRes.Data.SkipWhenTrue).equals(true); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Rules/Skip Rules/Delete skip rule.bru b/bruno/form-service/Field Rules/Skip Rules/Delete skip rule.bru new file mode 100644 index 0000000..a64bae1 --- /dev/null +++ b/bruno/form-service/Field Rules/Skip Rules/Delete skip rule.bru @@ -0,0 +1,30 @@ +meta { + name: Delete Skip Rule + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-skip-rules/{{SKIP_RULE_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Skip Rule is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Rules/Skip Rules/Get skip rule by id.bru b/bruno/form-service/Field Rules/Skip Rules/Get skip rule by id.bru new file mode 100644 index 0000000..bcdd4a8 --- /dev/null +++ b/bruno/form-service/Field Rules/Skip Rules/Get skip rule by id.bru @@ -0,0 +1,38 @@ +meta { + name: Get Skip Rule by ID + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/field-skip-rules/{{SKIP_RULE_ID}} + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Skip Rule has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('SkipWhenTrue'); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Field Rules/Skip Rules/Search skip rules.bru b/bruno/form-service/Field Rules/Skip Rules/Search skip rules.bru new file mode 100644 index 0000000..2296c76 --- /dev/null +++ b/bruno/form-service/Field Rules/Skip Rules/Search skip rules.bru @@ -0,0 +1,68 @@ +meta { + name: Search Skip Rules + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-skip-rules/search?PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~name: Age + ~isActive: true + ~operationType: Logical +} + +script:post-response { +} + +tests { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Search results have correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Search results contain skip rules", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var firstItem = jsonRes.Data.Items[0]; + expect(firstItem).to.have.property('id'); + expect(firstItem).to.have.property('Name'); + expect(firstItem).to.have.property('Description'); + expect(firstItem).to.have.property('Priority'); + expect(firstItem).to.have.property('IsActive'); + expect(firstItem).to.have.property('OperationType'); + expect(firstItem).to.have.property('OperationId'); + expect(firstItem).to.have.property('SkipWhenTrue'); + expect(firstItem).to.have.property('LogicId'); + expect(firstItem).to.have.property('CreatedAt'); + expect(firstItem).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Rules/Skip Rules/Update skip rule.bru b/bruno/form-service/Field Rules/Skip Rules/Update skip rule.bru new file mode 100644 index 0000000..aea62d0 --- /dev/null +++ b/bruno/form-service/Field Rules/Skip Rules/Update skip rule.bru @@ -0,0 +1,62 @@ +meta { + name: Update Skip Rule + type: http + seq: 3 +} + +put { + url: {{BASE_URL}}/field-skip-rules/{{SKIP_RULE_ID}} + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Updated Age Skip Rule", + "Description": "Updated description for age skip rule", + "Priority": 2, + "IsActive": false, + "OperationType": "Mathematical", + "OperationId": "2ad466ac-7f4f-4ee1-8f54-784f2eccf1b9", + "SkipWhenTrue": false, + "LogicId": "221a286a-51cb-419d-8a31-4aa0700d43b8" + } +} + +script:post-response { +} + +tests { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Skip Rule is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Age Skip Rule'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated description for age skip rule'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data.Priority).equals(2); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(false); + expect(jsonRes.Data).to.have.property('OperationType'); + expect(jsonRes.Data.OperationType).equals('Mathematical'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('SkipWhenTrue'); + expect(jsonRes.Data.SkipWhenTrue).equals(false); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Rules/Skip Rules/folder.bru b/bruno/form-service/Field Rules/Skip Rules/folder.bru new file mode 100644 index 0000000..52bfbcb --- /dev/null +++ b/bruno/form-service/Field Rules/Skip Rules/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Skip Rules + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Rules/Validation Rules/Create validation rule.bru b/bruno/form-service/Field Rules/Validation Rules/Create validation rule.bru new file mode 100644 index 0000000..ffa0934 --- /dev/null +++ b/bruno/form-service/Field Rules/Validation Rules/Create validation rule.bru @@ -0,0 +1,76 @@ +meta { + name: Create Validation Rule + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/field-validation-rules + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Adult check rule", + "Description": "Validate that person is adult", + "Priority": 1, + "IsActive": true, + "OperationType": "Logical", + "OperationId": "aa48f853-acdc-4fbf-be44-a73ef7c8016f", + + "ErrorWhenFalse": true, + "ErrorMessage": "Check user is adult or not", + + "LogicId": "bf348643-c23d-499a-afd3-617782d06e21", + "FallbackRuleId": "{{FALLBACK_RULE_ID}}" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("VALIDATION_RULE_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } +} + +tests { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Validation Rule is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Adult check rule'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Validate that person is adult'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data.Priority).equals(1); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('OperationType'); + expect(jsonRes.Data.OperationType).equals('Logical'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data.OperationId).equals('b8a1c0b0-c01c-4659-a5fd-f9f6c2a22457'); + expect(jsonRes.Data).to.have.property('ErrorWhenFalse'); + expect(jsonRes.Data.ErrorWhenFalse).equals(true); + expect(jsonRes.Data).to.have.property('ErrorMessage'); + expect(jsonRes.Data.ErrorMessage).equals('Check user is adult or not'); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Rules/Validation Rules/Delete validation rule.bru b/bruno/form-service/Field Rules/Validation Rules/Delete validation rule.bru new file mode 100644 index 0000000..4756a22 --- /dev/null +++ b/bruno/form-service/Field Rules/Validation Rules/Delete validation rule.bru @@ -0,0 +1,30 @@ +meta { + name: Delete Validation Rule + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/field-validation-rules/{{VALIDATION_RULE_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Validation Rule is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Field Rules/Validation Rules/Get validation rule by id.bru b/bruno/form-service/Field Rules/Validation Rules/Get validation rule by id.bru new file mode 100644 index 0000000..63cdd1b --- /dev/null +++ b/bruno/form-service/Field Rules/Validation Rules/Get validation rule by id.bru @@ -0,0 +1,40 @@ +meta { + name: Get Validation Rule by ID + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/field-validation-rules/cdeacdc1-ba25-48d9-8b15-3e926987eb25 + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Validation Rule has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('ErrorWhenFalse'); + expect(jsonRes.Data).to.have.property('ErrorMessage'); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Rules/Validation Rules/Search validation rules.bru b/bruno/form-service/Field Rules/Validation Rules/Search validation rules.bru new file mode 100644 index 0000000..82b32ed --- /dev/null +++ b/bruno/form-service/Field Rules/Validation Rules/Search validation rules.bru @@ -0,0 +1,69 @@ +meta { + name: Search Validation Rules + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/field-validation-rules/search?PageIndex=0&ItemsPerPage=10&OrderBy=CreatedAt&Order=DESC + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~name: Email + ~isActive: true + ~operationType: Logical +} + +script:post-response { +} + +tests { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Search results have correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Search results contain validation rules", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var firstItem = jsonRes.Data.Items[0]; + expect(firstItem).to.have.property('id'); + expect(firstItem).to.have.property('Name'); + expect(firstItem).to.have.property('Description'); + expect(firstItem).to.have.property('Priority'); + expect(firstItem).to.have.property('IsActive'); + expect(firstItem).to.have.property('OperationType'); + expect(firstItem).to.have.property('OperationId'); + expect(firstItem).to.have.property('ErrorWhenFalse'); + expect(firstItem).to.have.property('ErrorMessage'); + expect(firstItem).to.have.property('LogicId'); + expect(firstItem).to.have.property('CreatedAt'); + expect(firstItem).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Field Rules/Validation Rules/Update validation rule.bru b/bruno/form-service/Field Rules/Validation Rules/Update validation rule.bru new file mode 100644 index 0000000..bff8d75 --- /dev/null +++ b/bruno/form-service/Field Rules/Validation Rules/Update validation rule.bru @@ -0,0 +1,65 @@ +meta { + name: Update Validation Rule + type: http + seq: 3 +} + +put { + url: {{BASE_URL}}/field-validation-rules/{{VALIDATION_RULE_ID}} + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Updated Email Validation Rule", + "Description": "Updated email validation with enhanced checks", + "Priority": 2, + "IsActive": false, + "OperationType": "Composition", + "OperationId": "14aff0d6-0d9e-4337-90a8-ed0078a91a44", + "ErrorWhenFalse": false, + "ErrorMessage": "Please enter a valid email address with proper domain", + "LogicId": "15f0cedf-3f46-4498-a090-5a4360e26a21" + } +} + +script:post-response { +} + +tests { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Validation Rule is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Email Validation Rule'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated email validation with enhanced checks'); + expect(jsonRes.Data).to.have.property('Priority'); + expect(jsonRes.Data.Priority).equals(2); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(false); + expect(jsonRes.Data).to.have.property('OperationType'); + expect(jsonRes.Data.OperationType).equals('Composition'); + expect(jsonRes.Data).to.have.property('OperationId'); + expect(jsonRes.Data).to.have.property('ErrorWhenFalse'); + expect(jsonRes.Data.ErrorWhenFalse).equals(false); + expect(jsonRes.Data).to.have.property('ErrorMessage'); + expect(jsonRes.Data.ErrorMessage).equals('Please enter a valid email address with proper domain'); + expect(jsonRes.Data).to.have.property('LogicId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Field Rules/Validation Rules/folder.bru b/bruno/form-service/Field Rules/Validation Rules/folder.bru new file mode 100644 index 0000000..8bbbeb3 --- /dev/null +++ b/bruno/form-service/Field Rules/Validation Rules/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Validation Rules + type: folder +} \ No newline at end of file diff --git a/bruno/form-service/Field Rules/folder.bru b/bruno/form-service/Field Rules/folder.bru new file mode 100644 index 0000000..19f4159 --- /dev/null +++ b/bruno/form-service/Field Rules/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Field Rules + seq: 8 +} diff --git a/bruno/form-service/Form Field/Create a new form field.bru b/bruno/form-service/Form Field/Create a new form field.bru new file mode 100644 index 0000000..4f2bf8a --- /dev/null +++ b/bruno/form-service/Form Field/Create a new form field.bru @@ -0,0 +1,97 @@ +meta { + name: Create a new form field + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/form-fields + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "ParentTemplateId": "{{TEMPLATE_ID}}", + "ParentSectionId": "{{SECTION_ID}}", + "Title": "What is your favorite color?", + "Description": "Please select your favorite color from the options below", + "ResponseType": "SingleChoiceSelection", + "Score": 5, + "Sequence": 1, + "CorrectAnswer": "Blue", + "IsRequired": true, + "Hint": "Choose the color you like the most", + "Options": [ + { + "Text": "Red", + "Sequence": 1, + "ImageUrl": "https://example.com/red.jpg" + }, + { + "Text": "Blue", + "Sequence": 2, + "ImageUrl": "https://example.com/blue.jpg" + }, + { + "Text": "Green", + "Sequence": 3, + "ImageUrl": "https://example.com/green.jpg" + }, + { + "Text": "Yellow", + "Sequence": 4, + "ImageUrl": "https://example.com/yellow.jpg" + } + ], + "ImageResourceId": "550e8400-e29b-41d4-a716-446655440000", + "RangeMin": 1, + "RangeMax": 10, + "DefaultExpectedUnit": "units", + "PageBreakAfter": false + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("FORM_FIELD_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Form field is created", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('TemplateId'); + expect(jsonRes.Data).to.have.property('ParentSectionId'); + expect(jsonRes.Data).to.have.property('Title'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('DisplayCode'); + expect(jsonRes.Data).to.have.property('ResponseType'); + expect(jsonRes.Data).to.have.property('Score'); + expect(jsonRes.Data).to.have.property('Sequence'); + expect(jsonRes.Data).to.have.property('CorrectAnswer'); + expect(jsonRes.Data).to.have.property('IsRequired'); + expect(jsonRes.Data).to.have.property('Hint'); + expect(jsonRes.Data).to.have.property('Options'); + expect(jsonRes.Data).to.have.property('ImageResourceId'); + expect(jsonRes.Data).to.have.property('RangeMin'); + expect(jsonRes.Data).to.have.property('RangeMax'); + expect(jsonRes.Data).to.have.property('DefaultExpectedUnit'); + expect(jsonRes.Data).to.have.property('PageBreakAfter'); + }); +} diff --git a/bruno/form-service/Form Field/Create boolean form field.bru b/bruno/form-service/Form Field/Create boolean form field.bru new file mode 100644 index 0000000..d8694eb --- /dev/null +++ b/bruno/form-service/Form Field/Create boolean form field.bru @@ -0,0 +1,49 @@ +meta { + name: Create boolean form field + type: http + seq: 9 +} + +post { + url: {{BASE_URL}}/form-fields + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "ParentTemplateId": "{{TEMPLATE_ID}}", + "ParentSectionId": "{{SECTION_ID}}", + "Title": "Do you agree to the terms and conditions?", + "Description": "Please read the terms and conditions and indicate your agreement", + "ResponseType": "Boolean", + "Score": 1, + "CorrectAnswer": "true", + "IsRequired": true, + "Hint": "Select Yes to agree or No to disagree", + "QuestionImageUrl": "https://example.com/terms-field.jpg", + "RangeMin": 0, + "RangeMax": 1 + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Boolean form field is created", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.ResponseType).to.equal('Boolean'); + expect(jsonRes.Data.CorrectAnswer).to.equal('true'); + }); +} diff --git a/bruno/form-service/Form Field/Create number form field.bru b/bruno/form-service/Form Field/Create number form field.bru new file mode 100644 index 0000000..5a7e306 --- /dev/null +++ b/bruno/form-service/Form Field/Create number form field.bru @@ -0,0 +1,50 @@ +meta { + name: Create number form field + type: http + seq: 8 +} + +post { + url: {{BASE_URL}}/form-fields + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "ParentTemplateId": "{{TEMPLATE_ID}}", + "ParentSectionId": "{{SECTION_ID}}", + "Title": "What is your age?", + "Description": "Please enter your current age in years", + "ResponseType": "Integer", + "Score": 2, + "CorrectAnswer": "25", + "IsRequired": true, + "Hint": "Enter a number between 1 and 120", + // "QuestionImageUrl": "https://example.com/age-field.jpg", + "RangeMin": 1, + "RangeMax": 120 + } +} + +tests { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Number form field is created", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.ResponseType).to.equal('Integer'); + expect(jsonRes.Data.RangeMin).to.equal(1); + expect(jsonRes.Data.RangeMax).to.equal(120); + }); +} diff --git a/bruno/form-service/Form Field/Create text form field.bru b/bruno/form-service/Form Field/Create text form field.bru new file mode 100644 index 0000000..abb57ce --- /dev/null +++ b/bruno/form-service/Form Field/Create text form field.bru @@ -0,0 +1,49 @@ +meta { + name: Create text form field + type: http + seq: 7 +} + +post { + url: {{BASE_URL}}/form-fields + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "ParentTemplateId": "{{TEMPLATE_ID}}", + "ParentSectionId": "{{SECTION_ID}}", + "Title": "What is your email?", + "Description": "Please enter your email as it appears on official documents", + "ResponseType": "Text", + "Score": 3, + "CorrectAnswer": "johndoe@gmail.com", + "IsRequired": true, + "Hint": "Enter your email" + // "QuestionImageUrl": "https://example.com/name-field.jpg", + // "RangeMin": 2, + // "RangeMax": 50 + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Text form field is created", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.ResponseType).to.equal('Text'); + expect(jsonRes.Data.IsRequired).to.equal(true); + }); +} diff --git a/bruno/form-service/Form Field/Delete form field.bru b/bruno/form-service/Form Field/Delete form field.bru new file mode 100644 index 0000000..85872e6 --- /dev/null +++ b/bruno/form-service/Form Field/Delete form field.bru @@ -0,0 +1,29 @@ +meta { + name: Delete form field + type: http + seq: 6 +} + +delete { + url: {{BASE_URL}}/form-fields/{{FORM_FIELD_ID}} + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Form field is deleted", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} \ No newline at end of file diff --git a/form-service/Question/Get question by id.bru b/bruno/form-service/Form Field/Get form field by id.bru similarity index 68% rename from form-service/Question/Get question by id.bru rename to bruno/form-service/Form Field/Get form field by id.bru index d6b6c78..31311af 100644 --- a/form-service/Question/Get question by id.bru +++ b/bruno/form-service/Form Field/Get form field by id.bru @@ -1,38 +1,42 @@ meta { - name: Get question by id + name: Get form field by id type: http seq: 2 } get { - url: {{BASE_URL}}/questions/{{QUESTION_ID}} + url: {{BASE_URL}}/form-fields/81bdaaef-f6f2-4fe8-81f5-c29cdd56876c body: none auth: none } +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + tests { - test("Request is successfull", function () { - expect(res.getStatus()).to.equal(201); + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); expect(jsonRes.Status).to.eql('Success'); }); - test("Question is created", function () { + test("Form field data is returned", function () { var jsonRes = res.getBody(); expect(jsonRes.Data).to.have.property('id'); - expect(jsonRes.Data).to.have.property('ParentTemplateId'); - expect(jsonRes.Data).to.have.property('ParentSectionId'); expect(jsonRes.Data).to.have.property('Title'); expect(jsonRes.Data).to.have.property('Description'); expect(jsonRes.Data).to.have.property('DisplayCode'); expect(jsonRes.Data).to.have.property('ResponseType'); expect(jsonRes.Data).to.have.property('Score'); expect(jsonRes.Data).to.have.property('CorrectAnswer'); + expect(jsonRes.Data).to.have.property('IsRequired'); expect(jsonRes.Data).to.have.property('Hint'); expect(jsonRes.Data).to.have.property('Options'); expect(jsonRes.Data).to.have.property('QuestionImageUrl'); expect(jsonRes.Data).to.have.property('RangeMin'); expect(jsonRes.Data).to.have.property('RangeMax'); - }); } diff --git a/bruno/form-service/Form Field/Get form fields by template id.bru b/bruno/form-service/Form Field/Get form fields by template id.bru new file mode 100644 index 0000000..1994183 --- /dev/null +++ b/bruno/form-service/Form Field/Get form fields by template id.bru @@ -0,0 +1,31 @@ +meta { + name: Get form fields by template id + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/form-fields/by-template-id/{{TEMPLATE_ID}} + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Form fields data is returned", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Title'); + expect(jsonRes.Data).to.have.property('ResponseType'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Form Field/Update form field.bru b/bruno/form-service/Form Field/Update form field.bru new file mode 100644 index 0000000..d587213 --- /dev/null +++ b/bruno/form-service/Form Field/Update form field.bru @@ -0,0 +1,75 @@ +meta { + name: Update form field + type: http + seq: 5 +} + +put { + url: {{BASE_URL}}/form-fields/{{FORM_FIELD_ID}} + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Title": "Updated: What is your favorite color?", + "Description": "Updated: Please select your favorite color from the options below", + "ResponseType": "SingleChoiceSelection", + "Score": 10, + "CorrectAnswer": "Green", + "IsRequired": true, + "Hint": "Updated: Choose the color you like the most", + "QuestionImageUrl": "https://example.com/updated-color-image.jpg", + "RangeMin": 1, + "RangeMax": 15, + "Options": [ + { + "Text": "Red", + "Sequence": "1", + "ImageUrl": "https://example.com/red.jpg" + }, + { + "Text": "Blue", + "Sequence": "2", + "ImageUrl": "https://example.com/blue.jpg" + }, + { + "Text": "Green", + "Sequence": "3", + "ImageUrl": "https://example.com/green.jpg" + }, + { + "Text": "Yellow", + "Sequence": "4", + "ImageUrl": "https://example.com/yellow.jpg" + }, + { + "Text": "Purple", + "Sequence": "5", + "ImageUrl": "https://example.com/purple.jpg" + } + ] + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Form field is updated", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.Title).to.include('Updated:'); + expect(jsonRes.Data.Score).to.equal(10); + expect(jsonRes.Data.CorrectAnswer).to.equal('Green'); + }); +} diff --git a/bruno/form-service/Form Field/Update number field.bru b/bruno/form-service/Form Field/Update number field.bru new file mode 100644 index 0000000..407d25e --- /dev/null +++ b/bruno/form-service/Form Field/Update number field.bru @@ -0,0 +1,26 @@ +meta { + name: Update number field + type: http + seq: 4 +} + +put { + url: {{BASE_URL}}/form-fields/{{FORM_FIELD_ID}} + body: json + auth: inherit +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + // { + // "ValidateLogicId":"bf348643-c23d-499a-afd3-617782d06e21" + // } + { + "CalculateLogicId": "7baf5767-3e07-426d-9a6e-92a2a15de2cb" + } +} diff --git a/bruno/form-service/Form Field/folder.bru b/bruno/form-service/Form Field/folder.bru new file mode 100644 index 0000000..3719d4d --- /dev/null +++ b/bruno/form-service/Form Field/folder.bru @@ -0,0 +1,8 @@ +meta { + name: Form Field + seq: 5 +} + +auth { + mode: none +} diff --git a/form-service/Form Submission/Create new form.bru b/bruno/form-service/Form Submission/Create new form.bru similarity index 79% rename from form-service/Form Submission/Create new form.bru rename to bruno/form-service/Form Submission/Create new form.bru index 77c640f..15177c7 100644 --- a/form-service/Form Submission/Create new form.bru +++ b/bruno/form-service/Form Submission/Create new form.bru @@ -10,16 +10,22 @@ post { auth: none } +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + body:json { { - "FormTemplateId":"163bac66-4af0-40dc-a09f-bfbe866e7e89" //Form Template Id - // "FormUrl": "This is Third form", + "FormTemplateId":"{{TEMPLATE_ID}}" //Form Template Id + // "FormUrl": "This is Third form" // "AnsweredByUserId": "c9e5b365-a0ff-4f73-b168-f3a4263b777e", //Submitter / User Id // "Status": "InProgress" } } -tests { +script:post-response { try { var jsonRes = res.getBody(); bru.setEnvVar("SUBMISSION_ID", jsonRes.Data.id); diff --git a/form-service/Form Submission/Delete a form record.bru b/bruno/form-service/Form Submission/Delete a form record.bru similarity index 75% rename from form-service/Form Submission/Delete a form record.bru rename to bruno/form-service/Form Submission/Delete a form record.bru index f69500d..d052c44 100644 --- a/form-service/Form Submission/Delete a form record.bru +++ b/bruno/form-service/Form Submission/Delete a form record.bru @@ -1,7 +1,7 @@ meta { name: Delete a form record type: http - seq: 7 + seq: 6 } delete { @@ -10,7 +10,13 @@ delete { auth: none } -tests { +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/Form Submission/Generic search.bru b/bruno/form-service/Form Submission/Generic search.bru new file mode 100644 index 0000000..19f4309 --- /dev/null +++ b/bruno/form-service/Form Submission/Generic search.bru @@ -0,0 +1,39 @@ +meta { + name: Generic search + type: http + seq: 5 +} + +get { + url: {{BASE_URL}}/form-submissions/search?encrypted=7590ce800dfbb072e02b8af29bb315b6d9937707dc86ce4c0d74cc15ca7f45f8 + body: none + auth: none +} + +params:query { + encrypted: 7590ce800dfbb072e02b8af29bb315b6d9937707dc86ce4c0d74cc15ca7f45f8 + ~pageIndex: 0 + ~itemsPerPage: 10 + ~orderBy: CreatedAt + ~order: DESC + ~id: {{SUBMISSION_ID}} +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successfull", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("User records are returned", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data.Items.length).greaterThan(0); + }); +} diff --git a/form-service/Form Submission/Get form by id.bru b/bruno/form-service/Form Submission/Get form by id.bru similarity index 54% rename from form-service/Form Submission/Get form by id.bru rename to bruno/form-service/Form Submission/Get form by id.bru index f7b1732..0d848bd 100644 --- a/form-service/Form Submission/Get form by id.bru +++ b/bruno/form-service/Form Submission/Get form by id.bru @@ -9,3 +9,9 @@ get { body: none auth: none } + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} diff --git a/form-service/Form Submission/Submit the instance of template.bru b/bruno/form-service/Form Submission/Submit the instance of template.bru similarity index 58% rename from form-service/Form Submission/Submit the instance of template.bru rename to bruno/form-service/Form Submission/Submit the instance of template.bru index 6c25885..e831377 100644 --- a/form-service/Form Submission/Submit the instance of template.bru +++ b/bruno/form-service/Form Submission/Submit the instance of template.bru @@ -1,7 +1,7 @@ meta { name: Submit the instance of template type: http - seq: 5 + seq: 4 } post { @@ -9,3 +9,9 @@ post { body: none auth: none } + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} diff --git a/form-service/Form Submission/Update a form record.bru b/bruno/form-service/Form Submission/Update a form record.bru similarity index 78% rename from form-service/Form Submission/Update a form record.bru rename to bruno/form-service/Form Submission/Update a form record.bru index 2ba7d6a..1fb20c7 100644 --- a/form-service/Form Submission/Update a form record.bru +++ b/bruno/form-service/Form Submission/Update a form record.bru @@ -10,16 +10,23 @@ put { auth: none } +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + body:json { { - "FormTemplateId":"2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6", - "FormUrl": "This is Third form", - "AnsweredByUserId": "05b04a84-ce2d-4c5a-9528-4cc168e0ad0a", + "FormTemplateId":"{{TEMPLATE_ID}}", + // "FormUrl": "This is Third form", + // "AnsweredByUserId": "05b04a84-ce2d-4c5a-9528-4cc168e0ad0a", + "UserId": "6e0793de-49f0-4bce-b4e0-de0c808f3037", "Status": "LinkShared" } } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/Form Submission/folder.bru b/bruno/form-service/Form Submission/folder.bru new file mode 100644 index 0000000..433d9f4 --- /dev/null +++ b/bruno/form-service/Form Submission/folder.bru @@ -0,0 +1,8 @@ +meta { + name: Form Submission + seq: 14 +} + +auth { + mode: none +} diff --git a/bruno/form-service/Form Template Approval/Create form template approval.bru b/bruno/form-service/Form Template Approval/Create form template approval.bru new file mode 100644 index 0000000..60d1038 --- /dev/null +++ b/bruno/form-service/Form Template Approval/Create form template approval.bru @@ -0,0 +1,57 @@ +meta { + name: Create Form Template Approval + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/form-template-approvals + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "TemplateId": "{{TEMPLATE_ID}}", + "ApproverUserId": "{{USER_ID}}", + "Approved": false, + "ReviewComments": "Initial approval request" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("FORM_TEMPLATE_APPROVAL_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Form Template Approval is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('TemplateId'); + expect(jsonRes.Data.TemplateId).equals('{{TEMPLATE_ID}}'); + expect(jsonRes.Data).to.have.property('ApproverUserId'); + expect(jsonRes.Data.ApproverUserId).equals('{{USER_ID}}'); + expect(jsonRes.Data).to.have.property('Approved'); + expect(jsonRes.Data.Approved).equals(false); + expect(jsonRes.Data).to.have.property('ReviewComments'); + expect(jsonRes.Data.ReviewComments).equals('Initial approval request'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Form Template Approval/Delete form template approval.bru b/bruno/form-service/Form Template Approval/Delete form template approval.bru new file mode 100644 index 0000000..3e61c31 --- /dev/null +++ b/bruno/form-service/Form Template Approval/Delete form template approval.bru @@ -0,0 +1,30 @@ +meta { + name: Delete Form Template Approval + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/form-template-approvals/{{FORM_TEMPLATE_APPROVAL_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Form Template Approval is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Form Template Approval/Get form template approval by id.bru b/bruno/form-service/Form Template Approval/Get form template approval by id.bru new file mode 100644 index 0000000..37810c4 --- /dev/null +++ b/bruno/form-service/Form Template Approval/Get form template approval by id.bru @@ -0,0 +1,36 @@ +meta { + name: Get Form Template Approval by ID + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/form-template-approvals/{{FORM_TEMPLATE_APPROVAL_ID}} + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Form Template Approval has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{FORM_TEMPLATE_APPROVAL_ID}}'); + expect(jsonRes.Data).to.have.property('TemplateId'); + expect(jsonRes.Data).to.have.property('ApproverUserId'); + expect(jsonRes.Data).to.have.property('Approved'); + expect(jsonRes.Data).to.have.property('ReviewComments'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Form Template Approval/Search form template approvals.bru b/bruno/form-service/Form Template Approval/Search form template approvals.bru new file mode 100644 index 0000000..9ebb5e6 --- /dev/null +++ b/bruno/form-service/Form Template Approval/Search form template approvals.bru @@ -0,0 +1,59 @@ +meta { + name: Search Form Template Approvals + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/form-template-approvals/search?pageIndex=0&itemsPerPage=10&orderBy=CreatedAt&order=DESC + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~status: Pending +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Form Template Approvals search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Form Template Approvals have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('TemplateId'); + expect(item).to.have.property('ApproverUserId'); + expect(item).to.have.property('Approved'); + expect(item).to.have.property('ReviewComments'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Form Template Approval/Update form template approval.bru b/bruno/form-service/Form Template Approval/Update form template approval.bru new file mode 100644 index 0000000..733d2c9 --- /dev/null +++ b/bruno/form-service/Form Template Approval/Update form template approval.bru @@ -0,0 +1,48 @@ +meta { + name: Update Form Template Approval + type: http + seq: 2 +} + +put { + url: {{BASE_URL}}/form-template-approvals/{{FORM_TEMPLATE_APPROVAL_ID}} + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "ApproverUserId": "{{USER_ID}}", + "TemplateId": "{{TEMPLATE_ID}}", + "Approved": false, + "ReviewComments": "Template approved after review" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Form Template Approval is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{FORM_TEMPLATE_APPROVAL_ID}}'); + expect(jsonRes.Data).to.have.property('TemplateId'); + expect(jsonRes.Data).to.have.property('ApproverUserId'); + expect(jsonRes.Data).to.have.property('Approved'); + expect(jsonRes.Data.Approved).equals(true); + expect(jsonRes.Data).to.have.property('ReviewComments'); + expect(jsonRes.Data.ReviewComments).equals('Template approved after review'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Form Template Approval/folder.bru b/bruno/form-service/Form Template Approval/folder.bru new file mode 100644 index 0000000..ab08975 --- /dev/null +++ b/bruno/form-service/Form Template Approval/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Form Template Approval + seq: 13 +} diff --git a/form-service/Form section/Create new form section.bru b/bruno/form-service/Form section/Create new form section.bru similarity index 79% rename from form-service/Form section/Create new form section.bru rename to bruno/form-service/Form section/Create new form section.bru index 98bc4b4..9945cea 100644 --- a/form-service/Form section/Create new form section.bru +++ b/bruno/form-service/Form section/Create new form section.bru @@ -10,6 +10,12 @@ post { auth: none } +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + body:json { // { // "ParentFormTemplateId": "64888ed6-7452-48ad-8f4e-48448f21259f", //Form template id @@ -21,16 +27,15 @@ body:json { // "ParentSectionId": "2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6" //any uuid value // } { - "ParentFormTemplateId": "b78158bd-7496-48f1-b625-2c489b9c65a9", - // "SectionIdentifier": "new form section 1" - // "Title": "New form section ", - // "Description": "sf esdrg", - // "Sequence":"abc" - "ParentSectionId": "d04ca675-d4eb-4c6a-9820-6ba1cce3e625" + "ParentFormTemplateId": "00b7484e-584b-4488-a655-6eb63dfdbb1d", + "Title": "Section 1", + "Description": "This is a new form section for organizing form fields", + "Sequence": 1, + "ParentSectionId": "{{PARENT_SECTION_ID}}" } } -tests { +script:post-response { try { var jsonRes = res.getBody(); bru.setEnvVar("SECTION_ID", jsonRes.Data.id); diff --git a/form-service/Form section/Delete the form section record.bru b/bruno/form-service/Form section/Delete the form section record.bru similarity index 77% rename from form-service/Form section/Delete the form section record.bru rename to bruno/form-service/Form section/Delete the form section record.bru index 3e3c7ab..bcb8f49 100644 --- a/form-service/Form section/Delete the form section record.bru +++ b/bruno/form-service/Form section/Delete the form section record.bru @@ -10,7 +10,13 @@ delete { auth: none } -tests { +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/form-service/User/Generic search.bru b/bruno/form-service/Form section/Generic search.bru similarity index 57% rename from form-service/User/Generic search.bru rename to bruno/form-service/Form section/Generic search.bru index 188d756..fea33ff 100644 --- a/form-service/User/Generic search.bru +++ b/bruno/form-service/Form section/Generic search.bru @@ -5,17 +5,26 @@ meta { } get { - url: {{BASE_URL}}/users/search?username=Username&password=Password + url: {{BASE_URL}}/form-sections/search?parentFormTemplateId={{TEMPLATE_ID}} body: none auth: none } +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + params:query { - username: Username - password: Password + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + parentFormTemplateId: {{TEMPLATE_ID}} } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/form-service/Form section/Get form section by id.bru b/bruno/form-service/Form section/Get form section by id.bru similarity index 86% rename from form-service/Form section/Get form section by id.bru rename to bruno/form-service/Form section/Get form section by id.bru index 5335a0b..fe6e2f5 100644 --- a/form-service/Form section/Get form section by id.bru +++ b/bruno/form-service/Form section/Get form section by id.bru @@ -10,7 +10,13 @@ get { auth: none } -tests { +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/Form section/Get form section by template id.bru b/bruno/form-service/Form section/Get form section by template id.bru new file mode 100644 index 0000000..8d2f734 --- /dev/null +++ b/bruno/form-service/Form section/Get form section by template id.bru @@ -0,0 +1,17 @@ +meta { + name: Get form section by template id + type: http + seq: 6 +} + +get { + url: {{BASE_URL}}/form-sections/templateId/{{TEMPLATE_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} diff --git a/form-service/Form section/Update form section.bru b/bruno/form-service/Form section/Update form section.bru similarity index 92% rename from form-service/Form section/Update form section.bru rename to bruno/form-service/Form section/Update form section.bru index e35e451..8fd7331 100644 --- a/form-service/Form section/Update form section.bru +++ b/bruno/form-service/Form section/Update form section.bru @@ -10,6 +10,12 @@ put { auth: none } +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + body:json { // { // // "TemplateId": "2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6", @@ -25,11 +31,11 @@ body:json { "Title": "first section", "Description": "description", // "Sequence": "1", - "ParentSectionId": "6eb7e68d-608b-4076-8a9d-bbc467ab9211" + "ParentSectionId": "{{PARENT_SECTION_ID}}" } } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/Form section/folder.bru b/bruno/form-service/Form section/folder.bru new file mode 100644 index 0000000..e114fc0 --- /dev/null +++ b/bruno/form-service/Form section/folder.bru @@ -0,0 +1,8 @@ +meta { + name: Form section + seq: 4 +} + +auth { + mode: none +} diff --git a/form-service/Form template/Delete form template record.bru b/bruno/form-service/Form template/Delete form template record.bru similarity index 77% rename from form-service/Form template/Delete form template record.bru rename to bruno/form-service/Form template/Delete form template record.bru index a3ff73a..c36e136 100644 --- a/form-service/Form template/Delete form template record.bru +++ b/bruno/form-service/Form template/Delete form template record.bru @@ -10,7 +10,13 @@ delete { auth: none } -tests { +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/Form template/Export Form Template.bru b/bruno/form-service/Form template/Export Form Template.bru new file mode 100644 index 0000000..f567836 --- /dev/null +++ b/bruno/form-service/Form template/Export Form Template.bru @@ -0,0 +1,17 @@ +meta { + name: Export Form Template + type: http + seq: 8 +} + +get { + url: + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} diff --git a/form-service/Form template/Generic search.bru b/bruno/form-service/Form template/Generic search.bru similarity index 57% rename from form-service/Form template/Generic search.bru rename to bruno/form-service/Form template/Generic search.bru index fd119fb..a1d4b21 100644 --- a/form-service/Form template/Generic search.bru +++ b/bruno/form-service/Form template/Generic search.bru @@ -5,18 +5,29 @@ meta { } get { - url: {{BASE_URL}}/form-templates/search?id=210939f1-c6c4-4274-843d-72f44f77da65 + url: {{BASE_URL}}/form-templates/search?order=ASC&ownerUserId=9f3de126-dde8-4511-b17c-e5c16ca957e8 body: none auth: none } params:query { - id: 210939f1-c6c4-4274-843d-72f44f77da65 + order: ASC + ownerUserId: 9f3de126-dde8-4511-b17c-e5c16ca957e8 + ~pageIndex: 10 + ~itemsPerPage: 10 + ~orderBy: Title + ~id: {{TEMPLATE_ID}} ~title: What is your full name ~description: about testing the assessment } -tests { +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/Form template/Get Details by template id.bru b/bruno/form-service/Form template/Get Details by template id.bru new file mode 100644 index 0000000..4333240 --- /dev/null +++ b/bruno/form-service/Form template/Get Details by template id.bru @@ -0,0 +1,17 @@ +meta { + name: Get Details by template id + type: http + seq: 5 +} + +get { + url: {{BASE_URL}}/form-templates/{{TEMPLATE_ID}}/details + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} diff --git a/bruno/form-service/Form template/Get Form Submissions.bru b/bruno/form-service/Form template/Get Form Submissions.bru new file mode 100644 index 0000000..fc31e82 --- /dev/null +++ b/bruno/form-service/Form template/Get Form Submissions.bru @@ -0,0 +1,17 @@ +meta { + name: Get Form Submissions + type: http + seq: 7 +} + +get { + url: {{BASE_URL}}/form-templates/{{TEMPLATE_ID}}/submissions + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} diff --git a/form-service/Form template/Get form template by id.bru b/bruno/form-service/Form template/Get form template by id.bru similarity index 87% rename from form-service/Form template/Get form template by id.bru rename to bruno/form-service/Form template/Get form template by id.bru index b2b06be..8b7ff7f 100644 --- a/form-service/Form template/Get form template by id.bru +++ b/bruno/form-service/Form template/Get form template by id.bru @@ -10,7 +10,13 @@ get { auth: none } -tests { +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/Form template/Get template with fallback rules.bru b/bruno/form-service/Form template/Get template with fallback rules.bru new file mode 100644 index 0000000..23e0eee --- /dev/null +++ b/bruno/form-service/Form template/Get template with fallback rules.bru @@ -0,0 +1,109 @@ +meta { + name: Get Template with Fallback Rules + type: http + seq: 10 +} + +get { + url: {{BASE_URL}}/form-templates/{{TEMPLATE_ID}}/details + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes).to.have.property('FormSections'); + }); + + test("Template includes fallback rules in form fields", function () { + var jsonRes = res.getBody(); + if (jsonRes.FormSections && jsonRes.FormSections.length > 0) { + var hasFallbackRules = false; + + // Check all form sections for fallback rules + jsonRes.FormSections.forEach(function(section) { + if (section.FormFields && section.FormFields.length > 0) { + section.FormFields.forEach(function(field) { + // Check Skip Logic rules for fallback rules + if (field.SkipLogic && field.SkipLogic.Rules) { + field.SkipLogic.Rules.forEach(function(rule) { + if (rule.FallbackRule) { + hasFallbackRules = true; + expect(rule.FallbackRule).to.have.property('id'); + expect(rule.FallbackRule).to.have.property('Action'); + expect(rule.FallbackRule).to.have.property('ActionMessage'); + expect(rule.FallbackRule).to.have.property('ExecutionOrder'); + expect(rule.FallbackRule).to.have.property('StopOnSuccess'); + } + }); + } + + // Check Calculate Logic rules for fallback rules + if (field.CalculateLogic && field.CalculateLogic.Rules) { + field.CalculateLogic.Rules.forEach(function(rule) { + if (rule.FallbackRule) { + hasFallbackRules = true; + expect(rule.FallbackRule).to.have.property('id'); + expect(rule.FallbackRule).to.have.property('Action'); + expect(rule.FallbackRule).to.have.property('ActionMessage'); + expect(rule.FallbackRule).to.have.property('ExecutionOrder'); + expect(rule.FallbackRule).to.have.property('StopOnSuccess'); + } + }); + } + + // Check Validate Logic rules for fallback rules + if (field.ValidateLogic && field.ValidateLogic.Rules) { + field.ValidateLogic.Rules.forEach(function(rule) { + if (rule.FallbackRule) { + hasFallbackRules = true; + expect(rule.FallbackRule).to.have.property('id'); + expect(rule.FallbackRule).to.have.property('Action'); + expect(rule.FallbackRule).to.have.property('ActionMessage'); + expect(rule.FallbackRule).to.have.property('ExecutionOrder'); + expect(rule.FallbackRule).to.have.property('StopOnSuccess'); + } + }); + } + }); + } + }); + + console.log("Found fallback rules in template:", hasFallbackRules); + } + }); + + test("Fallback rules have proper action types", function () { + var jsonRes = res.getBody(); + var validActions = ['SET_DEFAULT', 'SHOW_MESSAGE', 'SKIP_FIELD', 'RETRY', 'CLEAR_FIELD', 'DISABLE_FIELD']; + + if (jsonRes.FormSections && jsonRes.FormSections.length > 0) { + jsonRes.FormSections.forEach(function(section) { + if (section.FormFields && section.FormFields.length > 0) { + section.FormFields.forEach(function(field) { + // Check all rule types for fallback rules + var ruleTypes = ['SkipLogic', 'CalculateLogic', 'ValidateLogic']; + + ruleTypes.forEach(function(ruleType) { + if (field[ruleType] && field[ruleType].Rules) { + field[ruleType].Rules.forEach(function(rule) { + if (rule.FallbackRule && rule.FallbackRule.Action) { + expect(validActions).to.include(rule.FallbackRule.Action); + } + }); + } + }); + }); + } + }); + } + }); +} diff --git a/bruno/form-service/Form template/Preview Form Template.bru b/bruno/form-service/Form template/Preview Form Template.bru new file mode 100644 index 0000000..5f6dac8 --- /dev/null +++ b/bruno/form-service/Form template/Preview Form Template.bru @@ -0,0 +1,17 @@ +meta { + name: Preview Form Template + type: http + seq: 9 +} + +get { + url: + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} diff --git a/form-service/Form template/Update form template record.bru b/bruno/form-service/Form template/Update form template record.bru similarity index 89% rename from form-service/Form template/Update form template record.bru rename to bruno/form-service/Form template/Update form template record.bru index 045f005..41be1dc 100644 --- a/form-service/Form template/Update form template record.bru +++ b/bruno/form-service/Form template/Update form template record.bru @@ -10,6 +10,12 @@ put { auth: none } +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + body:json { { "Title": "Work Load Capacity of company", @@ -18,13 +24,14 @@ body:json { "Type": "Questionnaire", "DisplayCode": "xyz1234asssssdfghj", "OwnerUserId": "16377833-8e6f-41b4-944a-98a91815a4d5", - "RootSectionId": "d04ca675-d4eb-4c6a-9820-6ba1cce3e625", + // "RootSectionId": "d04ca675-d4eb-4c6a-9820-6ba1cce3e625", "DefaultSectionNumbering": true, + "IsFavourite": false, "ItemsPerPage":"OneQuestion" } } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/form-service/Form template/create a form template.bru b/bruno/form-service/Form template/create a form template.bru similarity index 73% rename from form-service/Form template/create a form template.bru rename to bruno/form-service/Form template/create a form template.bru index a310fba..3f87fd8 100644 --- a/form-service/Form template/create a form template.bru +++ b/bruno/form-service/Form template/create a form template.bru @@ -10,21 +10,27 @@ post { auth: none } +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + body:json { { - "Title": "Assessment First", + "Title": "Test 1", "Description": "This is description", "CurrentVersion": 1, "Type": "Questionnaire", - // "DisplayCode": "xyz1234", - "OwnerUserId": "16377833-8e6f-41b4-944a-98a91815a4d5", - // "RootSectionId": "9618c6a8-0555-4a14-95ec-1946ec09c8e0", - "DefaultSectionNumbering": true, - "ItemsPerPage":"OneQuestion" + "OwnerUserId": "{{USER_ID}}", + // "RootSectionId": "{{SECTION_ID}}", + "DefaultSectionNumbering": false, + "IsFavourite": true, + "ItemsPerPage": "OneQuestion" } } -tests { +script:post-response { try { var jsonRes = res.getBody(); bru.setEnvVar("TEMPLATE_ID", jsonRes.Data.id); @@ -50,6 +56,8 @@ tests { expect(jsonRes.Data).to.have.property('DisplayCode'); expect(jsonRes.Data).to.have.property('OwnerUserId'); expect(jsonRes.Data).to.have.property('RootSectionId'); - expect(jsonRes.Data).to.have.property('DefaultSectionNumbering'); + expect(jsonRes.Data).to.have.property('DefaultSectionNumbering'); + expect(jsonRes.Data).to.have.property('IsFavourite'); + expect(jsonRes.Data.IsFavourite).to.equal(true); }); } diff --git a/bruno/form-service/Form template/folder.bru b/bruno/form-service/Form template/folder.bru new file mode 100644 index 0000000..5944c4e --- /dev/null +++ b/bruno/form-service/Form template/folder.bru @@ -0,0 +1,8 @@ +meta { + name: Form template + seq: 3 +} + +auth { + mode: none +} diff --git a/form-service/Health Check Request.bru b/bruno/form-service/Health Check Request.bru similarity index 62% rename from form-service/Health Check Request.bru rename to bruno/form-service/Health Check Request.bru index c6b71ee..a7a28f5 100644 --- a/form-service/Health Check Request.bru +++ b/bruno/form-service/Health Check Request.bru @@ -1,11 +1,11 @@ meta { name: Health Check Request type: http - seq: 1 + seq: 16 } get { - url: {{BASE_URL}} + url: http://localhost:5555/health-check body: none auth: none } diff --git a/bruno/form-service/Input Unit List/Create input unit list.bru b/bruno/form-service/Input Unit List/Create input unit list.bru new file mode 100644 index 0000000..1ad92d9 --- /dev/null +++ b/bruno/form-service/Input Unit List/Create input unit list.bru @@ -0,0 +1,83 @@ +meta { + name: Create Input Unit List + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/input-unit-lists + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Temperature Units", + "Description": "Common temperature measurement units", + // "DisplayCode": "TEMP_UNITS", + // "IsActive": true, + "Units": [ + { + "Name": "Celsius", + "Symbol": "°C", + "Description": "Celsius temperature scale", + "ConversionFactor": 1.0, + "IsDefault": true + }, + { + "Name": "Fahrenheit", + "Symbol": "°F", + "Description": "Fahrenheit temperature scale", + "ConversionFactor": 1.8, + "IsDefault": false + }, + { + "Name": "Kelvin", + "Symbol": "K", + "Description": "Kelvin temperature scale", + "ConversionFactor": 1.0, + "IsDefault": false + } + ] + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("INPUT_UNIT_LIST_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Input Unit List is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Temperature Units'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Common temperature measurement units'); + expect(jsonRes.Data).to.have.property('DisplayCode'); + expect(jsonRes.Data.DisplayCode).equals('TEMP_UNITS'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('Units'); + expect(jsonRes.Data.Units).to.be.an('array'); + expect(jsonRes.Data.Units.length).to.equal(3); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Input Unit List/Delete input unit list.bru b/bruno/form-service/Input Unit List/Delete input unit list.bru new file mode 100644 index 0000000..f73a482 --- /dev/null +++ b/bruno/form-service/Input Unit List/Delete input unit list.bru @@ -0,0 +1,30 @@ +meta { + name: Delete Input Unit List + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/input-unit-lists/{{INPUT_UNIT_LIST_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Input Unit List is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Input Unit List/Get input unit list by id.bru b/bruno/form-service/Input Unit List/Get input unit list by id.bru new file mode 100644 index 0000000..e91363a --- /dev/null +++ b/bruno/form-service/Input Unit List/Get input unit list by id.bru @@ -0,0 +1,38 @@ +meta { + name: Get Input Unit List by ID + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/input-unit-lists/{{INPUT_UNIT_LIST_ID}} + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Input Unit List has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{INPUT_UNIT_LIST_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('DisplayCode'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data).to.have.property('Units'); + expect(jsonRes.Data.Units).to.be.an('array'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Input Unit List/Search input unit lists.bru b/bruno/form-service/Input Unit List/Search input unit lists.bru new file mode 100644 index 0000000..720e97b --- /dev/null +++ b/bruno/form-service/Input Unit List/Search input unit lists.bru @@ -0,0 +1,62 @@ +meta { + name: Search Input Unit Lists + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/input-unit-lists/search + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~Name: Temperature + ~IsActive: true +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Input Unit Lists search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Input Unit Lists have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('Name'); + expect(item).to.have.property('Description'); + expect(item).to.have.property('DisplayCode'); + expect(item).to.have.property('IsActive'); + expect(item).to.have.property('Units'); + expect(item.Units).to.be.an('array'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Input Unit List/Update input unit list.bru b/bruno/form-service/Input Unit List/Update input unit list.bru new file mode 100644 index 0000000..8f223a6 --- /dev/null +++ b/bruno/form-service/Input Unit List/Update input unit list.bru @@ -0,0 +1,83 @@ +meta { + name: Update Input Unit List + type: http + seq: 2 +} + +put { + url: {{BASE_URL}}/input-unit-lists/{{INPUT_UNIT_LIST_ID}} + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Updated Temperature Units", + "Description": "Updated common temperature measurement units", + // "DisplayCode": "TEMP_UNITS_V2", + // "IsActive": true, + "Units": [ + { + "Name": "Celsius", + "Symbol": "°C", + "Description": "Celsius temperature scale", + "ConversionFactor": 1.0, + "IsDefault": true + }, + { + "Name": "Fahrenheit", + "Symbol": "°F", + "Description": "Fahrenheit temperature scale", + "ConversionFactor": 1.8, + "IsDefault": false + }, + { + "Name": "Kelvin", + "Symbol": "K", + "Description": "Kelvin temperature scale", + "ConversionFactor": 1.0, + "IsDefault": false + }, + { + "Name": "Rankine", + "Symbol": "°R", + "Description": "Rankine temperature scale", + "ConversionFactor": 1.8, + "IsDefault": false + } + ] + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Input Unit List is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{INPUT_UNIT_LIST_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Temperature Units'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated common temperature measurement units'); + expect(jsonRes.Data).to.have.property('DisplayCode'); + expect(jsonRes.Data.DisplayCode).equals('TEMP_UNITS_V2'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('Units'); + expect(jsonRes.Data.Units).to.be.an('array'); + expect(jsonRes.Data.Units.length).to.equal(4); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Input Unit List/folder.bru b/bruno/form-service/Input Unit List/folder.bru new file mode 100644 index 0000000..da41f7b --- /dev/null +++ b/bruno/form-service/Input Unit List/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Input Unit List + seq: 11 +} diff --git a/form-service/Question Response/Create a new response.bru b/bruno/form-service/Question Response/Create a new response.bru similarity index 75% rename from form-service/Question Response/Create a new response.bru rename to bruno/form-service/Question Response/Create a new response.bru index 6f8aa20..76214ab 100644 --- a/form-service/Question Response/Create a new response.bru +++ b/bruno/form-service/Question Response/Create a new response.bru @@ -5,26 +5,31 @@ meta { } post { - url: {{BASE_URL}}/question-responses/save + url: {{BASE_URL}}/question-responses body: json auth: none } +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + body:json { - // { - // "FormSubmissionId": "2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6", - // "QuestionId": "1ce7590c-b37d-4668-b324-d20f0a86c923", - // "ResponseType": "Text", - // "IntegerValue": 1, - // "FloatValue": 1.2, - // "BooleanValue": true, - // "DateTimeValue": "2024-03-08 18:25:30.843", - // "Url": "https://meet.google.com/zrb-tyni-rdz", - // "FileResourceId": "0c9ea637-cb8d-468e-b4c7-303d2604c1b5", - // "TextValue": "hjsj1234-erhh233", - // "SubmissionTimestamp": "2024-03-08 18:25:30.843", - // "LastSaveTimestamp": "2024-03-08 18:25:30.843" - // } + { + "FormSubmissionId": "{{SUBMISSION_ID}}", + "QuestionId": "{{QUESTION_ID}}", + "ResponseType": "Text", + "IntegerValue": 25, + "FloatValue": 3.14, + "BooleanValue": true, + "DateTimeValue": "2024-03-08T18:25:30.843Z", + "Url": "https://example.com/resource", + "FileResourceId": "{{FILE_RESOURCE_ID}}", + "TextValue": "This is a sample text response", + "UserResponse": "Yes" + } // { // "FormSubmissionId": "2ed8ebf0-bfac-462c-9012-9a3ab5f59ba6", // "QuestionId": "1ce7590c-b37d-4668-b324-d20f0a86c923", @@ -93,19 +98,19 @@ body:json { // "FormSubmissionId": "a4728261-1b9a-47a4-aabe-7a3437429639" // } - { - "Data": { - "2255a13c-f4a3-4bcf-9125-e947adc262af": "Answer of first question", - // "f242a2f0-ab75-4423-bb3d-b08f496a66b9": 123456, - // "39b788f6-fbb2-4b77-bf38-d9c2b779bb26": 6, - // "d397554c-3ac5-4a25-8824-92bffb33e644": ["Prashant", "Pandurang", "Kharade"], - "02f21e5d-787f-409b-8456-f2ed691bc2b1": "true" - }, - "FormSubmissionId": "8df8d6a5-5f35-46d8-8cae-4838a7a43d41" - } + // { + // "Data": { + // "2255a13c-f4a3-4bcf-9125-e947adc262af": "Answer of first question", + // // "f242a2f0-ab75-4423-bb3d-b08f496a66b9": 123456, + // // "39b788f6-fbb2-4b77-bf38-d9c2b779bb26": 6, + // // "d397554c-3ac5-4a25-8824-92bffb33e644": ["Prashant", "Pandurang", "Kharade"], + // "02f21e5d-787f-409b-8456-f2ed691bc2b1": "true" + // }, + // "FormSubmissionId": "19dd2f7e-3001-432d-99b8-93d0495bc542" + // } } -tests { +script:post-response { try { var jsonRes = res.getBody(); bru.setEnvVar("RESPONSE_ID", jsonRes.Data.id); diff --git a/form-service/Question Response/Delete a response.bru b/bruno/form-service/Question Response/Delete a response.bru similarity index 76% rename from form-service/Question Response/Delete a response.bru rename to bruno/form-service/Question Response/Delete a response.bru index fbbb394..61e67da 100644 --- a/form-service/Question Response/Delete a response.bru +++ b/bruno/form-service/Question Response/Delete a response.bru @@ -10,7 +10,13 @@ delete { auth: none } -tests { +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/form-service/Question Response/Generic search.bru b/bruno/form-service/Question Response/Generic search.bru similarity index 55% rename from form-service/Question Response/Generic search.bru rename to bruno/form-service/Question Response/Generic search.bru index d762c0a..0976978 100644 --- a/form-service/Question Response/Generic search.bru +++ b/bruno/form-service/Question Response/Generic search.bru @@ -5,18 +5,28 @@ meta { } get { - url: {{BASE_URL}}/question-responses/search?responseType=Text + url: {{BASE_URL}}/question-responses/search?formSubmissionId=04e0865b-d0d9-4150-ac83-b3eb6f8ad4cd body: none auth: none } params:query { - responseType: Text - ~formSubmissionId: a8b15839-1592-43b7-b586-89765cf99222 + formSubmissionId: 04e0865b-d0d9-4150-ac83-b3eb6f8ad4cd + ~pageIndex: 0 + ~itemsPerPage: 10 + ~orderBy: CreatedAt + ~order: DESC + ~responseType: Text ~questionId: b56027ee-bdba-4182-bed6-d544a5bcc8e4 } -tests { +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/Question Response/Get response by id.bru b/bruno/form-service/Question Response/Get response by id.bru new file mode 100644 index 0000000..51de4cb --- /dev/null +++ b/bruno/form-service/Question Response/Get response by id.bru @@ -0,0 +1,17 @@ +meta { + name: Get response by id + type: http + seq: 2 +} + +get { + url: {{BASE_URL}}/question-responses/{{RESPONSE_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} diff --git a/bruno/form-service/Question Response/Save Question Response.bru b/bruno/form-service/Question Response/Save Question Response.bru new file mode 100644 index 0000000..1876446 --- /dev/null +++ b/bruno/form-service/Question Response/Save Question Response.bru @@ -0,0 +1,37 @@ +meta { + name: Save Question Response + type: http + seq: 8 +} + +post { + url: {{BASE_URL}}/question-responses/save + body: text + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:text { + { + "QuestionResponses": [{ + "id": "a9ee5e15-cb9a-4989-8709-293c33563045", + "FormSubmissionId": "{{SUBMISSION_ID}}", + "QuestionId": "{{QUESTION_ID}}", + "ResponseType": "Text", + "IntegerValue": 1, + "FloatValue": 1.2, + "BooleanValue": "1", + "DateTimeValue": "2024-03-08T12:55:31.000Z", + "Url": "https://meet.google.com/zrb-tyni-rdz", + "FileResourceId": "0c9ea637-cb8d-468e-b4c7-303d2604c1b5", + "SubmissionTimestamp": null, + "LastSaveTimestamp": "2025-06-21T11:43:57.000Z" + }], + "FormSubmissionKey": "{{SUBMISSION_KEY}}" + } +} diff --git a/bruno/form-service/Question Response/Save the responses.bru b/bruno/form-service/Question Response/Save the responses.bru new file mode 100644 index 0000000..4e7a535 --- /dev/null +++ b/bruno/form-service/Question Response/Save the responses.bru @@ -0,0 +1,90 @@ +meta { + name: Save the responses + type: http + seq: 7 +} + +post { + url: {{BASE_URL}}/question-responses/save + body: json + auth: inherit +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "FormSubmissionKey": "f0854f9dd3b095a0a2716b0ab2feda6935e4d4a1d341b6b9bfc36dfa6dde6931", + "QuestionResponses": [ + { + "id": null, + "FormSubmissionId": "d6e7e2f2-a337-46de-b9e5-57a0214ad679", + "ResponseType": "SingleChoiceSelection", + "QuestionId": "12e89baa-b3be-4ecd-bbeb-4f8e64d75c62", + "IntegerValue": null, + "FloatValue": null, + "BooleanValue": null, + "DateTimeValue": null, + "Url": null, + "TextValue": "Blue", + "FileResourceId": null + }, + { + "id": null, + "FormSubmissionId": "d6e7e2f2-a337-46de-b9e5-57a0214ad679", + "ResponseType": "Integer", + "QuestionId": "4e68c332-e741-491d-9faa-9b294fee4daf", + "IntegerValue": 2, + "FloatValue": null, + "BooleanValue": null, + "DateTimeValue": null, + "Url": null, + "TextValue": null, + "FileResourceId": null + }, + { + "id": null, + "FormSubmissionId": "d6e7e2f2-a337-46de-b9e5-57a0214ad679", + "ResponseType": "Text", + "QuestionId": "811734b5-2ab9-45a6-a248-36cc46b29ad6", + "IntegerValue": null, + "FloatValue": null, + "BooleanValue": null, + "DateTimeValue": null, + "Url": null, + "TextValue": "prashant kharade", + "FileResourceId": null + }, + { + "id": null, + "FormSubmissionId": "d6e7e2f2-a337-46de-b9e5-57a0214ad679", + "ResponseType": "MultiChoiceSelection", + "QuestionId": "347de100-ba70-4a96-b3dc-b181224ebb49", + "IntegerValue": null, + "FloatValue": null, + "BooleanValue": null, + "DateTimeValue": null, + "Url": null, + "TextValue": "[\"one\",\"two\"]", + "FileResourceId": null + }, + { + "id": null, + "FormSubmissionId": "d6e7e2f2-a337-46de-b9e5-57a0214ad679", + "ResponseType": "SingleChoiceSelection", + "QuestionId": "bd1a658a-195d-4c63-b48b-06137d5bc91c", + "IntegerValue": null, + "FloatValue": null, + "BooleanValue": null, + "DateTimeValue": null, + "Url": null, + "TextValue": "one", + "FileResourceId": null + } + ] + } +} diff --git a/form-service/Question Response/Update a existing response.bru b/bruno/form-service/Question Response/Update a existing response.bru similarity index 94% rename from form-service/Question Response/Update a existing response.bru rename to bruno/form-service/Question Response/Update a existing response.bru index f40070d..0b29d71 100644 --- a/form-service/Question Response/Update a existing response.bru +++ b/bruno/form-service/Question Response/Update a existing response.bru @@ -10,6 +10,12 @@ put { auth: none } +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + body:json { { "ResponseType": "Boolean", @@ -23,7 +29,7 @@ body:json { } } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/Question Response/folder.bru b/bruno/form-service/Question Response/folder.bru new file mode 100644 index 0000000..201aed3 --- /dev/null +++ b/bruno/form-service/Question Response/folder.bru @@ -0,0 +1,8 @@ +meta { + name: Question Response + seq: 9 +} + +auth { + mode: none +} diff --git a/bruno/form-service/Template Folder/Create template folder.bru b/bruno/form-service/Template Folder/Create template folder.bru new file mode 100644 index 0000000..38fe9ec --- /dev/null +++ b/bruno/form-service/Template Folder/Create template folder.bru @@ -0,0 +1,58 @@ +meta { + name: Create Template Folder + type: http + seq: 1 +} + +post { + url: {{BASE_URL}}/template-folders + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Survey Forms", + "Description": "Collection of survey form templates", + "ParentFolderId": "c544da4e-4fe5-4a37-aa22-d47cdb547ff7" + } +} + +script:post-response { + try { + var jsonRes = res.getBody(); + bru.setEnvVar("TEMPLATE_FOLDER_ID", jsonRes.Data.id); + } + catch (error) { + console.log(error.message); + } + + test("Request is successful", function () { + expect(res.getStatus()).to.equal(201); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Template Folder is created with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Survey Forms'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Collection of survey form templates'); + expect(jsonRes.Data).to.have.property('DisplayCode'); + expect(jsonRes.Data.DisplayCode).equals('SURVEY_FORMS'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('ParentFolderId'); + expect(jsonRes.Data.ParentFolderId).equals(null); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Template Folder/Delete template folder.bru b/bruno/form-service/Template Folder/Delete template folder.bru new file mode 100644 index 0000000..da03f4d --- /dev/null +++ b/bruno/form-service/Template Folder/Delete template folder.bru @@ -0,0 +1,30 @@ +meta { + name: Delete Template Folder + type: http + seq: 5 +} + +delete { + url: {{BASE_URL}}/template-folders/{{TEMPLATE_FOLDER_ID}} + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Template Folder is deleted successfully", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.equal(true); + }); +} diff --git a/bruno/form-service/Template Folder/Get template folder by id.bru b/bruno/form-service/Template Folder/Get template folder by id.bru new file mode 100644 index 0000000..769874f --- /dev/null +++ b/bruno/form-service/Template Folder/Get template folder by id.bru @@ -0,0 +1,37 @@ +meta { + name: Get Template Folder by ID + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/template-folders/{{TEMPLATE_FOLDER_ID}} + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Template Folder has correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{TEMPLATE_FOLDER_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data).to.have.property('DisplayCode'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data).to.have.property('ParentFolderId'); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} \ No newline at end of file diff --git a/bruno/form-service/Template Folder/Search template folders.bru b/bruno/form-service/Template Folder/Search template folders.bru new file mode 100644 index 0000000..5b6de66 --- /dev/null +++ b/bruno/form-service/Template Folder/Search template folders.bru @@ -0,0 +1,61 @@ +meta { + name: Search Template Folders + type: http + seq: 4 +} + +get { + url: {{BASE_URL}}/template-folders/search?pageIndex=0 + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +params:query { + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + ~Name: Survey + ~IsActive: true +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Template Folders search returns correct structure", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('TotalCount'); + expect(jsonRes.Data).to.have.property('RetrievedCount'); + expect(jsonRes.Data).to.have.property('PageIndex'); + expect(jsonRes.Data).to.have.property('ItemsPerPage'); + expect(jsonRes.Data).to.have.property('Order'); + expect(jsonRes.Data).to.have.property('OrderedBy'); + expect(jsonRes.Data).to.have.property('Items'); + expect(jsonRes.Data.Items).to.be.an('array'); + }); + + test("Template Folders have correct properties", function () { + var jsonRes = res.getBody(); + if (jsonRes.Data.Items.length > 0) { + var item = jsonRes.Data.Items[0]; + expect(item).to.have.property('id'); + expect(item).to.have.property('Name'); + expect(item).to.have.property('Description'); + expect(item).to.have.property('DisplayCode'); + expect(item).to.have.property('IsActive'); + expect(item).to.have.property('ParentFolderId'); + expect(item).to.have.property('CreatedAt'); + expect(item).to.have.property('UpdatedAt'); + } + }); +} diff --git a/bruno/form-service/Template Folder/Update template folder.bru b/bruno/form-service/Template Folder/Update template folder.bru new file mode 100644 index 0000000..d7d6f7e --- /dev/null +++ b/bruno/form-service/Template Folder/Update template folder.bru @@ -0,0 +1,51 @@ +meta { + name: Update Template Folder + type: http + seq: 2 +} + +put { + url: {{BASE_URL}}/template-folders/{{TEMPLATE_FOLDER_ID}} + body: json + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +body:json { + { + "Name": "Updated Survey Forms", + "Description": "Updated collection of survey form templates", + "ParentFolderId": "c544da4e-4fe5-4a37-aa22-d47cdb547ff7" + } +} + +script:post-response { + test("Request is successful", function () { + expect(res.getStatus()).to.equal(200); + var jsonRes = res.getBody(); + expect(jsonRes.Status).to.eql('Success'); + }); + + test("Template Folder is updated with correct properties", function () { + var jsonRes = res.getBody(); + expect(jsonRes.Data).to.have.property('id'); + expect(jsonRes.Data.id).equals('{{TEMPLATE_FOLDER_ID}}'); + expect(jsonRes.Data).to.have.property('Name'); + expect(jsonRes.Data.Name).equals('Updated Survey Forms'); + expect(jsonRes.Data).to.have.property('Description'); + expect(jsonRes.Data.Description).equals('Updated collection of survey form templates'); + expect(jsonRes.Data).to.have.property('DisplayCode'); + expect(jsonRes.Data.DisplayCode).equals('SURVEY_FORMS_V2'); + expect(jsonRes.Data).to.have.property('IsActive'); + expect(jsonRes.Data.IsActive).equals(true); + expect(jsonRes.Data).to.have.property('ParentFolderId'); + expect(jsonRes.Data.ParentFolderId).equals(null); + expect(jsonRes.Data).to.have.property('CreatedAt'); + expect(jsonRes.Data).to.have.property('UpdatedAt'); + }); +} diff --git a/bruno/form-service/Template Folder/folder.bru b/bruno/form-service/Template Folder/folder.bru new file mode 100644 index 0000000..b6c0e5c --- /dev/null +++ b/bruno/form-service/Template Folder/folder.bru @@ -0,0 +1,4 @@ +meta { + name: Template Folder + seq: 12 +} diff --git a/form-service/User Login Session/Create a new session.bru b/bruno/form-service/User Login Session/Create a new session.bru similarity index 72% rename from form-service/User Login Session/Create a new session.bru rename to bruno/form-service/User Login Session/Create a new session.bru index fbfdce1..1dd6ad3 100644 --- a/form-service/User Login Session/Create a new session.bru +++ b/bruno/form-service/User Login Session/Create a new session.bru @@ -10,6 +10,12 @@ post { auth: none } +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + body:json { { "UserId": "b2e2baf0-fc4f-47ec-a01a-20bcd18c2c9b", diff --git a/form-service/User Login Session/Delete a existing session.bru b/bruno/form-service/User Login Session/Delete a existing session.bru similarity index 60% rename from form-service/User Login Session/Delete a existing session.bru rename to bruno/form-service/User Login Session/Delete a existing session.bru index 428b411..d4fdbf1 100644 --- a/form-service/User Login Session/Delete a existing session.bru +++ b/bruno/form-service/User Login Session/Delete a existing session.bru @@ -9,3 +9,9 @@ delete { body: none auth: none } + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} diff --git a/form-service/User Login Session/Get a login session by id.bru b/bruno/form-service/User Login Session/Get a login session by id.bru similarity index 60% rename from form-service/User Login Session/Get a login session by id.bru rename to bruno/form-service/User Login Session/Get a login session by id.bru index 82e79e8..ceaa157 100644 --- a/form-service/User Login Session/Get a login session by id.bru +++ b/bruno/form-service/User Login Session/Get a login session by id.bru @@ -9,3 +9,9 @@ get { body: none auth: none } + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} diff --git a/form-service/User Login Session/Update a session.bru b/bruno/form-service/User Login Session/Update a session.bru similarity index 73% rename from form-service/User Login Session/Update a session.bru rename to bruno/form-service/User Login Session/Update a session.bru index 2c1c5ac..bdf10d4 100644 --- a/form-service/User Login Session/Update a session.bru +++ b/bruno/form-service/User Login Session/Update a session.bru @@ -10,6 +10,12 @@ put { auth: none } +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + body:json { { "UserId": "05b04a84-ce2d-4c5a-9528-4cc168e0ad0a", diff --git a/bruno/form-service/User Login Session/folder.bru b/bruno/form-service/User Login Session/folder.bru new file mode 100644 index 0000000..e851d1e --- /dev/null +++ b/bruno/form-service/User Login Session/folder.bru @@ -0,0 +1,8 @@ +meta { + name: User Login Session + seq: 15 +} + +auth { + mode: none +} diff --git a/form-service/User/Delete user.bru b/bruno/form-service/User/Delete user.bru similarity index 75% rename from form-service/User/Delete user.bru rename to bruno/form-service/User/Delete user.bru index 9cd0680..975df52 100644 --- a/form-service/User/Delete user.bru +++ b/bruno/form-service/User/Delete user.bru @@ -10,7 +10,13 @@ delete { auth: none } -tests { +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/form-service/Form section/Generic search.bru b/bruno/form-service/User/Generic search.bru similarity index 57% rename from form-service/Form section/Generic search.bru rename to bruno/form-service/User/Generic search.bru index 65e3db5..e2633ff 100644 --- a/form-service/Form section/Generic search.bru +++ b/bruno/form-service/User/Generic search.bru @@ -5,16 +5,27 @@ meta { } get { - url: {{BASE_URL}}/form-sections/search?parentFormTemplateId=51688651-458d-4b8f-922e-28ce072cfa15 + url: {{BASE_URL}}/users/search?username="Username"&password="Password" body: none auth: none } +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + params:query { - parentFormTemplateId: 51688651-458d-4b8f-922e-28ce072cfa15 + pageIndex: 0 + itemsPerPage: 10 + orderBy: CreatedAt + order: DESC + username: "Username" + password: "Password" } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/User/Get All Users.bru b/bruno/form-service/User/Get All Users.bru new file mode 100644 index 0000000..e0e39a6 --- /dev/null +++ b/bruno/form-service/User/Get All Users.bru @@ -0,0 +1,17 @@ +meta { + name: Get All Users + type: http + seq: 6 +} + +get { + url: {{BASE_URL}}/users/all + body: none + auth: none +} + +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} diff --git a/form-service/User/Get user by id.bru b/bruno/form-service/User/Get user by id.bru similarity index 85% rename from form-service/User/Get user by id.bru rename to bruno/form-service/User/Get user by id.bru index fa325a6..17ace57 100644 --- a/form-service/User/Get user by id.bru +++ b/bruno/form-service/User/Get user by id.bru @@ -10,7 +10,13 @@ get { auth: none } -tests { +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/form-service/User/New user.bru b/bruno/form-service/User/New user.bru similarity index 89% rename from form-service/User/New user.bru rename to bruno/form-service/User/New user.bru index 04db521..1ceb043 100644 --- a/form-service/User/New user.bru +++ b/bruno/form-service/User/New user.bru @@ -10,6 +10,12 @@ post { auth: none } +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + body:json { { "FirstName": "FirstName", @@ -22,7 +28,7 @@ body:json { } } -tests { +script:post-response { try { var jsonRes = res.getBody(); bru.setEnvVar("USER_ID", jsonRes.Data.id); diff --git a/form-service/User/Update user record.bru b/bruno/form-service/User/Update user record.bru similarity index 91% rename from form-service/User/Update user record.bru rename to bruno/form-service/User/Update user record.bru index 8f34539..811fd7d 100644 --- a/form-service/User/Update user record.bru +++ b/bruno/form-service/User/Update user record.bru @@ -10,6 +10,12 @@ put { auth: none } +headers { + x-api-key: {{INTERNAL_API_KEY}} + Authorization: Bearer {{JWT_TOKEN}} + Content-Type: application/json +} + body:json { { "FirstName": "FirstName", @@ -22,7 +28,7 @@ body:json { } } -tests { +script:post-response { test("Request is successfull", function () { expect(res.getStatus()).to.equal(200); var jsonRes = res.getBody(); diff --git a/bruno/form-service/User/folder.bru b/bruno/form-service/User/folder.bru new file mode 100644 index 0000000..b8e707b --- /dev/null +++ b/bruno/form-service/User/folder.bru @@ -0,0 +1,8 @@ +meta { + name: User + seq: 1 +} + +auth { + mode: none +} diff --git a/form-service/bruno.json b/bruno/form-service/bruno.json similarity index 100% rename from form-service/bruno.json rename to bruno/form-service/bruno.json diff --git a/bruno/form-service/check app.bru b/bruno/form-service/check app.bru new file mode 100644 index 0000000..79e001b --- /dev/null +++ b/bruno/form-service/check app.bru @@ -0,0 +1,11 @@ +meta { + name: check app + type: http + seq: 17 +} + +get { + url: {{BASE_URL}} + body: none + auth: inherit +} diff --git a/bruno/form-service/check docs.bru b/bruno/form-service/check docs.bru new file mode 100644 index 0000000..bd8a7a6 --- /dev/null +++ b/bruno/form-service/check docs.bru @@ -0,0 +1,11 @@ +meta { + name: check docs + type: http + seq: 18 +} + +get { + url: http://localhost:5555/api/docs + body: none + auth: inherit +} diff --git a/bruno/form-service/collection.bru b/bruno/form-service/collection.bru new file mode 100644 index 0000000..412e8c0 --- /dev/null +++ b/bruno/form-service/collection.bru @@ -0,0 +1,8 @@ +meta { + name: form-service + seq: 1 +} + +auth { + mode: none +} diff --git a/bruno/form-service/environments/forms-service.bru b/bruno/form-service/environments/forms-service.bru new file mode 100644 index 0000000..c07a49a --- /dev/null +++ b/bruno/form-service/environments/forms-service.bru @@ -0,0 +1,21 @@ +vars { + BASE_URL: http://localhost:5555/api/v1 + USER_ID: 9f3de126-dde8-4511-b17c-e5c16ca957e8 + TEMPLATE_ID: 00b7484e-584b-4488-a655-6eb63dfdbb1d + SECTION_ID: 34c8e7ee-3b97-4c9e-9c70-26fdcf01e64a + QUESTION_ID: + FORM_FIELD_ID: c88c6869-2ee7-4888-8b5e-3002ed9ec24b + SUBMISSION_ID: + RESPONSE_ID: + PARENT_SECTION_ID: 8def9dfd-e4fa-4225-bc9b-41225daf309d + SUBMISSION_KEY: + INTERNAL_API_KEY: T26BP24-MRGMRYE-JB90S0V-NC93PY0 + API_CLIENT_INTERNAL_KEY: T26BP24-MRGMRYE-JB90S0V-NC93PY0 + JWT_TOKEN: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOiJkOWI2ZTllNS02ODQxLTQ5ZTQtOGIyMi0wYmZlYzM0MWQxNTMiLCJUZW5hbnRJZCI6ImZiN2Y1ZTU4LTljYWUtNDRlMi1hNmNlLWY1MTE2MDU1MjkwNiIsIlRlbmFudENvZGUiOiJkZWZhdWx0IiwiVGVuYW50TmFtZSI6ImRlZmF1bHQiLCJEaXNwbGF5TmFtZSI6IlN5c3RlbSBhZG1pbiIsIlBob25lQ29kZSI6Iis5MSIsIlBob25lTnVtYmVyIjoiMDAwMDAwMDAwMCIsIkVtYWlsIjoic3lzLmFkbWluQGluZmxlY3Rpb256b25lLmNvbSIsIlVzZXJOYW1lIjoiYWRtaW4iLCJSb2xlcyI6W3siaWQiOiJkOGM2YjA4ZC02ZDAwLTQzYjMtOWZiMi1kY2M5OTNlZDJiNmIiLCJOYW1lIjoiU3lzdGVtIGFkbWluIn1dLCJTZXNzaW9uSWQiOiJlYTAzZDg3MS1jZDA3LTQ1OWItOWM4MC0xMDVjYjliNjYzNmMiLCJJc1Rlc3RVc2VyIjpmYWxzZSwiaWF0IjoxNzUzMDc1NzY2LCJleHAiOjE3NTMxODM3NjZ9.OYoH8v5qtZ6qVaLB2MHf9RasOiRkM39Fb4S_kdSEtUQ + FIELD_ID: fa9abe1b-7c5d-42a0-b7bc-4ec61bc2d452 + AGE_FIELD_ID: 81bdaaef-f6f2-4fe8-81f5-c29cdd56876c + BASE_AGE_FIELD_ID: c88c6869-2ee7-4888-8b5e-3002ed9ec24b + VALIDATION_LOGIC_ID: bf348643-c23d-499a-afd3-617782d06e21 + LOGICAL_OPERATION_ID: aa48f853-acdc-4fbf-be44-a73ef7c8016f + FALLBACK_RULE_ID: +} diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000..d794dbe --- /dev/null +++ b/docs/.nojekyll @@ -0,0 +1 @@ +# This file tells GitHub Pages not to process this directory with Jekyll \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..2f02566 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,187 @@ +# Form Service Documentation + +> A comprehensive Node.js microservice for dynamic form building, management, and processing with advanced skip logic and conditional field operations. + +
+ Latest Version: 1.0.0
+ Node.js Version: 18+
+ Database: PostgreSQL, MySQL, SQLite
+ License: MIT +
+ +## 🚀 Quick Start + +Get up and running with Form Service in minutes: + +```bash +# Clone the repository +git clone https://github.com/your-org/form-service.git +cd form-service + +# Install dependencies +npm install + +# Configure environment +cp config.local.json.example config.local.json +# Edit config.local.json with your settings + +# Start the service +npm start +``` + +## 📚 Viewing Documentation + +### Option 1: Through Form Service (Recommended) +```bash +# Start your form service +npm run dev + +# Then open in browser: +# http://localhost:3000/api/docs +``` + +### Option 2: Standalone Documentation Server +```bash +# Start documentation server only +npm run docs + +# Then open in browser: +# http://localhost:8080 +``` + +### Option 3: Windows Users +```bash +# Double-click the batch file +serve-docs.bat +``` + +## ✨ Key Features + +- **Dynamic Form Builder**: Create complex forms with conditional logic +- **Skip Logic Engine**: Advanced field visibility and skip conditions +- **Validation Rules**: Comprehensive field validation with custom rules +- **Calculation Logic**: Mathematical and logical operations on form fields +- **Field Operations**: Composition, iteration, and function expressions +- **RESTful API**: Complete CRUD operations for all entities +- **Multi-database Support**: PostgreSQL, MySQL, and SQLite +- **TypeScript**: Full type safety and IntelliSense support + +## 🏗️ Architecture Overview + +```mermaid +graph TB + A[Client Applications] --> B[Form Service API] + B --> C[Authentication Layer] + B --> D[Form Templates] + B --> E[Form Submissions] + B --> F[Field Logic Engine] + B --> G[Validation Engine] + B --> H[Calculation Engine] + + D --> I[Database Layer] + E --> I + F --> I + G --> I + H --> I + + I --> J[(PostgreSQL/MySQL/SQLite)] +``` + +## 📚 Documentation Sections + +### Getting Started +- [Quick Start Guide](about/quickstart.md) - Get up and running in 5 minutes +- [Installation](about/installation.md) - Detailed installation instructions +- [Configuration](about/configuration.md) - Environment and service configuration + +### API Reference +- [REST API Overview](api/overview.md) - Complete API documentation +- [Authentication](api/authentication.md) - Security and authentication +- [Form Templates](api/form-templates.md) - Form template management +- [Form Fields](api/form-fields.md) - Field configuration and types +- [Form Submissions](api/form-submissions.md) - Data collection and processing + +### Core Concepts +- [Skip Logic](guides/skip-logic.md) - Conditional field visibility +- [Validation Rules](guides/validation-rules.md) - Field validation +- [Calculation Logic](guides/calculation-logic.md) - Mathematical operations +- [Field Operations](guides/field-operations.md) - Advanced field manipulation + +### Examples +- [Basic Form Creation](examples/basic-form.md) - Simple form example +- [Conditional Fields](examples/conditional-fields.md) - Skip logic examples +- [Validation Examples](examples/validation-examples.md) - Validation scenarios +- [API Integration](examples/api-integration-examples.md) - Client integration + +## 🔧 API Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| `GET` | `/api/health` | Health check | +| `POST` | `/api/forms/templates` | Create form template | +| `GET` | `/api/forms/templates` | List form templates | +| `POST` | `/api/forms/submissions` | Submit form data | +| `GET` | `/api/forms/submissions` | Get form submissions | +| `POST` | `/api/fields/logic` | Create field logic | +| `POST` | `/api/fields/rules` | Create field rules | + +## 🛠️ Development + +### Prerequisites +- Node.js 18+ +- PostgreSQL/MySQL/SQLite +- npm or yarn + +### Local Development +```bash +# Install dependencies +npm install + +# Run in development mode +npm run dev + +# Run tests +npm test + +# Build for production +npm run build +``` + +### Environment Variables +```bash +# Database Configuration +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=form_service +DB_USER=postgres +DB_PASSWORD=password + +# Server Configuration +PORT=3000 +NODE_ENV=development + +# JWT Configuration +JWT_SECRET=your-secret-key +JWT_EXPIRES_IN=24h +``` + +## 🤝 Contributing + +We welcome contributions! Please see our [Contributing Guide](about/contributing.md) for details. + +## 📄 License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## 🆘 Support + +- **Documentation**: [docs.yourservice.com](https://docs.yourservice.com) +- **Issues**: [GitHub Issues](https://github.com/your-org/form-service/issues) +- **Discussions**: [GitHub Discussions](https://github.com/your-org/form-service/discussions) +- **Email**: support@yourservice.com + +--- + +
+ Note: This documentation is for Form Service v1.0.0. For older versions, please check the [changelog](about/changelog.md). +
\ No newline at end of file diff --git a/docs/_sidebar.md b/docs/_sidebar.md new file mode 100644 index 0000000..265278c --- /dev/null +++ b/docs/_sidebar.md @@ -0,0 +1,56 @@ + + +* [🏠 Home](/) + +## 📚 Getting Started +* [Quick Start](about/quickstart.md) +* [Installation](about/installation.md) +* [Configuration](about/configuration.md) + +## 🔧 API Reference +* [REST API Overview](api/overview.md) +* [Authentication](api/authentication.md) +* [Form Templates](api/form-templates.md) +* [Form Fields](api/form-fields.md) +* [Form Submissions](api/form-submissions.md) +* [Field Logic](api/field-logic.md) +* [Field Rules](api/field-rules.md) +* [Field Operations](api/field-operations.md) +* [Question Responses](api/question-responses.md) +* [User Management](api/user-management.md) + +## 🏗️ Architecture +* [System Overview](schema/system-overview.md) +* [Database Schema](schema/database-schema.md) +* [Domain Models](schema/domain-models.md) +* [Service Architecture](schema/service-architecture.md) + +## 📖 Guides +* [Form Builder Guide](guides/form-builder.md) +* [Skip Logic Implementation](guides/skip-logic.md) +* [Validation Rules](guides/validation-rules.md) +* [Calculation Logic](guides/calculation-logic.md) +* [Field Operations](guides/field-operations.md) +* [API Integration](guides/api-integration.md) +* [Testing](guides/testing.md) + +## 💡 Examples +* [Basic Form Creation](examples/basic-form.md) +* [Conditional Fields](examples/conditional-fields.md) +* [Skip Logic Examples](examples/skip-logic-examples.md) +* [Validation Examples](examples/validation-examples.md) +* [Calculation Examples](examples/calculation-examples.md) +* [API Integration Examples](examples/api-integration-examples.md) + +## 🔍 Advanced Topics +* [Performance Optimization](guides/performance.md) +* [Security Best Practices](guides/security.md) +* [Deployment](guides/deployment.md) +* [Troubleshooting](guides/troubleshooting.md) +* [Migration Guide](guides/migration.md) + +## 📋 Reference +* [Error Codes](api/error-codes.md) +* [Data Types](schema/data-types.md) +* [Glossary](about/glossary.md) +* [Changelog](about/changelog.md) \ No newline at end of file diff --git a/docs/about/configuration.md b/docs/about/configuration.md new file mode 100644 index 0000000..9f238b9 --- /dev/null +++ b/docs/about/configuration.md @@ -0,0 +1,578 @@ +# Configuration Guide + +> Complete configuration guide for Form Service + +## Overview + +Form Service uses a flexible configuration system that supports multiple environments and configuration sources. The configuration is loaded from JSON files and environment variables, with environment variables taking precedence. + +## Configuration Sources + +### 1. Configuration Files + +The service loads configuration from two main files: + +- **`config.json`** - Default configuration (version controlled) +- **`config.local.json`** - Local overrides (not version controlled) + +### 2. Environment Variables + +Environment variables override file-based configuration and are used for sensitive data like passwords and secrets. + +## Configuration Structure + +### System Configuration + +```json +{ + "SystemIdentifier": "Form Service", + "Auth": { + "Authentication": "Custom", + "Authorization": "Custom", + "UseRefreshToken": true, + "AccessTokenExpiresInSeconds": 2592000, + "RefreshTokenExpiresInSeconds": 2592000 + }, + "Database": { + "Type": "SQL", + "ORM": "TypeORM" + }, + "FileStorage": { + "Provider": "Custom" + }, + "Communication": { + "Email": { + "Provider": "Mock" + } + }, + "TemporaryFolders": { + "Upload": "./tmp/resources/uploads/", + "Download": "./tmp/resources/downloads/", + "CleanupFolderBeforeMinutes": 10 + } +} +``` + +### Environment Variables + +#### Database Configuration +```bash +# Database Type and ORM +DB_TYPE=postgres +DB_ORM=typeorm + +# Database Connection +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=form_service +DB_USER=postgres +DB_PASSWORD=your_secure_password +DB_DIALECT=postgres + +# Database Pool Settings +DB_POOL_MIN=5 +DB_POOL_MAX=20 +DB_POOL_ACQUIRE=30000 +DB_POOL_IDLE=10000 +``` + +#### Server Configuration +```bash +# Server Settings +PORT=3000 +HOST=localhost +NODE_ENV=development + +# CORS Settings +CORS_ORIGIN=http://localhost:3000 +CORS_METHODS=GET,POST,PUT,DELETE,PATCH +CORS_ALLOWED_HEADERS=Content-Type,Authorization + +# Rate Limiting +RATE_LIMIT_WINDOW=15m +RATE_LIMIT_MAX=100 +RATE_LIMIT_SKIP_SUCCESSFUL_REQUESTS=false +``` + +#### Authentication Configuration +```bash +# JWT Settings +JWT_SECRET=your-super-secret-jwt-key-here +JWT_EXPIRES_IN=24h +JWT_REFRESH_EXPIRES_IN=7d + +# Session Settings +SESSION_SECRET=your-session-secret +SESSION_MAX_AGE=86400000 +``` + +#### File Storage Configuration +```bash +# Storage Provider +STORAGE_PROVIDER=local +STORAGE_PATH=./uploads + +# AWS S3 (if using S3) +AWS_ACCESS_KEY_ID=your-access-key +AWS_SECRET_ACCESS_KEY=your-secret-key +AWS_REGION=us-east-1 +AWS_S3_BUCKET=your-bucket-name +``` + +#### Logging Configuration +```bash +# Logging Level +LOG_LEVEL=info +LOG_FILE=./logs/app.log +LOG_MAX_SIZE=10m +LOG_MAX_FILES=5 + +# Logging Format +LOG_FORMAT=json +LOG_COLORIZE=false +``` + +## Environment-Specific Configuration + +### Development Environment + +```json +{ + "SystemIdentifier": "Form Service - Development", + "Database": { + "Type": "SQL", + "ORM": "TypeORM" + }, + "FileStorage": { + "Provider": "Custom" + }, + "Communication": { + "Email": { + "Provider": "Mock" + } + }, + "Logging": { + "Level": "debug", + "File": "./logs/dev.log" + } +} +``` + +### Production Environment + +```json +{ + "SystemIdentifier": "Form Service - Production", + "Database": { + "Type": "SQL", + "ORM": "TypeORM" + }, + "FileStorage": { + "Provider": "AWS-S3" + }, + "Communication": { + "Email": { + "Provider": "SendGrid" + } + }, + "Logging": { + "Level": "warn", + "File": "./logs/prod.log" + } +} +``` + +## Database Configuration + +### PostgreSQL Configuration + +```json +{ + "Database": { + "Type": "SQL", + "ORM": "TypeORM", + "PostgreSQL": { + "Host": "localhost", + "Port": 5432, + "Database": "form_service", + "Username": "postgres", + "Password": "password", + "SSL": false, + "Pool": { + "Min": 5, + "Max": 20, + "Acquire": 30000, + "Idle": 10000 + } + } + } +} +``` + +### MySQL Configuration + +```json +{ + "Database": { + "Type": "SQL", + "ORM": "TypeORM", + "MySQL": { + "Host": "localhost", + "Port": 3306, + "Database": "form_service", + "Username": "root", + "Password": "password", + "Charset": "utf8mb4", + "Pool": { + "Min": 5, + "Max": 20, + "Acquire": 30000, + "Idle": 10000 + } + } + } +} +``` + +### SQLite Configuration + +```json +{ + "Database": { + "Type": "SQL", + "ORM": "TypeORM", + "SQLite": { + "Database": "./data/form_service.db", + "Logging": false + } + } +} +``` + +## File Storage Configuration + +### Local Storage + +```json +{ + "FileStorage": { + "Provider": "Custom", + "Local": { + "Path": "./uploads", + "MaxFileSize": "10MB", + "AllowedTypes": ["image/*", "application/pdf", "text/*"] + } + } +} +``` + +### AWS S3 Storage + +```json +{ + "FileStorage": { + "Provider": "AWS-S3", + "S3": { + "Bucket": "your-bucket-name", + "Region": "us-east-1", + "ACL": "private", + "MaxFileSize": "10MB" + } + } +} +``` + +## Authentication Configuration + +### JWT Configuration + +```json +{ + "Auth": { + "Authentication": "Custom", + "Authorization": "Custom", + "JWT": { + "Secret": "your-jwt-secret", + "ExpiresIn": "24h", + "RefreshExpiresIn": "7d", + "Issuer": "form-service", + "Audience": "form-service-users" + }, + "UseRefreshToken": true, + "AccessTokenExpiresInSeconds": 2592000, + "RefreshTokenExpiresInSeconds": 6048000 + } +} +``` + +### Session Configuration + +```json +{ + "Auth": { + "Session": { + "Secret": "your-session-secret", + "MaxAge": 86400000, + "Secure": false, + "HttpOnly": true, + "SameSite": "lax" + } + } +} +``` + +## Communication Configuration + +### Email Configuration + +```json +{ + "Communication": { + "Email": { + "Provider": "SendGrid", + "SendGrid": { + "ApiKey": "your-sendgrid-api-key", + "FromEmail": "noreply@yourservice.com", + "FromName": "Form Service" + } + } + } +} +``` + +### SMS Configuration + +```json +{ + "Communication": { + "SMS": { + "Provider": "Twilio", + "Twilio": { + "AccountSid": "your-account-sid", + "AuthToken": "your-auth-token", + "FromNumber": "+1234567890" + } + } + } +} +``` + +## Security Configuration + +### CORS Configuration + +```json +{ + "Security": { + "CORS": { + "Origin": ["http://localhost:3000", "https://yourservice.com"], + "Methods": ["GET", "POST", "PUT", "DELETE", "PATCH"], + "AllowedHeaders": ["Content-Type", "Authorization"], + "Credentials": true, + "MaxAge": 86400 + } + } +} +``` + +### Rate Limiting + +```json +{ + "Security": { + "RateLimit": { + "WindowMs": 900000, + "Max": 100, + "SkipSuccessfulRequests": false, + "SkipFailedRequests": false + } + } +} +``` + +## Monitoring Configuration + +### Health Check + +```json +{ + "Monitoring": { + "HealthCheck": { + "Enabled": true, + "Path": "/health", + "Interval": 30000, + "Timeout": 5000 + } + } +} +``` + +### Metrics + +```json +{ + "Monitoring": { + "Metrics": { + "Enabled": true, + "Path": "/metrics", + "CollectDefault": true + } + } +} +``` + +## Configuration Validation + +### Schema Validation + +The configuration is validated against a schema to ensure all required fields are present and have correct types. + +```typescript +interface ConfigurationSchema { + SystemIdentifier: string; + Auth: { + Authentication: string; + Authorization: string; + UseRefreshToken: boolean; + AccessTokenExpiresInSeconds: number; + RefreshTokenExpiresInSeconds: number; + }; + Database: { + Type: string; + ORM: string; + }; + FileStorage: { + Provider: string; + }; + Communication: { + Email: { + Provider: string; + }; + }; + TemporaryFolders: { + Upload: string; + Download: string; + CleanupFolderBeforeMinutes: number; + }; +} +``` + +### Environment Variable Validation + +```bash +# Validate required environment variables +npm run validate:env + +# Check configuration +npm run validate:config +``` + +## Configuration Management + +### Configuration Loading Order + +1. **Default Configuration** (`config.json`) +2. **Environment-Specific Configuration** (`config.local.json`) +3. **Environment Variables** (override file settings) +4. **Runtime Configuration** (command line arguments) + +### Configuration Reloading + +```typescript +// Reload configuration at runtime +import { ConfigurationManager } from './config/configuration.manager'; + +ConfigurationManager.reloadConfigurations(); +``` + +### Configuration Export + +```bash +# Export current configuration +npm run config:export + +# Import configuration +npm run config:import +``` + +## Best Practices + +### Security Best Practices + +1. **Never commit sensitive data** to version control +2. **Use environment variables** for secrets and passwords +3. **Rotate secrets regularly** in production +4. **Use strong passwords** and API keys +5. **Enable SSL/TLS** in production + +### Configuration Best Practices + +1. **Use descriptive names** for configuration keys +2. **Group related settings** logically +3. **Provide default values** for optional settings +4. **Validate configuration** at startup +5. **Document all configuration options** + +### Environment Best Practices + +1. **Separate configurations** by environment +2. **Use consistent naming** conventions +3. **Limit environment variables** to what's necessary +4. **Use configuration files** for complex settings +5. **Test configuration** in all environments + +## Troubleshooting + +### Common Configuration Issues + +#### 1. Configuration Not Loading +```bash +# Check configuration file syntax +node -e "console.log(require('./config.json'))" + +# Verify environment variables +env | grep DB_ +``` + +#### 2. Database Connection Issues +```bash +# Test database connection +psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME + +# Check database status +sudo systemctl status postgresql +``` + +#### 3. File Storage Issues +```bash +# Check file permissions +ls -la uploads/ + +# Test file upload +curl -X POST -F "file=@test.txt" http://localhost:3000/api/upload +``` + +### Configuration Debugging + +```bash +# Enable debug logging +LOG_LEVEL=debug npm start + +# Show configuration at startup +DEBUG=config npm start +``` + +## Next Steps + +After configuring your service: + +1. **Test Configuration**: Run validation checks +2. **Start Service**: Verify all settings work correctly +3. **Monitor Logs**: Check for configuration-related errors +4. **Backup Configuration**: Store secure copies of production configs +5. **Document Changes**: Keep track of configuration modifications + +For additional help: +- 📖 [Installation Guide](installation.md) - Setup instructions +- 🔧 [API Reference](api/overview.md) - API configuration +- 🛠️ [Development Guide](guides/development.md) - Development setup \ No newline at end of file diff --git a/docs/about/installation.md b/docs/about/installation.md new file mode 100644 index 0000000..550bc65 --- /dev/null +++ b/docs/about/installation.md @@ -0,0 +1,355 @@ +# Installation Guide + +> Complete installation instructions for Form Service + +## System Requirements + +### Minimum Requirements +- **Node.js**: 18.0.0 or higher +- **npm**: 8.0.0 or higher (or **yarn** 1.22.0 or higher) +- **RAM**: 2GB minimum, 4GB recommended +- **Disk Space**: 1GB minimum +- **Operating System**: Windows 10+, macOS 10.15+, or Linux (Ubuntu 18.04+) + +### Database Requirements +- **PostgreSQL**: 12.0 or higher (recommended) +- **MySQL**: 8.0 or higher +- **SQLite**: 3.0 or higher (development only) + +### Optional Dependencies +- **Redis**: 6.0 or higher (for caching) +- **Docker**: 20.10 or higher (for containerized deployment) +- **PM2**: For production process management + +## Installation Methods + +### Method 1: Direct Installation + +#### Step 1: Clone Repository +```bash +# Clone the repository +git clone https://github.com/your-org/form-service.git +cd form-service + +# Checkout the latest release (optional) +git checkout v1.0.0 +``` + +#### Step 2: Install Dependencies +```bash +# Install Node.js dependencies +npm install + +# Install global dependencies (optional) +npm install -g pm2 typescript ts-node +``` + +#### Step 3: Environment Configuration +```bash +# Copy configuration files +cp config.local.json.example config.local.json +cp .env.example .env + +# Edit configuration files +nano config.local.json +nano .env +``` + +#### Step 4: Database Setup +```bash +# PostgreSQL setup +sudo -u postgres createdb form_service +sudo -u postgres psql -c "CREATE USER form_user WITH PASSWORD 'your_password';" +sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE form_service TO form_user;" + +# MySQL setup +mysql -u root -p +CREATE DATABASE form_service; +CREATE USER 'form_user'@'localhost' IDENTIFIED BY 'your_password'; +GRANT ALL PRIVILEGES ON form_service.* TO 'form_user'@'localhost'; +FLUSH PRIVILEGES; +EXIT; +``` + +#### Step 5: Run Migrations +```bash +# Generate Prisma client +npx prisma generate + +# Run database migrations +npx prisma migrate deploy + +# Seed database (optional) +npm run seed +``` + +#### Step 6: Start the Service +```bash +# Development mode +npm run dev + +# Production mode +npm start + +# Using PM2 (recommended for production) +pm2 start dist/index.js --name "form-service" +pm2 save +pm2 startup +``` + +### Method 2: Docker Installation + +#### Step 1: Clone Repository +```bash +git clone https://github.com/your-org/form-service.git +cd form-service +``` + +#### Step 2: Environment Configuration +```bash +# Copy environment file +cp .env.example .env + +# Edit environment variables +nano .env +``` + +#### Step 3: Docker Compose Setup +```bash +# Start all services +docker-compose up -d + +# View logs +docker-compose logs -f form-service + +# Stop services +docker-compose down +``` + +#### Docker Compose Configuration +```yaml +version: '3.8' +services: + form-service: + build: . + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - DB_HOST=postgres + - DB_PORT=5432 + - DB_NAME=form_service + - DB_USER=postgres + - DB_PASSWORD=password + depends_on: + - postgres + - redis + volumes: + - ./logs:/app/logs + - ./uploads:/app/uploads + + postgres: + image: postgres:14 + environment: + - POSTGRES_DB=form_service + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=password + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + + redis: + image: redis:7-alpine + ports: + - "6379:6379" + volumes: + - redis_data:/data + +volumes: + postgres_data: + redis_data: +``` + +## Configuration + +### Environment Variables + +#### Required Variables +```bash +# Database Configuration +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=form_service +DB_USER=postgres +DB_PASSWORD=your_password +DB_DIALECT=postgres + +# Server Configuration +PORT=3000 +NODE_ENV=development + +# JWT Configuration +JWT_SECRET=your-super-secret-jwt-key +JWT_EXPIRES_IN=24h +``` + +#### Optional Variables +```bash +# Redis Configuration +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_PASSWORD= + +# File Storage +STORAGE_PROVIDER=local +STORAGE_PATH=./uploads + +# Logging +LOG_LEVEL=info +LOG_FILE=./logs/app.log + +# CORS +CORS_ORIGIN=http://localhost:3000 + +# Rate Limiting +RATE_LIMIT_WINDOW=15m +RATE_LIMIT_MAX=100 +``` + +## Verification + +### Health Check +```bash +# Test the service +curl http://localhost:3000/api/health + +# Expected response +{ + "status": "ok", + "timestamp": "2024-01-15T10:30:00.000Z", + "version": "1.0.0" +} +``` + +### API Test +```bash +# Test form templates endpoint +curl http://localhost:3000/api/forms/templates + +# Expected response +{ + "success": true, + "data": { + "items": [], + "pagination": { + "page": 1, + "limit": 10, + "total": 0, + "totalPages": 0 + } + } +} +``` + +### Documentation Access +```bash +# Access documentation +curl http://localhost:3000/api/docs + +# Should return the docsify documentation +``` + +## Troubleshooting + +### Common Issues + +#### 1. Database Connection Failed +```bash +# Check database status +sudo systemctl status postgresql + +# Check connection +psql -h localhost -U form_user -d form_service + +# Verify environment variables +echo $DB_HOST $DB_PORT $DB_NAME +``` + +#### 2. Port Already in Use +```bash +# Find process using port +lsof -i :3000 + +# Kill process +kill -9 + +# Or change port in configuration +``` + +#### 3. Permission Denied +```bash +# Fix file permissions +chmod +x entrypoint.sh +chmod -R 755 uploads/ +chmod -R 755 logs/ +``` + +#### 4. Node.js Version Issues +```bash +# Check Node.js version +node --version + +# Use nvm to install correct version +nvm install 18 +nvm use 18 +``` + +### Log Files +```bash +# Application logs +tail -f logs/app.log + +# PM2 logs +pm2 logs form-service + +# Docker logs +docker-compose logs -f form-service +``` + +## Security Considerations + +### Production Security +1. **Change Default Passwords**: Update all default passwords +2. **Use HTTPS**: Configure SSL/TLS certificates +3. **Firewall Configuration**: Restrict access to necessary ports +4. **Regular Updates**: Keep dependencies updated +5. **Backup Strategy**: Implement regular database backups +6. **Monitoring**: Set up monitoring and alerting + +### Environment Security +```bash +# Secure environment variables +export NODE_ENV=production +export JWT_SECRET=$(openssl rand -hex 32) +export DB_PASSWORD=$(openssl rand -base64 32) + +# Use .env file for sensitive data +echo "JWT_SECRET=$JWT_SECRET" >> .env +echo "DB_PASSWORD=$DB_PASSWORD" >> .env +``` + +## Next Steps + +After successful installation: + +1. **Configure Authentication**: Set up user authentication +2. **Create First Form**: Follow the [Quick Start Guide](quickstart.md) +3. **Set Up Monitoring**: Configure logging and monitoring +4. **Test API**: Use the [Bruno Collection](/) for API testing +5. **Deploy to Production**: Follow production deployment guidelines + +For additional help: +- 📖 [Documentation](/) - Complete guide +- 🐛 [GitHub Issues](https://github.com/your-org/form-service/issues) - Report problems +- 💬 [Discussions](https://github.com/your-org/form-service/discussions) - Ask questions \ No newline at end of file diff --git a/docs/about/quickstart.md b/docs/about/quickstart.md new file mode 100644 index 0000000..ca5b9e9 --- /dev/null +++ b/docs/about/quickstart.md @@ -0,0 +1,271 @@ +# Quick Start Guide + +> Get up and running with Form Service in under 5 minutes + +## Prerequisites + +Before you begin, ensure you have the following installed: + +- **Node.js** 18.0.0 or higher +- **npm** 8.0.0 or higher (or **yarn** 1.22.0 or higher) +- **Database**: PostgreSQL 12+, MySQL 8.0+, or SQLite 3.0+ + +## Step 1: Clone and Install + +```bash +# Clone the repository +git clone https://github.com/your-org/form-service.git +cd form-service + +# Install dependencies +npm install +``` + +## Step 2: Configuration + +Copy the example configuration file and customize it: + +```bash +# Copy configuration template +cp config.local.json.example config.local.json + +# Edit the configuration file +nano config.local.json +``` + +### Basic Configuration Example + +```json +{ + "database": { + "host": "localhost", + "port": 5432, + "database": "form_service", + "username": "postgres", + "password": "your_password", + "dialect": "postgres" + }, + "server": { + "port": 3000, + "host": "localhost" + }, + "jwt": { + "secret": "your-super-secret-jwt-key", + "expiresIn": "24h" + }, + "logging": { + "level": "info" + } +} +``` + +## Step 3: Database Setup + +### Option A: PostgreSQL (Recommended) + +```bash +# Create database +createdb form_service + +# Run migrations (if using TypeORM) +npm run migration:run +``` + +### Option B: MySQL + +```sql +CREATE DATABASE form_service; +``` + +### Option C: SQLite + +```bash +# SQLite database will be created automatically +# No additional setup required +``` + +## Step 4: Start the Service + +```bash +# Development mode with hot reload +npm run dev + +# Production mode +npm start +``` + +The service will be available at `http://localhost:3000` + +## Step 5: Verify Installation + +Test the health endpoint: + +```bash +curl http://localhost:3000/api/health +``` + +Expected response: +```json +{ + "status": "ok", + "timestamp": "2024-01-15T10:30:00.000Z", + "version": "1.0.0" +} +``` + +## Step 6: Create Your First Form + +### Using the API + +```bash +# Create a form template +curl -X POST http://localhost:3000/api/forms/templates \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Contact Form", + "description": "Simple contact form", + "sections": [ + { + "name": "Personal Information", + "fields": [ + { + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true + }, + { + "name": "email", + "label": "Email Address", + "type": "email", + "required": true + } + ] + } + ] + }' +``` + +### Using Bruno (API Client) + +1. Open the `bruno/form-service` folder in Bruno +2. Import the collection from `Postman/form-service.postman_collection.json` +3. Set up your environment variables +4. Run the "Create a form template" request + +## Step 7: Submit Form Data + +```bash +# Submit form data +curl -X POST http://localhost:3000/api/forms/submissions \ + -H "Content-Type: application/json" \ + -d '{ + "templateId": "your-template-id", + "responses": [ + { + "fieldName": "fullName", + "value": "John Doe" + }, + { + "fieldName": "email", + "value": "john@example.com" + } + ] + }' +``` + +## 🎉 Congratulations! + +You've successfully set up Form Service! Here's what you can do next: + +### Next Steps + +1. **Explore the API**: Check out the [API Documentation](api/overview.md) +2. **Learn Skip Logic**: See [Skip Logic Guide](guides/skip-logic.md) +3. **Build Complex Forms**: Follow the [Form Builder Guide](guides/form-builder.md) +4. **Add Validation**: Learn about [Validation Rules](guides/validation-rules.md) + +### Common Commands + +```bash +# Development +npm run dev # Start development server +npm run build # Build for production +npm test # Run tests +npm run test:watch # Run tests in watch mode + +# Database +npm run migration:run # Run database migrations +npm run migration:revert # Revert last migration +npm run seed # Seed database with sample data + +# Linting and Formatting +npm run lint # Run ESLint +npm run lint:fix # Fix linting issues +npm run format # Format code with Prettier +``` + +### Troubleshooting + +
+ Common Issues: + + - **Port already in use**: Change the port in `config.local.json` + - **Database connection failed**: Check your database credentials + - **JWT errors**: Ensure your JWT secret is properly configured +
+ +### Getting Help + +- 📖 [Full Documentation](/) - Complete guide +- 🐛 [GitHub Issues](https://github.com/your-org/form-service/issues) - Report bugs +- 💬 [Discussions](https://github.com/your-org/form-service/discussions) - Ask questions +- 📧 [Email Support](mailto:support@yourservice.com) - Direct support + +## 🔧 Documentation Troubleshooting + +### If Documentation Shows "Loading..." + +The documentation requires a web server to run properly. Here are the solutions: + +#### Solution 1: Use the Form Service (Recommended) +```bash +npm run dev +# Then visit: http://localhost:3000/api/docs +``` + +#### Solution 2: Use Standalone Documentation Server +```bash +npm run docs +# Then visit: http://localhost:8080 +``` + +#### Solution 3: Use Python (if available) +```bash +cd docs +python -m http.server 8080 +# Then visit: http://localhost:8080 +``` + +#### Solution 4: Use Node.js http-server +```bash +npm install -g http-server +cd docs +http-server -p 8080 +# Then visit: http://localhost:8080 +``` + +### Common Issues + +1. **"Loading..." message**: You're trying to open the file directly. Use a web server instead. +2. **CORS errors**: The browser is blocking local file access. Use a web server. +3. **Missing styles**: Static files aren't being served. Use a web server. +4. **Broken links**: Relative paths don't work with file:// protocol. Use a web server. + +**Remember**: Always use `http://` URLs, never `file://` URLs for the documentation. + +--- + +
+ Pro Tip: Use the Bruno API client included in the project for easier API testing and development. +
\ No newline at end of file diff --git a/docs/api/authentication.md b/docs/api/authentication.md new file mode 100644 index 0000000..665cc03 --- /dev/null +++ b/docs/api/authentication.md @@ -0,0 +1,519 @@ +# Authentication + +> Authentication and authorization for Form Service API + +## Overview + +Form Service uses JWT (JSON Web Tokens) for authentication and implements role-based access control (RBAC) for authorization. All API endpoints (except public ones) require valid authentication. + +## Authentication Flow + +```mermaid +sequenceDiagram + participant Client + participant API + participant Auth Service + participant Database + + Client->>API: POST /auth/login + API->>Auth Service: Validate credentials + Auth Service->>Database: Check user credentials + Database-->>Auth Service: User data + Auth Service-->>API: JWT token + API-->>Client: Access token + Refresh token + + Client->>API: API request with Bearer token + API->>Auth Service: Validate JWT + Auth Service-->>API: User context + API-->>Client: Response +``` + +## Authentication Endpoints + +### Login + +POST `/api/auth/login` + +Authenticate a user and receive access tokens. + +**Request Body:** +```json +{ + "email": "user@example.com", + "password": "your_password" +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "expiresIn": 86400, + "user": { + "id": "user-uuid", + "email": "user@example.com", + "firstName": "John", + "lastName": "Doe", + "role": "admin", + "permissions": ["forms:read", "forms:write", "users:read"] + } + } +} +``` + +### Refresh Token + +POST `/api/auth/refresh` + +Refresh an expired access token using a refresh token. + +**Request Body:** +```json +{ + "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "expiresIn": 86400 + } +} +``` + +### Logout + +POST `/api/auth/logout` + +Invalidate the current session and refresh token. + +**Request Headers:** +```http +Authorization: Bearer +``` + +**Response:** +```json +{ + "success": true, + "message": "Successfully logged out" +} +``` + +### Register + +POST `/api/auth/register` + +Register a new user account. + +**Request Body:** +```json +{ + "email": "newuser@example.com", + "password": "secure_password", + "firstName": "Jane", + "lastName": "Smith", + "role": "user" +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "user": { + "id": "user-uuid", + "email": "newuser@example.com", + "firstName": "Jane", + "lastName": "Smith", + "role": "user", + "createdAt": "2024-01-15T10:30:00.000Z" + } + } +} +``` + +## Using Authentication + +### Including Tokens in Requests + +All authenticated requests must include the JWT token in the Authorization header: + +```http +Authorization: Bearer +``` + +### Example API Request + +```bash +curl -X GET http://localhost:3000/api/forms/templates \ + -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \ + -H "Content-Type: application/json" +``` + +### JavaScript Example + +```javascript +const token = 'your-jwt-token'; + +fetch('/api/forms/templates', { + method: 'GET', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + } +}) +.then(response => response.json()) +.then(data => console.log(data)); +``` + +## User Roles and Permissions + +### Available Roles + +| Role | Description | Permissions | +|------|-------------|-------------| +| `admin` | System administrator | All permissions | +| `manager` | Form manager | Forms, submissions, users | +| `user` | Regular user | Own forms and submissions | +| `viewer` | Read-only access | View forms and submissions | + +### Permission System + +Permissions are granular and follow the format: `resource:action` + +#### Form Permissions +- `forms:read` - View form templates +- `forms:write` - Create and edit form templates +- `forms:delete` - Delete form templates +- `forms:publish` - Publish forms + +#### Submission Permissions +- `submissions:read` - View form submissions +- `submissions:write` - Create form submissions +- `submissions:delete` - Delete form submissions +- `submissions:export` - Export submission data + +#### User Permissions +- `users:read` - View user information +- `users:write` - Create and edit users +- `users:delete` - Delete users + +#### System Permissions +- `system:config` - Modify system configuration +- `system:logs` - View system logs +- `system:backup` - Create system backups + +### Checking Permissions + +```typescript +// Check if user has specific permission +if (user.permissions.includes('forms:write')) { + // Allow form creation +} + +// Check if user has role +if (user.role === 'admin') { + // Allow admin actions +} +``` + +## Error Responses + +### Authentication Errors + +#### 401 Unauthorized +```json +{ + "success": false, + "error": { + "code": "AUTHENTICATION_ERROR", + "message": "Invalid or missing authentication token", + "details": "Token is required for this endpoint" + } +} +``` + +#### 403 Forbidden +```json +{ + "success": false, + "error": { + "code": "AUTHORIZATION_ERROR", + "message": "Insufficient permissions", + "details": "User does not have 'forms:write' permission" + } +} +``` + +#### 422 Validation Error +```json +{ + "success": false, + "error": { + "code": "VALIDATION_ERROR", + "message": "Invalid credentials", + "details": [ + { + "field": "email", + "message": "Invalid email format" + }, + { + "field": "password", + "message": "Password must be at least 8 characters" + } + ] + } +} +``` + +## Security Best Practices + +### Token Management + +1. **Store tokens securely**: Use secure storage (not localStorage) +2. **Rotate tokens regularly**: Refresh tokens before expiration +3. **Handle token expiration**: Implement automatic refresh logic +4. **Logout properly**: Invalidate tokens on logout + +### Password Security + +1. **Strong passwords**: Minimum 8 characters with complexity +2. **Password hashing**: Passwords are hashed using bcrypt +3. **Rate limiting**: Login attempts are rate-limited +4. **Account lockout**: Temporary lockout after failed attempts + +### API Security + +1. **HTTPS only**: All API communication should use HTTPS +2. **CORS configuration**: Proper CORS settings for web clients +3. **Rate limiting**: API endpoints are rate-limited +4. **Input validation**: All inputs are validated and sanitized + +## Token Configuration + +### JWT Settings + +```json +{ + "jwt": { + "secret": "your-super-secret-jwt-key", + "expiresIn": "24h", + "refreshExpiresIn": "7d", + "issuer": "form-service", + "audience": "form-service-users" + } +} +``` + +### Token Payload + +```json +{ + "sub": "user-uuid", + "email": "user@example.com", + "role": "admin", + "permissions": ["forms:read", "forms:write"], + "iat": 1642233600, + "exp": 1642320000, + "iss": "form-service", + "aud": "form-service-users" +} +``` + +## Session Management + +### Session Configuration + +```json +{ + "session": { + "secret": "your-session-secret", + "maxAge": 86400000, + "secure": false, + "httpOnly": true, + "sameSite": "lax" + } +} +``` + +### Session Storage + +Sessions can be stored in: +- **Memory** (development only) +- **Redis** (recommended for production) +- **Database** (PostgreSQL, MySQL) + +## Multi-factor Authentication (MFA) + +### TOTP Setup + +POST `/api/auth/mfa/setup` + +Setup TOTP-based two-factor authentication. + +**Request Headers:** +```http +Authorization: Bearer +``` + +**Response:** +```json +{ + "success": true, + "data": { + "qrCode": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...", + "secret": "JBSWY3DPEHPK3PXP", + "backupCodes": ["12345678", "87654321", "11223344"] + } +} +``` + +### MFA Verification + +POST `/api/auth/mfa/verify` + +Verify TOTP code during login. + +**Request Body:** +```json +{ + "email": "user@example.com", + "password": "password", + "totpCode": "123456" +} +``` + +## OAuth Integration + +### Google OAuth + +GET `/api/auth/google` + +Initiate Google OAuth flow. + +**Response:** +```json +{ + "success": true, + "data": { + "authUrl": "https://accounts.google.com/oauth/authorize?..." + } +} +``` + +### OAuth Callback + +GET `/api/auth/google/callback` + +Handle OAuth callback and create user session. + +**Query Parameters:** +- `code` - Authorization code from OAuth provider +- `state` - CSRF protection state + +## API Key Authentication + +### Generate API Key + +POST `/api/auth/api-keys` + +Generate a new API key for service-to-service communication. + +**Request Headers:** +```http +Authorization: Bearer +``` + +**Request Body:** +```json +{ + "name": "Integration API Key", + "permissions": ["forms:read", "submissions:read"] +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "apiKey": "fs_live_1234567890abcdef...", + "name": "Integration API Key", + "permissions": ["forms:read", "submissions:read"], + "createdAt": "2024-01-15T10:30:00.000Z" + } +} +``` + +### Using API Keys + +```http +Authorization: Bearer fs_live_1234567890abcdef... +``` + +## Troubleshooting + +### Common Issues + +#### 1. Token Expired +```json +{ + "success": false, + "error": { + "code": "TOKEN_EXPIRED", + "message": "Access token has expired" + } +} +``` + +**Solution**: Use refresh token to get new access token. + +#### 2. Invalid Token Format +```json +{ + "success": false, + "error": { + "code": "INVALID_TOKEN", + "message": "Invalid token format" + } +} +``` + +**Solution**: Ensure token is properly formatted as `Bearer `. + +#### 3. Insufficient Permissions +```json +{ + "success": false, + "error": { + "code": "INSUFFICIENT_PERMISSIONS", + "message": "User does not have required permissions" + } +} +``` + +**Solution**: Check user role and permissions, contact administrator. + +### Debug Mode + +Enable debug logging for authentication: + +```bash +LOG_LEVEL=debug AUTH_DEBUG=true npm start +``` + +## Next Steps + +- 🔐 [Security Best Practices](guides/security.md) - Security guidelines +- 👥 [User Management](api/user-management.md) - User administration +- 🔑 [API Keys](guides/api-keys.md) - Service-to-service authentication +- 🛡️ [Rate Limiting](guides/rate-limiting.md) - API protection \ No newline at end of file diff --git a/docs/api/form-templates.md b/docs/api/form-templates.md new file mode 100644 index 0000000..8d9769c --- /dev/null +++ b/docs/api/form-templates.md @@ -0,0 +1,696 @@ +# Form Templates + +> Complete API reference for form template management + +## Overview + +Form templates define the structure, fields, and logic of forms. They serve as reusable blueprints for creating forms with consistent layouts and behavior. + +## Form Template Structure + +```json +{ + "id": "template-uuid", + "name": "Contact Form", + "description": "A simple contact form for customer inquiries", + "version": "1.0.0", + "status": "published", + "sections": [ + { + "id": "section-uuid", + "name": "Personal Information", + "description": "Basic contact details", + "order": 1, + "fields": [ + { + "id": "field-uuid", + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true, + "order": 1, + "validation": { + "minLength": 2, + "maxLength": 100 + } + } + ] + } + ], + "createdBy": "user-uuid", + "createdAt": "2024-01-15T10:30:00.000Z", + "updatedAt": "2024-01-15T10:30:00.000Z" +} +``` + +## API Endpoints + +### Create Form Template + +POST `/api/forms/templates` + +Create a new form template. + +**Request Headers:** +```http +Authorization: Bearer +Content-Type: application/json +``` + +**Request Body:** +```json +{ + "name": "Contact Form", + "description": "A simple contact form for customer inquiries", + "sections": [ + { + "name": "Personal Information", + "description": "Basic contact details", + "order": 1, + "fields": [ + { + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true, + "order": 1, + "placeholder": "Enter your full name", + "validation": { + "minLength": 2, + "maxLength": 100 + } + }, + { + "name": "email", + "label": "Email Address", + "type": "email", + "required": true, + "order": 2, + "placeholder": "Enter your email address", + "validation": { + "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" + } + } + ] + } + ] +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "id": "template-uuid-123", + "name": "Contact Form", + "description": "A simple contact form for customer inquiries", + "version": "1.0.0", + "status": "draft", + "sections": [ + { + "id": "section-uuid-1", + "name": "Personal Information", + "description": "Basic contact details", + "order": 1, + "fields": [ + { + "id": "field-uuid-1", + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true, + "order": 1, + "placeholder": "Enter your full name", + "validation": { + "minLength": 2, + "maxLength": 100 + } + } + ] + } + ], + "createdBy": "user-uuid", + "createdAt": "2024-01-15T10:30:00.000Z", + "updatedAt": "2024-01-15T10:30:00.000Z" + } +} +``` + +### Get Form Templates + +GET `/api/forms/templates` + +List all form templates with pagination and filtering. + +**Request Headers:** +```http +Authorization: Bearer +``` + +**Query Parameters:** +- `page` (number): Page number (default: 1) +- `limit` (number): Items per page (default: 10, max: 100) +- `search` (string): Search by name or description +- `status` (string): Filter by status (draft, published, archived) +- `createdBy` (string): Filter by creator +- `sortBy` (string): Sort field (name, createdAt, updatedAt) +- `sortOrder` (string): Sort order (asc, desc) + +**Example Request:** +```bash +curl -X GET "http://localhost:3000/api/forms/templates?page=1&limit=10&status=published" \ + -H "Authorization: Bearer " +``` + +**Response:** +```json +{ + "success": true, + "data": { + "items": [ + { + "id": "template-uuid-123", + "name": "Contact Form", + "description": "A simple contact form for customer inquiries", + "version": "1.0.0", + "status": "published", + "createdBy": "user-uuid", + "createdAt": "2024-01-15T10:30:00.000Z", + "updatedAt": "2024-01-15T10:30:00.000Z" + } + ], + "pagination": { + "page": 1, + "limit": 10, + "total": 25, + "totalPages": 3, + "hasNext": true, + "hasPrev": false + } + } +} +``` + +### Get Form Template by ID + +GET `/api/forms/templates/:id` + +Get a specific form template by ID. + +**Request Headers:** +```http +Authorization: Bearer +``` + +**Path Parameters:** +- `id` (string): Template UUID + +**Response:** +```json +{ + "success": true, + "data": { + "id": "template-uuid-123", + "name": "Contact Form", + "description": "A simple contact form for customer inquiries", + "version": "1.0.0", + "status": "published", + "sections": [ + { + "id": "section-uuid-1", + "name": "Personal Information", + "description": "Basic contact details", + "order": 1, + "fields": [ + { + "id": "field-uuid-1", + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true, + "order": 1, + "placeholder": "Enter your full name", + "validation": { + "minLength": 2, + "maxLength": 100 + } + } + ] + } + ], + "createdBy": "user-uuid", + "createdAt": "2024-01-15T10:30:00.000Z", + "updatedAt": "2024-01-15T10:30:00.000Z" + } +} +``` + +### Update Form Template + +PUT `/api/forms/templates/:id` + +Update an existing form template. + +**Request Headers:** +```http +Authorization: Bearer +Content-Type: application/json +``` + +**Path Parameters:** +- `id` (string): Template UUID + +**Request Body:** +```json +{ + "name": "Updated Contact Form", + "description": "An updated contact form with additional fields", + "sections": [ + { + "name": "Personal Information", + "description": "Basic contact details", + "order": 1, + "fields": [ + { + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true, + "order": 1 + }, + { + "name": "phone", + "label": "Phone Number", + "type": "tel", + "required": false, + "order": 2 + } + ] + } + ] +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "id": "template-uuid-123", + "name": "Updated Contact Form", + "description": "An updated contact form with additional fields", + "version": "1.1.0", + "status": "draft", + "sections": [...], + "updatedAt": "2024-01-15T11:30:00.000Z" + } +} +``` + +### Delete Form Template + +DELETE `/api/forms/templates/:id` + +Delete a form template. + +**Request Headers:** +```http +Authorization: Bearer +``` + +**Path Parameters:** +- `id` (string): Template UUID + +**Response:** +```json +{ + "success": true, + "message": "Form template deleted successfully" +} +``` + +### Publish Form Template + +POST `/api/forms/templates/:id/publish` + +Publish a draft form template. + +**Request Headers:** +```http +Authorization: Bearer +``` + +**Path Parameters:** +- `id` (string): Template UUID + +**Response:** +```json +{ + "success": true, + "data": { + "id": "template-uuid-123", + "status": "published", + "publishedAt": "2024-01-15T12:00:00.000Z" + } +} +``` + +### Archive Form Template + +POST `/api/forms/templates/:id/archive` + +Archive a published form template. + +**Request Headers:** +```http +Authorization: Bearer +``` + +**Path Parameters:** +- `id` (string): Template UUID + +**Response:** +```json +{ + "success": true, + "data": { + "id": "template-uuid-123", + "status": "archived", + "archivedAt": "2024-01-15T12:00:00.000Z" + } +} +``` + +### Duplicate Form Template + +POST `/api/forms/templates/:id/duplicate` + +Create a copy of an existing form template. + +**Request Headers:** +```http +Authorization: Bearer +Content-Type: application/json +``` + +**Path Parameters:** +- `id` (string): Template UUID + +**Request Body:** +```json +{ + "name": "Contact Form - Copy", + "description": "A copy of the original contact form" +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "id": "template-uuid-456", + "name": "Contact Form - Copy", + "description": "A copy of the original contact form", + "version": "1.0.0", + "status": "draft", + "sections": [...], + "createdAt": "2024-01-15T12:30:00.000Z" + } +} +``` + +## Field Types + +### Supported Field Types + +| Type | Description | Validation Options | +|------|-------------|-------------------| +| `text` | Single line text input | minLength, maxLength, pattern | +| `textarea` | Multi-line text input | minLength, maxLength | +| `email` | Email address input | pattern (email validation) | +| `tel` | Telephone number input | pattern | +| `number` | Numeric input | min, max, step | +| `date` | Date picker | min, max | +| `datetime` | Date and time picker | min, max | +| `select` | Dropdown selection | options (array) | +| `radio` | Radio button group | options (array) | +| `checkbox` | Checkbox group | options (array) | +| `boolean` | Single checkbox | - | +| `file` | File upload | maxSize, allowedTypes | +| `hidden` | Hidden field | - | + +### Field Configuration + +```json +{ + "name": "fieldName", + "label": "Field Label", + "type": "text", + "required": true, + "order": 1, + "placeholder": "Enter value", + "defaultValue": "", + "helpText": "Additional help text", + "validation": { + "minLength": 2, + "maxLength": 100, + "pattern": "^[a-zA-Z]+$", + "customMessage": "Custom validation message" + }, + "options": [ + {"value": "option1", "label": "Option 1"}, + {"value": "option2", "label": "Option 2"} + ], + "conditional": { + "show": { + "field": "otherField", + "operator": "equals", + "value": "show" + } + } +} +``` + +## Template Status + +### Status Types + +| Status | Description | +|--------|-------------| +| `draft` | Template is being edited | +| `published` | Template is live and can be used | +| `archived` | Template is no longer active | +| `deleted` | Template is marked for deletion | + +### Status Transitions + +```mermaid +stateDiagram-v2 + [*] --> draft + draft --> published : publish + published --> draft : edit + published --> archived : archive + archived --> published : restore + archived --> deleted : delete + deleted --> [*] +``` + +## Template Versioning + +### Version Management + +- **Major Version**: Breaking changes (1.0.0 → 2.0.0) +- **Minor Version**: New features (1.0.0 → 1.1.0) +- **Patch Version**: Bug fixes (1.0.0 → 1.0.1) + +### Version History + +```json +{ + "versions": [ + { + "version": "1.0.0", + "status": "published", + "createdAt": "2024-01-15T10:30:00.000Z", + "createdBy": "user-uuid" + }, + { + "version": "1.1.0", + "status": "draft", + "createdAt": "2024-01-15T11:30:00.000Z", + "createdBy": "user-uuid" + } + ] +} +``` + +## Template Categories + +### Category Management + +```json +{ + "categories": [ + { + "id": "category-uuid", + "name": "Contact Forms", + "description": "Forms for collecting contact information" + }, + { + "id": "category-uuid-2", + "name": "Surveys", + "description": "Survey and feedback forms" + } + ] +} +``` + +### Assign Categories + +POST `/api/forms/templates/:id/categories` + +**Request Body:** +```json +{ + "categoryIds": ["category-uuid", "category-uuid-2"] +} +``` + +## Template Sharing + +### Share Template + +POST `/api/forms/templates/:id/share` + +Share a template with other users or teams. + +**Request Body:** +```json +{ + "users": ["user-uuid-1", "user-uuid-2"], + "teams": ["team-uuid-1"], + "permissions": ["read", "write"] +} +``` + +### Get Shared Templates + +GET `/api/forms/templates/shared` + +Get templates shared with the current user. + +## Template Analytics + +### Get Template Analytics + +GET `/api/forms/templates/:id/analytics` + +Get analytics for a specific template. + +**Query Parameters:** +- `startDate` (string): Start date (ISO format) +- `endDate` (string): End date (ISO format) +- `metrics` (string): Comma-separated metrics + +**Response:** +```json +{ + "success": true, + "data": { + "submissions": { + "total": 150, + "thisWeek": 25, + "thisMonth": 100 + }, + "completionRate": 85.5, + "averageTime": 180, + "topFields": [ + {"field": "email", "completionRate": 95.2}, + {"field": "fullName", "completionRate": 92.1} + ] + } +} +``` + +## Error Responses + +### Validation Errors + +```json +{ + "success": false, + "error": { + "code": "VALIDATION_ERROR", + "message": "Validation failed", + "details": [ + { + "field": "name", + "message": "Name is required" + }, + { + "field": "sections", + "message": "At least one section is required" + } + ] + } +} +``` + +### Not Found Error + +```json +{ + "success": false, + "error": { + "code": "NOT_FOUND", + "message": "Form template not found", + "details": "Template with ID 'template-uuid' does not exist" + } +} +``` + +### Permission Error + +```json +{ + "success": false, + "error": { + "code": "AUTHORIZATION_ERROR", + "message": "Insufficient permissions", + "details": "User does not have permission to update this template" + } +} +``` + +## Best Practices + +### Template Design + +1. **Clear Structure**: Use logical sections and field ordering +2. **Descriptive Labels**: Use clear, user-friendly field labels +3. **Helpful Placeholders**: Provide examples in placeholders +4. **Appropriate Validation**: Set reasonable validation rules +5. **Mobile-Friendly**: Consider mobile device usage + +### Performance + +1. **Limit Field Count**: Keep forms under 50 fields for best performance +2. **Optimize Images**: Compress images in file upload fields +3. **Use Conditional Logic**: Show/hide fields based on user input +4. **Cache Templates**: Cache frequently accessed templates + +### Security + +1. **Validate Input**: Always validate user input +2. **Sanitize Data**: Clean data before storage +3. **Access Control**: Implement proper permissions +4. **Audit Trail**: Log template changes + +## Next Steps + +- 📝 [Form Fields](api/form-fields.md) - Field configuration details +- 🔗 [Skip Logic](guides/skip-logic.md) - Conditional field visibility +- ✅ [Validation Rules](guides/validation-rules.md) - Field validation +- 📊 [Form Submissions](api/form-submissions.md) - Data collection \ No newline at end of file diff --git a/docs/api/overview.md b/docs/api/overview.md new file mode 100644 index 0000000..d617807 --- /dev/null +++ b/docs/api/overview.md @@ -0,0 +1,632 @@ +# API Overview + +> Complete API reference for Form Service + +## Introduction + +Form Service provides a comprehensive RESTful API for managing forms, templates, submissions, and user data. All API endpoints return JSON responses and follow consistent error handling patterns. + +## Base URL + +``` +Development: http://localhost:3000/api +Production: https://your-domain.com/api +``` + +## Authentication + +Most API endpoints require authentication using JWT tokens. Include the token in the Authorization header: + +```http +Authorization: Bearer +``` + +## Response Format + +All API responses follow a consistent format: + +### Success Response + +```json +{ + "success": true, + "data": { + // Response data here + }, + "message": "Optional success message" +} +``` + +### Error Response + +```json +{ + "success": false, + "error": { + "code": "ERROR_CODE", + "message": "Human-readable error message", + "details": "Additional error details" + } +} +``` + +## HTTP Status Codes + +| Code | Description | +|------|-------------| +| 200 | Success | +| 201 | Created | +| 400 | Bad Request | +| 401 | Unauthorized | +| 403 | Forbidden | +| 404 | Not Found | +| 422 | Validation Error | +| 429 | Rate Limited | +| 500 | Internal Server Error | + +## API Endpoints + +### Authentication + +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/auth/login` | User login | +| POST | `/auth/register` | User registration | +| POST | `/auth/refresh` | Refresh access token | +| POST | `/auth/logout` | User logout | +| GET | `/auth/profile` | Get user profile | + +### Form Templates + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/forms/templates` | List form templates | +| POST | `/forms/templates` | Create form template | +| GET | `/forms/templates/:id` | Get template by ID | +| PUT | `/forms/templates/:id` | Update template | +| DELETE | `/forms/templates/:id` | Delete template | +| POST | `/forms/templates/:id/publish` | Publish template | +| POST | `/forms/templates/:id/archive` | Archive template | +| POST | `/forms/templates/:id/duplicate` | Duplicate template | + +### Form Fields + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/forms/fields` | List form fields | +| POST | `/forms/fields` | Create form field | +| GET | `/forms/fields/:id` | Get field by ID | +| PUT | `/forms/fields/:id` | Update field | +| DELETE | `/forms/fields/:id` | Delete field | + +### Form Sections + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/forms/sections` | List form sections | +| POST | `/forms/sections` | Create form section | +| GET | `/forms/sections/:id` | Get section by ID | +| PUT | `/forms/sections/:id` | Update section | +| DELETE | `/forms/sections/:id` | Delete section | + +### Form Submissions + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/submissions` | List form submissions | +| POST | `/submissions` | Create form submission | +| GET | `/submissions/:id` | Get submission by ID | +| PUT | `/submissions/:id` | Update submission | +| DELETE | `/submissions/:id` | Delete submission | +| GET | `/submissions/:id/export` | Export submission data | + +### Question Responses + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/responses` | List question responses | +| POST | `/responses` | Create question response | +| GET | `/responses/:id` | Get response by ID | +| PUT | `/responses/:id` | Update response | +| DELETE | `/responses/:id` | Delete response | + +### Field Logic + +#### Skip Logic + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-logic/skip-logic` | List skip logic rules | +| POST | `/field-logic/skip-logic` | Create skip logic rule | +| GET | `/field-logic/skip-logic/:id` | Get skip logic by ID | +| PUT | `/field-logic/skip-logic/:id` | Update skip logic | +| DELETE | `/field-logic/skip-logic/:id` | Delete skip logic | + +#### Validation Logic + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-logic/validation-logic` | List validation logic | +| POST | `/field-logic/validation-logic` | Create validation logic | +| GET | `/field-logic/validation-logic/:id` | Get validation logic by ID | +| PUT | `/field-logic/validation-logic/:id` | Update validation logic | +| DELETE | `/field-logic/validation-logic/:id` | Delete validation logic | + +#### Calculation Logic + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-logic/calculation-logic` | List calculation logic | +| POST | `/field-logic/calculation-logic` | Create calculation logic | +| GET | `/field-logic/calculation-logic/:id` | Get calculation logic by ID | +| PUT | `/field-logic/calculation-logic/:id` | Update calculation logic | +| DELETE | `/field-logic/calculation-logic/:id` | Delete calculation logic | + +### Field Operations + +#### Logical Operations + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-operations/logical` | List logical operations | +| POST | `/field-operations/logical` | Create logical operation | +| GET | `/field-operations/logical/:id` | Get logical operation by ID | +| PUT | `/field-operations/logical/:id` | Update logical operation | +| DELETE | `/field-operations/logical/:id` | Delete logical operation | + +#### Mathematical Operations + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-operations/mathematical` | List mathematical operations | +| POST | `/field-operations/mathematical` | Create mathematical operation | +| GET | `/field-operations/mathematical/:id` | Get mathematical operation by ID | +| PUT | `/field-operations/mathematical/:id` | Update mathematical operation | +| DELETE | `/field-operations/mathematical/:id` | Delete mathematical operation | + +#### Composition Operations + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-operations/composition` | List composition operations | +| POST | `/field-operations/composition` | Create composition operation | +| GET | `/field-operations/composition/:id` | Get composition operation by ID | +| PUT | `/field-operations/composition/:id` | Update composition operation | +| DELETE | `/field-operations/composition/:id` | Delete composition operation | + +### Field Rules + +#### Skip Rules + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-rules/skip-rules` | List skip rules | +| POST | `/field-rules/skip-rules` | Create skip rule | +| GET | `/field-rules/skip-rules/:id` | Get skip rule by ID | +| PUT | `/field-rules/skip-rules/:id` | Update skip rule | +| DELETE | `/field-rules/skip-rules/:id` | Delete skip rule | + +#### Validation Rules + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-rules/validation-rules` | List validation rules | +| POST | `/field-rules/validation-rules` | Create validation rule | +| GET | `/field-rules/validation-rules/:id` | Get validation rule by ID | +| PUT | `/field-rules/validation-rules/:id` | Update validation rule | +| DELETE | `/field-rules/validation-rules/:id` | Delete validation rule | + +#### Calculation Rules + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/field-rules/calculation-rules` | List calculation rules | +| POST | `/field-rules/calculation-rules` | Create calculation rule | +| GET | `/field-rules/calculation-rules/:id` | Get calculation rule by ID | +| PUT | `/field-rules/calculation-rules/:id` | Update calculation rule | +| DELETE | `/field-rules/calculation-rules/:id` | Delete calculation rule | + +### Users + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/users` | List users | +| POST | `/users` | Create user | +| GET | `/users/:id` | Get user by ID | +| PUT | `/users/:id` | Update user | +| DELETE | `/users/:id` | Delete user | +| GET | `/users/search` | Search users | + +### User Sessions + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/user-sessions` | List user sessions | +| POST | `/user-sessions` | Create user session | +| GET | `/user-sessions/:id` | Get session by ID | +| DELETE | `/user-sessions/:id` | Delete session | + +### Form Template Approvals + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/form-template-approvals` | List template approvals | +| POST | `/form-template-approvals` | Create template approval | +| GET | `/form-template-approvals/:id` | Get approval by ID | +| PUT | `/form-template-approvals/:id` | Update approval | +| DELETE | `/form-template-approvals/:id` | Delete approval | + +### Template Folders + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/template-folders` | List template folders | +| POST | `/template-folders` | Create template folder | +| GET | `/template-folders/:id` | Get folder by ID | +| PUT | `/template-folders/:id` | Update folder | +| DELETE | `/template-folders/:id` | Delete folder | + +### Favorite Templates + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/favorite-templates` | List favorite templates | +| POST | `/favorite-templates` | Create favorite template | +| GET | `/favorite-templates/:id` | Get favorite by ID | +| DELETE | `/favorite-templates/:id` | Delete favorite | + +### Input Unit Lists + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/input-unit-lists` | List input unit lists | +| POST | `/input-unit-lists` | Create input unit list | +| GET | `/input-unit-lists/:id` | Get unit list by ID | +| PUT | `/input-unit-lists/:id` | Update unit list | +| DELETE | `/input-unit-lists/:id` | Delete unit list | + +### System + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/health` | Health check | +| GET | `/docs` | API documentation | + +## Query Parameters + +### Pagination + +Most list endpoints support pagination: + +```http +GET /api/forms/templates?page=1&limit=10 +``` + +**Parameters:** +- `page` (number): Page number (default: 1) +- `limit` (number): Items per page (default: 10, max: 100) + +### Filtering + +Many endpoints support filtering: + +```http +GET /api/forms/templates?status=published&createdBy=user-uuid +``` + +### Sorting + +Sort results by specific fields: + +```http +GET /api/forms/templates?sortBy=createdAt&sortOrder=desc +``` + +**Parameters:** +- `sortBy` (string): Field to sort by +- `sortOrder` (string): Sort order (`asc` or `desc`) + +### Search + +Search across text fields: + +```http +GET /api/forms/templates?search=contact form +``` + +## Request Headers + +### Common Headers + +```http +Content-Type: application/json +Authorization: Bearer +Accept: application/json +``` + +### Optional Headers + +```http +X-Request-ID: unique-request-id +X-Client-Version: 1.0.0 +X-User-Agent: custom-user-agent +``` + +## Rate Limiting + +API endpoints are rate-limited to prevent abuse: + +- **Authentication endpoints**: 5 requests per minute +- **General endpoints**: 100 requests per minute +- **File upload endpoints**: 10 requests per minute + +Rate limit headers are included in responses: + +```http +X-RateLimit-Limit: 100 +X-RateLimit-Remaining: 95 +X-RateLimit-Reset: 1642233600 +``` + +## Error Handling + +### Common Error Codes + +| Code | Description | +|------|-------------| +| `VALIDATION_ERROR` | Request data validation failed | +| `AUTHENTICATION_ERROR` | Invalid or missing authentication | +| `AUTHORIZATION_ERROR` | Insufficient permissions | +| `NOT_FOUND` | Resource not found | +| `DUPLICATE_ERROR` | Resource already exists | +| `RATE_LIMIT_ERROR` | Rate limit exceeded | +| `INTERNAL_ERROR` | Internal server error | + +### Error Response Example + +```json +{ + "success": false, + "error": { + "code": "VALIDATION_ERROR", + "message": "Validation failed", + "details": [ + { + "field": "email", + "message": "Invalid email format" + }, + { + "field": "password", + "message": "Password must be at least 8 characters" + } + ] + } +} +``` + +## Data Types + +### UUID + +All IDs are UUIDs (version 4): + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +### Timestamps + +All timestamps are in ISO 8601 format: + +```json +{ + "createdAt": "2024-01-15T10:30:00.000Z", + "updatedAt": "2024-01-15T11:30:00.000Z" +} +``` + +### JSONB + +Complex data is stored as JSONB: + +```json +{ + "validation_rules": { + "minLength": 2, + "maxLength": 100, + "pattern": "^[a-zA-Z]+$" + } +} +``` + +## File Upload + +### Upload Endpoint + +```http +POST /api/upload +Content-Type: multipart/form-data +``` + +### File Limits + +- **Max file size**: 10MB +- **Allowed types**: Images, PDFs, documents +- **Max files per request**: 5 + +### Upload Response + +```json +{ + "success": true, + "data": { + "files": [ + { + "id": "file-uuid", + "filename": "document.pdf", + "size": 1024000, + "url": "https://storage.example.com/files/document.pdf", + "mimeType": "application/pdf" + } + ] + } +} +``` + +## Webhooks + +### Webhook Configuration + +```json +{ + "url": "https://your-app.com/webhooks/form-submission", + "events": ["form.submitted", "form.updated"], + "secret": "webhook-secret-key" +} +``` + +### Webhook Events + +| Event | Description | +|-------|-------------| +| `form.submitted` | Form submission created | +| `form.updated` | Form submission updated | +| `form.deleted` | Form submission deleted | +| `template.published` | Form template published | +| `template.archived` | Form template archived | + +### Webhook Payload + +```json +{ + "event": "form.submitted", + "timestamp": "2024-01-15T10:30:00.000Z", + "data": { + "submissionId": "submission-uuid", + "templateId": "template-uuid", + "submittedBy": "user-uuid" + } +} +``` + +## SDKs and Libraries + +### JavaScript/TypeScript + +```bash +npm install @form-service/sdk +``` + +```javascript +import { FormService } from '@form-service/sdk'; + +const client = new FormService({ + baseUrl: 'https://api.example.com', + token: 'your-jwt-token' +}); + +// Create form template +const template = await client.templates.create({ + name: 'Contact Form', + description: 'A simple contact form' +}); +``` + +### Python + +```bash +pip install form-service-sdk +``` + +```python +from form_service import FormService + +client = FormService( + base_url='https://api.example.com', + token='your-jwt-token' +) + +# Create form template +template = client.templates.create({ + 'name': 'Contact Form', + 'description': 'A simple contact form' +}) +``` + +## Testing + +### Using Bruno Collection + +The project includes a Bruno collection for API testing: + +1. **Install Bruno**: Download from [bruno.com](https://www.bruno.com) +2. **Import Collection**: Import the `bruno/form-service/collection.bru` file +3. **Configure Environment**: Set up environment variables +4. **Run Tests**: Execute requests and test scenarios + +### Example Test Request + +```http +POST {{baseUrl}}/api/forms/templates +Authorization: Bearer {{authToken}} +Content-Type: application/json + +{ + "name": "Test Form", + "description": "A test form for API testing", + "sections": [ + { + "name": "Basic Information", + "fields": [ + { + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true + } + ] + } + ] +} +``` + +## Versioning + +API versioning is handled through URL paths: + +``` +/api/v1/forms/templates # Version 1 +/api/v2/forms/templates # Version 2 +``` + +Current version: `v1` + +## Deprecation Policy + +- **Deprecation notice**: 6 months advance notice +- **Breaking changes**: Only in major versions +- **Backward compatibility**: Maintained within major versions + +## Support + +### Documentation + +- **API Docs**: Available at `/api/docs` +- **Examples**: See the [examples](examples/) directory +- **SDK Docs**: Available in each SDK package + +### Getting Help + +- **GitHub Issues**: Report bugs and request features +- **Discussions**: Ask questions and share ideas +- **Email Support**: Contact support@example.com + +## Next Steps + +- 🔐 [Authentication](api/authentication.md) - Authentication and authorization +- 📝 [Form Templates](api/form-templates.md) - Form template management +- 📊 [Form Submissions](api/form-submissions.md) - Data collection +- 🔗 [Skip Logic](guides/skip-logic.md) - Conditional field visibility +- 🗄️ [Database Schema](schema/database-schema.md) - Database design \ No newline at end of file diff --git a/docs/examples/basic-form.md b/docs/examples/basic-form.md new file mode 100644 index 0000000..9a81dae --- /dev/null +++ b/docs/examples/basic-form.md @@ -0,0 +1,744 @@ +# Basic Form Creation Example + +> Learn how to create a simple contact form using the Form Service API + +## Overview + +This example demonstrates how to create a basic contact form with multiple field types, sections, and simple validation. We'll walk through the entire process from form template creation to form submission. + +## Form Structure + +Our contact form will have the following structure: + +``` +Contact Form +├── Personal Information +│ ├── Full Name (text, required) +│ ├── Email Address (email, required) +│ └── Phone Number (tel, optional) +├── Address Information +│ ├── Street Address (text, required) +│ ├── City (text, required) +│ ├── State (select, required) +│ └── ZIP Code (text, required) +└── Additional Information + ├── Subject (text, required) + └── Message (textarea, required) +``` + +## Step 1: Create Form Template + +### API Request + +POST `/api/forms/templates` + +```json +{ + "name": "Contact Form", + "description": "A simple contact form for customer inquiries", + "sections": [ + { + "name": "Personal Information", + "description": "Basic contact details", + "order": 1, + "fields": [ + { + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true, + "order": 1, + "placeholder": "Enter your full name", + "validation": { + "minLength": 2, + "maxLength": 100 + } + }, + { + "name": "email", + "label": "Email Address", + "type": "email", + "required": true, + "order": 2, + "placeholder": "Enter your email address", + "validation": { + "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" + } + }, + { + "name": "phone", + "label": "Phone Number", + "type": "tel", + "required": false, + "order": 3, + "placeholder": "Enter your phone number", + "validation": { + "pattern": "^[+]?[1-9]\\d{1,14}$" + } + } + ] + }, + { + "name": "Address Information", + "description": "Mailing address details", + "order": 2, + "fields": [ + { + "name": "streetAddress", + "label": "Street Address", + "type": "text", + "required": true, + "order": 1, + "placeholder": "Enter your street address" + }, + { + "name": "city", + "label": "City", + "type": "text", + "required": true, + "order": 2, + "placeholder": "Enter your city" + }, + { + "name": "state", + "label": "State", + "type": "select", + "required": true, + "order": 3, + "options": [ + {"value": "AL", "label": "Alabama"}, + {"value": "AK", "label": "Alaska"}, + {"value": "AZ", "label": "Arizona"}, + {"value": "AR", "label": "Arkansas"}, + {"value": "CA", "label": "California"}, + {"value": "CO", "label": "Colorado"}, + {"value": "CT", "label": "Connecticut"}, + {"value": "DE", "label": "Delaware"}, + {"value": "FL", "label": "Florida"}, + {"value": "GA", "label": "Georgia"}, + {"value": "HI", "label": "Hawaii"}, + {"value": "ID", "label": "Idaho"}, + {"value": "IL", "label": "Illinois"}, + {"value": "IN", "label": "Indiana"}, + {"value": "IA", "label": "Iowa"}, + {"value": "KS", "label": "Kansas"}, + {"value": "KY", "label": "Kentucky"}, + {"value": "LA", "label": "Louisiana"}, + {"value": "ME", "label": "Maine"}, + {"value": "MD", "label": "Maryland"}, + {"value": "MA", "label": "Massachusetts"}, + {"value": "MI", "label": "Michigan"}, + {"value": "MN", "label": "Minnesota"}, + {"value": "MS", "label": "Mississippi"}, + {"value": "MO", "label": "Missouri"}, + {"value": "MT", "label": "Montana"}, + {"value": "NE", "label": "Nebraska"}, + {"value": "NV", "label": "Nevada"}, + {"value": "NH", "label": "New Hampshire"}, + {"value": "NJ", "label": "New Jersey"}, + {"value": "NM", "label": "New Mexico"}, + {"value": "NY", "label": "New York"}, + {"value": "NC", "label": "North Carolina"}, + {"value": "ND", "label": "North Dakota"}, + {"value": "OH", "label": "Ohio"}, + {"value": "OK", "label": "Oklahoma"}, + {"value": "OR", "label": "Oregon"}, + {"value": "PA", "label": "Pennsylvania"}, + {"value": "RI", "label": "Rhode Island"}, + {"value": "SC", "label": "South Carolina"}, + {"value": "SD", "label": "South Dakota"}, + {"value": "TN", "label": "Tennessee"}, + {"value": "TX", "label": "Texas"}, + {"value": "UT", "label": "Utah"}, + {"value": "VT", "label": "Vermont"}, + {"value": "VA", "label": "Virginia"}, + {"value": "WA", "label": "Washington"}, + {"value": "WV", "label": "West Virginia"}, + {"value": "WI", "label": "Wisconsin"}, + {"value": "WY", "label": "Wyoming"} + ] + }, + { + "name": "zipCode", + "label": "ZIP Code", + "type": "text", + "required": true, + "order": 4, + "placeholder": "Enter your ZIP code", + "validation": { + "pattern": "^\\d{5}(-\\d{4})?$" + } + } + ] + }, + { + "name": "Additional Information", + "description": "Inquiry details", + "order": 3, + "fields": [ + { + "name": "subject", + "label": "Subject", + "type": "text", + "required": true, + "order": 1, + "placeholder": "Enter the subject of your inquiry", + "validation": { + "minLength": 5, + "maxLength": 200 + } + }, + { + "name": "message", + "label": "Message", + "type": "textarea", + "required": true, + "order": 2, + "placeholder": "Enter your message", + "rows": 5, + "validation": { + "minLength": 10, + "maxLength": 2000 + } + } + ] + } + ] +} +``` + +### Response + +```json +{ + "success": true, + "data": { + "id": "template-uuid-123", + "name": "Contact Form", + "description": "A simple contact form for customer inquiries", + "sections": [ + { + "id": "section-uuid-1", + "name": "Personal Information", + "description": "Basic contact details", + "order": 1, + "fields": [ + { + "id": "field-uuid-1", + "name": "fullName", + "label": "Full Name", + "type": "text", + "required": true, + "order": 1, + "placeholder": "Enter your full name", + "validation": { + "minLength": 2, + "maxLength": 100 + } + }, + { + "id": "field-uuid-2", + "name": "email", + "label": "Email Address", + "type": "email", + "required": true, + "order": 2, + "placeholder": "Enter your email address", + "validation": { + "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" + } + }, + { + "id": "field-uuid-3", + "name": "phone", + "label": "Phone Number", + "type": "tel", + "required": false, + "order": 3, + "placeholder": "Enter your phone number", + "validation": { + "pattern": "^[+]?[1-9]\\d{1,14}$" + } + } + ] + } + ], + "createdAt": "2024-01-15T10:30:00.000Z", + "updatedAt": "2024-01-15T10:30:00.000Z" + } +} +``` + +## Step 2: Submit Form Data + +### API Request + +POST `/api/forms/submissions` + +```json +{ + "templateId": "template-uuid-123", + "responses": [ + { + "fieldName": "fullName", + "value": "John Doe" + }, + { + "fieldName": "email", + "value": "john.doe@example.com" + }, + { + "fieldName": "phone", + "value": "+1-555-123-4567" + }, + { + "fieldName": "streetAddress", + "value": "123 Main Street" + }, + { + "fieldName": "city", + "value": "New York" + }, + { + "fieldName": "state", + "value": "NY" + }, + { + "fieldName": "zipCode", + "value": "10001" + }, + { + "fieldName": "subject", + "value": "Product Inquiry" + }, + { + "fieldName": "message", + "value": "I would like to know more about your products and pricing. Please send me additional information." + } + ] +} +``` + +### Response + +```json +{ + "success": true, + "data": { + "id": "submission-uuid-456", + "templateId": "template-uuid-123", + "responses": [ + { + "id": "response-uuid-1", + "fieldName": "fullName", + "value": "John Doe", + "fieldId": "field-uuid-1" + }, + { + "id": "response-uuid-2", + "fieldName": "email", + "value": "john.doe@example.com", + "fieldId": "field-uuid-2" + } + ], + "status": "submitted", + "submittedAt": "2024-01-15T10:35:00.000Z", + "createdAt": "2024-01-15T10:35:00.000Z" + } +} +``` + +## Step 3: Retrieve Form Submissions + +### API Request + +GET `/api/forms/submissions?templateId=template-uuid-123` + +### Response + +```json +{ + "success": true, + "data": { + "items": [ + { + "id": "submission-uuid-456", + "templateId": "template-uuid-123", + "responses": [ + { + "fieldName": "fullName", + "value": "John Doe" + }, + { + "fieldName": "email", + "value": "john.doe@example.com" + } + ], + "status": "submitted", + "submittedAt": "2024-01-15T10:35:00.000Z" + } + ], + "pagination": { + "page": 1, + "limit": 10, + "total": 1, + "totalPages": 1, + "hasNext": false, + "hasPrev": false + } + } +} +``` + +## Using Bruno Collection + +### 1. Import the Collection + +1. Open Bruno +2. Import the collection from `bruno/form-service/collection.bru` +3. Set up environment variables in `bruno/form-service/environments/forms-service.bru` + +### 2. Environment Variables + +```json +{ + "baseUrl": "http://localhost:3000/api", + "templateId": "", + "submissionId": "" +} +``` + +### 3. Run the Requests + +1. **Create Form Template**: Run "Create a form template" request +2. **Copy Template ID**: Copy the returned template ID to environment variable +3. **Submit Form**: Run "Create a new form submission" request +4. **View Submissions**: Run "Get form submissions" request + +## Frontend Integration Example + +### React Component + +```jsx +import React, { useState } from 'react'; + +const ContactForm = () => { + const [formData, setFormData] = useState({ + fullName: '', + email: '', + phone: '', + streetAddress: '', + city: '', + state: '', + zipCode: '', + subject: '', + message: '' + }); + + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const handleSubmit = async (e) => { + e.preventDefault(); + setLoading(true); + setError(null); + + try { + const response = await fetch('/api/forms/submissions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + templateId: 'template-uuid-123', + responses: Object.entries(formData).map(([fieldName, value]) => ({ + fieldName, + value + })) + }) + }); + + const result = await response.json(); + + if (result.success) { + alert('Form submitted successfully!'); + setFormData({ + fullName: '', + email: '', + phone: '', + streetAddress: '', + city: '', + state: '', + zipCode: '', + subject: '', + message: '' + }); + } else { + setError(result.error.message); + } + } catch (err) { + setError('Failed to submit form. Please try again.'); + } finally { + setLoading(false); + } + }; + + const handleChange = (field, value) => { + setFormData(prev => ({ + ...prev, + [field]: value + })); + }; + + return ( +
+

Contact Us

+ + {error &&
{error}
} + +
+

Personal Information

+ +
+ + handleChange('fullName', e.target.value)} + placeholder="Enter your full name" + required + /> +
+ +
+ + handleChange('email', e.target.value)} + placeholder="Enter your email address" + required + /> +
+ +
+ + handleChange('phone', e.target.value)} + placeholder="Enter your phone number" + /> +
+
+ +
+

Address Information

+ +
+ + handleChange('streetAddress', e.target.value)} + placeholder="Enter your street address" + required + /> +
+ +
+
+ + handleChange('city', e.target.value)} + placeholder="Enter your city" + required + /> +
+ +
+ + +
+ +
+ + handleChange('zipCode', e.target.value)} + placeholder="Enter your ZIP code" + required + /> +
+
+
+ +
+

Additional Information

+ +
+ + handleChange('subject', e.target.value)} + placeholder="Enter the subject of your inquiry" + required + /> +
+ +
+ +