From bb1601fd09c4db75c656978174f2da37b7dcc96c Mon Sep 17 00:00:00 2001 From: nuwangeek Date: Sun, 8 Mar 2026 14:41:18 +0530 Subject: [PATCH] service integration --- .../hbs/bot_responses_to_messages.handlebars | 14 + DSL/DMapper/rag-search/lib/helpers.js | 29 ++ DSL/Ruuter.public/services/GET/.guard | 28 ++ .../GET/account/user-profile-settings.yml | 27 ++ .../services/GET/active-services.yml | 20 + DSL/Ruuter.public/services/GET/generic/.guard | 4 + .../accounts/customer-support-activity.yml | 22 + .../accounts/user-profile-settings.yml | 26 ++ .../GET/generic/accounts/user-role.yml | 19 + .../services/GET/generic/csa/active-chats.yml | 76 ++++ .../services/GET/generic/userinfo.yml | 29 ++ DSL/Ruuter.public/services/GET/get-sticky.yml | 40 ++ .../services/GET/internal/domain-file.yml | 40 ++ .../GET/internal/return-file-locations.yml | 27 ++ .../GET/mocks/client-input-variables.yml | 19 + .../services/GET/mocks/service-settings.yml | 19 + .../services/GET/mocks/validation-mock.yml | 26 ++ .../services/GET/rasa/rule-names.yml | 29 ++ .../services/GET/secrets-with-priority.yml | 34 ++ DSL/Ruuter.public/services/GET/secrets.yml | 20 + .../services/GET/service-settings.yml | 19 + .../services/GET/services/active/.guard | 4 + .../services/GET/services/draft/.guard | 4 + .../services/GET/services/inactive/.guard | 4 + .../services/GET/services/log-by-request.yml | 33 ++ .../services/GET/services/log-by-service.yml | 33 ++ .../GET/services/services-detailed/nok.yml | 52 +++ .../services/GET/services/statistics.yml | 21 + .../services/GET/services/status.yml | 24 ++ DSL/Ruuter.public/services/GET/slots.yml | 26 ++ .../services/GET/steps/preferences.yml | 72 ++++ .../services/GET/sticky/example.yml | 58 +++ DSL/Ruuter.public/services/POST/.guard | 28 ++ DSL/Ruuter.public/services/POST/auth/.guard | 4 + .../services/POST/auth/login.yml | 101 +++++ DSL/Ruuter.public/services/POST/csv.yml | 50 +++ .../POST/dates/calculate-difference.yml | 82 ++++ .../services/POST/endpoints/common.yml | 48 +++ .../services/POST/file/rename.yml | 42 ++ .../services/POST/mocks/RBAC-mock.yml | 50 +++ .../POST/mocks/dates/calculate-difference.yml | 136 +++++++ .../services/POST/mocks/service-settings.yml | 19 + .../services/POST/mocks/services/add.yml | 44 ++ .../mocks/services/open-api-spec-mock.yml | 17 + .../services/POST/mocks/user-info.yml | 52 +++ .../services/POST/mocks/validation-mock.yml | 26 ++ .../services/POST/rasa/rules/add.yml | 129 ++++++ .../services/POST/saveJsonToYml.yml | 39 ++ .../services/POST/service-by-id.yml | 90 +++++ .../services/POST/service-settings.yml | 29 ++ DSL/Ruuter.public/services/POST/services.yml | 43 ++ .../services/POST/services/active/.guard | 4 + .../services/active/Broneeringu_kinnitus.yml | 65 +++ .../active/Kalastusloa_uuendamise_teade.yml | 60 +++ .../POST/services/active/Koolivaheajad.yml | 63 +++ .../services/active/Lihtne_test_teenus.yml | 61 +++ .../services/active/customer_feedback.yml | 82 ++++ .../services/POST/services/add.yml | 177 ++++++++ .../POST/services/create-endpoint.yml | 46 +++ .../POST/services/delete-endpoint.yml | 34 ++ .../services/POST/services/delete.yml | 155 +++++++ .../services/domain-intent-service-link.yml | 157 ++++++++ .../services/POST/services/draft/.guard | 4 + .../services/POST/services/draft/test.tmp | 48 +++ .../services/POST/services/edit.yml | 381 ++++++++++++++++++ .../POST/services/endpoint-url-validation.yml | 32 ++ .../POST/services/import-services.yml | 71 ++++ .../services/POST/services/inactive/.guard | 4 + .../services/POST/services/open-api-spec.yml | 34 ++ .../POST/services/requests/explain.yml | 95 +++++ .../services/POST/services/resql/add.yml | 62 +++ .../services/POST/services/status.yml | 303 ++++++++++++++ .../POST/services/update-endpoint.yml | 61 +++ .../services/POST/steps/preferences.yml | 68 ++++ DSL/Ruuter.public/services/POST/user-info.yml | 16 + DSL/Ruuter.public/services/TEMPLATES/RBAC.yml | 51 +++ .../TEMPLATES/check-user-authority.yml | 50 +++ .../services/TEMPLATES/client-input.yml | 19 + .../services/TEMPLATES/direct-to-cs.yml | 42 ++ .../services/TEMPLATES/end-conversation.yml | 42 ++ .../services/TEMPLATES/file-generate.yml | 45 +++ .../services/TEMPLATES/file-signing.yml | 35 ++ .../services/TEMPLATES/open-webpage.yml | 44 ++ .../TEMPLATES/send-message-to-client.yml | 42 ++ DSL/Ruuter.public/services/TEMPLATES/siga.yml | 132 ++++++ DSL/Ruuter.public/services/TEMPLATES/tara.yml | 51 +++ .../TEMPLATES/validation-template.yml | 56 +++ constants.ini | 5 +- docs/HYBRID_SEARCH_CLASSIFICATION.md | 59 ++- docs/TOOL_CLASSIFIER_AND_SERVICE_WORKFLOW.md | 262 ++++++------ new.txt | 38 ++ src/tool_classifier/constants.py | 3 + .../workflows/service_workflow.py | 331 ++++++--------- tests/data/classification_test_queries.json | 266 ++++++++++++ 94 files changed, 5246 insertions(+), 337 deletions(-) create mode 100644 DSL/DMapper/rag-search/hbs/bot_responses_to_messages.handlebars create mode 100644 DSL/Ruuter.public/services/GET/.guard create mode 100644 DSL/Ruuter.public/services/GET/account/user-profile-settings.yml create mode 100644 DSL/Ruuter.public/services/GET/active-services.yml create mode 100644 DSL/Ruuter.public/services/GET/generic/.guard create mode 100644 DSL/Ruuter.public/services/GET/generic/accounts/customer-support-activity.yml create mode 100644 DSL/Ruuter.public/services/GET/generic/accounts/user-profile-settings.yml create mode 100644 DSL/Ruuter.public/services/GET/generic/accounts/user-role.yml create mode 100644 DSL/Ruuter.public/services/GET/generic/csa/active-chats.yml create mode 100644 DSL/Ruuter.public/services/GET/generic/userinfo.yml create mode 100644 DSL/Ruuter.public/services/GET/get-sticky.yml create mode 100644 DSL/Ruuter.public/services/GET/internal/domain-file.yml create mode 100644 DSL/Ruuter.public/services/GET/internal/return-file-locations.yml create mode 100644 DSL/Ruuter.public/services/GET/mocks/client-input-variables.yml create mode 100644 DSL/Ruuter.public/services/GET/mocks/service-settings.yml create mode 100644 DSL/Ruuter.public/services/GET/mocks/validation-mock.yml create mode 100644 DSL/Ruuter.public/services/GET/rasa/rule-names.yml create mode 100644 DSL/Ruuter.public/services/GET/secrets-with-priority.yml create mode 100644 DSL/Ruuter.public/services/GET/secrets.yml create mode 100644 DSL/Ruuter.public/services/GET/service-settings.yml create mode 100644 DSL/Ruuter.public/services/GET/services/active/.guard create mode 100644 DSL/Ruuter.public/services/GET/services/draft/.guard create mode 100644 DSL/Ruuter.public/services/GET/services/inactive/.guard create mode 100644 DSL/Ruuter.public/services/GET/services/log-by-request.yml create mode 100644 DSL/Ruuter.public/services/GET/services/log-by-service.yml create mode 100644 DSL/Ruuter.public/services/GET/services/services-detailed/nok.yml create mode 100644 DSL/Ruuter.public/services/GET/services/statistics.yml create mode 100644 DSL/Ruuter.public/services/GET/services/status.yml create mode 100644 DSL/Ruuter.public/services/GET/slots.yml create mode 100644 DSL/Ruuter.public/services/GET/steps/preferences.yml create mode 100644 DSL/Ruuter.public/services/GET/sticky/example.yml create mode 100644 DSL/Ruuter.public/services/POST/.guard create mode 100644 DSL/Ruuter.public/services/POST/auth/.guard create mode 100644 DSL/Ruuter.public/services/POST/auth/login.yml create mode 100644 DSL/Ruuter.public/services/POST/csv.yml create mode 100644 DSL/Ruuter.public/services/POST/dates/calculate-difference.yml create mode 100644 DSL/Ruuter.public/services/POST/endpoints/common.yml create mode 100644 DSL/Ruuter.public/services/POST/file/rename.yml create mode 100644 DSL/Ruuter.public/services/POST/mocks/RBAC-mock.yml create mode 100644 DSL/Ruuter.public/services/POST/mocks/dates/calculate-difference.yml create mode 100644 DSL/Ruuter.public/services/POST/mocks/service-settings.yml create mode 100644 DSL/Ruuter.public/services/POST/mocks/services/add.yml create mode 100644 DSL/Ruuter.public/services/POST/mocks/services/open-api-spec-mock.yml create mode 100644 DSL/Ruuter.public/services/POST/mocks/user-info.yml create mode 100644 DSL/Ruuter.public/services/POST/mocks/validation-mock.yml create mode 100644 DSL/Ruuter.public/services/POST/rasa/rules/add.yml create mode 100644 DSL/Ruuter.public/services/POST/saveJsonToYml.yml create mode 100644 DSL/Ruuter.public/services/POST/service-by-id.yml create mode 100644 DSL/Ruuter.public/services/POST/service-settings.yml create mode 100644 DSL/Ruuter.public/services/POST/services.yml create mode 100644 DSL/Ruuter.public/services/POST/services/active/.guard create mode 100644 DSL/Ruuter.public/services/POST/services/active/Broneeringu_kinnitus.yml create mode 100644 DSL/Ruuter.public/services/POST/services/active/Kalastusloa_uuendamise_teade.yml create mode 100644 DSL/Ruuter.public/services/POST/services/active/Koolivaheajad.yml create mode 100644 DSL/Ruuter.public/services/POST/services/active/Lihtne_test_teenus.yml create mode 100644 DSL/Ruuter.public/services/POST/services/active/customer_feedback.yml create mode 100644 DSL/Ruuter.public/services/POST/services/add.yml create mode 100644 DSL/Ruuter.public/services/POST/services/create-endpoint.yml create mode 100644 DSL/Ruuter.public/services/POST/services/delete-endpoint.yml create mode 100644 DSL/Ruuter.public/services/POST/services/delete.yml create mode 100644 DSL/Ruuter.public/services/POST/services/domain-intent-service-link.yml create mode 100644 DSL/Ruuter.public/services/POST/services/draft/.guard create mode 100644 DSL/Ruuter.public/services/POST/services/draft/test.tmp create mode 100644 DSL/Ruuter.public/services/POST/services/edit.yml create mode 100644 DSL/Ruuter.public/services/POST/services/endpoint-url-validation.yml create mode 100644 DSL/Ruuter.public/services/POST/services/import-services.yml create mode 100644 DSL/Ruuter.public/services/POST/services/inactive/.guard create mode 100644 DSL/Ruuter.public/services/POST/services/open-api-spec.yml create mode 100644 DSL/Ruuter.public/services/POST/services/requests/explain.yml create mode 100644 DSL/Ruuter.public/services/POST/services/resql/add.yml create mode 100644 DSL/Ruuter.public/services/POST/services/status.yml create mode 100644 DSL/Ruuter.public/services/POST/services/update-endpoint.yml create mode 100644 DSL/Ruuter.public/services/POST/steps/preferences.yml create mode 100644 DSL/Ruuter.public/services/POST/user-info.yml create mode 100644 DSL/Ruuter.public/services/TEMPLATES/RBAC.yml create mode 100644 DSL/Ruuter.public/services/TEMPLATES/check-user-authority.yml create mode 100644 DSL/Ruuter.public/services/TEMPLATES/client-input.yml create mode 100644 DSL/Ruuter.public/services/TEMPLATES/direct-to-cs.yml create mode 100644 DSL/Ruuter.public/services/TEMPLATES/end-conversation.yml create mode 100644 DSL/Ruuter.public/services/TEMPLATES/file-generate.yml create mode 100644 DSL/Ruuter.public/services/TEMPLATES/file-signing.yml create mode 100644 DSL/Ruuter.public/services/TEMPLATES/open-webpage.yml create mode 100644 DSL/Ruuter.public/services/TEMPLATES/send-message-to-client.yml create mode 100644 DSL/Ruuter.public/services/TEMPLATES/siga.yml create mode 100644 DSL/Ruuter.public/services/TEMPLATES/tara.yml create mode 100644 DSL/Ruuter.public/services/TEMPLATES/validation-template.yml create mode 100644 new.txt create mode 100644 tests/data/classification_test_queries.json diff --git a/DSL/DMapper/rag-search/hbs/bot_responses_to_messages.handlebars b/DSL/DMapper/rag-search/hbs/bot_responses_to_messages.handlebars new file mode 100644 index 00000000..aa023019 --- /dev/null +++ b/DSL/DMapper/rag-search/hbs/bot_responses_to_messages.handlebars @@ -0,0 +1,14 @@ +[ +{{#each data.botMessages}} + { + "chatId": "{{../data.chatId}}", + "content": "{{filterControlCharacters result}}", + "buttons": "[{{#each ../data.buttons}}{\"title\": \"{{#if (eq title true)}}Yes{{else if (eq title false)}}No{{else}}{{{title}}}{{/if}}\",\"payload\": \"{{{payload}}}\"}{{#unless @last}},{{/unless}}{{/each}}]", + "authorTimestamp": "{{../data.authorTimestamp}}", + "authorId": "{{../data.authorId}}", + "authorFirstName": "{{../data.authorFirstName}}", + "authorLastName": "{{../data.authorLastName}}", + "created": "{{../data.created}}" + }{{#unless @last}},{{/unless}} +{{/each}} +] diff --git a/DSL/DMapper/rag-search/lib/helpers.js b/DSL/DMapper/rag-search/lib/helpers.js index 6f5e74f9..7ecbb7c8 100644 --- a/DSL/DMapper/rag-search/lib/helpers.js +++ b/DSL/DMapper/rag-search/lib/helpers.js @@ -168,6 +168,11 @@ export function getAgencyDataAvailable(agencyId) { return (combinedValue % 2) === 0; } +export function filterControlCharacters(str) { + if (typeof str !== "string") return str; + return str.replace(/[\x00-\x1F\x7F]/g, " "); +} + export function json(context) { return JSON.stringify(context); } @@ -269,3 +274,27 @@ export function filterDataByAgency(aggregatedData, startIndex, agencyId, pageSiz return JSON.stringify(result); } + +export function calculateDateDifference(value) { + const { startDate, endDate, outputType } = value; + const sDate = new Date(startDate); + const eDate = new Date(endDate); + const timeDifferenceInSeconds = (eDate.getTime() - sDate.getTime()) / 1000; + + switch (outputType?.toLowerCase()) { + case 'years': + return eDate.getFullYear() - sDate.getFullYear(); + case 'months': + return eDate.getMonth() - sDate.getMonth() + + (12 * (eDate.getFullYear() - sDate.getFullYear())) + case 'hours': + return Math.round(Math.abs(eDate - sDate) / 36e5); + case 'minutes': + return Math.floor(timeDifferenceInSeconds / 60); + case 'seconds': + return timeDifferenceInSeconds; + default: + return Math.round(timeDifferenceInSeconds / (3600 * 24)); + } +} + diff --git a/DSL/Ruuter.public/services/GET/.guard b/DSL/Ruuter.public/services/GET/.guard new file mode 100644 index 00000000..4fd565b6 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/.guard @@ -0,0 +1,28 @@ +check_for_cookie: + switch: + - condition: ${incoming.headers == null || incoming.headers.cookie == null} + next: guard_fail + next: authenticate + +authenticate: + template: "[#SERVICE_PROJECT_LAYER]/check-user-authority" + requestType: templates + headers: + cookie: ${incoming.headers.cookie} + result: authority_result + +check_authority_result: + switch: + - condition: ${authority_result !== "false"} + next: guard_success + next: guard_fail + +guard_success: + return: "success" + status: 200 + next: end + +guard_fail: + return: "unauthorized" + status: 401 + next: end diff --git a/DSL/Ruuter.public/services/GET/account/user-profile-settings.yml b/DSL/Ruuter.public/services/GET/account/user-profile-settings.yml new file mode 100644 index 00000000..320d4af9 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/account/user-profile-settings.yml @@ -0,0 +1,27 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'USER-PROFILE-SETTINGS'" + method: get + accepts: json + returns: json + namespace: service + +step_1: + call: reflect.mock + args: + response: + response: + - userId: EE30303039914 + forwardedChatPopupNotifications: false + forwardedChatSoundNotifications: false + forwardedChatEmailNotifications: false + newChatPopupNotifications: false + newChatSoundNotifications: false + newChatEmailNotifications: false + useAutocorrect: true + result: reflected_request + +step_2: + wrapper: false + return: ${reflected_request.response.body} diff --git a/DSL/Ruuter.public/services/GET/active-services.yml b/DSL/Ruuter.public/services/GET/active-services.yml new file mode 100644 index 00000000..873bb54e --- /dev/null +++ b/DSL/Ruuter.public/services/GET/active-services.yml @@ -0,0 +1,20 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'ACTIVE-SERVICES'" + method: get + accepts: json + returns: json + namespace: service + +get_services_list: + call: http.post + args: + url: "[#SERVICE_RESQL]/get-active-services-list" + result: results + +return_ok: + status: 200 + wrapper: false + return: ${results.response.body} + next: end diff --git a/DSL/Ruuter.public/services/GET/generic/.guard b/DSL/Ruuter.public/services/GET/generic/.guard new file mode 100644 index 00000000..64435377 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/generic/.guard @@ -0,0 +1,4 @@ +guard_allow_all: + return: "success" + status: 200 + next: end diff --git a/DSL/Ruuter.public/services/GET/generic/accounts/customer-support-activity.yml b/DSL/Ruuter.public/services/GET/generic/accounts/customer-support-activity.yml new file mode 100644 index 00000000..25c2e33b --- /dev/null +++ b/DSL/Ruuter.public/services/GET/generic/accounts/customer-support-activity.yml @@ -0,0 +1,22 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'CUSTOMER-SUPPORT-ACTIVITY'" + method: get + accepts: json + returns: json + namespace: service + +step_1: + call: reflect.mock + args: + response: + response: + - idCode: 'EE49902216518' + active: 'true' + status: 'idle' + result: reflected_request + +step_2: + wrapper: true + return: ${reflected_request.response.body} diff --git a/DSL/Ruuter.public/services/GET/generic/accounts/user-profile-settings.yml b/DSL/Ruuter.public/services/GET/generic/accounts/user-profile-settings.yml new file mode 100644 index 00000000..344b83cb --- /dev/null +++ b/DSL/Ruuter.public/services/GET/generic/accounts/user-profile-settings.yml @@ -0,0 +1,26 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'USER-PROFILE-SETTINGS'" + method: get + accepts: json + returns: json + namespace: service + +step_1: + call: reflect.mock + args: + response: + data: + - userId: EE30303039914 + forwardedChatPopupNotifications: false + forwardedChatSoundNotifications: false + forwardedChatEmailNotifications: false + newChatPopupNotifications: false + newChatSoundNotifications: false + newChatEmailNotifications: false + useAutocorrect: true + result: reflected_request + +step_2: + return: ${reflected_request.response.body} diff --git a/DSL/Ruuter.public/services/GET/generic/accounts/user-role.yml b/DSL/Ruuter.public/services/GET/generic/accounts/user-role.yml new file mode 100644 index 00000000..7794dbb6 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/generic/accounts/user-role.yml @@ -0,0 +1,19 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'USER-ROLE'" + method: get + accepts: json + returns: json + namespace: service + +step_1: + call: reflect.mock + args: + response: + response: + - "ROLE_ADMINISTRATOR" + result: reflected_request + +step_2: + return: ${reflected_request.response.body} diff --git a/DSL/Ruuter.public/services/GET/generic/csa/active-chats.yml b/DSL/Ruuter.public/services/GET/generic/csa/active-chats.yml new file mode 100644 index 00000000..02d95eb8 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/generic/csa/active-chats.yml @@ -0,0 +1,76 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'ACTIVE-CHATS'" + method: get + accepts: json + returns: json + namespace: service + +step_1: + call: reflect.mock + args: + response: + response: + - id: '22fa5630-6f92-4d50-92ba-685c872383af' + customerSupportId: '' + customerSupportDisplayName: '' + endUserId: '' + endUserFirstName: '' + endUserLastName: '' + status: 'OPEN' + created: '2023-01-17T13:18:38.808+00:00' + updated: '2023-01-17T13:19:26.348+00:00' + ended: null + endUserOs: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36' + endUserUrl: 'https://test.buerokratt.ee/' + forwardedToName: null + forwardedByUser: '' + forwardedFromCsa: '' + forwardedToCsa: '' + lastMessage: 'Suunan teid klienditeenindajale. Varuge natukene kannatust.' + contactsMessage: null + lastMessageTimestamp: '2023-01-17T13:19:26.316+00:00' + - id: '5206b7bd-0812-40a8-ae1d-3774f07f06f0' + customerSupportId: '' + customerSupportDisplayName: '' + endUserId: '' + endUserFirstName: '' + endUserLastName: '' + status: 'OPEN' + created: '2023-01-19T13:38:32.421+00:00' + updated: '2023-01-19T13:38:32.430+00:00' + ended: null + endUserOs: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36' + endUserUrl: 'https://test.buerokratt.ee/' + forwardedToName: null + forwardedByUser: '' + forwardedFromCsa: '' + forwardedToCsa: '' + lastMessage: 'aitäh' + contactsMessage: null + lastMessageTimestamp: '2022-11-23T09:33:56.803+00:00' + - id: 'b7bba1c2-b7ab-4b17-825a-2d66a7d16fc4' + customerSupportId: '' + customerSupportDisplayName: '' + endUserId: '' + endUserFirstName: '' + endUserLastName: '' + status: 'OPEN' + created: '2023-01-19T13:38:32.421+00:00' + updated: '2023-01-19T13:38:32.430+00:00' + ended: null + endUserOs: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36' + endUserUrl: 'https://test.buerokratt.ee/' + forwardedToName: null + forwardedByUser: '' + forwardedFromCsa: '' + forwardedToCsa: '' + lastMessage: 'dasnhpwa' + contactsMessage: null + lastMessageTimestamp: '2023-01-18T12:24:54.557+00:00' + result: reflected_request + +step_2: + wrapper: true + return: ${reflected_request.response.body} diff --git a/DSL/Ruuter.public/services/GET/generic/userinfo.yml b/DSL/Ruuter.public/services/GET/generic/userinfo.yml new file mode 100644 index 00000000..1278132c --- /dev/null +++ b/DSL/Ruuter.public/services/GET/generic/userinfo.yml @@ -0,0 +1,29 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'USERINFO'" + method: get + accepts: json + returns: json + namespace: service + +step_1: + call: reflect.mock + args: + response: + data: + firstName: OK + lastName: TESTNUMBER + idCode: EE30303039914 + displayName: OK + JWTCreated: 1704724715000 + login: EE30303039914 + csaEmail: mail@mail.ee + authorities: + - ROLE_ADMINISTRATOR + csaTitle: OG + JWTExpirationTimestamp: 1704739715000 + result: reflected_request + +step_2: + return: ${reflected_request.response.body} diff --git a/DSL/Ruuter.public/services/GET/get-sticky.yml b/DSL/Ruuter.public/services/GET/get-sticky.yml new file mode 100644 index 00000000..ca906d6d --- /dev/null +++ b/DSL/Ruuter.public/services/GET/get-sticky.yml @@ -0,0 +1,40 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'GET-STICKY'" + method: get + accepts: json + returns: json + namespace: service + allowlist: + params: + - field: name + type: string + description: "Parameter 'name'" + +check_for_parameters: + switch: + - condition: ${incoming.params == null || incoming.params.name == null} + next: get_all_sticky_services + next: get_single_sticky_service + +get_single_sticky_service: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/ruuter/sticky/steps" + query: + name: ${incoming.params.name} + result: results + next: return_ok + +get_all_sticky_services: + call: http.get + args: + url: "[#SERVICE_DMAPPER]/ruuter/sticky" + result: results + next: return_ok + +return_ok: + status: 200 + return: ${results.response.body} + next: end diff --git a/DSL/Ruuter.public/services/GET/internal/domain-file.yml b/DSL/Ruuter.public/services/GET/internal/domain-file.yml new file mode 100644 index 00000000..2e42dc93 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/internal/domain-file.yml @@ -0,0 +1,40 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'DOMAIN-FILE'" + method: get + accepts: json + returns: json + namespace: service + allowlist: + headers: + - field: cookie + type: string + description: "Cookie field" + +getFileLocations: + call: http.get + args: + url: "[#SERVICE_RUUTER]/internal/return-file-locations" + headers: + cookie: ${incoming.headers.cookie} + result: fileLocations + +getDomainFile: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/read-file" + body: + file_path: ${fileLocations.response.body.response.domain_location} + result: domainFile + +convertYamlToJson: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/conversion/yaml_to_json" + body: + file: ${domainFile.response.body.file} + result: domainData + +return_value: + return: ${domainData.response.body} diff --git a/DSL/Ruuter.public/services/GET/internal/return-file-locations.yml b/DSL/Ruuter.public/services/GET/internal/return-file-locations.yml new file mode 100644 index 00000000..0ebab6ab --- /dev/null +++ b/DSL/Ruuter.public/services/GET/internal/return-file-locations.yml @@ -0,0 +1,27 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'RETURN-FILE-LOCATIONS'" + method: get + accepts: json + returns: json + namespace: service + allowlist: + headers: + - field: cookie + type: string + description: "Cookie field" + +assign_step: + assign: + locations: + rules_location: "[#DMAPPER_LOCATIONS_PATH]/data/rules.yml" + stories_location: "[#DMAPPER_LOCATIONS_PATH]/data/stories.yml" + domain_location: "[#DMAPPER_LOCATIONS_PATH]/data/domain.yml" + test_stories_location: "[#DMAPPER_LOCATIONS_PATH]/test_stories.yml" + intents_location: "[#DMAPPER_LOCATIONS_PATH]/nlu/" + regex_location: "[#DMAPPER_LOCATIONS_PATH]/regex" + training_result_location: "[#DMAPPER_LOCATIONS_PATH]/results" + config_location: "[#DMAPPER_LOCATIONS_PATH]/data/config.yml" +return_value: + return: ${locations} diff --git a/DSL/Ruuter.public/services/GET/mocks/client-input-variables.yml b/DSL/Ruuter.public/services/GET/mocks/client-input-variables.yml new file mode 100644 index 00000000..dab285a3 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/mocks/client-input-variables.yml @@ -0,0 +1,19 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'CLIENT-INPUT-VARIABLES'" + method: get + accepts: json + returns: json + namespace: service + +mock_variables: + call: reflect.mock + args: + response: + data: ['{{user.firstname}}', '{{user.lastname}}', '{{user.birthdate}}', '{{user.email}}', '{{invoice.total}}', '{{invoice.subtotal}}'] + result: mock_res + +return_result: + wrapper: false + return: ${mock_res.response.body.data} diff --git a/DSL/Ruuter.public/services/GET/mocks/service-settings.yml b/DSL/Ruuter.public/services/GET/mocks/service-settings.yml new file mode 100644 index 00000000..35482b26 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/mocks/service-settings.yml @@ -0,0 +1,19 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'SERVICE-SETTINGS'" + method: get + accepts: json + returns: json + namespace: service + +call_mock: + call: reflect.mock + args: + response: + maxInputTry: 4 + result: mock_res + +return_result: + wrapper: false + return: ${mock_res.response.body} diff --git a/DSL/Ruuter.public/services/GET/mocks/validation-mock.yml b/DSL/Ruuter.public/services/GET/mocks/validation-mock.yml new file mode 100644 index 00000000..e4452555 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/mocks/validation-mock.yml @@ -0,0 +1,26 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'VALIDATION-MOCK'" + method: get + accepts: json + returns: json + namespace: service + +call_mock: + call: reflect.mock + args: + response: + project: "Bürokratt" + website: "www.kratid.ee" + result: mock_res + +call_template: + template: "[#SERVICE_PROJECT_LAYER]/validation-template" + requestType: templates + body: + response: ${mock_res.response} + result: templateResult + +return_result: + return: ${templateResult} diff --git a/DSL/Ruuter.public/services/GET/rasa/rule-names.yml b/DSL/Ruuter.public/services/GET/rasa/rule-names.yml new file mode 100644 index 00000000..5fd45b29 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/rasa/rule-names.yml @@ -0,0 +1,29 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'RULE-NAMES'" + method: get + accepts: json + returns: json + namespace: service + +getRules: + call: http.get + args: + url: "[#SERVICE_OPENSEARCH]/rules/_search?size=1000" + result: getRulesResult + +mapRulesData: + call: http.post + args: + url: "[#SERVICE_DMAPPER_HBS]/get_rule_names" + headers: + type: 'json' + body: + hits: ${getRulesResult.response.body.hits.hits} + result: rulesData + next: returnSuccess + +returnSuccess: + return: ${rulesData.response.body} + next: end diff --git a/DSL/Ruuter.public/services/GET/secrets-with-priority.yml b/DSL/Ruuter.public/services/GET/secrets-with-priority.yml new file mode 100644 index 00000000..602eeff6 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/secrets-with-priority.yml @@ -0,0 +1,34 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'SECRETS-WITH-PRIORITY'" + method: get + accepts: json + returns: json + namespace: service + +check_parameters: + switch: + - condition: ${incoming.params !== null && incoming.params.type === 'test'} + next: get_ruuter_secrets_test_priority + next: get_ruuter_secrets_prod_priority + +get_ruuter_secrets_prod_priority: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/secrets/get-with-priority" + result: results + next: return_ok + +get_ruuter_secrets_test_priority: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/secrets/get-with-priority?priority=test" + result: results + next: return_ok + +return_ok: + status: 200 + wrapper: false + return: ${results.response.body} + next: end diff --git a/DSL/Ruuter.public/services/GET/secrets.yml b/DSL/Ruuter.public/services/GET/secrets.yml new file mode 100644 index 00000000..60913c81 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/secrets.yml @@ -0,0 +1,20 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'SECRETS'" + method: get + accepts: json + returns: json + namespace: service + +get_ruuter_secrets: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/secrets/get-all" + result: results + +return_ok: + status: 200 + wrapper: false + return: ${results.response.body} + next: end diff --git a/DSL/Ruuter.public/services/GET/service-settings.yml b/DSL/Ruuter.public/services/GET/service-settings.yml new file mode 100644 index 00000000..abe4ff0f --- /dev/null +++ b/DSL/Ruuter.public/services/GET/service-settings.yml @@ -0,0 +1,19 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'SERVICE-SETTINGS'" + method: get + accepts: json + returns: json + namespace: service + +updateSettings: + call: http.post + args: + url: "[#SERVICE_RESQL]/get-settings" + result: results + +returnSuccess: + wrapper: false + status: 200 + return: ${results.response.body} diff --git a/DSL/Ruuter.public/services/GET/services/active/.guard b/DSL/Ruuter.public/services/GET/services/active/.guard new file mode 100644 index 00000000..64435377 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/services/active/.guard @@ -0,0 +1,4 @@ +guard_allow_all: + return: "success" + status: 200 + next: end diff --git a/DSL/Ruuter.public/services/GET/services/draft/.guard b/DSL/Ruuter.public/services/GET/services/draft/.guard new file mode 100644 index 00000000..64435377 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/services/draft/.guard @@ -0,0 +1,4 @@ +guard_allow_all: + return: "success" + status: 200 + next: end diff --git a/DSL/Ruuter.public/services/GET/services/inactive/.guard b/DSL/Ruuter.public/services/GET/services/inactive/.guard new file mode 100644 index 00000000..64435377 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/services/inactive/.guard @@ -0,0 +1,4 @@ +guard_allow_all: + return: "success" + status: 200 + next: end diff --git a/DSL/Ruuter.public/services/GET/services/log-by-request.yml b/DSL/Ruuter.public/services/GET/services/log-by-request.yml new file mode 100644 index 00000000..9ee096b1 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/services/log-by-request.yml @@ -0,0 +1,33 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'LOG-BY-REQUEST'" + method: get + accepts: json + returns: json + namespace: service + +get_services_stat: + call: http.post + args: + url: "[#SERVICE_OPENSEARCH]/services/_search/template" + body: + id: 'get-log-by-request' + params: ${incoming.params} + result: results + +check_result: + switch: + - condition: ${results.response.body.found === true} + next: return_ok + next: return_not_found + +return_not_found: + status: 404 + return: 'index not found' + next: end + +return_ok: + status: 200 + return: ${results.response.body._source} + next: end diff --git a/DSL/Ruuter.public/services/GET/services/log-by-service.yml b/DSL/Ruuter.public/services/GET/services/log-by-service.yml new file mode 100644 index 00000000..0613800a --- /dev/null +++ b/DSL/Ruuter.public/services/GET/services/log-by-service.yml @@ -0,0 +1,33 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'LOG-BY-SERVICE'" + method: get + accepts: json + returns: json + namespace: service + +get_services_stat: + call: http.post + args: + url: "[#SERVICE_OPENSEARCH]/services/_search/template" + body: + id: 'get-log-by-service' + params: ${incoming.params} + result: results + +check_result: + switch: + - condition: ${results.response.body.found === true} + next: return_ok + next: return_not_found + +return_not_found: + status: 404 + return: 'index not found' + next: end + +return_ok: + status: 200 + return: ${results.response.body._source} + next: end diff --git a/DSL/Ruuter.public/services/GET/services/services-detailed/nok.yml b/DSL/Ruuter.public/services/GET/services/services-detailed/nok.yml new file mode 100644 index 00000000..5fea8712 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/services/services-detailed/nok.yml @@ -0,0 +1,52 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'NOK'" + method: get + accepts: json + returns: json + namespace: service + allowlist: + params: + - field: page + type: number + description: "Parameter 'page'" + - field: page_size + type: number + description: "Parameter 'page_size'" + - field: sorting + type: string + description: "Parameter 'sorting'" + - field: order + type: string + description: "Parameter 'order'" + +getFaults: + call: http.post + args: + url: "[#SERVICE_OPENSEARCH]/ruuterlog/_search" + query: + from: ${(incoming.params.page - 1) * incoming.params.page_size} + size: ${incoming.params.page_size} + _source_excludes: "stackTrace,statusCode" + body: + sort: [{ "timestamp": { "order": "${incoming.params.order}" } }] + query: + match_phrase_prefix: + dslName: + query: "services/active" + result: getFaultsResult + +mapFaultsData: + call: http.post + args: + url: "[#SERVICE_DMAPPER_HBS]/get-faults" + headers: + type: "json" + body: + data: { "hits": "${getFaultsResult.response.body.hits.hits}" } + result: faultsData + +returnSuccess: + wrapper: false + return: ${[faultsData.response.body, getFaultsResult.response.body.hits.total.value]} diff --git a/DSL/Ruuter.public/services/GET/services/statistics.yml b/DSL/Ruuter.public/services/GET/services/statistics.yml new file mode 100644 index 00000000..6b3110fc --- /dev/null +++ b/DSL/Ruuter.public/services/GET/services/statistics.yml @@ -0,0 +1,21 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'STATISTICS'" + method: get + accepts: json + returns: json + namespace: service + +get_services_stat: + call: http.post + args: + url: "[#SERVICE_OPENSEARCH]/services/_search/template" + body: + id: 'get-services-stat' + result: results + +return_ok: + status: 200 + return: ${results.response.body.hits.hits} + next: end diff --git a/DSL/Ruuter.public/services/GET/services/status.yml b/DSL/Ruuter.public/services/GET/services/status.yml new file mode 100644 index 00000000..779451af --- /dev/null +++ b/DSL/Ruuter.public/services/GET/services/status.yml @@ -0,0 +1,24 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'STATUS'" + method: get + accepts: json + returns: json + namespace: service + allowlist: + params: + - field: id + type: string + description: "Parameter 'id'" + +get_status: + call: http.post + args: + url: "[#SERVICE_RESQL]/status" + body: + id: ${incoming.params.id} + result: res + +return_value: + return: ${res.response.body} diff --git a/DSL/Ruuter.public/services/GET/slots.yml b/DSL/Ruuter.public/services/GET/slots.yml new file mode 100644 index 00000000..d38375cd --- /dev/null +++ b/DSL/Ruuter.public/services/GET/slots.yml @@ -0,0 +1,26 @@ +declaration: + call: declare + version: 0.1 + description: "Get slots from OpenSearch" + method: get + accepts: json + returns: json + namespace: service + +getSlots: + call: http.post + args: + url: "[#SERVICE_OPENSEARCH]/domain/_search/template" + body: + id: "domain-objects-with-pagination" + params: + type: "slots" + filter: "" + from: 0 + size: 1000 + result: getSlotsResult + +returnSuccess: + return: ${getSlotsResult.response.body.hits.hits[0].fields.filtered_items[0]} + wrapper: false + next: end diff --git a/DSL/Ruuter.public/services/GET/steps/preferences.yml b/DSL/Ruuter.public/services/GET/steps/preferences.yml new file mode 100644 index 00000000..283ac696 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/steps/preferences.yml @@ -0,0 +1,72 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'PREFERENCES'" + method: get + accepts: json + returns: json + namespace: service + +get_user_info: + call: http.post + args: + url: "[#SERVICE_TIM]/jwt/custom-jwt-userinfo" + contentType: plaintext + headers: + cookie: ${incoming.headers.cookie} + plaintext: "customJwtCookie" + result: res + +check_user_info_response: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: assignIdCode + next: return_unauthorized + +assignIdCode: + assign: + idCode: ${res.response.body.idCode} + +get_user_step_preferences: + call: http.post + args: + url: "[#SERVICE_RESQL]/get-user-step-preferences" + body: + user_id_code: ${idCode} + result: preferences + +check_preferences_response: + switch: + - condition: ${preferences.response.body.length > 0} + next: return_preferences + next: seed_default_user_preferences + +seed_default_user_preferences: + call: http.post + args: + url: "[#SERVICE_RESQL]/seed-user-step-preferences" + body: + user_id_code: ${idCode} + result: seed_preferences_res + next: refetch_user_step_preferences + +refetch_user_step_preferences: + call: http.post + args: + url: "[#SERVICE_RESQL]/get-user-step-preferences" + body: + user_id_code: ${idCode} + result: refetched_preferences + +return_refetched_preferences: + return: ${refetched_preferences.response.body[0]} + next: end + +return_preferences: + return: ${preferences.response.body[0]} + next: end + +return_unauthorized: + status: 401 + return: "unauthorized" + next: end diff --git a/DSL/Ruuter.public/services/GET/sticky/example.yml b/DSL/Ruuter.public/services/GET/sticky/example.yml new file mode 100644 index 00000000..cedef902 --- /dev/null +++ b/DSL/Ruuter.public/services/GET/sticky/example.yml @@ -0,0 +1,58 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'EXAMPLE'" + method: get + accepts: json + returns: json + namespace: service + allowlist: + headers: + - field: cookie + type: string + description: "Cookie field" + +check_for_cookie: + switch: + - condition: ${incoming.headers.cookie == null || incoming.headers.cookie == ""} + next: return_unauthorized + next: extract_request_data + +extract_request_data: + assign: + cookie: ${incoming.headers.cookie} + +extract_cookie_data: + call: http.post + args: + url: "[#SERVICE_RUUTER]/mocks/mock-custom-jwt-userinfo" + headers: + cookie: ${cookie} + body: + cookieName: "customJwtCookie" + result: jwtResult + next: allow_only_admins + +allow_only_admins: + switch: + - condition: ${jwtResult.response.body.response.authorities.includes("ROLE_ADMIN")} + next: get_data + next: return_unauthorized + +get_data: + call: reflect.mock + args: + response: + type: "mock-value" + id: 1234567 + result: reflectedRequest + next: return_value + +return_value: + return: ${reflectedRequest.response.body} + next: end + +return_unauthorized: + status: 401 + return: "unauthorized" + next: end diff --git a/DSL/Ruuter.public/services/POST/.guard b/DSL/Ruuter.public/services/POST/.guard new file mode 100644 index 00000000..4fd565b6 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/.guard @@ -0,0 +1,28 @@ +check_for_cookie: + switch: + - condition: ${incoming.headers == null || incoming.headers.cookie == null} + next: guard_fail + next: authenticate + +authenticate: + template: "[#SERVICE_PROJECT_LAYER]/check-user-authority" + requestType: templates + headers: + cookie: ${incoming.headers.cookie} + result: authority_result + +check_authority_result: + switch: + - condition: ${authority_result !== "false"} + next: guard_success + next: guard_fail + +guard_success: + return: "success" + status: 200 + next: end + +guard_fail: + return: "unauthorized" + status: 401 + next: end diff --git a/DSL/Ruuter.public/services/POST/auth/.guard b/DSL/Ruuter.public/services/POST/auth/.guard new file mode 100644 index 00000000..64435377 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/auth/.guard @@ -0,0 +1,4 @@ +guard_allow_all: + return: "success" + status: 200 + next: end diff --git a/DSL/Ruuter.public/services/POST/auth/login.yml b/DSL/Ruuter.public/services/POST/auth/login.yml new file mode 100644 index 00000000..30771935 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/auth/login.yml @@ -0,0 +1,101 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'LOGIN'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: login + type: string + description: "Body field 'login'" + - field: password + type: string + description: "Body field 'password'" + +check_for_required_parameters: + switch: + - condition: ${incoming.body.login == null || incoming.body.password == null} + next: return_incorrect_request + next: extract_request_data + +extract_request_data: + assign: + login: ${incoming.body.login} + password: ${incoming.body.password} + next: login_user + +login_user: + call: http.post + args: + url: "[#SERVICE_USERS_RESQL]/get-user-by-login" + body: + login: ${login} + password: ${password} + result: results + next: check_login_result + +check_login_result: + switch: + - condition: ${results.response.body.length != 0} + next: get_session_length + next: return_user_not_found + +get_session_length: + call: http.post + args: + url: "[#SERVICE_USERS_RESQL]/get-configuration" + body: + key: "session_length" + result: session_result + next: check_session_length_result + +check_session_length_result: + switch: + - condition: ${session_result.response.body.length != 0} + next: generate_cookie + next: return_session_length_not_found + +generate_cookie: + call: http.post + args: + url: "[#SERVICE_TIM]/jwt/custom-jwt-generate" + body: + JWTName: "customJwtCookie" + expirationInMinutes: ${session_result.response.body[0].value} + content: ${results.response.body[0]} + result: cookie_result + next: assign_cookie + +assign_cookie: + assign: + setCookie: + customJwtCookie: ${cookie_result.response.body.token} + Domain: "[#DOMAIN]" + Secure: true + HttpOnly: true + SameSite: "Lax" + next: return_value + +return_value: + headers: + Set-Cookie: ${setCookie} + return: ${cookie_result.response.body.token} + next: end + +return_session_length_not_found: + status: 400 + return: "Could not fetch session length" + next: end + +return_user_not_found: + status: 400 + return: "User Not Found" + next: end + +return_incorrect_request: + status: 400 + return: "Required parameter(s) missing" + next: end diff --git a/DSL/Ruuter.public/services/POST/csv.yml b/DSL/Ruuter.public/services/POST/csv.yml new file mode 100644 index 00000000..52e1f177 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/csv.yml @@ -0,0 +1,50 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'CSV'" + method: post + accepts: json + returns: data + namespace: service + allowlist: + body: + - field: data + type: string + description: "Body field 'data'" + - field: del + type: string + description: "Body field 'del'" + - field: qul + type: string + description: "Body field 'qul'" + +check_for_required_parameters: + switch: + - condition: ${incoming.body == null} + next: return_incorrect_request + next: get_csv + +get_csv: + call: http.post + args: + url: "[#SERVICE_DMAPPER_HBS]/get-csv" + headers: + type: 'csv' + body: + data: ${incoming.body.data} + del: ${incoming.body.del} + qul: ${incoming.body.qul} + layout: false + result: result + +return_value: + wrapper: false + headers: + Content-disposition: "attachment;filename=result.csv" + return: ${result.response.body.response} + next: end + +return_incorrect_request: + status: 400 + return: 'missing parameters' + next: end diff --git a/DSL/Ruuter.public/services/POST/dates/calculate-difference.yml b/DSL/Ruuter.public/services/POST/dates/calculate-difference.yml new file mode 100644 index 00000000..2c5afd4e --- /dev/null +++ b/DSL/Ruuter.public/services/POST/dates/calculate-difference.yml @@ -0,0 +1,82 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'CALCULATE-DIFFERENCE'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: startDate + type: string + description: "Body field 'startDate'" + - field: endDate + type: string + description: "Body field 'endDate'" + params: + - field: outputType + type: string + description: "Parameter 'outputType'" + +check_for_body: + switch: + - condition: ${incoming.body == null} + next: return_incorrect_request + next: check_for_required_parameters + +check_for_required_parameters: + switch: + - condition: ${incoming.body.startDate == null} + next: return_incorrect_request + next: extract_request_data + +extract_request_data: + assign: + outputType: ${incoming.params.outputType ?? 'days'} + startDate: ${incoming.body.startDate} + endDate: ${incoming.body.endDate ?? new Date().toISOString()} + +check_is_end_date_greater_than_start_date: + switch: + - condition: ${new Date(endDate) < new Date(startDate)} + next: return_incorrect_date + +check_is_output_type_valid: + switch: + - condition: ${outputType !== null && !['years','months','hours','days','minutes', 'seconds'].includes(outputType)} + next: return_incorrect_output_type + +calculate_difference: + call: http.post + args: + url: "[#SERVICE_DMAPPER_HBS]/calculate-date-difference" + headers: + type: "json" + body: + startDate: ${startDate} + endDate: ${endDate} + outputType: ${outputType} + layout: false + result: result + next: return_value + +return_value: + status: 200 + return: ${result.response.body} + next: end + +return_incorrect_date: + status: 400 + return: "Start date can not be greater than the end date/ today" + next: end + +return_incorrect_output_type: + status: 400 + return: "Output type must be: years, months, hours, days, minutes or seconds" + next: end + +return_incorrect_request: + status: 400 + return: "Start date is required" + next: end diff --git a/DSL/Ruuter.public/services/POST/endpoints/common.yml b/DSL/Ruuter.public/services/POST/endpoints/common.yml new file mode 100644 index 00000000..611faca7 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/endpoints/common.yml @@ -0,0 +1,48 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'Common'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: pagination + type: string + description: "Body field 'pagination'" + - field: page + type: string + description: "Body field 'page'" + - field: pageSize + type: string + description: "Body field 'pageSize'" + - field: sorting + type: string + description: "Body field 'sorting'" + - field: search + type: string + description: "Body field 'search'" + +extract_request_data: + assign: + pagination: ${incoming.body.pagination} + page: ${incoming.body.page} + pageSize: ${incoming.body.pageSize} + sorting: ${incoming.body.sorting} + search: ${incoming.body.search} + +get_common_endpoints: + call: http.post + args: + url: "[#SERVICE_RESQL]/endpoints/get_common_endpoints" + body: + pagination: ${pagination} + page: ${page} + page_size: ${pageSize} + sorting: ${sorting} + search: ${search} + result: res + +return_result: + return: ${res.response.body} diff --git a/DSL/Ruuter.public/services/POST/file/rename.yml b/DSL/Ruuter.public/services/POST/file/rename.yml new file mode 100644 index 00000000..c0e7b1c6 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/file/rename.yml @@ -0,0 +1,42 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'RENAME'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: current_path + type: string + description: "Body field 'current_path'" + - field: new_path + type: string + description: "Body field 'new_path'" + +check_for_body: + switch: + - condition: ${incoming.body == null || incoming.body.current_path == null || incoming.body.new_path == null} + next: return_incorrect_request + +rename_file: + call: http.post + args: + url: "[#SERVICE_DMAPPER_HBS]/rename" + headers: + type: 'json' + body: + current_path: ${incoming.body.current_path} + new_path: ${incoming.body.new_path} + layout: false + result: result + +return_value: + status: 200 + return: ${result.response.body} + next: end + +return_incorrect_request: + status: 400 + return: "Start date is required" diff --git a/DSL/Ruuter.public/services/POST/mocks/RBAC-mock.yml b/DSL/Ruuter.public/services/POST/mocks/RBAC-mock.yml new file mode 100644 index 00000000..eabaf3f8 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/mocks/RBAC-mock.yml @@ -0,0 +1,50 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'RBAC-MOCK'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: allowedRoles + type: object + description: "Body field 'allowedRoles'" + - field: userId + type: string + description: "Body field 'userId'" + + +check_for_body: + switch: + - condition: ${incoming.body == null} + next: missing_parameter + +extract_request_data: + assign: + userId: ${incoming.body.userId} + allowedRoles: ${incoming.body.allowedRoles.sort()} + +check_for_required_parameters: + switch: + - condition: ${userId === null || allowedRoles === null} + next: missing_parameter + next: fetch_user_roles_from_db + +fetch_user_roles_from_db: + call: reflect.mock + args: + response: + isAllowed: TRUE + result: result + +return_value: + status: 200 + return: "${result.response.body}" + next: end + +missing_parameter: + status: 400 + return: "userId, allowedRoles - missing" + next: end diff --git a/DSL/Ruuter.public/services/POST/mocks/dates/calculate-difference.yml b/DSL/Ruuter.public/services/POST/mocks/dates/calculate-difference.yml new file mode 100644 index 00000000..261e2273 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/mocks/dates/calculate-difference.yml @@ -0,0 +1,136 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'CALCULATE-DIFFERENCE'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: startDate + type: string + description: "Body field 'startDate'" + - field: endDate + type: string + description: "Body field 'endDate'" + params: + - field: outputType + type: string + description: "Parameter 'outputType'" + +check_for_body: + switch: + - condition: ${incoming.body == null} + next: return_incorrect_request + next: check_for_required_parameters + +check_for_required_parameters: + switch: + - condition: ${incoming.body.startDate == null} + next: return_incorrect_request + next: extract_request_data + +extract_request_data: + assign: + outputType: ${incoming.params.outputType ?? 'days'} + startDate: ${incoming.body.startDate} + endDate: ${incoming.body.endDate ?? new Date().toISOString()} + +check_is_end_date_greater_than_start_date: + switch: + - condition: ${new Date(endDate) < new Date(startDate)} + next: return_incorrect_date + +check_is_output_type_valid: + switch: + - condition: ${outputType !== null && !['years','months','hours','days','minutes', 'seconds'].includes(outputType)} + next: return_incorrect_output_type + +calculate_difference: + switch: + - condition: ${outputType === 'years'} + next: calculate_difference_in_years + - condition: ${outputType === 'months'} + next: calculate_difference_in_months + - condition: ${outputType === 'hours'} + next: calculate_difference_in_hours + - condition: ${outputType === 'minutes'} + next: calculate_difference_in_minutes + - condition: ${outputType === 'seconds'} + next: calculate_difference_in_seconds + next: calculate_difference_in_days + +calculate_difference_in_years: + call: reflect.mock + args: + response: { + result: 0 + } + result: result + next: return_value + +calculate_difference_in_months: + call: reflect.mock + args: + response: { + result: 11 + } + result: result + next: return_value + +calculate_difference_in_days: + call: reflect.mock + args: + response: { + result: 1 + } + result: result + next: return_value + +calculate_difference_in_hours: + call: reflect.mock + args: + response: { + result: 24 + } + result: result + next: return_value + +calculate_difference_in_minutes: + call: reflect.mock + args: + response: { + result: 59 + } + result: result + next: return_value + +calculate_difference_in_seconds: + call: reflect.mock + args: + response: { + result: 201 + } + result: result + next: return_value + +return_value: + status: 200 + return: ${result.response.body} + next: end + +return_incorrect_date: + status: 400 + return: "Start date can not be greater than the end date/ today" + next: end + +return_incorrect_output_type: + status: 400 + return: "Output type must be: years, months, hours, days, minutes or seconds" + next: end + +return_incorrect_request: + status: 400 + return: "Start date is required" + next: end diff --git a/DSL/Ruuter.public/services/POST/mocks/service-settings.yml b/DSL/Ruuter.public/services/POST/mocks/service-settings.yml new file mode 100644 index 00000000..c22da07f --- /dev/null +++ b/DSL/Ruuter.public/services/POST/mocks/service-settings.yml @@ -0,0 +1,19 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'SERVICE-SETTINGS'" + method: post + accepts: json + returns: json + namespace: service + +call_mock: + call: reflect.mock + args: + response: + maxInputTry: 4 + result: mock_res + +return_result: + wrapper: false + return: ${mock_res.response.body} diff --git a/DSL/Ruuter.public/services/POST/mocks/services/add.yml b/DSL/Ruuter.public/services/POST/mocks/services/add.yml new file mode 100644 index 00000000..3b1e46c2 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/mocks/services/add.yml @@ -0,0 +1,44 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'ADD'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: name + type: string + description: "Body field 'name'" + - field: description + type: string + description: "Body field 'description'" + +check_for_body: + switch: + - condition: ${incoming.body == null} + next: return_incorrect_request + next: check_for_required_parameters + +check_for_required_parameters: + switch: + - condition: ${incoming.body.name == null || incoming.body.description == null} + next: return_incorrect_request + next: service_add + +service_add: + call: reflect.mock + args: + response: {} + result: createdService + +return_value: + status: 201 + wrapper: FALSE + return: "" + next: end + +return_incorrect_request: + status: 400 + return: "Required parameter(s) missing" diff --git a/DSL/Ruuter.public/services/POST/mocks/services/open-api-spec-mock.yml b/DSL/Ruuter.public/services/POST/mocks/services/open-api-spec-mock.yml new file mode 100644 index 00000000..e8ea3f87 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/mocks/services/open-api-spec-mock.yml @@ -0,0 +1,17 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'OPEN-API-SPEC-MOCK'" + method: post + accepts: json + returns: json + namespace: service + +get_message: + call: http.get + args: + url: https://petstore3.swagger.io/api/v3/openapi.json + result: res + +return_value: + return: ${res.response.body} diff --git a/DSL/Ruuter.public/services/POST/mocks/user-info.yml b/DSL/Ruuter.public/services/POST/mocks/user-info.yml new file mode 100644 index 00000000..ac0b322a --- /dev/null +++ b/DSL/Ruuter.public/services/POST/mocks/user-info.yml @@ -0,0 +1,52 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'USER-INFO'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: isTokenExpired + type: boolean + description: "Body field 'isTokenExpired'" + +check_for_body: + switch: + - condition: ${incoming.body == null} + next: return_user_info_mock + next: do_decision + +do_decision: + switch: + - condition: ${incoming.body.isTokenExpired === true} + next: return_unauthorized + next: return_user_info_mock + +return_unauthorized: + status: 401 + return: "Unauthorized" + next: end + +return_user_info_mock: + call: reflect.mock + args: + response: + sub: "" + firstName: "MARY ÄNN" + idCode: "EE60001019906" + displayName: "MARY ÄNN" + iss: "test.buerokratt.ee" + exp: 1670250948 + login: "EE60001019906" + iat: 1670243748 + jti: "e14a5084-3b30-4a55-8720-c2ee22f43c2c" + authorities: [ + "ROLE_ADMINISTRATOR" + ] + result: reflected_request + next: return_value + +return_value: + return: ${reflected_request.response} diff --git a/DSL/Ruuter.public/services/POST/mocks/validation-mock.yml b/DSL/Ruuter.public/services/POST/mocks/validation-mock.yml new file mode 100644 index 00000000..fb8cb4a7 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/mocks/validation-mock.yml @@ -0,0 +1,26 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'VALIDATION-MOCK'" + method: post + accepts: json + returns: json + namespace: service + +call_mock: + call: reflect.mock + args: + response: + project: "Bürokratt" + website: "www.kratid.ee" + result: mock_res + +call_template: + template: "[#SERVICE_PROJECT_LAYER]/validation-template" + requestType: templates + body: + response: ${mock_res.response} + result: templateResult + +return_result: + return: ${templateResult} diff --git a/DSL/Ruuter.public/services/POST/rasa/rules/add.yml b/DSL/Ruuter.public/services/POST/rasa/rules/add.yml new file mode 100644 index 00000000..c41581ab --- /dev/null +++ b/DSL/Ruuter.public/services/POST/rasa/rules/add.yml @@ -0,0 +1,129 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'ADD'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: data + type: object + description: "Body field 'data'" + headers: + - field: cookie + type: string + description: "Cookie field" + +assign_values: + assign: + body: ${incoming.body.data} + +validateRules: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/validate/validate-stories-rules" + body: + story: ${body} + category: "rules" + result: validateRulesResult + +validateRulesCheck: + switch: + - condition: ${validateRulesResult.response.body.result == true} + next: getRuleNames + next: returnDuplicateIntentOrEntity + +getRuleNames: + call: http.get + args: + url: "[#SERVICE_RUUTER]/rasa/rule-names" + headers: + cookie: ${incoming.headers.cookie} + result: ruleResult + +validateRuleName: + switch: + - condition: ${ruleResult.response.body.response.names == null} + next: getFileLocations + - condition: ${!ruleResult.response.body.response.names.includes(body.id)} + next: getFileLocations + next: returnStoryExists + +getFileLocations: + call: http.get + args: + url: "[#SERVICE_RUUTER]/internal/return-file-locations" + headers: + cookie: ${incoming.headers.cookie} + result: fileLocations + +getRulesFile: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/read-file" + body: + file_path: ${fileLocations.response.body.response.rules_location} + result: ruleFile + +convertYamlToJson: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/conversion/yaml_to_json" + body: + file: ${ruleFile.response.body.file} + result: rulesData + +mergeRules: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/merge" + body: + array1: ${rulesData.response.body.rules ?? []} + array2: ${[body]} + iteratee: "rule" + result: mergedRules + +convertJsonToYaml: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/conversion/json-to-yaml-stories" + headers: + content-type: "application/json" + body: + rules: ${mergedRules.response.body.array} + result: rulesYaml + +saveRulesFile: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/create" + body: + file_path: ${fileLocations.response.body.response.rules_location} + content: ${rulesYaml.response.body.json} + result: fileResult + next: updateOpenSearch + +updateOpenSearch: + call: http.post + args: + url: "[#SERVICES_PIPELINE]/bulk/rules/rule" + body: + input: ${rulesYaml.response.body.json} + result: updateSearchResult + next: returnSuccess + +returnSuccess: + return: "Rule added" + next: end + +returnRuleExists: + return: "Rule exists" + status: 409 + next: end + +returnDuplicateIntentOrEntity: + return: "Rule may not have duplicate consecutive intents or entities" + status: 406 + next: end diff --git a/DSL/Ruuter.public/services/POST/saveJsonToYml.yml b/DSL/Ruuter.public/services/POST/saveJsonToYml.yml new file mode 100644 index 00000000..4305cb28 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/saveJsonToYml.yml @@ -0,0 +1,39 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'SAVEJSONTOYML'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: result + type: string + description: "Body field 'result'" + params: + - field: location + type: string + description: "Parameter 'location'" + +toYml: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/conversion/json_to_yaml_data" + body: + data: ${incoming.body.result} + result: r + +saveFile: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/create" + body: + file_path: ${incoming.params.location} + content: ${r.response.body.yaml} + result: fileResult + +saved_seccessfully: + reloadDsl: true + return: "" + next: end diff --git a/DSL/Ruuter.public/services/POST/service-by-id.yml b/DSL/Ruuter.public/services/POST/service-by-id.yml new file mode 100644 index 00000000..45fd1b03 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/service-by-id.yml @@ -0,0 +1,90 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'SERVICE-BY-ID'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + header: + - field: cookie + type: string + description: "Cookie field" + body: + - field: id + type: string + description: "Body field 'id'" + - field: search + type: string + description: "Body field 'search'" + +extract_request_data: + assign: + id: ${incoming.body.id} + search: ${incoming.body.search} + +get_user_info: + call: http.post + args: + url: "[#SERVICE_TIM]/jwt/custom-jwt-userinfo" + contentType: plaintext + headers: + cookie: ${incoming.headers.cookie} + plaintext: "customJwtCookie" + result: res + +check_user_info_response: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: assignIdCode + next: return_unauthorized + +assignIdCode: + assign: + idCode: ${res.response.body.idCode} + +get_service_by_id: + call: http.post + args: + url: "[#SERVICE_RESQL]/get-service-by-id" + body: + id: ${id} + result: service_results + +get_endpoints_by_service_id: + call: http.post + args: + url: "[#SERVICE_RESQL]/endpoints/get_endpoints_by_service_id" + body: + id: ${id} + user_id_code: ${idCode} + search: ${search} + result: endpoints_results + +prepare_results: + assign: + results: + id: ${service_results.response.body[0].id} + name: ${service_results.response.body[0].name} + description: ${service_results.response.body[0].description} + slot: ${service_results.response.body[0].slot} + examples: ${service_results.response.body[0].examples} + entities: ${service_results.response.body[0].entities} + state: ${service_results.response.body[0].state} + type: ${service_results.response.body[0].type} + isCommon: ${service_results.response.body[0].isCommon} + structure: ${service_results.response.body[0].structure} + endpoints: ${endpoints_results.response.body} + serviceId: ${service_results.response.body[0].serviceId} + +return_ok: + status: 200 + wrapper: false + return: ${results} + next: end + +return_unauthorized: + status: 401 + return: "unauthorized" + next: end diff --git a/DSL/Ruuter.public/services/POST/service-settings.yml b/DSL/Ruuter.public/services/POST/service-settings.yml new file mode 100644 index 00000000..af9021c4 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/service-settings.yml @@ -0,0 +1,29 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'SERVICE-SETTINGS'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: name + type: string + description: "Body field 'name'" + - field: value + type: string + description: "Body field 'value'" + +updateSettings: + call: http.post + args: + url: "[#SERVICE_RESQL]/update-settings" + body: + name: ${incoming.body.name} + value: ${incoming.body.value} + result: getResult + +returnSuccess: + status: 200 + return: 'ok' diff --git a/DSL/Ruuter.public/services/POST/services.yml b/DSL/Ruuter.public/services/POST/services.yml new file mode 100644 index 00000000..8188fac2 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services.yml @@ -0,0 +1,43 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'SERVICES'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: page + type: number + description: "Body field 'page'" + - field: page_size + type: number + description: "Body field 'page_size'" + - field: sorting + type: string + description: "Body field 'sorting'" + - field: is_common + type: boolean + description: "Body field 'is_common'" + +get_services_list: + call: http.post + args: + url: "[#SERVICE_RESQL]/get-services-list" + body: + page: ${incoming.body.page} + page_size: ${incoming.body.page_size} + sorting: ${incoming.body.sorting} + is_common: ${incoming.body.is_common} + limit: 400 + result: services_res + +assign_services_result: + assign: + services: ${services_res.response.body} + +return_ok: + status: 200 + return: ${[services]} + next: end diff --git a/DSL/Ruuter.public/services/POST/services/active/.guard b/DSL/Ruuter.public/services/POST/services/active/.guard new file mode 100644 index 00000000..64435377 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/active/.guard @@ -0,0 +1,4 @@ +guard_allow_all: + return: "success" + status: 200 + next: end diff --git a/DSL/Ruuter.public/services/POST/services/active/Broneeringu_kinnitus.yml b/DSL/Ruuter.public/services/POST/services/active/Broneeringu_kinnitus.yml new file mode 100644 index 00000000..ff07aba5 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/active/Broneeringu_kinnitus.yml @@ -0,0 +1,65 @@ +declaration: + call: declare + version: 0.1 + description: Teenuse test, mis kinnitab kasutaja broneeringu ja tagastab + sisestatud väärtuse. + method: post + accepts: json + returns: json + namespace: service + allowList: + body: + - field: chatId + type: string + description: The chat ID for the message + - field: authorId + type: string + description: The author ID for the message + - field: input + type: object + description: The Input from the user +prepare: + assign: + chatId: ${incoming.body.chatId} + authorId: ${incoming.body.authorId} + input: ${incoming.body.input} + buttons: [] + res: + result: "" + next: assign_1 +assign_1: + assign: + entity: ${incoming.body.input[0]} + next: sõnum_kliendile_1 +sõnum_kliendile_1: + assign: + res: + result: "Teie broneering on registreeritud. Kohtumiseni! Entity: ${entity}" + next: teenuse_lõpetamine_1 +teenuse_lõpetamine_1: + template: "[#SERVICE_PROJECT_LAYER]/end-conversation" + requestType: templates + body: + message: "" + result: teenuse_lõpetamine_1_result + next: format_messages +format_messages: + call: http.post + args: + url: "[#SERVICE_DMAPPER_HBS]/bot_responses_to_messages" + headers: + type: json + body: + data: + botMessages: ${[res]} + chatId: ${chatId ?? ''} + authorId: ${authorId ?? ''} + authorFirstName: "" + authorLastName: "" + authorTimestamp: ${new Date().toISOString()} + created: ${new Date().toISOString()} + buttons: ${buttons ?? []} + result: formatMessage + next: service-end +service-end: + return: ${formatMessage.response.body ?? ''} diff --git a/DSL/Ruuter.public/services/POST/services/active/Kalastusloa_uuendamise_teade.yml b/DSL/Ruuter.public/services/POST/services/active/Kalastusloa_uuendamise_teade.yml new file mode 100644 index 00000000..18b213d7 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/active/Kalastusloa_uuendamise_teade.yml @@ -0,0 +1,60 @@ +declaration: + call: declare + version: 0.1 + description: Teenuse test, mis teavitab kasutajat, et tema kalastusluba vajab uuendamist. + method: post + accepts: json + returns: json + namespace: service + allowList: + body: + - field: chatId + type: string + description: The chat ID for the message + - field: authorId + type: string + description: The author ID for the message + - field: input + type: object + description: The Input from the user +prepare: + assign: + chatId: ${incoming.body.chatId} + authorId: ${incoming.body.authorId} + input: ${incoming.body.input} + buttons: [] + res: + result: "" + next: send_message_to_client_1 +send_message_to_client_1: + assign: + res: + result: Su kalastusluba vajab uuendamist! + next: end_service_1 +end_service_1: + template: "[#SERVICE_PROJECT_LAYER]/end-conversation" + requestType: templates + body: + message: "" + result: end_service_1_result + next: format_messages +format_messages: + call: http.post + args: + url: "[#SERVICE_DMAPPER_HBS]/bot_responses_to_messages" + headers: + type: json + body: + data: + botMessages: ${[res]} + chatId: ${chatId ?? ''} + authorId: ${authorId ?? ''} + authorFirstName: "" + authorLastName: "" + authorTimestamp: ${new Date().toISOString()} + created: ${new Date().toISOString()} + buttons: ${buttons ?? []} + result: formatMessage + next: service-end +service-end: + return: ${formatMessage.response.body ?? ''} diff --git a/DSL/Ruuter.public/services/POST/services/active/Koolivaheajad.yml b/DSL/Ruuter.public/services/POST/services/active/Koolivaheajad.yml new file mode 100644 index 00000000..df123c09 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/active/Koolivaheajad.yml @@ -0,0 +1,63 @@ +declaration: + call: declare + version: 0.1 + description: Kasutaja soovib infot koolivaheaegade kohta antud õppeaastal. + method: post + accepts: json + returns: json + namespace: service + allowList: + body: + - field: chatId + type: string + description: The chat ID for the message + - field: authorId + type: string + description: The author ID for the message + - field: input + type: object + description: The Input from the user +prepare: + assign: + chatId: ${incoming.body.chatId} + authorId: ${incoming.body.authorId} + input: ${incoming.body.input} + buttons: [] + res: + result: "" + next: sõnum_kliendile_1 +sõnum_kliendile_1: + assign: + res: + result: E 20. oktoober 2025 - P 26. oktoober 2025 Sügisvaheaeg E 22. detsember + 2025 - P 11. jaanuar 2026 Jõuluvaheaeg E 23. veebruar 2026 - P 01. märts + 2026 Talvevaheaeg E 13. aprill 2026 - P 19. aprill 2026 Kevadvaheaeg K + 17. juuni 2026 - E 31. august 2026 Suvevaheaeg + next: teenuse_lõpetamine_1 +teenuse_lõpetamine_1: + template: "[#SERVICE_PROJECT_LAYER]/end-conversation" + requestType: templates + body: + message: "" + result: teenuse_lõpetamine_1_result + next: format_messages +format_messages: + call: http.post + args: + url: "[#SERVICE_DMAPPER_HBS]/bot_responses_to_messages" + headers: + type: json + body: + data: + botMessages: ${[res]} + chatId: ${chatId ?? ''} + authorId: ${authorId ?? ''} + authorFirstName: "" + authorLastName: "" + authorTimestamp: ${new Date().toISOString()} + created: ${new Date().toISOString()} + buttons: ${buttons ?? []} + result: formatMessage + next: service-end +service-end: + return: ${formatMessage.response.body ?? ''} diff --git a/DSL/Ruuter.public/services/POST/services/active/Lihtne_test_teenus.yml b/DSL/Ruuter.public/services/POST/services/active/Lihtne_test_teenus.yml new file mode 100644 index 00000000..67373c3d --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/active/Lihtne_test_teenus.yml @@ -0,0 +1,61 @@ +declaration: + call: declare + version: 0.1 + description: Lihtne testteenus, mis küsib kasutajalt valikvastustega küsimusi ja + suunab vastavalt vastusele järgmisse sammu. + method: post + accepts: json + returns: json + namespace: service + allowList: + body: + - field: chatId + type: string + description: The chat ID for the message + - field: authorId + type: string + description: The author ID for the message + - field: input + type: object + description: The Input from the user +prepare: + assign: + chatId: ${incoming.body.chatId} + authorId: ${incoming.body.authorId} + input: ${incoming.body.input} + buttons: [] + res: + result: "" + next: multi_choice_question_1 +multi_choice_question_1: + assign: + buttons: + - id: "1" + title: Jah + payload: "#service, /POST/services/active/lihtne_teenus_test_mcq_1_0" + - id: "2" + title: Ei + payload: "#service, /POST/services/active/lihtne_teenus_test_mcq_1_1" + res: + result: lithsa teenuse küsims + next: format_messages +format_messages: + call: http.post + args: + url: "[#SERVICE_DMAPPER_HBS]/bot_responses_to_messages" + headers: + type: json + body: + data: + botMessages: ${[res]} + chatId: ${chatId ?? ''} + authorId: ${authorId ?? ''} + authorFirstName: "" + authorLastName: "" + authorTimestamp: ${new Date().toISOString()} + created: ${new Date().toISOString()} + buttons: ${buttons ?? []} + result: formatMessage + next: service-end +service-end: + return: ${formatMessage.response.body ?? ''} diff --git a/DSL/Ruuter.public/services/POST/services/active/customer_feedback.yml b/DSL/Ruuter.public/services/POST/services/active/customer_feedback.yml new file mode 100644 index 00000000..0f858468 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/active/customer_feedback.yml @@ -0,0 +1,82 @@ +declaration: + call: declare + version: 0.1 + description: Description placeholder for 'customer_feedback' + method: post + accepts: json + returns: json + namespace: service + allowList: + body: + - field: chatId + type: string + description: The chat ID for the message + - field: authorId + type: string + description: The author ID for the message + - field: input + type: object + description: The Input from the user +prepare: + assign: + chatId: ${incoming.body.chatId} + authorId: ${incoming.body.authorId} + input: ${incoming.body.input} + buttons: [] + res: + result: "" + next: assign_1 +assign_1: + assign: + customer_rating: ${incoming.body.input[0]} + next: condition_1 +condition_1: + switch: + - condition: ${customer_rating > 3} + next: send_message_to_client_1 + next: send_message_to_client_2 +send_message_to_client_1: + assign: + res: + result: Thank you for your positive feedback with ${customer_rating} + next: end_service_1 +send_message_to_client_2: + assign: + res: + result: Thank you for your feedback with rating ${customer_rating} . and we are + trying to improve the system + next: end_service_2 +end_service_1: + template: "[#SERVICE_PROJECT_LAYER]/end-conversation" + requestType: templates + body: + message: "" + result: end_service_1_result + next: format_messages +end_service_2: + template: "[#SERVICE_PROJECT_LAYER]/end-conversation" + requestType: templates + body: + message: "" + result: end_service_2_result + next: format_messages +format_messages: + call: http.post + args: + url: "[#SERVICE_DMAPPER_HBS]/bot_responses_to_messages" + headers: + type: json + body: + data: + botMessages: ${[res]} + chatId: ${chatId ?? ''} + authorId: ${authorId ?? ''} + authorFirstName: "" + authorLastName: "" + authorTimestamp: ${new Date().toISOString()} + created: ${new Date().toISOString()} + buttons: ${buttons ?? []} + result: formatMessage + next: service-end +service-end: + return: ${formatMessage.response.body ?? ''} diff --git a/DSL/Ruuter.public/services/POST/services/add.yml b/DSL/Ruuter.public/services/POST/services/add.yml new file mode 100644 index 00000000..a9a81ec4 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/add.yml @@ -0,0 +1,177 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'ADD'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + header: + - field: cookie + type: string + description: "Cookie field" + body: + - field: content + type: string + description: "Body field 'content'" + - field: description + type: string + description: "Body field 'description'" + - field: entities + type: object + description: "Body field 'Entities'" + - field: examples + type: object + description: "Body field 'Examples'" + - field: isCommon + type: boolean + description: "Body field 'isCommon'" + - field: name + type: string + description: "Body field 'name'" + - field: serviceId + type: string + description: "Body field 'serviceId'" + - field: structure + type: object + description: "Body field 'structure'" + - field: type + type: string + description: "Body field 'type'" + - field: updateServiceDb + type: boolean + description: "Body field 'updateServiceDb'" + - field: state + type: string + description: "Body field 'state'" + +check_for_required_parameters: + switch: + - condition: ${incoming.body.name == null || incoming.body.description == null || incoming.body.type == null || incoming.body.content == null || incoming.body.serviceId == null || incoming.body.isCommon == null || incoming.body.structure == null} + next: return_incorrect_request + next: extract_request_data + +extract_request_data: + assign: + name: ${incoming.body.name} + description: ${incoming.body.description} + slot: ${""} + entities: ${incoming.body.entities} + examples: ${incoming.body.examples} + type: ${incoming.body.type.toUpperCase()} + content: ${incoming.body.content} + serviceId: ${incoming.body.serviceId} + isCommon: ${incoming.body.isCommon} + structure: ${incoming.body.structure} + state: ${incoming.body.state} + next: check_if_update_service_db + +check_if_update_service_db: + switch: + - condition: ${incoming.body.updateServiceDb === true} + next: check_if_name_exists + next: delete_all_mcq_files + +check_if_name_exists: + call: http.post + args: + url: "[#SERVICE_RESQL]/services/check_name_exist" + body: + name: ${name} + result: name_exists_res + next: check_name_exists_result + +check_name_exists_result: + switch: + - condition: ${name_exists_res.response.body[0].nameExists} + next: return_name_already_exists + next: service_add + +service_add: + call: http.post + args: + url: "[#SERVICE_RESQL]/add" + body: + name: ${name} + description: ${description} + slot: ${slot} + entities: ${entities} + examples: ${examples} + ruuter_type: ${type} + service_id: ${serviceId} + is_common: ${isCommon} + state: ${state} + structure: ${structure} + result: createdService + next: convert_json_content_to_yml + +delete_all_mcq_files: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/delete-all-that-starts-with" + body: + path: "[#RUUTER_SERVICES_PATH]/${type}/services/draft" + keyword: "${name}_" + result: deleteRes + +convert_json_content_to_yml: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/conversion/json_to_yaml_data" + body: + data: ${content} + result: ymlResult + +check_for_type: + switch: + - condition: ${type === 'GET'} + next: add_get_dsl + next: add_post_dsl + +add_get_dsl: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/create" + body: + file_path: "[#RUUTER_SERVICES_GET_PATH]/draft/${name}.tmp" + content: ${ymlResult.response.body.yaml} + result: results + next: check_result + +add_post_dsl: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/create" + body: + file_path: "[#RUUTER_SERVICES_POST_PATH]/draft/${name}.tmp" + content: ${ymlResult.response.body.yaml} + result: results + next: check_result + +check_result: + switch: + - condition: ${200 <= results.response.statusCodeValue && results.response.statusCodeValue < 300} + next: return_ok + next: return_bad_request + +return_ok: + reloadDsl: true + status: 200 + return: ${results.response.body.message} + next: end + +return_bad_request: + status: 400 + return: ${results.response.body.message} + next: end + +return_incorrect_request: + status: 400 + return: "Required parameter(s) missing" + next: end + +return_name_already_exists: + status: 409 + return: "Service name already exists" + next: end diff --git a/DSL/Ruuter.public/services/POST/services/create-endpoint.yml b/DSL/Ruuter.public/services/POST/services/create-endpoint.yml new file mode 100644 index 00000000..2c73fd45 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/create-endpoint.yml @@ -0,0 +1,46 @@ +declaration: + call: declare + version: 0.1 + description: "Creates a new endpoint" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: endpointId + type: string + description: "Endpoint UUID" + - field: name + type: string + description: "Endpoint name" + - field: type + type: string + description: "Endpoint type" + - field: isCommon + type: boolean + description: "Endpoint common status" + - field: serviceId + type: string + description: "Service UUID" + - field: definitions + type: object + description: "Endpoint definitions" + +create_endpoint: + call: http.post + args: + url: "[#SERVICE_RESQL]/endpoints/create_endpoint" + body: + endpointId: ${incoming.body.endpointId} + name: ${incoming.body.name} + type: ${incoming.body.type} + isCommon: ${incoming.body.isCommon} + serviceId: ${incoming.body.serviceId ?? ''} + definitions: ${incoming.body.definitions} + result: res + +return_ok: + status: 200 + return: "Endpoint created" + next: end diff --git a/DSL/Ruuter.public/services/POST/services/delete-endpoint.yml b/DSL/Ruuter.public/services/POST/services/delete-endpoint.yml new file mode 100644 index 00000000..f585495e --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/delete-endpoint.yml @@ -0,0 +1,34 @@ +declaration: + call: declare + version: 0.1 + description: "Deletes an endpoint" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: id + type: string + description: "Endpoint UUID" + +delete_endpoint: + call: http.post + args: + url: "[#SERVICE_RESQL]/endpoints/delete_endpoint" + body: + id: ${incoming.body.id} + result: res + +remove_from_preferences: + call: http.post + args: + url: "[#SERVICE_RESQL]/endpoints/remove_endpoint_from_preferences" + body: + endpoint_id: ${incoming.body.id} + result: preferences_res + +return_ok: + status: 200 + return: "Endpoint deleted" + next: end diff --git a/DSL/Ruuter.public/services/POST/services/delete.yml b/DSL/Ruuter.public/services/POST/services/delete.yml new file mode 100644 index 00000000..c9a7bef5 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/delete.yml @@ -0,0 +1,155 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'DELETE'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + headers: + - field: cookie + type: string + description: "Cookie field" + body: + - field: id + type: string + description: "Body field 'id'" + - field: type + type: string + description: "Body field 'type'" + +check_for_required_parameters: + switch: + - condition: ${incoming.body.id == null || incoming.body.type == null} + next: return_incorrect_request + next: extract_request_data + +extract_request_data: + assign: + id: ${incoming.body.id} + ruuter_type: ${incoming.body.type.toUpperCase()} + next: get_service_name + +get_service_name: + call: http.post + args: + url: "[#SERVICE_RESQL]/get-service-name-by-id" + body: + id: ${id} + result: name_res + next: get_current_status + +get_current_status: + call: http.post + args: + url: "[#SERVICE_RESQL]/status" + body: + id: ${id} + result: status_res + next: check_status + +check_status: + switch: + - condition: ${status_res.response.body[0].currentState === 'active'} + next: return_cannot_delete_active_service + - condition: ${status_res.response.body[0].currentState === 'ready'} + next: assign_draft_path + next: assign_old_path + +assign_old_path: + assign: + old_file_status_path: ${status_res.response.body[0].currentState} + next: delete_service + +assign_draft_path: + assign: + old_file_status_path: "draft" + next: delete_service + +delete_service: + call: http.post + args: + url: "[#SERVICE_RESQL]/delete-service" + body: + id: ${id} + result: res + next: check_service_file_exists + +check_service_file_exists: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/exists" + body: + file_path: "[#RUUTER_SERVICES_PATH]/${ruuter_type}/[#RUUTER_SERVICES_DIR_PATH]/${old_file_status_path}/${name_res.response.body[0].name}.tmp" + result: service_file_exists + next: validate_service_file_exists + +validate_service_file_exists: + switch: + - condition: ${!!service_file_exists.response.body} + next: delete_deactivated_service + next: delete_endpoints_by_service_id + +delete_deactivated_service: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/delete" + body: + file_path: "[#RUUTER_SERVICES_PATH]/${ruuter_type}/[#RUUTER_SERVICES_DIR_PATH]/${old_file_status_path}/${name_res.response.body[0].name}.tmp" + result: results + next: check_result + +check_result: + switch: + - condition: ${200 <= results.response.statusCodeValue && results.response.statusCodeValue < 300} + next: delete_endpoints_by_service_id + next: return_bad_request + +delete_endpoints_by_service_id: + call: http.post + args: + url: "[#SERVICE_RESQL]/endpoints/delete_endpoints_by_service_id" + body: + serviceId: ${id} + result: delete_endpoint_results + next: remove_service_endpoints_from_preferences + +remove_service_endpoints_from_preferences: + call: http.post + args: + url: "[#SERVICE_RESQL]/endpoints/remove_service_endpoints_from_preferences" + body: + serviceId: ${id} + result: remove_preferences_results + next: delete_mcq_files + +delete_mcq_files: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/delete-all-that-starts-with" + body: + path: "[#RUUTER_SERVICES_PATH]/${ruuter_type}/[#RUUTER_SERVICES_DIR_PATH]/${old_file_status_path}" + keyword: "${name_res.response.body[0].name}_" + result: deleted_mcq + +return_ok: + reloadDsl: true + status: 200 + return: "Service Deleted Successfully" + next: end + +return_bad_request: + status: 400 + return: ${results.response.body.message} + next: end + +return_incorrect_request: + status: 400 + return: "Required parameter(s) missing" + next: end + +return_cannot_delete_active_service: + status: 400 + return: "Cannot delete active service" + next: end diff --git a/DSL/Ruuter.public/services/POST/services/domain-intent-service-link.yml b/DSL/Ruuter.public/services/POST/services/domain-intent-service-link.yml new file mode 100644 index 00000000..b38fcd43 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/domain-intent-service-link.yml @@ -0,0 +1,157 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'domain-intent-service-link'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: intent + type: string + description: "Body field 'intent'" + - field: serviceName + type: string + description: "Body field 'serviceName'" + - field: serviceMethod + type: string + description: "Body field 'serviceMethod'" + - field: serviceSlot + type: string + description: "Slot input" + - field: type + type: string + description: "Body field 'type'" + header: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + serviceName: "${incoming.body.serviceName || ''}" + serviceMethod: "${incoming.body.serviceMethod || 'POST'}" + serviceSlot: ", ({${incoming.body.serviceSlot ? incoming.body.serviceSlot : ''}})" + intent: "${incoming.body.intent}" + type: "${incoming.body.type}" + service_path: "#service, /${serviceMethod}/services/active/${serviceName + (incoming.body.serviceSlot ? serviceSlot : '')}" + +get_file_locations: + call: http.get + args: + url: "[#SERVICE_RUUTER]/internal/return-file-locations" + headers: + cookie: ${incoming.headers.cookie} + result: fileLocations + next: get_domain_file + +get_domain_file: + call: http.get + args: + url: "[#SERVICE_RUUTER]/internal/domain-file" + headers: + cookie: ${incoming.headers.cookie} + result: domainData + next: assign_domain_file_data + +assign_domain_file_data: + assign: + domain_data_json: ${domainData.response.body.response} + next: check_if_intent_exists + +check_if_intent_exists: + switch: + - condition: ${domain_data_json.intents.includes(intent)} + next: update_existing_domain_response + next: return_intent_does_not_exist + +update_existing_domain_response: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/domain/update-existing-response" + body: + json: ${domain_data_json.responses} + searchKey: ${intent} + newKey: ${"utter_" + intent} + newKeyValue: '${type === "Add" ? service_path : "UNKNOWN"}' + deleteOldValue: false + createIfAbsent: true + result: updatedResponses + next: check_for_type + +check_for_type: + switch: + - condition: ${type === "Add"} + next: assignRuleData + next: convert_domain_json_to_yaml + +assignRuleData: + assign: + data: + rule: "rule${intent}" + steps: [ + { + intent: "${intent}", + }, + { + action: "utter_${intent}", + }, + ] + next: add_rule + +add_rule: + call: http.post + args: + url: "[#SERVICE_RUUTER]/rasa/rules/add" + headers: + cookie: ${incoming.headers.cookie} + body: + data: ${data} + result: add_rule_res + next: convert_domain_json_to_yaml + +convert_domain_json_to_yaml: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/conversion/json_to_yaml_domain" + body: + version: ${domain_data_json.version} + session_config: ${domain_data_json.session_config} + intents: ${domain_data_json.intents} + entities: ${domain_data_json.entities} + slots: ${domain_data_json.slots} + forms: ${domain_data_json.forms} + actions: ${domain_data_json.actions} + responses: ${updatedResponses.response.body} + result: domainYaml + next: resave_domain_file + +resave_domain_file: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/create" + body: + file_path: ${fileLocations.response.body.response.domain_location} + content: ${domainYaml.response.body.json} + result: fileResult + next: updateOpenSearch + +updateOpenSearch: + call: http.post + args: + url: "[#SERVICES_PIPELINE]/bulk/domain" + body: + input: ${domainYaml.response.body.json} + result: updateSearchResult + next: return_result + +return_result: + status: 200 + return: "Connection request sent successfully" + next: end + +return_intent_does_not_exist: + status: 400 + return: "Intent does not exists" + next: end diff --git a/DSL/Ruuter.public/services/POST/services/draft/.guard b/DSL/Ruuter.public/services/POST/services/draft/.guard new file mode 100644 index 00000000..64435377 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/draft/.guard @@ -0,0 +1,4 @@ +guard_allow_all: + return: "success" + status: 200 + next: end diff --git a/DSL/Ruuter.public/services/POST/services/draft/test.tmp b/DSL/Ruuter.public/services/POST/services/draft/test.tmp new file mode 100644 index 00000000..2625647c --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/draft/test.tmp @@ -0,0 +1,48 @@ +declaration: + call: declare + version: 0.1 + description: Description placeholder for 'test' + method: post + accepts: json + returns: json + namespace: service + allowList: + body: + - field: chatId + type: string + description: The chat ID for the message + - field: authorId + type: string + description: The author ID for the message + - field: input + type: object + description: The Input from the user +prepare: + assign: + chatId: ${incoming.body.chatId} + authorId: ${incoming.body.authorId} + input: ${incoming.body.input} + buttons: [] + res: + result: "" + next: format_messages +format_messages: + call: http.post + args: + url: "[#SERVICE_DMAPPER_HBS]/bot_responses_to_messages" + headers: + type: json + body: + data: + botMessages: ${[res]} + chatId: ${chatId ?? ''} + authorId: ${authorId ?? ''} + authorFirstName: "" + authorLastName: "" + authorTimestamp: ${new Date().toISOString()} + created: ${new Date().toISOString()} + buttons: ${buttons ?? []} + result: formatMessage + next: service-end +service-end: + return: ${formatMessage.response.body ?? ''} diff --git a/DSL/Ruuter.public/services/POST/services/edit.yml b/DSL/Ruuter.public/services/POST/services/edit.yml new file mode 100644 index 00000000..57563146 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/edit.yml @@ -0,0 +1,381 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'EDIT'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: content + type: string + description: "Body field 'content'" + - field: description + type: string + description: "Body field 'description'" + - field: isCommon + type: boolean + description: "Body field 'isCommon'" + - field: entities + type: object + description: "Body field 'Entities'" + - field: examples + type: object + description: "Body field 'Examples'" + - field: name + type: string + description: "Body field 'name'" + - field: structure + type: object + description: "Body field 'structure'" + - field: type + type: string + description: "Body field 'type'" + - field: updateServiceDb + type: boolean + description: "Body field 'updateServiceDb'" + - field: state + type: string + description: "Body field 'state'" + params: + - field: id + type: string + description: "Parameter 'id'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + id: ${incoming.params.id} + name: ${incoming.body.name} + description: ${incoming.body.description} + isCommon: ${incoming.body.isCommon} + slot: ${""} + entities: ${incoming.body.entities} + examples: ${incoming.body.examples} + type: ${incoming.body.type} + content: ${incoming.body.content} + structure: ${incoming.body.structure} + updateServiceDb: ${incoming.body.updateServiceDb} + state: ${incoming.body.state} + +check_for_update_service_db: + switch: + - condition: ${incoming.body.updateServiceDb === true} + next: get_service + next: check_for_content + +get_service: + call: http.post + args: + url: "[#SERVICE_RESQL]/services/get_services_by_ids" + body: + serviceIds: "${id}" + result: get_service_result + +check_if_name_is_the_same: + switch: + - condition: ${get_service_result.response.body[0].name === name} + next: delete_all_mcq_files + next: check_if_name_exists + +check_if_name_exists: + call: http.post + args: + url: "[#SERVICE_RESQL]/services/check_name_exist" + body: + name: ${name} + result: name_exists_res + next: check_name_exists_result + +check_name_exists_result: + switch: + - condition: ${name_exists_res.response.body[0].nameExists} + next: return_name_already_exists + next: delete_all_mcq_files + +delete_all_mcq_files: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/delete-all-that-starts-with" + body: + path: "[#RUUTER_SERVICES_PATH]/${type}/services/draft" + keyword: "${name}_" + result: deleteRes + next: check_for_content + +check_for_content: + switch: + - condition: ${content === null} + next: check_for_required_parameters + next: convert_json_content_to_yml + +convert_json_content_to_yml: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/conversion/json_to_yaml_data" + body: + data: ${content} + result: ymlResult + +check_for_type: + switch: + - condition: ${type === 'GET'} + next: add_get_dsl + next: add_post_dsl + +add_get_dsl: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/create" + body: + file_path: "[#RUUTER_SERVICES_GET_PATH]/draft/${name}.tmp" + content: ${ymlResult.response.body.yaml} + result: results + next: check_for_required_parameters + +add_post_dsl: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/create" + body: + file_path: "[#RUUTER_SERVICES_POST_PATH]/draft/${name}.tmp" + content: ${ymlResult.response.body.yaml} + result: results + next: check_for_required_parameters + +check_for_required_parameters: + switch: + - condition: ${id === null || name === null || description === null} + next: return_incorrect_request + - condition: ${type === null} + next: return_incorrect_request + +upper_case_type: + assign: + type: ${type.toUpperCase()} + +check_type: + switch: + - condition: ${type !== 'GET' && type !== 'POST'} + next: return_incorrect_request + +check_if_update_service_db: + switch: + - condition: ${incoming.body.updateServiceDb === true} + next: get_service_by_id + next: return_ok + +get_service_by_id: + call: http.post + args: + url: "[#SERVICE_RESQL]/get-service-by-id" + body: + id: ${id} + result: old_service_result + +assign_values: + assign: + old_name: ${old_service_result.response.body[0].name} + old_structure: ${old_service_result.response.body[0].structure} + old_state: ${old_service_result.response.body[0].state} + service_type: ${old_service_result.response.body[0].type} + +check_new_structure: + switch: + - condition: ${structure === null} + next: use_old_structure + - condition: ${structure !== null} + next: use_new_structure + +use_new_structure: + assign: + new_structure: ${structure} + next: rename_dsl + +use_old_structure: + assign: + new_structure: ${old_structure.value} + next: rename_dsl + +rename_dsl: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/move" + body: + file_path: "[#RUUTER_SERVICES_PATH]/${type}/[#RUUTER_SERVICES_DIR_PATH]/${old_state}/${old_name}.tmp" + new_path: "[#RUUTER_SERVICES_PATH]/${type}/[#RUUTER_SERVICES_DIR_PATH]/${old_state}/${name}.tmp" + result: results + +service_edit: + call: http.post + args: + url: "[#SERVICE_RESQL]/edit" + body: + id: ${id} + name: ${name} + description: ${description} + is_common: ${isCommon ?? false} + slot: ${slot} + examples: ${examples} + entities: ${entities} + structure: ${new_structure} + state: ${state ?? 'draft'} + result: editedService + +check for_state: + switch: + - condition: ${state === 'draft'} + next: check_remove_blob_then_draft + - condition: ${state === 'active'} + next: check_if_service_data_exists + next: check_remove_blob_then_ok + +check_remove_blob_then_draft: + switch: + - condition: ${old_state === 'active'} + next: delete_from_storage_edit + next: change_state_to_draft + +check_remove_blob_then_ok: + switch: + - condition: ${old_state === 'active'} + next: delete_from_storage_edit + next: return_ok + +delete_from_storage_edit: + call: http.delete + args: + url: "[#STORAGE_FERRY]/v1/files/delete" + body: + files: + - storageAccountId: "[#STORAGE_FERRY_ACCOUNT_ID]" + container: "[#STORAGE_FERRY_CONTAINER]" + fileName: "${old_name}.json" + result: ferry_delete_result + next: check_if_azure_configured_after_delete_edit + +check_if_azure_configured_after_delete_edit: + switch: + - condition: ${"[#AZURE_SEARCH_SERVICE_NAME]" !== "" && "[#AZURE_SEARCH_INDEXER_NAME]" !== "" && "[#AZURE_SEARCH_API_KEY]" !== ""} + next: trigger_azure_indexer_after_delete_edit + next: log_azure_not_configured_after_delete_edit + +log_azure_not_configured_after_delete_edit: + log: "Warning! Azure Search configuration not found. Skipping Azure indexer trigger. Please configure AZURE_SEARCH_SERVICE_NAME, AZURE_SEARCH_INDEXER_NAME, and AZURE_SEARCH_API_KEY in constants.ini" + next: after_delete_from_storage_edit + +trigger_azure_indexer_after_delete_edit: + call: http.post + args: + url: "https://[#AZURE_SEARCH_SERVICE_NAME].search.windows.net/indexers/[#AZURE_SEARCH_INDEXER_NAME]/run?api-version=2024-07-01" + headers: + api-key: "[#AZURE_SEARCH_API_KEY]" + Content-Type: "application/json" + result: azure_indexer_result + next: after_delete_from_storage_edit + +after_delete_from_storage_edit: + switch: + - condition: ${state === 'draft'} + next: change_state_to_draft + next: return_ok + +change_state_to_draft: + call: http.post + args: + url: "[#SERVICE_RUUTER]/services/status" + headers: + cookie: ${incoming.headers.cookie} + body: + id: ${id} + state: "draft" + type: ${service_type ?? 'POST'} + result: changeStateResult + next: return_ok + +check_if_service_data_exists: + switch: + - condition: ${old_service_result !== undefined && old_service_result !== null} + next: generate_service_json_from_existing + next: get_service_data_for_json + +get_service_data_for_json: + call: http.post + args: + url: "[#SERVICE_RESQL]/get-service-by-id" + body: + id: ${id} + result: service_data_result + next: generate_service_json + +generate_service_json_from_existing: + assign: + service_data_result: ${old_service_result} + next: generate_service_json + +generate_service_json: + call: http.post + args: + url: "[#SERVICE_DMAPPER_HBS]/service_declaration" + headers: + type: 'json' + body: + name: ${name} + description: ${service_data_result.response.body[0].description} + examples: ${service_data_result.response.body[0].examples} + entities: ${service_data_result.response.body[0].entities} + result: service_json_result + next: replace_in_storage + +replace_in_storage: + call: http.post + args: + url: "[#STORAGE_FERRY]/v1/files/create" + body: + files: + - storageAccountId: "[#STORAGE_FERRY_ACCOUNT_ID]" + container: "[#STORAGE_FERRY_CONTAINER]" + fileName: "${name}.json" + content: ${JSON.stringify(service_json_result.response.body)} + result: ferry_upload_result + next: check_if_azure_configured + +check_if_azure_configured: + switch: + - condition: ${"[#AZURE_SEARCH_SERVICE_NAME]" !== "" && "[#AZURE_SEARCH_INDEXER_NAME]" !== "" && "[#AZURE_SEARCH_API_KEY]" !== ""} + next: trigger_azure_indexer + next: log_azure_not_configured + +log_azure_not_configured: + log: "Warning! Azure Search configuration not found. Skipping Azure indexer trigger. Please configure AZURE_SEARCH_SERVICE_NAME, AZURE_SEARCH_INDEXER_NAME, and AZURE_SEARCH_API_KEY in constants.ini" + next: return_ok + +trigger_azure_indexer: + call: http.post + args: + url: "https://[#AZURE_SEARCH_SERVICE_NAME].search.windows.net/indexers/[#AZURE_SEARCH_INDEXER_NAME]/run?api-version=2024-07-01" + headers: + api-key: "[#AZURE_SEARCH_API_KEY]" + Content-Type: "application/json" + result: azure_indexer_result + next: return_ok + +return_ok: + reloadDsl: true + status: 200 + return: "Edited Successfully" + next: end + +return_incorrect_request: + status: 400 + return: "Required parameter(s) missing" + next: end + +return_name_already_exists: + status: 409 + return: "Service name already exists" + next: end diff --git a/DSL/Ruuter.public/services/POST/services/endpoint-url-validation.yml b/DSL/Ruuter.public/services/POST/services/endpoint-url-validation.yml new file mode 100644 index 00000000..a6280794 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/endpoint-url-validation.yml @@ -0,0 +1,32 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'ENDPOINT-URL-VALIDATION'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: type + type: string + description: "Body field 'type'" + - field: url + type: string + description: "Body field 'url'" + +extract_request_data: + assign: + url: ${incoming.body.url} + type: ${incoming.body.type} + +call_template: + template: "[#SERVICE_PROJECT_LAYER]/validation-template" + requestType: templates + body: + response: ${url} + type: ${type} + result: templateResult + +return_result: + return: ${templateResult} diff --git a/DSL/Ruuter.public/services/POST/services/import-services.yml b/DSL/Ruuter.public/services/POST/services/import-services.yml new file mode 100644 index 00000000..89a51644 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/import-services.yml @@ -0,0 +1,71 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'IMPORT-SERVICES'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: services + type: object + description: "Body field 'services'" + - field: timezone + type: string + description: "Body field 'timezone'" + +extract_request_data: + assign: + services: ${incoming.body.services ?? []} + names: ${services.map(s => s.fileName).join(",") ?? []} + timezone: ${incoming.body.timezone} + +get_import_names: + call: http.post + args: + url: "[#SERVICE_RESQL]/get-import-names" + body: + names: ${names} + timezone: ${timezone} + result: import_names_res + +assign_imported_names: + assign: + imported_names: ${import_names_res.response.body[0].names.split(",")} + services: "$=services.map((s, i) => ({ ...s, fileName: imported_names[i] }))=" + file_names: ${services.map(s => s.fileName)} + +insert_services: + call: http.post + args: + url: "[#SERVICE_RESQL]/add-services" + body: + names: ${file_names} + structures: ${services.map(s => s.flowData)} + result: insert_services_res + +convert_json_content_to_yml: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/conversion/json_to_yaml_data_multiple" + body: + data: ${services.map(s => s.content)} + result: ymls_res + +prepare_files: + assign: + file_paths: "$=file_names.map(name => `[#RUUTER_SERVICES_POST_PATH]/draft/${name}.tmp`)=" + yaml_contents: ${ymls_res.response.body.yamls} + +add_dsls: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/create_multiple" + body: + file_paths: ${file_paths} + contents: ${yaml_contents} + result: add_dsls_res + +return_result: + return: "Services imported successfully" diff --git a/DSL/Ruuter.public/services/POST/services/inactive/.guard b/DSL/Ruuter.public/services/POST/services/inactive/.guard new file mode 100644 index 00000000..64435377 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/inactive/.guard @@ -0,0 +1,4 @@ +guard_allow_all: + return: "success" + status: 200 + next: end diff --git a/DSL/Ruuter.public/services/POST/services/open-api-spec.yml b/DSL/Ruuter.public/services/POST/services/open-api-spec.yml new file mode 100644 index 00000000..3dcc2c85 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/open-api-spec.yml @@ -0,0 +1,34 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'OPEN-API-SPEC'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: url + type: string + description: "Body field 'url'" + +check_for_required_parameters: + switch: + - condition: ${incoming.body == null || incoming.body.url == null} + next: return_incorrect_request + next: get_spec + +get_spec: + call: http.get + args: + url: ${incoming.body.url} + result: result + +return_value: + return: ${result.response.body} + next: end + +return_incorrect_request: + status: 400 + return: "missing parameters" + next: end diff --git a/DSL/Ruuter.public/services/POST/services/requests/explain.yml b/DSL/Ruuter.public/services/POST/services/requests/explain.yml new file mode 100644 index 00000000..e5fce84d --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/requests/explain.yml @@ -0,0 +1,95 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'EXPLAIN'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: requests + type: object + description: "request object containing multiple requests" + +check_for_requests: + switch: + - condition: ${incoming.body.requests == null} + next: missing_requests + next: initialize_responses + +initialize_responses: + assign: + requests: ${incoming.body.requests} + responses: [] + index: 0 + next: process_next_request + +process_next_request: + switch: + - condition: ${index < requests.length} + next: assign_current_request + next: return_responses + +assign_current_request: + assign: + current_request: ${requests[index]} + next: check_method + +check_method: + switch: + - condition: ${current_request.method.toLowerCase() == 'post'} + next: request_explain_post + next: request_explain_get + +request_explain_get: + call: http.get + args: + url: ${current_request.url} + headers: + Content-Type: "application/json" + result: res + next: assign_result + +request_explain_post: + call: http.post + args: + url: ${current_request.url} + headers: + Content-Type: "application/json" + body: + data: ${current_request.body} + result: res + next: assign_result + +assign_result: + assign: + result_res: ${[res.response.body]} + next: check_responses_list + +check_responses_list: + switch: + - condition: ${responses.length === 0} + next: append_first_response + next: append_response + +append_first_response: + assign: + responses: ${[responses, ...result_res]} + index: ${index + 1} + next: process_next_request + +append_response: + assign: + responses: ${[...responses, ...result_res]} + index: ${index + 1} + next: process_next_request + +return_responses: + return: ${responses.splice(1 , responses.length - 1)} + next: end + +missing_requests: + status: 400 + return: "required requests were not provided" + next: end \ No newline at end of file diff --git a/DSL/Ruuter.public/services/POST/services/resql/add.yml b/DSL/Ruuter.public/services/POST/services/resql/add.yml new file mode 100644 index 00000000..b936d8fe --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/resql/add.yml @@ -0,0 +1,62 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'ADD'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: name + type: string + description: "Body field 'name'" + - field: sql + type: string + description: "Body field 'sql'" + +check_for_body: + switch: + - condition: ${incoming.body == null} + next: missing_parameter + +extract_request_data: + assign: + name: ${incoming.body.name} + sql: ${incoming.body.sql} + +check_for_required_parameters: + switch: + - condition: ${name == null || sql == null} + next: missing_parameter + next: add_resql + +missing_parameter: + status: 400 + return: "required parameters were not provided" + next: end + +add_resql: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/create" + body: + file_path: "/Resql/services/${name}.sql" + content: ${sql} + result: results + +check_result: + switch: + - condition: ${200 <= results.response.statusCodeValue && results.response.statusCodeValue < 300} + next: return_ok + next: return_bad_request + +return_ok: + status: 200 + return: ${results.response.body.message} + next: end + +return_bad_request: + status: 400 + return: ${results.response.body.message} + next: end diff --git a/DSL/Ruuter.public/services/POST/services/status.yml b/DSL/Ruuter.public/services/POST/services/status.yml new file mode 100644 index 00000000..788aad71 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/status.yml @@ -0,0 +1,303 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'STATUS'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: id + type: string + description: "Body field 'id'" + - field: state + type: string + description: "Body field 'state'" + - field: type + type: string + description: "Body field 'type'" + headers: + - field: cookie + type: string + description: "Cookie field" + +extract_request_data: + assign: + id: ${incoming.body.id} + new_state: ${incoming.body.state} + ruuter_type: ${incoming.body.type} + +check_for_required_parameters: + switch: + - condition: ${id === null || new_state === null || ruuter_type === null} + next: return_incorrect_request + - condition: ${new_state === "ready"} + next: set_plain_status + next: get_current_status + +get_current_status: + call: http.post + args: + url: "[#SERVICE_RESQL]/status" + body: + id: ${id} + result: status_res + next: assign_old_status_and_path + +assign_old_status_and_path: + assign: + old_file_status_path: "${status_res.response.body[0].currentState === 'ready' ? 'draft' : status_res.response.body[0].currentState}" + old_file_end: "${status_res.response.body[0].currentState !== 'active' ? '.tmp' : '.yml'}" + next: check_status + +check_status: + switch: + - condition: ${new_state === "draft"} + next: set_status + - condition: ${status_res.response.body[0].currentState === new_state} + next: return_same_state_update + next: set_status + +set_status: + call: http.post + args: + url: "[#SERVICE_RESQL]/set-status" + body: + id: ${id} + new_state: ${new_state} + result: res + next: get_status_name + +set_plain_status: + call: http.post + args: + url: "[#SERVICE_RESQL]/set-status" + body: + id: ${id} + new_state: ${new_state} + result: draft_res + next: return_ok + +get_status_name: + call: http.post + args: + url: "[#SERVICE_RESQL]/get-service-name-by-id" + body: + id: ${id} + result: name_res + next: assign_values + +assign_values: + assign: + name: ${name_res.response.body[0].name} + service_name: "service_${name_res.response.body[0].name}" + next: check_file_exists + +check_file_exists: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/exists" + body: + file_path: "[#RUUTER_SERVICES_PATH]/${ruuter_type}/[#RUUTER_SERVICES_DIR_PATH]/${old_file_status_path}/${name + old_file_end}" + result: service_file_exists_result + next: validate_file_exists + +validate_file_exists: + switch: + - condition: ${!!service_file_exists_result.response.body} + next: check_for_status + next: return_service_file_missing + +check_for_status: + switch: + - condition: ${new_state === "active"} + next: activate_service + - condition: ${new_state === "draft"} + next: draft_service + next: deactivate_service + +activate_service: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/move" + body: + file_path: "[#RUUTER_SERVICES_PATH]/${ruuter_type}/[#RUUTER_SERVICES_DIR_PATH]/${old_file_status_path}/${name + old_file_end}" + new_path: "[#RUUTER_SERVICES_PATH]/${ruuter_type}/[#RUUTER_SERVICES_DIR_PATH]/active/${name}.yml" + result: activate_service_result + next: get_service_data_for_json + +get_service_data_for_json: + call: http.post + args: + url: "[#SERVICE_RESQL]/get-service-by-id" + body: + id: ${id} + result: service_data_result + next: generate_service_json + +generate_service_json: + call: http.post + args: + url: "[#SERVICE_DMAPPER_HBS]/service_declaration" + headers: + type: 'json' + body: + name: ${name} + description: ${service_data_result.response.body[0].description} + examples: ${service_data_result.response.body[0].examples} + entities: ${service_data_result.response.body[0].entities} + result: service_json_result + next: create_in_storage + +create_in_storage: + call: http.post + args: + url: "[#STORAGE_FERRY]/v1/files/create" + body: + files: + - storageAccountId: "[#STORAGE_FERRY_ACCOUNT_ID]" + container: "[#STORAGE_FERRY_CONTAINER]" + fileName: "${name}.json" + content: ${JSON.stringify(service_json_result.response.body)} + result: ferry_upload_result + next: check_if_azure_configured_after_create + +check_if_azure_configured_after_create: + switch: + - condition: ${"[#AZURE_SEARCH_SERVICE_NAME]" !== "" && "[#AZURE_SEARCH_INDEXER_NAME]" !== "" && "[#AZURE_SEARCH_API_KEY]" !== ""} + next: trigger_azure_indexer_after_create + next: log_azure_not_configured_after_create + +log_azure_not_configured_after_create: + log: "Warning! Azure Search configuration not found. Skipping Azure indexer trigger. Please configure AZURE_SEARCH_SERVICE_NAME, AZURE_SEARCH_INDEXER_NAME, and AZURE_SEARCH_API_KEY in constants.ini" + next: activate_all_mcq_services + +trigger_azure_indexer_after_create: + call: http.post + args: + url: "https://[#AZURE_SEARCH_SERVICE_NAME].search.windows.net/indexers/[#AZURE_SEARCH_INDEXER_NAME]/run?api-version=2024-07-01" + headers: + api-key: "[#AZURE_SEARCH_API_KEY]" + Content-Type: "application/json" + result: azure_indexer_result + next: activate_all_mcq_services + +activate_all_mcq_services: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/move-all-that-starts-with" + body: + file_path: "[#RUUTER_SERVICES_PATH]/${ruuter_type}/[#RUUTER_SERVICES_DIR_PATH]/${old_file_status_path}" + new_path: "[#RUUTER_SERVICES_PATH]/${ruuter_type}/[#RUUTER_SERVICES_DIR_PATH]/active" + keyword: "${name}_" + format: "yml" + result: active_move_results + next: return_ok + +deactivate_service: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/move" + body: + file_path: "[#RUUTER_SERVICES_PATH]/${ruuter_type}/[#RUUTER_SERVICES_DIR_PATH]/${old_file_status_path}/${name + old_file_end}" + new_path: "[#RUUTER_SERVICES_PATH]/${ruuter_type}/[#RUUTER_SERVICES_DIR_PATH]/inactive/${name}.tmp" + result: deactivate_service_result + next: delete_from_storage + +delete_from_storage: + call: http.delete + args: + url: "[#STORAGE_FERRY]/v1/files/delete" + body: + files: + - storageAccountId: "[#STORAGE_FERRY_ACCOUNT_ID]" + container: "[#STORAGE_FERRY_CONTAINER]" + fileName: "${name}.json" + result: ferry_delete_result + next: check_if_azure_configured_after_delete + +check_if_azure_configured_after_delete: + switch: + - condition: ${"[#AZURE_SEARCH_SERVICE_NAME]" !== "" && "[#AZURE_SEARCH_INDEXER_NAME]" !== "" && "[#AZURE_SEARCH_API_KEY]" !== ""} + next: trigger_azure_indexer_after_delete + next: log_azure_not_configured_after_delete + +log_azure_not_configured_after_delete: + log: "Warning! Azure Search configuration not found. Skipping Azure indexer trigger. Please configure AZURE_SEARCH_SERVICE_NAME, AZURE_SEARCH_INDEXER_NAME, and AZURE_SEARCH_API_KEY in constants.ini" + next: dactivate_all_mcq_services + +trigger_azure_indexer_after_delete: + call: http.post + args: + url: "https://[#AZURE_SEARCH_SERVICE_NAME].search.windows.net/indexers/[#AZURE_SEARCH_INDEXER_NAME]/run?api-version=2024-07-01" + headers: + api-key: "[#AZURE_SEARCH_API_KEY]" + Content-Type: "application/json" + result: azure_indexer_result + next: dactivate_all_mcq_services + +dactivate_all_mcq_services: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/move-all-that-starts-with" + body: + file_path: "[#RUUTER_SERVICES_PATH]/${ruuter_type}/[#RUUTER_SERVICES_DIR_PATH]/${old_file_status_path}" + new_path: "[#RUUTER_SERVICES_PATH]/${ruuter_type}/[#RUUTER_SERVICES_DIR_PATH]/inactive" + keyword: "${name}_" + format: "tmp" + result: inactive_move_results + next: return_ok + +draft_service: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/move" + body: + file_path: "[#RUUTER_SERVICES_PATH]/${ruuter_type}/[#RUUTER_SERVICES_DIR_PATH]/${old_file_status_path}/${name + old_file_end}" + new_path: "[#RUUTER_SERVICES_PATH]/${ruuter_type}/[#RUUTER_SERVICES_DIR_PATH]/draft/${name}.tmp" + result: draft_service_result + next: draft_all_mcq_services + +draft_all_mcq_services: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/file-manager/move-all-that-starts-with" + body: + file_path: "[#RUUTER_SERVICES_PATH]/${ruuter_type}/[#RUUTER_SERVICES_DIR_PATH]/${old_file_status_path}" + new_path: "[#RUUTER_SERVICES_PATH]/${ruuter_type}/[#RUUTER_SERVICES_DIR_PATH]/draft" + keyword: "${name}_" + format: "tmp" + result: inactive_move_results + next: return_ok + +return_ok: + reloadDsl: true + status: 200 + return: "Status Changed Successfully" + next: end + +return_bad_request: + status: 400 + return: ${err_result.response.body.message} + next: end + +return_incorrect_request: + status: 400 + return: "Required parameter(s) missing" + next: end + +return_same_state_update: + status: 200 + return: "Service is already in this state" + next: end + +return_service_file_missing: + status: 500 + return: "Service file to update is missing" + next: end + +return_intent_does_not_exist: + status: 400 + return: "does not exists" + next: end diff --git a/DSL/Ruuter.public/services/POST/services/update-endpoint.yml b/DSL/Ruuter.public/services/POST/services/update-endpoint.yml new file mode 100644 index 00000000..9228079d --- /dev/null +++ b/DSL/Ruuter.public/services/POST/services/update-endpoint.yml @@ -0,0 +1,61 @@ +declaration: + call: declare + version: 0.1 + description: "Updates an existing endpoint" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: name + type: string + description: "Endpoint name" + - field: type + type: string + description: "Endpoint type" + - field: isCommon + type: boolean + description: "Endpoint common status" + - field: serviceId + type: string + description: "Service UUID" + - field: definitions + type: object + description: "Endpoint definitions" + params: + - field: id + type: string + description: "Endpoint UUID" + +extract_request_data: + assign: + id: ${incoming.params.id} + +check_for_type: + switch: + - condition: ${id == null} + next: return_no_type_error + +update_endpoint: + call: http.post + args: + url: "[#SERVICE_RESQL]/endpoints/update_endpoint" + body: + endpointId: ${id} + name: ${incoming.body.name} + type: ${incoming.body.type} + isCommon: ${incoming.body.isCommon} + serviceId: ${incoming.body.serviceId ?? ''} + definitions: ${incoming.body.definitions} + result: res + +return_ok: + status: 200 + return: "Endpoint updated" + next: end + +return_no_type_error: + status: 400 + return: "Please provide an endpoint ID" + next: end diff --git a/DSL/Ruuter.public/services/POST/steps/preferences.yml b/DSL/Ruuter.public/services/POST/steps/preferences.yml new file mode 100644 index 00000000..61f72722 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/steps/preferences.yml @@ -0,0 +1,68 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'PREFERENCES'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: steps + type: string + description: "Body field 'steps'" + - field: endpoints + type: string + description: "Body field 'endpoints'" + +extractRequestData: + assign: + steps: ${incoming.body.steps.join(",")} + endpoints: ${incoming.body.endpoints.join(",")} + +get_user_info: + call: http.post + args: + url: "[#SERVICE_TIM]/jwt/custom-jwt-userinfo" + contentType: plaintext + headers: + cookie: ${incoming.headers.cookie} + plaintext: "customJwtCookie" + result: res + +check_user_info_response: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: assignIdCode + next: return_unauthorized + +assignIdCode: + assign: + idCode: ${res.response.body.idCode} + +update_user_step_preferences: + call: http.post + args: + url: "[#SERVICE_RESQL]/update-user-step-preferences" + body: + steps: "{${steps}}" + endpoints: "{${endpoints}}" + user_id_code: ${idCode} + result: update_preferences_res + +get_user_step_preferences: + call: http.post + args: + url: "[#SERVICE_RESQL]/get-user-step-preferences" + body: + user_id_code: ${idCode} + result: preferences + +return_preferences: + return: ${preferences.response.body[0]} + next: end + +return_unauthorized: + status: 401 + return: "unauthorized" + next: end diff --git a/DSL/Ruuter.public/services/POST/user-info.yml b/DSL/Ruuter.public/services/POST/user-info.yml new file mode 100644 index 00000000..e6309aa5 --- /dev/null +++ b/DSL/Ruuter.public/services/POST/user-info.yml @@ -0,0 +1,16 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'USER-INFO'" + method: post + accepts: json + returns: json + namespace: service + +get_tara_info: + template: "[#SERVICE_PROJECT_LAYER]/tara" + requestType: templates + result: TARA + +return_authorized: + return: ${TARA.response.body} diff --git a/DSL/Ruuter.public/services/TEMPLATES/RBAC.yml b/DSL/Ruuter.public/services/TEMPLATES/RBAC.yml new file mode 100644 index 00000000..fbe0f033 --- /dev/null +++ b/DSL/Ruuter.public/services/TEMPLATES/RBAC.yml @@ -0,0 +1,51 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'RBAC'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: allowedRoles + type: object + description: "Body field 'allowedRoles'" + - field: userId + type: string + description: "Body field 'userId'" + +check_for_body: + switch: + - condition: ${incoming.body == null} + next: missing_parameter + +extract_request_data: + assign: + userId: ${incoming.body.userId} + allowedRoles: ${incoming.body.allowedRoles.sort()} + +check_for_required_parameters: + switch: + - condition: ${userId === null || allowedRoles === null} + next: missing_parameter + next: fetch_user_roles_from_db + +fetch_user_roles_from_db: + call: http.post + args: + url: "[#SERVICE_RESQL_USERS]:[#SERVICE_RESQL_USERS_PORT]/is-user-roles-allowed" + body: + userId: ${userId} + allowedRoles: ${allowedRoles} + result: result + +return_value: + status: 200 + return: "${result.response.body[0]}" + next: end + +missing_parameter: + status: 400 + return: "userId, allowedRoles - missing" + next: end diff --git a/DSL/Ruuter.public/services/TEMPLATES/check-user-authority.yml b/DSL/Ruuter.public/services/TEMPLATES/check-user-authority.yml new file mode 100644 index 00000000..25641137 --- /dev/null +++ b/DSL/Ruuter.public/services/TEMPLATES/check-user-authority.yml @@ -0,0 +1,50 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'CHECK-USER-AUTHORITY'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + headers: + - field: cookie + type: string + description: "Cookie field" + +get_cookie_info: + call: http.post + args: + url: "[#SERVICE_TIM]/jwt/custom-jwt-userinfo" + contentType: plaintext + headers: + cookie: ${incoming.headers.cookie} + plaintext: "customJwtCookie" + result: res + next: check_cookie_info_response + +check_cookie_info_response: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: check_user_authority + next: return_bad_request + +check_user_authority: + switch: + - condition: ${res.response.body.authorities.includes("ROLE_ADMINISTRATOR") || res.response.body.authorities.includes("ROLE_SERVICE_MANAGER")} + next: return_authorized + next: return_unauthorized + +return_authorized: + return: ${res.response.body} + next: end + +return_unauthorized: + status: 200 + return: false + next: end + +return_bad_request: + status: 400 + return: false + next: end diff --git a/DSL/Ruuter.public/services/TEMPLATES/client-input.yml b/DSL/Ruuter.public/services/TEMPLATES/client-input.yml new file mode 100644 index 00000000..1d635eae --- /dev/null +++ b/DSL/Ruuter.public/services/TEMPLATES/client-input.yml @@ -0,0 +1,19 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'CLIENT-INPUT'" + method: post + accepts: json + returns: json + namespace: service + +# TODO: replace with correct request to get user input +request_client_input: + call: reflect.mock + args: + response: + input: "Yes" + result: clientInput + +return_value: + return: ${clientInput.response.body} diff --git a/DSL/Ruuter.public/services/TEMPLATES/direct-to-cs.yml b/DSL/Ruuter.public/services/TEMPLATES/direct-to-cs.yml new file mode 100644 index 00000000..175f028e --- /dev/null +++ b/DSL/Ruuter.public/services/TEMPLATES/direct-to-cs.yml @@ -0,0 +1,42 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'DIRECT-TO-CS'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: message + type: string + description: "Body field 'message'" + +# Direct to customer support +check_for_body: + switch: + - condition: ${incoming.body == null || incoming.body.message == null || incoming.body.message == ""} + next: missing_body_parameter + next: extract_request_data + +extract_request_data: + assign: + message: ${incoming.body.message} + +# TODO: do the actual request +send_message_to_client: + call: reflect.mock + args: + response: + status: 'OK' + message: "Teid suunatakse klienditeenindusse" + result: result + +return_value: + return: ${result.response.body} + next: end + +missing_body_parameter: + status: 400 + return: 'message - missing' + next: end diff --git a/DSL/Ruuter.public/services/TEMPLATES/end-conversation.yml b/DSL/Ruuter.public/services/TEMPLATES/end-conversation.yml new file mode 100644 index 00000000..43dbf6be --- /dev/null +++ b/DSL/Ruuter.public/services/TEMPLATES/end-conversation.yml @@ -0,0 +1,42 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'END-CONVERSATION'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: message + type: string + description: "Body field 'message'" + +# End conversation +check_for_body: + switch: + - condition: ${incoming.body == null || incoming.body.message == null || incoming.body.message == ""} + next: missing_body_parameter + next: extract_request_data + +extract_request_data: + assign: + message: ${incoming.body.message} + +# TODO: do the actual request +send_message_to_client: + call: reflect.mock + args: + response: + status: 'OK' + message: "Teenus on lõpetatud" + result: result + +return_value: + return: ${result.response.body} + next: end + +missing_body_parameter: + status: 400 + return: 'message - missing' + next: end diff --git a/DSL/Ruuter.public/services/TEMPLATES/file-generate.yml b/DSL/Ruuter.public/services/TEMPLATES/file-generate.yml new file mode 100644 index 00000000..4eb9f423 --- /dev/null +++ b/DSL/Ruuter.public/services/TEMPLATES/file-generate.yml @@ -0,0 +1,45 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'FILE-GENERATE'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: fileContent + type: string + description: "Body field 'fileContent'" + - field: fileName + type: string + description: "Body field 'fileName'" + +check_for_body: + switch: + - condition: ${incoming.body == null || incoming.body.fileName == null || incoming.body.fileContent == null} + next: missing_body_parameters + next: extract_request_data + +extract_request_data: + assign: + fileName: ${incoming.body.fileName} + fileContent: ${incoming.body.fileContent} + +generate_pdf_file: + call: http.post + args: + url: "[#SERVICE_DMAPPER]/js/generate/pdf" + body: + filename: ${fileName} + template: ${fileContent} + result: result + +return: + return: ${result.response.body} + next: end + +missing_body_parameters: + status: 400 + return: "fileName, fileContent - missing" + next: end diff --git a/DSL/Ruuter.public/services/TEMPLATES/file-signing.yml b/DSL/Ruuter.public/services/TEMPLATES/file-signing.yml new file mode 100644 index 00000000..4d2571c5 --- /dev/null +++ b/DSL/Ruuter.public/services/TEMPLATES/file-signing.yml @@ -0,0 +1,35 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'FILE-SIGNING'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: country + type: string + description: "Body field 'country'" + - field: personIdentifier + type: string + description: "Body field 'personIdentifier'" + - field: phoneNr + type: string + description: "Body field 'phoneNr'" + - field: type + type: string + description: "Body field 'type'" + +siga_template_request: + template: siga + requestType: templates + body: + type: ${incoming.body.type} + personIdentifier: ${incoming.body.personIdentifier} + country: ${incoming.body.country} + phoneNr: ${incoming.body.phoneNr} + result: result + +return_result: + return: ${result.response.body} diff --git a/DSL/Ruuter.public/services/TEMPLATES/open-webpage.yml b/DSL/Ruuter.public/services/TEMPLATES/open-webpage.yml new file mode 100644 index 00000000..9435e427 --- /dev/null +++ b/DSL/Ruuter.public/services/TEMPLATES/open-webpage.yml @@ -0,0 +1,44 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'OPEN-WEBPAGE'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: link + type: string + description: "Body field 'link'" + - field: linkText + type: string + description: "Body field 'linkText'" + +check_for_body: + switch: + - condition: ${incoming.body == null || incoming.body.link == null || incoming.body.link == "" || incoming.body.linkText == null || incoming.body.linkText == ""} + next: missing_body_parameter + next: extract_request_data + +extract_request_data: + assign: + link: ${incoming.body.link} + linkText: ${incoming.body.linkText} + +send_link_to_client: + call: reflect.mock + args: + response: + status: "OK" + link: Link Text + result: result + +return_value: + return: ${result.response.body} + next: end + +missing_body_parameter: + status: 400 + return: "link, linkText - both or one of these fields are missing" + next: end diff --git a/DSL/Ruuter.public/services/TEMPLATES/send-message-to-client.yml b/DSL/Ruuter.public/services/TEMPLATES/send-message-to-client.yml new file mode 100644 index 00000000..0db2430f --- /dev/null +++ b/DSL/Ruuter.public/services/TEMPLATES/send-message-to-client.yml @@ -0,0 +1,42 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'SEND-MESSAGE-TO-CLIENT'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: message + type: string + description: "Body field 'message'" + +# Message to client element +check_for_body: + switch: + - condition: ${incoming.body == null || incoming.body.message == null || incoming.body.message == ""} + next: missing_body_parameter + next: extract_request_data + +extract_request_data: + assign: + message: ${incoming.body.message} + +# TODO: do the actual request +send_message_to_client: + call: reflect.mock + args: + response: + status: 'OK' + message: "Hello, Muki" + result: result + +return_value: + return: ${result.response.body} + next: end + +missing_body_parameter: + status: 400 + return: 'message - missing' + next: end diff --git a/DSL/Ruuter.public/services/TEMPLATES/siga.yml b/DSL/Ruuter.public/services/TEMPLATES/siga.yml new file mode 100644 index 00000000..adb8984b --- /dev/null +++ b/DSL/Ruuter.public/services/TEMPLATES/siga.yml @@ -0,0 +1,132 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'SIGA'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: containerType + type: string + description: "Body field 'containerType'" + - field: country + type: string + description: "Body field 'country'" + - field: file + type: string + description: "Body field 'file'" + - field: phoneNumber + type: string + description: "Body field 'phoneNumber'" + - field: type + type: string + description: "Body field 'type'" + +extract_request_data: + assign: + file: ${incoming.body.file} + signType: ${incoming.body.type} + country: ${incoming.body.country} + phoneNumber: ${incoming.body.phoneNumber} + containerType: ${incoming.body.containerType} + next: get_tara_info + +get_tara_info: + template: tara + requestType: templates + result: tara_res + next: extract_tara_data + +extract_tara_data: + assign: + identifier: ${tara_res.response.body.idCode} + next: check_for_container_type + +check_for_container_type: + switch: + - condition: ${containerType === "ASIC".toLowerCase()} + next: create_asic_container + - condition: ${containerType === "HASHCODE".toLowerCase()} + next: create_hashcode_container + next: missing_container_type + +create_asic_container: + call: http.post + args: + url: "[#SERVICE_SIGA]/create-container" + contentType: formdata + body: + file:file[0]:uploadedFile.pdf: ${file} + return: container_res + next: check_if_sign_type_missing + +create_hashcode_container: + call: http.post + args: + url: "[#SERVICE_SIGA]/create-hashcode-container" + contentType: formdata + body: + file:file[0]:uploadedFile.pdf: ${file} + return: container_res + next: check_if_sign_type_missing + +check_if_sign_type_missing: + switch: + - condition: ${signType === null} + next: missing_sign_type + next: check_for_sign_type + +check_for_sign_type: + switch: + - condition: ${signType === "smart_id"} + next: sign_via_smart_id + - condition: ${signType === "mobile_sign"} + next: sign_via_mobile + next: missing_sign_type + +sign_via_smart_id: + call: http.post + args: + url: "[#SERVICE_SIGA]/smartid-signing" + body: + containerId: ${container_res.response.body.id} + containerType: ${containerType.toUpperCase()} + personIdentifier: ${identifier} + country: ${country} + return: res + next: end + +sign_via_mobile: + call: http.post + args: + url: "[#SERVICE_SIGA]/mobile-signing" + body: + containerId: ${container_res.response.body.id} + containerType: ${containerType.toUpperCase()} + phoneNr: ${phoneNumber} + personIdentifier: ${identifier} + country: ${country} + return: res + next: end + +missing_smart_id_params: + status: 400 + return: "Id, country - missing" + next: end + +missing_mobile_sign_params: + status: 400 + return: "Phone number, country - missing" + next: end + +missing_sign_type: + status: 400 + return: "Sign type is missing" + next: end + +missing_container_type: + status: 400 + return: "Container type is missing" + next: end diff --git a/DSL/Ruuter.public/services/TEMPLATES/tara.yml b/DSL/Ruuter.public/services/TEMPLATES/tara.yml new file mode 100644 index 00000000..28ac86dc --- /dev/null +++ b/DSL/Ruuter.public/services/TEMPLATES/tara.yml @@ -0,0 +1,51 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'TARA'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + headers: + - field: cookie + type: string + description: "Cookie field" + + +check_for_body: + switch: + - condition: ${incoming.headers == null || incoming.headers.cookie == null} + next: missing_cookie + next: get_cookie_info + +get_cookie_info: + call: http.post + args: + url: "[#SERVICE_TIM]/jwt/custom-jwt-userinfo" + contentType: plaintext + headers: + cookie: ${incoming.headers.cookie} + plaintext: "customJwtCookie" + result: res + next: check_cookie_info_response + +check_cookie_info_response: + switch: + - condition: ${200 <= res.response.statusCodeValue && res.response.statusCodeValue < 300} + next: return_auth_result + next: return_bad_request + +return_auth_result: + return: ${res.response.body} + next: end + +return_bad_request: + status: 400 + return: false + next: end + +missing_cookie: + status: 401 + return: "no authentication cookie" + next: end diff --git a/DSL/Ruuter.public/services/TEMPLATES/validation-template.yml b/DSL/Ruuter.public/services/TEMPLATES/validation-template.yml new file mode 100644 index 00000000..63f21c88 --- /dev/null +++ b/DSL/Ruuter.public/services/TEMPLATES/validation-template.yml @@ -0,0 +1,56 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'VALIDATION-TEMPLATE'" + method: post + accepts: json + returns: json + namespace: service + allowlist: + body: + - field: response + type: string + description: "Body field 'response'" + - field: type + type: string + description: "Body field 'type'" + +assign_step: + assign: + res: ${incoming.body.response} + type: ${incoming.body.type} + next: check_for_type + +check_for_type: + switch: + - condition: ${type.toLowerCase() === 'get'} + next: validate_get_request + next: validate_post_request + +validate_get_request: + call: http.get + args: + url: ${res} + result: results + next: validate_status_code + +validate_post_request: + call: http.post + args: + url: ${res} + result: results + next: validate_status_code + +validate_status_code: + switch: + - condition: ${200 <= results.response.statusCodeValue && results.response.statusCodeValue < 300} + next: return_true + next: return_false + +return_true: + return: true + next: end + +return_false: + return: false + next: end diff --git a/constants.ini b/constants.ini index 63172d15..af507306 100644 --- a/constants.ini +++ b/constants.ini @@ -9,4 +9,7 @@ RAG_SEARCH_CRON_MANAGER=http://cron-manager:9010 RAG_SEARCH_LLM_ORCHESTRATOR=http://llm-orchestration-service:8100/orchestrate RAG_SEARCH_PROMPT_REFRESH=http://llm-orchestration-service:8100/prompt-config/refresh DOMAIN=localhost -DB_PASSWORD=dbadmin \ No newline at end of file +DB_PASSWORD=dbadmin +RAG_SEARCH_RUUTER_PUBLIC_INTERNAL_SERVICE=http://ruuter-public:8086/services +SERVICE_DMAPPER_HBS=http://data-mapper:3000/hbs/rag-search +SERVICE_PROJECT_LAYER=services \ No newline at end of file diff --git a/docs/HYBRID_SEARCH_CLASSIFICATION.md b/docs/HYBRID_SEARCH_CLASSIFICATION.md index 18c512ae..1de3f7f5 100644 --- a/docs/HYBRID_SEARCH_CLASSIFICATION.md +++ b/docs/HYBRID_SEARCH_CLASSIFICATION.md @@ -53,7 +53,8 @@ The system has two phases: | `src/intent_data_enrichment/main_enrichment.py` | Orchestrates per-example and summary point creation | | `src/intent_data_enrichment/qdrant_manager.py` | Qdrant collection management, upsert, and deletion | | `src/intent_data_enrichment/api_client.py` | LLM API calls (context generation, embeddings) | -| `src/intent_data_enrichment/models.py` | `EnrichedService` data model | +| `src/intent_data_enrichment/models.py` | `ServiceData`, `EnrichedService`, `EnrichmentResult` data models | +| `src/intent_data_enrichment/constants.py` | `EnrichmentConstants` — API URLs, Qdrant config, vector sizes, LLM prompt template | | `src/tool_classifier/sparse_encoder.py` | BM25-style sparse vector computation | ### What Changed: Single Embedding → Per-Example Indexing @@ -78,8 +79,8 @@ Service "Valuutakursid" → 4 Qdrant points dense: 3072-dim embedding of this exact text sparse: BM25 vector → {euro: 1.0, gbp: 1.0, kurss: 1.0, ...} - Point 3 (summary): "Valuutakursid - Kasutaja soovib infot..." - dense: 3072-dim embedding of name + description + LLM context + Point 3 (summary): "Service Name: Valuutakursid\nDescription: ...\nExample Queries: ...\nRequired Entities: ...\nEnriched Context: ..." + dense: 3072-dim embedding of combined text sparse: BM25 vector of combined text ``` @@ -101,9 +102,12 @@ Service "Valuutakursid" → 4 Qdrant points ```python # sparse_encoder.py +SPARSE_VOCAB_SIZE = 50_000 + text = "Mis suhe on euro ja usd vahel" tokens = re.findall(r"\w+", text.lower()) # ["mis", "suhe", "on", "euro", ...] -# Each token → hashed to index in [0, VOCAB_SIZE), value = term frequency +# Each token → MD5 hash (first 4 bytes) to index in [0, SPARSE_VOCAB_SIZE), value = term frequency +# Collisions are handled by summing values at the same index # Output: SparseVector(indices=[hash("mis"), hash("euro"), ...], values=[1.0, 1.0, ...]) ``` @@ -146,7 +150,7 @@ service_enrichment.sh │ ├─ Generate dense embedding (text-embedding-3-large) │ └─ Generate sparse vector (BM25 term hashing) │ - ├─ Step 3: Summary point (name + description + LLM context): + ├─ Step 3: Summary point (name + description + examples + entities + LLM context): │ ├─ Generate dense embedding │ └─ Generate sparse vector │ @@ -155,6 +159,17 @@ service_enrichment.sh └─ Step 5: Bulk upsert N+1 points to Qdrant ``` +### Summary Point Combined Text Format + +The summary point embeds a structured concatenation: +``` +Service Name: {name} +Description: {description} +Example Queries: {example1} | {example2} | ... +Required Entities: {entity1}, {entity2}, ... +Enriched Context: {LLM-generated context} +``` + ### Service Deletion When a service is deactivated, all its points are removed: @@ -186,12 +201,12 @@ POST /collections/intent_collections/points/query { "query": [0.023, -0.041, ...], # 3072-dim dense vector "using": "dense", - "limit": 6, + "limit": 6, # DENSE_SEARCH_TOP_K * 2 (3 * 2 = 6, allows dedup) "with_payload": true } ``` -Results are deduplicated by `service_id` (best score per service). +Results are deduplicated by `service_id` (best score per service), returning up to `DENSE_SEARCH_TOP_K` (3) unique services. **Why not use RRF scores?** Qdrant's RRF uses `1/(1+rank)`, producing fixed scores (0.50, 0.33, 0.25) regardless of actual relevance. A perfect match and a random query both get 0.50 for rank 1. Cosine similarity reflects true semantic closeness. @@ -203,6 +218,7 @@ Sparse prefetch is only included if the query produces a non-empty sparse vector ```python # classifier.py → _hybrid_search() +# First checks collection exists and has data (points_count > 0) POST /collections/intent_collections/points/query { "prefetch": [ @@ -215,6 +231,10 @@ POST /collections/intent_collections/points/query } ``` +> **Note:** Prefetch limit is `HYBRID_SEARCH_TOP_K * 2` (5 * 2 = 10). The sparse prefetch is conditionally added only when `sparse_vector.is_empty()` is False. + +Hybrid results are also deduplicated by `service_id` (best RRF score per service). + ### Routing Decision ``` @@ -251,6 +271,7 @@ Dense: Valuutakursid (cosine=0.5511), gap=0.2371 → Runs intent detection + entity extraction on matched service only → Entities: {currency_from: EUR, currency_to: THB} → Validation: PASSED ✓ +→ Calls service endpoint → Returns response ``` ### Path 3: AMBIGUOUS Service Match → LLM Confirmation @@ -285,17 +306,17 @@ SERVICE (Layer 1) → CONTEXT (Layer 2) → RAG (Layer 3) → OOD (Layer 4 | Path | Intent Detection | Entity Extraction | |------|-----------------|-------------------| | HIGH-CONFIDENCE | On 1 service (matched) | Yes — from LLM output | -| AMBIGUOUS | On 2-3 candidates | Yes — if LLM matches | +| AMBIGUOUS | On top candidates (from `top_results`) | Yes — if LLM matches | | Non-service | Not run | Not run | ### Intent Detection Module (DSPy) **File:** `src/tool_classifier/intent_detector.py` -The DSPy `IntentDetectionModule` receives: +The DSPy `IntentDetectionModule` uses `dspy.Predict` (direct prediction) and receives: - User query -- Candidate services (formatted as JSON) -- Conversation history (last 3 turns) +- Candidate services (formatted as JSON with service_id, name, description, required_entities, top 3 examples) +- Conversation history (last 3 turns, formatted as `{authorRole}: {message}`) It returns: ```json @@ -336,6 +357,18 @@ Entities dict → ordered array matching service schema: # Array: ["EUR", "THB"] ``` +### Service Endpoint Call + +After entity validation and transformation, the workflow calls the Ruuter active service endpoint: + +```python +# Endpoint: {RUUTER_SERVICE_BASE_URL}/services/active/{clean_service_name} +# Payload: {"chatId": "...", "authorId": "...", "input": ["EUR", "THB"]} +# Response: {"response": [{"content": "..."}]} → extracts content string +``` + +In streaming mode, the service content is wrapped as SSE events and streamed to the client. + --- ## Thresholds & Configuration @@ -387,7 +420,3 @@ Based on empirical testing with 42 Estonian queries (20 SERVICE, 22 RAG): - **Adding more services:** Score distributions improve naturally — service queries score higher, non-service score lower. - **Adding more examples per service:** Diverse phrasings expand the embedding coverage. Aim for 5-8 examples per service covering formal + informal + different word orders. - **Adjusting thresholds:** Monitor the logs (`Dense search: top=... cosine=...`) and adjust if real-world scores differ from test data. - -### Current Limitations - -- **Step 7 (Ruuter service call) is not yet implemented.** The service workflow currently returns a debug response with service metadata (endpoint URL, HTTP method, extracted entities) instead of calling the actual Ruuter service endpoint. See the `TODO: STEP 7` comments in `src/tool_classifier/workflows/service_workflow.py`. diff --git a/docs/TOOL_CLASSIFIER_AND_SERVICE_WORKFLOW.md b/docs/TOOL_CLASSIFIER_AND_SERVICE_WORKFLOW.md index 398299a8..ac92abb2 100644 --- a/docs/TOOL_CLASSIFIER_AND_SERVICE_WORKFLOW.md +++ b/docs/TOOL_CLASSIFIER_AND_SERVICE_WORKFLOW.md @@ -59,32 +59,45 @@ Handle queries that require calling external services/APIs: ### High-Level Flow +The service workflow has **3 routing paths** based on classification metadata from hybrid search: + +``` +Classification Result (from classifier.py) +│ +├─ needs_llm_confirmation = False (HIGH-CONFIDENCE) +│ → Skip discovery, run intent detection on matched service only +│ +├─ needs_llm_confirmation = True (AMBIGUOUS) +│ → Run LLM intent detection on top candidate services +│ +└─ No metadata (LEGACY / fallback) + → Full service discovery + optional semantic search + intent detection +``` + +Each path then continues through: ``` -1. Service Discovery - ↓ -2. Service Selection (Semantic Search or LLM-based) - ↓ -3. Intent Detection (DSPy LLM Call) - ↓ -4. Entity Extraction (From LLM Output) - ↓ -5. Entity Validation (Against Service Schema) - ↓ -6. Entity Transformation (Dict → Ordered Array) - ↓ -7. Service Call (TODO: Ruuter endpoint invocation) +1. Entity Extraction (from LLM output) +↓ +2. Entity Validation (against service schema) +↓ +3. Entity Transformation (Dict → Ordered Array) +↓ +4. Service Endpoint Construction +↓ +5. Service Call (Ruuter endpoint invocation) ``` --- -## 1. Service Discovery +## Service Discovery (Legacy Path) ### Method: `_call_service_discovery()` Calls Ruuter public endpoint to fetch available services: ```python -GET /rag-search/get-services-from-llm +GET {RAG_SEARCH_RUUTER_PUBLIC}/services/get-services +# Default: http://ruuter-public:8086/rag-search/services/get-services ``` **Response Structure:** @@ -122,16 +135,14 @@ if service_count <= 10: elif service_count > 10: # Many services → Use semantic search to narrow down - services = await _semantic_search_services(query, top_k=5) + services = await _semantic_search_services(query, top_k=10) ``` --- -## 2. Service Selection +## Semantic Search (When Many Services) -### Semantic Search (When Many Services) - -**Method:** `_semantic_search_services()` +### Method: `_semantic_search_services()` Uses Qdrant vector database to find relevant services: @@ -142,8 +153,8 @@ embedding = orchestration_service.create_embeddings_for_indexer([query]) # 2. Search Qdrant collection search_payload = { "vector": query_embedding, - "limit": 5, # Top 5 services - "score_threshold": 0.4, # Minimum similarity + "limit": 10, # Top 10 services (SEMANTIC_SEARCH_TOP_K) + "score_threshold": 0.2, # Minimum similarity (SEMANTIC_SEARCH_THRESHOLD) "with_payload": True } @@ -157,7 +168,7 @@ response = qdrant_client.post( --- -## 3. Intent Detection (LLM-Based) +## Intent Detection (LLM-Based) ### Method: `_detect_service_intent()` @@ -189,23 +200,24 @@ services_formatted = [ "name": "Currency Conversion", "description": "Convert EUR to other currencies", "required_entities": ["target_currency"], - "examples": ["How much is EUR in USD?", "Convert EUR to JPY"] + "examples": ["How much is EUR in USD?", "Convert EUR to JPY"] # Top 3 examples } ] # 2. Prepare conversation context (last 3 turns) conversation_context = """ -user: Hello -assistant: Hi! How can I help? -user: How much is 100 EUR in USD? +end_user: Hello +backoffice_user: Hi! How can I help? +end_user: How much is 100 EUR in USD? """ -# 3. Call DSPy module -intent_result = intent_detector.forward( - user_query="How much is 100 EUR in USD?", - services=services_formatted, - conversation_history=conversation_history -) +# 3. Call DSPy module (uses dspy.Predict, not ChainOfThought) +with self.llm_manager.use_task_local(): + intent_result = intent_module.forward( + user_query="How much is 100 EUR in USD?", + services=services_formatted, + conversation_history=conversation_history + ) ``` ### LLM Output Format @@ -226,8 +238,8 @@ The LLM returns structured JSON: ### Confidence Threshold ```python -if confidence < 0.7: - # Low confidence → Service workflow returns None → Fallback to RAG +if matched_service_id is None or confidence < 0.7: + # Low confidence → Service workflow returns None → Fallback to Context/RAG return None ``` @@ -251,7 +263,7 @@ costs_metric["intent_detection"] = usage_info --- -## 4. Entity Extraction +## Entity Extraction ### From LLM Output @@ -299,7 +311,7 @@ Entities are extracted as **key-value pairs** where: --- -## 5. Entity Validation +## Entity Validation ### Method: `_validate_entities()` @@ -367,7 +379,7 @@ validation_errors = ["Entity 'target_currency' has empty value"] --- -## 6. Entity Transformation +## Entity Transformation ### Method: `_transform_entities_to_array()` @@ -397,18 +409,14 @@ entities_array = ["USD", "EUR", "100"] ```python def _transform_entities_to_array( + self, entities_dict: Dict[str, str], entity_order: List[str] ) -> List[str]: """Transform entity dict to ordered array.""" - ordered_array = [] - - for entity_key in entity_order: - # Get value from dict, or empty string if missing - value = entities_dict.get(entity_key, "") - ordered_array.append(value) - - return ordered_array + if not entity_order: + return [] + return [entities_dict.get(key, "") for key in entity_order] ``` ### Example @@ -435,40 +443,62 @@ def _transform_entities_to_array( --- -## 7. Service Call (TODO: Step 7) +## Service Call (Step 7 — Implemented) ### Endpoint Construction ```python -endpoint_url = f"{RUUTER_BASE_URL}/services/active{service_name}" -# Example: "http://ruuter:8080/services/active/currency-conversion" -# (Note: service_name from service metadata, e.g., "/currency-conversion") +def _construct_service_endpoint(self, service_name: str, chat_id: str) -> str: + # Clean service name: strip whitespace, remove invisible Unicode chars, replace spaces with _ + clean_name = service_name.strip().translate(INVISIBLE_CHAR_TABLE).replace(" ", "_") + return f"{RUUTER_SERVICE_BASE_URL}/services/active/{clean_name}" + # Example: "http://ruuter-public:8086/services/services/active/Currency_Conversion" ``` -### Payload Construction (Planned) +### Payload Construction ```python payload = { + "chatId": chat_id, + "authorId": author_id, "input": entities_array, # ["USD", "EUR", "100"] - "authorId": request.authorId, - "chatId": request.chatId } ``` -### HTTP Call (Planned) +### HTTP Call ```python -# Non-streaming -response = await httpx.post( - endpoint_url, - json=payload, - timeout=5.0 -) +async def _call_service_endpoint( + self, endpoint_url, http_method, entities_array, chat_id, author_id +) -> Optional[str]: + async with httpx.AsyncClient(timeout=SERVICE_CALL_TIMEOUT) as client: + if http_method.upper() == "POST": + response = await client.post(endpoint_url, json=payload) + else: + response = await client.get(endpoint_url, params=payload) + + response.raise_for_status() + data = response.json() + + # Ruuter wraps the DSL return value in {"response": ...} + if isinstance(data, dict) and "response" in data: + data = data["response"] + + # DMapper returns a JSON array; each item has a "content" field + if isinstance(data, list) and len(data) > 0: + content = data[0].get("content", "") + return content if content else None +``` -# Streaming -async with httpx.stream("POST", endpoint_url, json=payload) as stream: - async for line in stream.aiter_lines(): - yield orchestration_service.format_sse(chat_id, line) +### Streaming Mode + +In streaming mode, the service content is wrapped as SSE events: + +```python +async def service_stream() -> AsyncIterator[str]: + yield orchestration_service.format_sse(chat_id, service_content) + yield orchestration_service.format_sse(chat_id, "END") + orchestration_service.log_costs(costs_metric) ``` --- @@ -483,28 +513,14 @@ async with httpx.stream("POST", endpoint_url, json=payload) as stream: ### Step-by-Step Execution -#### 1. Service Discovery -```json -{ - "service_count": 5, - "services": [ - { - "serviceId": "currency_conversion_eur", - "name": "Currency Conversion (EUR)", - "entities": ["target_currency"], - "examples": ["How much is EUR in USD?"] - } - ] -} -``` - -#### 2. Service Selection +#### 1. Classification (Hybrid Search) ```python -# Few services (5 <= 10) → Use all for intent detection -services = discovery_result["services"] +# Dense search finds best service match +# cosine=0.5511, gap=0.2371 +# → HIGH-CONFIDENCE path (needs_llm_confirmation=False) ``` -#### 3. Intent Detection (LLM Call) +#### 2. Intent Detection (LLM Call on matched service only) ```json { "matched_service_id": "currency_conversion_eur", @@ -516,12 +532,12 @@ services = discovery_result["services"] } ``` -#### 4. Entity Extraction +#### 3. Entity Extraction ```python entities_dict = {"target_currency": "THB"} ``` -#### 5. Entity Validation +#### 4. Entity Validation ```python validation_result = { "is_valid": True, @@ -531,7 +547,7 @@ validation_result = { } ``` -#### 6. Entity Transformation +#### 5. Entity Transformation ```python # Schema: ["target_currency"] # Dict: {"target_currency": "THB"} @@ -539,14 +555,17 @@ validation_result = { entities_array = ["THB"] ``` -#### 7. Service Call (TODO) +#### 6. Service Call ```python -# Planned implementation -response = await call_service( - url="http://ruuter:8080/currency/convert", - method="POST", - payload={"input": ["THB"], "chatId": "..."} +endpoint_url = "http://ruuter-public:8086/services/services/active/Currency_Conversion" +response = await _call_service_endpoint( + endpoint_url=endpoint_url, + http_method="POST", + entities_array=["THB"], + chat_id="...", + author_id="..." ) +# Returns content string from Ruuter response ``` --- @@ -580,16 +599,16 @@ LLM USAGE COSTS BREAKDOWN: ### When Service Workflow Returns None ```python -# Scenario 1: No service match (confidence < 0.7) -if not intent_result or intent_result.get("confidence", 0) < 0.7: +# Scenario 1: No service_id in context after intent detection +if not context.get("service_id"): return None # Fallback to CONTEXT layer -# Scenario 2: Service validation failed -if not validated_service: +# Scenario 2: Service metadata extraction failed +if not service_metadata: return None # Fallback to CONTEXT layer -# Scenario 3: No services discovered -if not services: +# Scenario 3: Service endpoint call failed +if service_content is None: return None # Fallback to CONTEXT layer ``` @@ -607,22 +626,31 @@ Query: "What is VAT?" ## Configuration Constants ```python -# Service discovery -RUUTER_BASE_URL = "http://ruuter.public:8080" -SERVICE_DISCOVERY_TIMEOUT = 5.0 # seconds +# Ruuter service configuration +RUUTER_BASE_URL = "http://ruuter-private:8086" +RUUTER_SERVICE_BASE_URL = "http://ruuter-public:8086/services" +RAG_SEARCH_RUUTER_PUBLIC = "http://ruuter-public:8086/rag-search" + +# Service call timeouts +SERVICE_CALL_TIMEOUT = 10 # seconds for external service calls +SERVICE_DISCOVERY_TIMEOUT = 10.0 # seconds for service discovery # Service selection thresholds -SERVICE_COUNT_THRESHOLD = 10 # Switch to semantic search if exceeded -MAX_SERVICES_FOR_LLM_CONTEXT = 20 # Max services to pass to LLM +SERVICE_COUNT_THRESHOLD = 10 # Switch to semantic search if exceeded +MAX_SERVICES_FOR_LLM_CONTEXT = 50 # Max services to pass to LLM # Semantic search -QDRANT_COLLECTION = "services_collection" -SEMANTIC_SEARCH_TOP_K = 5 # Top 5 relevant services -SEMANTIC_SEARCH_THRESHOLD = 0.4 # Minimum similarity score -QDRANT_TIMEOUT = 2.0 # seconds +QDRANT_COLLECTION = "intent_collections" +SEMANTIC_SEARCH_TOP_K = 10 # Top 10 relevant services +SEMANTIC_SEARCH_THRESHOLD = 0.2 # Minimum similarity score +QDRANT_TIMEOUT = 10.0 # seconds -# Intent detection -INTENT_CONFIDENCE_THRESHOLD = 0.7 # Minimum confidence to proceed +# Hybrid search classification (see HYBRID_SEARCH_CLASSIFICATION.md) +DENSE_MIN_THRESHOLD = 0.38 # Minimum cosine to consider service match +DENSE_HIGH_CONFIDENCE_THRESHOLD = 0.40 # Cosine for high-confidence path +DENSE_SCORE_GAP_THRESHOLD = 0.05 # Required gap between top two services +DENSE_SEARCH_TOP_K = 3 # Unique services from dense search +HYBRID_SEARCH_TOP_K = 5 # Results from hybrid RRF search ``` --- @@ -639,11 +667,13 @@ INTENT_CONFIDENCE_THRESHOLD = 0.7 # Minimum confidence to proceed - Schema defines canonical order - Missing entities → empty strings -### 3. **Two-Stage Service Selection** -- Few services (≤10): Pass all to LLM -- Many services (>10): Semantic search first +### 3. **Three Routing Paths** +- **High-confidence**: Hybrid search matched → skip discovery, intent on 1 service +- **Ambiguous**: Moderate match → intent detection on top candidates +- **Legacy**: No classification metadata → full discovery flow ### 4. **LLM-Based Intent Detection** +- Uses DSPy `dspy.Predict` (not ChainOfThought) for direct prediction - Intelligent service matching - Natural language understanding - Multilingual support (Estonian, English, Russian) @@ -653,8 +683,14 @@ INTENT_CONFIDENCE_THRESHOLD = 0.7 # Minimum confidence to proceed - Tracks intent detection LLM costs - Integrated with budget system +### 6. **Implemented Service Call** +- Calls Ruuter active service endpoint via httpx +- Handles POST and GET methods +- Parses DMapper response format (`{"response": [{"content": "..."}]}`) +- Cleans service name (invisible chars, whitespace → underscore) + --- ## Summary -The Tool Classifier's layer architecture enables intelligent query routing with graceful fallbacks. The Service Workflow (Layer 1) uses **LLM-based intent detection** to match user queries to external services, extract entities, validate them against service schemas, and prepare them for service invocation—all while maintaining comprehensive cost tracking and seamless integration with the broader RAG pipeline. +The Tool Classifier's layer architecture enables intelligent query routing with graceful fallbacks. The Service Workflow (Layer 1) uses **hybrid search classification** (dense + sparse + RRF) to route queries into 3 paths: high-confidence (skip discovery), ambiguous (LLM confirmation on candidates), or legacy (full discovery). It then uses **LLM-based intent detection** (DSPy Predict) to match user queries to external services, extract entities, validate them against service schemas, transform to ordered arrays, and **call the Ruuter active service endpoint** — all while maintaining comprehensive cost tracking and seamless integration with the broader RAG pipeline. diff --git a/new.txt b/new.txt new file mode 100644 index 00000000..9e7525fb --- /dev/null +++ b/new.txt @@ -0,0 +1,38 @@ +1️⃣ Broneeringu kinnitus (Booking Confirmation) + +Estonian → English + +Kas minu broneering on kinnitatud? +→ Is my booking confirmed? + +Palun kinnita minu broneering. +→ Please confirm my booking. + +Kas broneering sai edukalt tehtud? +→ Was the booking successfully made? + +2️⃣ Kalastusloa uuendamise teade (Fishing License Renewal) + +Estonian → English + +Kas minu kalastusluba tuleb uuendada? +→ Do I need to renew my fishing license? + +Millal mu kalastusluba aegub? +→ When does my fishing license expire? + +Kas mu kalastusluba on veel kehtiv? +→ Is my fishing license still valid? + +3️⃣ Koolivaheajad (School Holidays) + +Estonian → English + +Millal on järgmine koolivaheaeg? +→ When is the next school holiday? + +Kas sa saad öelda selle aasta koolivaheajad? +→ Can you tell me the school holidays for this year? + +Millal algab suvevaheaeg? +→ When does the summer holiday start? \ No newline at end of file diff --git a/src/tool_classifier/constants.py b/src/tool_classifier/constants.py index 65f30332..64fdbe55 100644 --- a/src/tool_classifier/constants.py +++ b/src/tool_classifier/constants.py @@ -37,6 +37,9 @@ RUUTER_BASE_URL = "http://ruuter-private:8086" """Base URL for Ruuter private service endpoints.""" +RUUTER_SERVICE_BASE_URL = "http://ruuter-public:8086/services" +"""Base URL for Ruuter public service endpoints (active services).""" + RAG_SEARCH_RUUTER_PUBLIC = "http://ruuter-public:8086/rag-search" """Public Ruuter endpoint for RAG search service discovery.""" diff --git a/src/tool_classifier/workflows/service_workflow.py b/src/tool_classifier/workflows/service_workflow.py index bb72f785..b7fe561b 100644 --- a/src/tool_classifier/workflows/service_workflow.py +++ b/src/tool_classifier/workflows/service_workflow.py @@ -20,9 +20,10 @@ QDRANT_PORT, QDRANT_TIMEOUT, RAG_SEARCH_RUUTER_PUBLIC, - RUUTER_BASE_URL, + RUUTER_SERVICE_BASE_URL, SEMANTIC_SEARCH_THRESHOLD, SEMANTIC_SEARCH_TOP_K, + SERVICE_CALL_TIMEOUT, SERVICE_COUNT_THRESHOLD, SERVICE_DISCOVERY_TIMEOUT, ) @@ -105,7 +106,6 @@ async def _semantic_search_services( return None try: - # Generate embedding using orchestration service embedding_result = self.orchestration_service.create_embeddings_for_indexer( texts=[query], environment=request.environment, @@ -120,12 +120,10 @@ async def _semantic_search_services( query_embedding = embeddings[0] - # Create Qdrant client with proper resource cleanup via context manager qdrant_url = f"http://{QDRANT_HOST}:{QDRANT_PORT}" async with httpx.AsyncClient( base_url=qdrant_url, timeout=QDRANT_TIMEOUT ) as client: - # Verify collection exists and has data try: collection_info = await client.get( f"/collections/{QDRANT_COLLECTION}" @@ -139,7 +137,6 @@ async def _semantic_search_services( except Exception as e: logger.warning(f"[{chat_id}] Could not verify collection: {e}") - # Search Qdrant collection search_payload = { "vector": query_embedding, "limit": top_k, @@ -167,7 +164,6 @@ async def _semantic_search_services( ) return None - # Transform Qdrant results to service format services: List[Dict[str, Any]] = [] for point in points: payload = point.get("payload", {}) @@ -180,8 +176,6 @@ async def _semantic_search_services( "description": payload.get("description"), "examples": payload.get("examples", []), "entities": payload.get("entities", []), - # Note: endpoint not stored in intent_collections, - # will be resolved via database lookup if needed "similarity_score": score, } services.append(service) @@ -234,30 +228,24 @@ async def _detect_service_intent( - usage_info: Cost and token usage information """ try: - # Ensure DSPy is configured with LLMManager if self.llm_manager: self.llm_manager.ensure_global_config() else: logger.error(f"[{chat_id}] LLM Manager not available") return None, {} - # Capture history length before LLM call for cost tracking lm = dspy.settings.lm history_length_before = ( len(lm.history) if lm and hasattr(lm, "history") else 0 ) - # Create DSPy module intent_module = IntentDetectionModule() - - # Convert conversation history to dict format history_dicts = [ {"authorRole": msg.authorRole, "message": msg.message} for msg in conversation_history if hasattr(msg, "authorRole") and hasattr(msg, "message") ] - # Call DSPy forward with task-local config with self.llm_manager.use_task_local(): intent_result = intent_module.forward( user_query=user_query, @@ -265,7 +253,6 @@ async def _detect_service_intent( conversation_history=history_dicts, ) - # Extract usage information after LLM call usage_info = get_lm_usage_since(history_length_before) return intent_result, usage_info @@ -342,31 +329,20 @@ def _extract_service_metadata( self, context: Dict[str, Any], chat_id: str ) -> Optional[Dict[str, Any]]: """Extract service and entity metadata from context.""" - # Check if service_id exists service_id = context.get("service_id") if not service_id: logger.error(f"[{chat_id}] Missing service_id in context") return None - # Check if service_data exists service_data = context.get("service_data") if not service_data: logger.error(f"[{chat_id}] Missing service_data in context") return None - # Extract entities dict from context (LLM extracted) entities_dict = context.get("entities", {}) - - # Extract entity schema from service_data (expected order) - entity_schema = service_data.get("entities", []) - if entity_schema is None: - entity_schema = [] - - # Extract service name + entity_schema = service_data.get("entities", []) or [] service_name = service_data.get("name", service_id) - - # Extract HTTP method (ruuter_type) - defaults to GET if not specified - ruuter_type = service_data.get("ruuter_type", "GET") + ruuter_type = service_data.get("ruuter_type", "POST") return { "service_id": service_id, @@ -417,10 +393,7 @@ def _validate_entities( if entity_key not in service_schema: extra_entities.append(entity_key) - # Determine overall validity - # We consider it valid even with missing entities (will send empty strings) - # Let the external service validate required parameters - is_valid = True # Always true - we proceed with partial entities + is_valid = True return { "is_valid": is_valid, @@ -435,29 +408,98 @@ def _transform_entities_to_array( """Transform entity dictionary to ordered array based on service schema.""" if not entity_order: return [] - - # Transform to ordered array, filling missing with empty strings return [entities_dict.get(key, "") for key in entity_order] + _INVISIBLE_CHAR_TABLE = str.maketrans( + "", "", "\u2060\u200b\u200c\u200d\ufeff\u00ad\u200e\u200f" + ) + def _construct_service_endpoint(self, service_name: str, chat_id: str) -> str: """Construct the full service endpoint URL for Ruuter.""" - return f"{RUUTER_BASE_URL}/services/active{service_name}" + clean_name = ( + service_name.strip().translate(self._INVISIBLE_CHAR_TABLE).replace(" ", "_") + ) + return f"{RUUTER_SERVICE_BASE_URL}/services/active/{clean_name}" - def _format_debug_response( + async def _call_service_endpoint( self, - service_name: str, endpoint_url: str, http_method: str, entities_array: List[str], - ) -> str: - """Format debug information for testing (temporary before Step 7 implementation).""" - entities_str = ", ".join(f'"{e}"' for e in entities_array) - return ( - f" Service Validated: {service_name}\n" - f" Endpoint URL: {endpoint_url}\n" - f" HTTP Method: {http_method}\n" - f" Extracted Entities: [{entities_str}]\n\n" - ) + chat_id: str, + author_id: str, + ) -> Optional[str]: + """Call the Ruuter active service endpoint and extract response content. + + Args: + endpoint_url: Full URL of the active service endpoint + http_method: HTTP method (POST/GET) + entities_array: Ordered entity values for the service + chat_id: Chat session ID + author_id: Author/user ID + + Returns: + Service response content string, or None on failure. + """ + payload = { + "chatId": chat_id, + "authorId": author_id, + "input": entities_array, + } + + try: + async with httpx.AsyncClient(timeout=SERVICE_CALL_TIMEOUT) as client: + if http_method.upper() == "POST": + response = await client.post(endpoint_url, json=payload) + else: + response = await client.get(endpoint_url, params=payload) + + response.raise_for_status() + data = response.json() + + # Ruuter wraps the DSL return value in {"response": ...} + # The inner value is the DMapper array from bot_responses_to_messages + if isinstance(data, dict) and "response" in data: + data = data["response"] + + # DMapper returns a JSON array; each item has a "content" field + if isinstance(data, list) and len(data) > 0: + content = data[0].get("content", "") + if content: + logger.info( + f"[{chat_id}] Service endpoint returned content " + f"({len(content)} chars)" + ) + return content + + logger.warning( + f"[{chat_id}] Service response missing 'content' field" + ) + return None + + logger.warning( + f"[{chat_id}] Unexpected service response format: {type(data)}" + ) + return None + + except httpx.TimeoutException: + logger.error( + f"[{chat_id}] Service endpoint timeout after {SERVICE_CALL_TIMEOUT}s: " + f"{endpoint_url}" + ) + return None + except httpx.HTTPStatusError as e: + logger.error( + f"[{chat_id}] Service endpoint HTTP error: " + f"{e.response.status_code} for {endpoint_url}" + ) + return None + except Exception as e: + logger.error( + f"[{chat_id}] Service endpoint call failed: {e}", + exc_info=True, + ) + return None async def _log_request_details( self, @@ -477,16 +519,13 @@ async def _log_request_details( chat_id = request.chatId logger.info(f"[{chat_id}] SERVICE WORKFLOW ({mode}): {request.message}") - # Service Discovery discovery_result = await self._call_service_discovery(chat_id) if discovery_result: - # Extract data from nested response structure response_data = discovery_result.get("response", {}) use_semantic = response_data.get("use_semantic_search", False) service_count = response_data.get("service_count", 0) - # Handle service_count if it's a string or NaN if isinstance(service_count, str): try: service_count = int(service_count) @@ -495,12 +534,10 @@ async def _log_request_details( services_from_ruuter = response_data.get("services", []) - # Use semantic search if count > threshold if service_count > SERVICE_COUNT_THRESHOLD: use_semantic = True if use_semantic: - # Use semantic search to find relevant services services = await self._semantic_search_services( query=request.message, request=request, @@ -567,34 +604,25 @@ async def execute_async( chat_id = request.chatId - # Create costs tracking dictionary (follows RAG workflow pattern) costs_metric: Dict[str, Dict[str, Any]] = {} - # Use parent time_metric or create new one if time_metric is None: time_metric = {} - # Check if classifier provided hybrid search metadata needs_llm_confirmation = context.get("needs_llm_confirmation") if needs_llm_confirmation is False: - # HIGH CONFIDENCE PATH: Classifier matched a service with high confidence - # Skip service discovery — use hybrid search match directly - matched_service_id = context.get("matched_service_id") matched_service_name = context.get("matched_service_name") cosine_score = context.get("cosine_score", 0.0) logger.info( - f"[{chat_id}] HIGH-CONFIDENCE SERVICE MATCH (non-streaming): " - f"{matched_service_name} (cosine_score={cosine_score:.4f}) - " - f"skipping discovery" + f"[{chat_id}] High-confidence service match: " + f"{matched_service_name} (score={cosine_score:.4f})" ) - # Get service details from top_results (already retrieved by classifier) top_results = context.get("top_results", []) if top_results: matched = top_results[0] - # Run entity extraction via LLM (DSPy) for this single service start_time = time.time() await self._process_intent_detection( services=[matched], @@ -605,24 +633,15 @@ async def execute_async( ) time_metric["service.intent_detection"] = time.time() - start_time - # Ensure service_data is populated from hybrid match - # _process_intent_detection may not set it if DSPy returns - # a different service_id format, so we populate it explicitly if not context.get("service_data"): context["service_id"] = matched.get("service_id") context["service_data"] = matched - logger.info( - f"[{chat_id}] Populated service_data from hybrid match: " - f"{matched.get('name')}" - ) elif needs_llm_confirmation is True: - # AMBIGUOUS PATH: Multiple services scored similarly - # Run LLM intent detection only on candidate services (not all services) top_results = context.get("top_results", []) logger.info( - f"[{chat_id}] AMBIGUOUS SERVICE MATCH (non-streaming): " - f"running LLM intent detection on {len(top_results)} candidates" + f"[{chat_id}] Ambiguous match: " + f"running intent detection on {len(top_results)} candidates" ) start_time = time.time() @@ -637,44 +656,26 @@ async def execute_async( time_metric["service.intent_detection"] = time.time() - start_time else: - # LEGACY PATH: No hybrid search metadata (classifier disabled or error) - # Full service discovery + intent detection (original behavior) start_time = time.time() await self._log_request_details( request, context, mode="non-streaming", costs_metric=costs_metric ) time_metric["service.discovery"] = time.time() - start_time - # Check if service was detected and validated if not context.get("service_id"): - logger.info( - f"[{chat_id}] No service detected or validated - " - f"returning None to fallback to next layer" - ) + logger.info(f"[{chat_id}] No service matched, falling back") return None - # Entity Transformation & Validation - logger.info(f"[{chat_id}] Entity Transformation:") - - # Step 1: Extract service metadata from context start_time = time.time() service_metadata = self._extract_service_metadata(context, chat_id) if not service_metadata: - logger.error( - f"[{chat_id}] - Metadata extraction failed - " - f"returning None to fallback" - ) return None - logger.info(f"[{chat_id}] - Service: {service_metadata['service_name']}") - logger.info( - f"[{chat_id}] - Schema entities: {service_metadata['entity_schema']}" - ) logger.info( - f"[{chat_id}] - Extracted entities: {service_metadata['entities_dict']}" + f"[{chat_id}] Service: {service_metadata['service_name']}, " + f"entities: {service_metadata['entities_dict']}" ) - # Step 2: Validate entities against schema validation_result = self._validate_entities( extracted_entities=service_metadata["entities_dict"], service_schema=service_metadata["entity_schema"], @@ -683,28 +684,11 @@ async def execute_async( ) time_metric["service.entity_validation"] = time.time() - start_time - logger.info( - f"[{chat_id}] - Validation status: " - f"{'PASSED ✓' if validation_result['is_valid'] else 'FAILED ✗'}" - ) - if validation_result["missing_entities"]: logger.warning( - f"[{chat_id}] - Missing entities (will send empty strings): " - f"{validation_result['missing_entities']}" - ) - - if validation_result["extra_entities"]: - logger.info( - f"[{chat_id}] - Extra entities (ignored): " - f"{validation_result['extra_entities']}" + f"[{chat_id}] Missing entities: {validation_result['missing_entities']}" ) - if validation_result["validation_errors"]: - for error in validation_result["validation_errors"]: - logger.warning(f"[{chat_id}] - Validation warning: {error}") - - # Step 3: Transform entities dict to ordered array entities_array = self._transform_entities_to_array( entities_dict=service_metadata["entities_dict"], entity_order=service_metadata["entity_schema"], @@ -713,46 +697,36 @@ async def execute_async( context["entities_array"] = entities_array context["validation_result"] = validation_result - # Construct service endpoint URL endpoint_url = self._construct_service_endpoint( service_name=service_metadata["service_name"], chat_id=chat_id ) - context["endpoint_url"] = endpoint_url context["http_method"] = service_metadata["ruuter_type"] - logger.info(f"[{chat_id}] Service prepared: {endpoint_url}") - - # TODO: STEP 7 - Call Ruuter service endpoint and return response - # 1. Build payload: {"input": entities_array, "authorId": request.authorId, "chatId": request.chatId} - # 2. Call endpoint using http_method (POST/GET) with SERVICE_CALL_TIMEOUT - # 3. Parse Ruuter response and extract result - # 4. Return OrchestrationResponse with actual service result - # 5. Handle errors (timeout, HTTP errors, malformed JSON) - - # STEP 6: Return debug response (temporary until Step 7 - Ruuter call implemented) - # REMOVE THIS BLOCK AFTER STEP 7 IMPLEMENTATION (START) - debug_content = self._format_debug_response( - service_name=service_metadata["service_name"], + start_time = time.time() + service_content = await self._call_service_endpoint( endpoint_url=endpoint_url, http_method=service_metadata["ruuter_type"], entities_array=entities_array, + chat_id=chat_id, + author_id=request.authorId, ) + time_metric["service.endpoint_call"] = time.time() - start_time - logger.info(f"[{chat_id}] Returning debug response (Step 7 pending)") - - # Log costs after service workflow completes (follows RAG workflow pattern) if self.orchestration_service: self.orchestration_service.log_costs(costs_metric) + if service_content is None: + logger.warning(f"[{chat_id}] Service endpoint call failed, falling back") + return None + return OrchestrationResponse( chatId=request.chatId, llmServiceActive=True, questionOutOfLLMScope=False, inputGuardFailed=False, - content=debug_content, + content=service_content, ) - # REMOVE THIS BLOCK AFTER STEP 7 IMPLEMENTATION (END) async def execute_streaming( self, @@ -772,23 +746,19 @@ async def execute_streaming( chat_id = request.chatId - # Create costs tracking dictionary (follows RAG workflow pattern) costs_metric: Dict[str, Dict[str, Any]] = {} - # Use parent time_metric or create new one if time_metric is None: time_metric = {} - # Check if classifier provided hybrid search metadata needs_llm_confirmation = context.get("needs_llm_confirmation") if needs_llm_confirmation is False: - # HIGH CONFIDENCE PATH: Skip discovery, use matched service matched_service_name = context.get("matched_service_name") cosine_score = context.get("cosine_score", 0.0) logger.info( - f"[{chat_id}] HIGH-CONFIDENCE SERVICE MATCH (streaming): " - f"{matched_service_name} (cosine_score={cosine_score:.4f})" + f"[{chat_id}] High-confidence service match: " + f"{matched_service_name} (score={cosine_score:.4f})" ) top_results = context.get("top_results", []) @@ -805,21 +775,15 @@ async def execute_streaming( ) time_metric["service.intent_detection"] = time.time() - start_time - # Ensure service_data is populated from hybrid match if not context.get("service_data"): context["service_id"] = matched.get("service_id") context["service_data"] = matched - logger.info( - f"[{chat_id}] Populated service_data from hybrid match: " - f"{matched.get('name')}" - ) elif needs_llm_confirmation is True: - # AMBIGUOUS PATH: Run LLM intent detection on candidates top_results = context.get("top_results", []) logger.info( - f"[{chat_id}] AMBIGUOUS SERVICE MATCH (streaming): " - f"{len(top_results)} candidates" + f"[{chat_id}] Ambiguous match: " + f"running intent detection on {len(top_results)} candidates" ) start_time = time.time() @@ -834,42 +798,25 @@ async def execute_streaming( time_metric["service.intent_detection"] = time.time() - start_time else: - # LEGACY PATH: Full service discovery (original behavior) start_time = time.time() await self._log_request_details( request, context, mode="streaming", costs_metric=costs_metric ) time_metric["service.discovery"] = time.time() - start_time - # Check if service was detected and validated if not context.get("service_id"): - logger.info( - f"[{chat_id}] No service detected or validated - " - f"returning None to fallback to next layer" - ) + logger.info(f"[{chat_id}] No service matched, falling back") return None - # Entity Transformation & Validation - logger.info(f"[{chat_id}] Entity Transformation:") - - # Step 1: Extract service metadata from context service_metadata = self._extract_service_metadata(context, chat_id) if not service_metadata: - logger.error( - f"[{chat_id}] - Metadata extraction failed - " - f"returning None to fallback" - ) return None - logger.info(f"[{chat_id}] - Service: {service_metadata['service_name']}") logger.info( - f"[{chat_id}] - Schema entities: {service_metadata['entity_schema']}" - ) - logger.info( - f"[{chat_id}] - Extracted entities: {service_metadata['entities_dict']}" + f"[{chat_id}] Service: {service_metadata['service_name']}, " + f"entities: {service_metadata['entities_dict']}" ) - # Step 2: Validate entities against schema validation_result = self._validate_entities( extracted_entities=service_metadata["entities_dict"], service_schema=service_metadata["entity_schema"], @@ -877,28 +824,11 @@ async def execute_streaming( chat_id=chat_id, ) - logger.info( - f"[{chat_id}] - Validation status: " - f"{'PASSED ✓' if validation_result['is_valid'] else 'FAILED ✗'}" - ) - if validation_result["missing_entities"]: logger.warning( - f"[{chat_id}] - Missing entities (will send empty strings): " - f"{validation_result['missing_entities']}" + f"[{chat_id}] Missing entities: {validation_result['missing_entities']}" ) - if validation_result["extra_entities"]: - logger.info( - f"[{chat_id}] - Extra entities (ignored): " - f"{validation_result['extra_entities']}" - ) - - if validation_result["validation_errors"]: - for error in validation_result["validation_errors"]: - logger.warning(f"[{chat_id}] - Validation warning: {error}") - - # Step 3: Transform entities dict to ordered array entities_array = self._transform_entities_to_array( entities_dict=service_metadata["entities_dict"], entity_order=service_metadata["entity_schema"], @@ -907,47 +837,32 @@ async def execute_streaming( context["entities_array"] = entities_array context["validation_result"] = validation_result - # Construct service endpoint URL endpoint_url = self._construct_service_endpoint( service_name=service_metadata["service_name"], chat_id=chat_id ) - context["endpoint_url"] = endpoint_url context["http_method"] = service_metadata["ruuter_type"] - logger.info(f"[{chat_id}] Service prepared: {endpoint_url}") - - # TODO: STEP 7 - Call Ruuter service endpoint and stream response - # 1. Build payload: {"input": entities_array, "authorId": request.authorId, "chatId": request.chatId} - # 2. Call endpoint using http_method (POST/GET) with SERVICE_CALL_TIMEOUT - # 3. Parse Ruuter response and extract result - # 4. Format result as SSE and yield chunks - # 5. Handle errors (timeout, HTTP errors, malformed JSON) - - # STEP 6: Return debug response as async iterator (temporary until Step 7) - # REMOVE THIS BLOCK AFTER STEP 7 IMPLEMENTATION (START) - debug_content = self._format_debug_response( - service_name=service_metadata["service_name"], + service_content = await self._call_service_endpoint( endpoint_url=endpoint_url, http_method=service_metadata["ruuter_type"], entities_array=entities_array, + chat_id=chat_id, + author_id=request.authorId, ) - logger.info(f"[{chat_id}] Streaming debug response (Step 7 pending)") + if service_content is None: + logger.warning(f"[{chat_id}] Service endpoint call failed, falling back") + return None if self.orchestration_service is None: raise RuntimeError("Orchestration service not initialized for streaming") - # Store reference for closure (helps type checker) orchestration_service = self.orchestration_service - async def debug_stream() -> AsyncIterator[str]: - yield orchestration_service.format_sse(chat_id, debug_content) + async def service_stream() -> AsyncIterator[str]: + yield orchestration_service.format_sse(chat_id, service_content) yield orchestration_service.format_sse(chat_id, "END") - - # Log costs after streaming completes (follows RAG workflow pattern) - # Must be inside generator because costs are accumulated during streaming orchestration_service.log_costs(costs_metric) - return debug_stream() - # REMOVE THIS BLOCK AFTER STEP 7 IMPLEMENTATION (END) + return service_stream() diff --git a/tests/data/classification_test_queries.json b/tests/data/classification_test_queries.json new file mode 100644 index 00000000..28bb4814 --- /dev/null +++ b/tests/data/classification_test_queries.json @@ -0,0 +1,266 @@ +[ + { + "query": "Mitu töötajat on ettevõttes Bolt?", + "expected_category": "SERVICE", + "expected_service_id": "common_service_companies_employees", + "language": "et" + }, + { + "query": "Kui palju inimesi töötab firmas Tallink?", + "expected_category": "SERVICE", + "expected_service_id": "common_service_companies_employees", + "language": "et" + }, + { + "query": "Mis on Swedbanki töötajate arv?", + "expected_category": "SERVICE", + "expected_service_id": "common_service_companies_employees", + "language": "et" + }, + { + "query": "Kui palju töötajaid on ettevõttel Eesti Energia?", + "expected_category": "SERVICE", + "expected_service_id": "common_service_companies_employees", + "language": "et" + }, + { + "query": "Mis on ettevõtte aasta käive?", + "expected_category": "SERVICE", + "expected_service_id": "common_service_companies_revenue", + "language": "et" + }, + { + "query": "Kui suur on firma käive?", + "expected_category": "SERVICE", + "expected_service_id": "common_service_companies_revenue", + "language": "et" + }, + { + "query": "Kui palju maksis ettevõte tööjõumakse?", + "expected_category": "SERVICE", + "expected_service_id": "common_service_companies_workforce_taxes", + "language": "et" + }, + { + "query": "Kui palju maksis ettevõte riiklikke makse?", + "expected_category": "SERVICE", + "expected_service_id": "common_service_companies_national_taxes", + "language": "et" + }, + { + "query": "Kes on firma tegelikud kasusaajad?", + "expected_category": "SERVICE", + "expected_service_id": "common_service_companies_beneficiaries", + "language": "et" + }, + { + "query": "Mis on ettevõtte kontaktandmed?", + "expected_category": "SERVICE", + "expected_service_id": "common_service_companies_contactdetails", + "language": "et" + }, + { + "query": "Millal on selle aasta koolivaheajad?", + "expected_category": "SERVICE", + "expected_service_id": "common_service_school_holiday", + "language": "et" + }, + { + "query": "Mis olid viimaste NBA mängude tulemused?", + "expected_category": "SERVICE", + "expected_service_id": "common_service_nba_results", + "language": "et" + }, + { + "query": "Mis on euro ja dollari vahetuskurss?", + "expected_category": "SERVICE", + "expected_service_id": "common_service_exchange_rate", + "language": "et" + }, + { + "query": "Mis on viis viimast avalikku algatust?", + "expected_category": "SERVICE", + "expected_service_id": "common_teenus_citizien_initiative", + "language": "et" + }, + { + "query": "Mis on hetkel populaarsemad rahvaalgatused?", + "expected_category": "SERVICE", + "expected_service_id": "common_teenus_citizien_initiative_popular", + "language": "et" + }, + { + "query": "Kui palju kasvasid tarbija hinnad eelmisel aastal?", + "expected_category": "SERVICE", + "expected_service_id": "common_service_CPI", + "language": "et" + }, + { + "query": "Mis ilm on Tallinnas?", + "expected_category": "SERVICE", + "expected_service_id": "common_teenus_ilm", + "language": "et" + }, + { + "query": "Kas Narvas on ilus ilm?", + "expected_category": "SERVICE", + "expected_service_id": "common_teenus_ilm", + "language": "et" + }, + { + "query": "Mis on ööpäeva odavaim elektri hind?", + "expected_category": "SERVICE", + "expected_service_id": "common_teenus_nordpool2", + "language": "et" + }, + { + "query": "Kus leida diiselkütuse hinnaindeks?", + "expected_category": "SERVICE", + "expected_service_id": "common_service_CPI", + "language": "et" + }, + { + "query": "Miks ID-kaart ei tööta e-teenustes, kuigi DigiDoc4 loeb kaardi andmed sisse?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Kuidas Safari brauseris vahemälu tühjendada, kui ID-kaardiga sisselogimine ei tööta?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Kuidas kontrollida ja lubada ID-kaardi jaoks vajalikke laiendusi Firefoxi brauseris?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Mida teha, kui Firefoxis puudub või ei tööta Web eID või PKCS11 loader laiendus?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Kuidas anda dokumendile digiallkiri DigiDoc4 abil Windows 10 või Windows 11 arvutis?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Kuidas allkirjastada mitu faili korraga DigiDoc4-s mobiil-ID abil?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Mida teha, kui mu telefon koos Mobiil-IDga on kadunud või varastatud?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Kuidas peatada ja hiljem taastada Mobiil-ID sertifikaadid?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Kuidas allkirjastada dokument DigiDoc rakenduses mobiil-ID abil samm-sammult?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Mida pean kontrollima enne, kui annan DigiDocis dokumendile mobiil-IDga digiallkirja ja kuidas see pärast salvestada?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Kuidas siseneda e-teenustesse mobiil-ID abil samm-sammult?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Mida teha, kui mobiil-IDga sisselogimisel kontrollkoodid ei kattu või küsitakse ootamatult PIN-koodi?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Mida pean tegema, kui mu ID-kaart või mobiiltelefon (Mobiil-ID) on kadunud või varastatud?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Kuidas peatada ja hiljem taastada ID-kaardi ja Mobiil-ID sertifikaadid?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Kuidas siseneda e-teenustesse mobiil-ID abil samm-sammult?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Mida teha, kui mobiil-IDga sisselogimisel kontrollkood ei kattu või küsitakse ootamatult PIN-koodi?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Kust saab alla laadida ja paigaldada ametliku ID-kaardi tarkvara (DigiDoc4 ja Web eID) Windowsi, macOS-i ja mobiili jaoks?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Milliseid rakendusi on vaja ID-kaardi ja digiallkirja kasutamiseks Androidi ja iPhone’i telefonis?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Milleks on ID-kaardi sertifikaadid ja mis vahe on PIN1- ja PIN2-sertifikaadil?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Mida teha, kui mu ID-kaart või mobiil-ID on kadunud ja kuidas sertifikaate peatada?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Kuidas allkirjastada dokumente mobiil-ID abil RIA DigiDoc rakenduses samm-sammult?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Kuidas salvestada ja jagada DigiDocis allkirjastatud dokumendiümbrik ning lisada korraga mitu faili?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Kuidas anda digiallkiri dokumentidele DigiDoc4 rakenduses mobiil-ID abil Windows 10 või 11 arvutis?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + }, + { + "query": "Kuidas allkirjastada mitu faili korraga DigiDoc4-s ja kontrollida mobiil-ID kontrollkoodi?", + "expected_category": "RAG", + "expected_service_id": "", + "language": "et" + } +]