diff --git a/.gitignore b/.gitignore index 59dedfa1e92ba13bfdca4fd313daf478c4736b43..8b26d49e3c0a0a970755e1167be3baf931e6b91c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,11 @@ /.pnp .pnp.js +# gitlab-ci-local +.gitlab-ci-local +cache +builds + # testing /coverage diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 51be4471b7373cec6e311acb50f8a96d0174f120..25c676d5691e1c22752eb55ec000e8d6d458e750 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,8 +1,11 @@ variables: CONTAINER_IMAGE: "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME" NODE_IMAGE: "node:16.14.0" + CYPRESS_IMAGE: "cypress/browsers:node16.14.0-slim-chrome99-ff97" + CYPRESS_CACHE_FOLDER: "cache/Cypress" stages: + - dependencies - build - lint - test @@ -14,17 +17,26 @@ default: tags: - docker +dependencies: + stage: dependencies + image: $NODE_IMAGE + script: + - npm install + artifacts: + expire_in: 1 hour + paths: + - node_modules + - ${CYPRESS_CACHE_FOLDER} + build: stage: build image: $NODE_IMAGE - script: - - npm install + script: - CI=false npm run build artifacts: expire_in: 1 hour paths: - build - - node_modules/ lint: stage: lint @@ -35,7 +47,13 @@ lint: reports: codequality: gl-codequality.json -test: +test-components-chrome: + stage: test + image: $CYPRESS_IMAGE + script: + - npx cypress run-ct --browser chrome + +test-jest: stage: test image: $NODE_IMAGE script: diff --git a/cypress.json b/cypress.json index 99a692f3f822fce3b86e2bc6d319d649b7b7a907..0a5edd497bed80b6e473fffbd49d50b21a1ce17b 100644 --- a/cypress.json +++ b/cypress.json @@ -1,4 +1,7 @@ { - "ignoreTestFiles": "*.js", - "testFiles": "**/*.{feature,features}" + "testFiles": "**/*.{feature,features}", + "component": { + "componentFolder": "src", + "testFiles": "**/*.spec.{js,jsx,ts,tsx}" + } } \ No newline at end of file diff --git a/cypress/fixtures/ccce-api.json b/cypress/fixtures/ccce-api.json new file mode 100644 index 0000000000000000000000000000000000000000..cd1e192b494bcf68d026932d01f95e026d843764 --- /dev/null +++ b/cypress/fixtures/ccce-api.json @@ -0,0 +1,4608 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "CCCE API", + "description": "CCCE backend", + "version": "0.0.1" + }, + "servers": [ + { + "url": "http://localhost:8080", + "description": "CCCE Deployment Backend" + } + ], + "tags": [ + { + "name": "Broadcasts" + }, + { + "name": "IOCs" + }, + { + "name": "Naming" + }, + { + "name": "Authentication" + }, + { + "name": "AWX" + }, + { + "name": "Statistics" + }, + { + "name": "Hosts" + }, + { + "name": "Deployments" + }, + { + "name": "Git" + }, + { + "name": "Monitoring" + } + ], + "paths": { + "/api/v1/iocs/{iocId}": { + "get": { + "tags": [ + "IOCs" + ], + "summary": "Find IOC details by ID", + "operationId": "getIoc", + "parameters": [ + { + "name": "iocId", + "in": "path", + "description": "Unique ID of IOC", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "Found IOC", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IocDetails" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "Ioc not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + } + }, + "put": { + "tags": [ + "IOCs" + ], + "summary": "Updating an IOC by ID", + "operationId": "updateIoc", + "parameters": [ + { + "name": "iocId", + "in": "path", + "description": "Unique ID of IOC", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IOCUpdateRequest" + } + } + }, + "required": true + }, + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "403": { + "description": "Forbidden: User doesn't have the necessary permissions", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "422": { + "description": "Unprocessable request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Remote service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "Ioc not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "IOC updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Ioc" + } + } + } + }, + "409": { + "description": "IOC already created or concurrent deployment, undeployment, start or stop for IOC is ongoing", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "delete": { + "tags": [ + "IOCs" + ], + "summary": "Deletes a single IOC based on the ID supplied", + "operationId": "deleteIoc", + "parameters": [ + { + "name": "iocId", + "in": "path", + "description": "Id of IOC to delete", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "403": { + "description": "Forbidden: User doesn't have the necessary permissions", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Remote service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "204": { + "description": "IOC deleted", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "IOC not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "409": { + "description": "Concurrent deployment, undeployment, start or stop for IOC is ongoing", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/deployments/undeploy-in-db/{iocId}": { + "put": { + "tags": [ + "Deployments" + ], + "summary": "Undeploy in DB", + "description": "Marks an IOC in DB as undeployed - will not call AWX", + "operationId": "unDeployInDb", + "parameters": [ + { + "name": "iocId", + "in": "path", + "description": "The id of the IOC to undeploy", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "422": { + "description": "IOC is not deployed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "409": { + "description": "Ongoing deployment, or undeployment for IOC", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "403": { + "description": "Forbidden: user doesn't have the necessary permissions to undeploy", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Undeployment created", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Deployment" + } + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/awx/jobs": { + "put": { + "tags": [ + "AWX" + ], + "summary": "Update an AWX job's status", + "operationId": "updateJob", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AwxJobMeta" + } + } + }, + "required": true + }, + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "Job not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Job updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "apiKey": [] + } + ] + } + }, + "/api/v1/awx/commands": { + "put": { + "tags": [ + "AWX" + ], + "summary": "Update an AWX ad-hoc command's status", + "operationId": "updateCommand", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AwxJobMeta" + } + } + }, + "required": true + }, + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "Command not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Command updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "apiKey": [] + } + ] + } + }, + "/api/v1/iocs": { + "get": { + "tags": [ + "IOCs" + ], + "summary": "List IOCs", + "operationId": "listIocs", + "parameters": [ + { + "name": "owner", + "in": "query", + "description": "User name", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "query", + "in": "query", + "description": "Search text (Search for Naming name, Owner)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "orderBy", + "in": "query", + "description": "Order by", + "required": false, + "schema": { + "type": "string", + "enum": [ + "ID", + "OWNER", + "IOC_NAME", + "GIT_REFERENCE" + ] + } + }, + { + "name": "isAsc", + "in": "query", + "description": "Order Ascending", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "page", + "in": "query", + "description": "Page offset", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "limit", + "in": "query", + "description": "Page size", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "A paged array of IOCs", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedIocResponse" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + } + }, + "post": { + "tags": [ + "IOCs" + ], + "summary": "Create a new IOC", + "operationId": "createIoc", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateIoc" + } + } + }, + "required": true + }, + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "424": { + "description": "Metadata file not found, or not processable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "409": { + "description": "IOC already created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "422": { + "description": "Unprocessable request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Remote service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "400": { + "description": "Incomplete request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "201": { + "description": "IOC created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Ioc" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "403": { + "description": "Forbidden: User doesn't have the necessary permissions in Gitlab", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/deployments/{iocId}": { + "post": { + "tags": [ + "Deployments" + ], + "summary": "Update and deploy an existing IOC", + "operationId": "updateAndDeployIoc", + "parameters": [ + { + "name": "iocId", + "in": "path", + "description": "The id of the IOC to deploy", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAndDeployIoc" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Deployment created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Deployment" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "424": { + "description": "Metadata file not found, or not processable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Remote service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "400": { + "description": "Incomplete request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "422": { + "description": "CSEntry host not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "IOC not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "409": { + "description": "Concurrent deployment, undeployment, start or stop for IOC is ongoing", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "403": { + "description": "Forbidden: User doesn't have the necessary permissions in Gitlab", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/deployments/{iocId}/stop": { + "post": { + "tags": [ + "Deployments" + ], + "summary": "Stop an existing IOC", + "operationId": "stopIoc", + "parameters": [ + { + "name": "iocId", + "in": "path", + "description": "The id of the IOC to stop", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "201": { + "description": "IOC stop initiated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdHocCommand" + } + } + } + }, + "503": { + "description": "Remote service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "IOC not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "409": { + "description": "Concurrent deployment, undeployment, start or stop for IOC is ongoing", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "422": { + "description": "IOC has no active deployment", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "403": { + "description": "Forbidden: User doesn't have the necessary permissions in Gitlab", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/deployments/{iocId}/start": { + "post": { + "tags": [ + "Deployments" + ], + "summary": "Start an existing IOC", + "operationId": "startIoc", + "parameters": [ + { + "name": "iocId", + "in": "path", + "description": "The id of the IOC to start", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "201": { + "description": "IOC start initiated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdHocCommand" + } + } + } + }, + "503": { + "description": "Remote service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "IOC not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "409": { + "description": "Concurrent deployment, undeployment, start or stop for IOC is ongoing", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "422": { + "description": "IOC has no active deployment", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "403": { + "description": "Forbidden: User doesn't have the necessary permissions in Gitlab", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/deployments/undeploy/{iocId}": { + "post": { + "tags": [ + "Deployments" + ], + "summary": "Start undeployment", + "operationId": "createUndeployment", + "parameters": [ + { + "name": "iocId", + "in": "path", + "description": "The id of the IOC to undeploy", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Undeployment" + } + } + }, + "required": true + }, + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "403": { + "description": "Forbidden: user doesn't have the necessary permissions in Gitlab", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "422": { + "description": "IOC is not deployed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Remote service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "409": { + "description": "Concurrent deployment, undeployment, start or stop for IOC is ongoing", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "201": { + "description": "Undeployment created", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Deployment" + } + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/authentication/renew": { + "post": { + "tags": [ + "Authentication" + ], + "summary": "Renewing JWT token", + "description": "Renewing valid, non-expired JWT token", + "operationId": "tokenRenew", + "responses": { + "503": { + "description": "Login server unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LoginResponse" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/authentication/login": { + "post": { + "tags": [ + "Authentication" + ], + "summary": "Login user and acquire JWT token", + "description": "Login user with username, and password in order to acquire JWT token", + "operationId": "login", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Login" + } + } + }, + "required": true + }, + "responses": { + "503": { + "description": "Login server unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "403": { + "description": "Bad username, and/or password", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LoginResponse" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + } + } + }, + "/healthcheck": { + "get": { + "tags": [ + "health-check-controller" + ], + "operationId": "getHealthCheck", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/api/v1/statistics/my-iocs": { + "get": { + "tags": [ + "Statistics" + ], + "summary": "Own IOCs' statistics", + "operationId": "personalStatistics", + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Personal statistics", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PersonalStatisticsResponse" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/statistics/general": { + "get": { + "tags": [ + "Statistics" + ], + "summary": "Set of statistics", + "operationId": "generalStatistics", + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Statistics about IOCs, deployments, and hosts", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralStatisticsResponse" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + } + } + }, + "/api/v1/statistics/general/iocStatistics": { + "get": { + "tags": [ + "Statistics" + ], + "summary": "IOC statistics", + "operationId": "iocStatistics", + "responses": { + "200": { + "description": "Statistics about IOCs deployed to hosts", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DeploymentOnHost" + } + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + } + } + }, + "/api/v1/statistics/general/deploymentStatistics": { + "get": { + "tags": [ + "Statistics" + ], + "summary": "Deployment statistics", + "operationId": "deploymentStatistics", + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Statistics about IOCs, deployments, and hosts", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DeploymentStatisticsResponse" + } + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + } + } + }, + "/api/v1/statistics/general/deployedIocHistory": { + "get": { + "tags": [ + "Statistics" + ], + "summary": "Deployed IOC history from DB for statistics", + "operationId": "iocDeploymentHistory", + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "History about deployed IOCs from DB for statistics", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ActiveIOCSForHistoryResponse" + } + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + } + } + }, + "/api/v1/statistics/general/activeIocStatistics": { + "get": { + "tags": [ + "Statistics" + ], + "summary": "Currently active IOC statistics", + "operationId": "currentlyActiveIocs", + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Statistics about currently active IOCs", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ActiveIocStatisticsResponse" + } + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + } + } + }, + "/api/v1/statistics/general/activeIocHistory": { + "get": { + "tags": [ + "Statistics" + ], + "summary": "Active IOC history from Prometheus for statistics", + "operationId": "activeIocHistory", + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "History about active IOCs for statistics", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ActiveIOCSForHistoryResponse" + } + } + } + } + } + } + } + }, + "/api/v1/naming/IOCNamesByName": { + "get": { + "tags": [ + "Naming" + ], + "summary": "Fetches IOC names by name from CCDB", + "operationId": "fetchIOCByName", + "parameters": [ + { + "name": "iocName", + "in": "query", + "description": "IOC name", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Naming names, and IDs from CCDB", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NameResponse" + } + } + } + } + }, + "503": { + "description": "Remote service unreachable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/monitoring/syslog/host/{hostName}": { + "get": { + "tags": [ + "Monitoring" + ], + "summary": "Fetches syslog lines for a specific host", + "operationId": "fetchSyslogLines", + "parameters": [ + { + "name": "hostName", + "in": "path", + "description": "Host name (without network part)", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "timeRange", + "in": "query", + "description": "Time range (in minutes)", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Logging server unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Log lines", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LokiResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/monitoring/status/ioc": { + "get": { + "tags": [ + "Monitoring" + ], + "summary": "Fetches the Prometheus status of the IOC", + "operationId": "fetchIocStatus", + "parameters": [ + { + "name": "iocId", + "in": "query", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Log lines", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IocStatusResponse" + } + } + } + } + } + } + }, + "/api/v1/monitoring/procserv/host/{hostName}": { + "get": { + "tags": [ + "Monitoring" + ], + "summary": "Fetches procServ Log lines for a specific host", + "operationId": "fetchProcServLogLines", + "parameters": [ + { + "name": "hostName", + "in": "path", + "description": "Host name (without network part)", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "iocName", + "in": "query", + "description": "Name of the IOC to get procServ logs", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "timeRange", + "in": "query", + "description": "Time range (in minutes)", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Logging server unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Log lines", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LokiResponse" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/iocs/my-iocs-with-alarms": { + "get": { + "tags": [ + "IOCs" + ], + "summary": "List own IOCs with alarms", + "operationId": "listOwnIocsWithAlarms", + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Array of own IOC with alarms", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IocWithAlarm" + } + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + } + } + }, + "/api/v1/hosts": { + "get": { + "tags": [ + "Hosts" + ], + "summary": "List CSEntry hosts", + "operationId": "listHosts", + "parameters": [ + { + "name": "query", + "in": "query", + "description": "Search text, this query string is passed directly to Elasticsearch. E.g.: To search all hosts where the string \"archiver\" appears in any field, use \"archiver\".To restrict the search to only the name field, use \"name:archiver\".Note that if you pass \"name:arch\", this will not match archiver as elasticsearch uses keywords for string matching. In this case you’d need to use a wildcard: \"name:arch*\".", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "description": "Page offset", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "limit", + "in": "query", + "description": "Page size", + "required": false, + "schema": { + "maximum": 100, + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A paged array of CSEntry hosts", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedCSEntryHostResponse" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Remote server exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + } + } + }, + "/api/v1/hosts/{hostCSEntryId}/iocs": { + "get": { + "tags": [ + "Hosts" + ], + "summary": "Find associated IOCs for the host", + "operationId": "findAssociatedIocsByHostId", + "parameters": [ + { + "name": "hostCSEntryId", + "in": "path", + "description": "Unique CSEntry ID of the host", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "page", + "in": "query", + "description": "Page offset", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "limit", + "in": "query", + "description": "Page size", + "required": false, + "schema": { + "maximum": 100, + "type": "string" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Found Host", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedAssociatedIocs" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + } + } + }, + "/api/v1/hosts/with-status/{hostCSEntryId}": { + "get": { + "tags": [ + "Hosts" + ], + "summary": "Find host by CSEntry ID and get status", + "operationId": "findHostById", + "parameters": [ + { + "name": "hostCSEntryId", + "in": "path", + "description": "Unique CSEntry ID of the host", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Found Host", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CSEntryHostWithStatus" + } + } + } + }, + "503": { + "description": "Remote server exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + } + } + }, + "/api/v1/hosts/csentry": { + "get": { + "tags": [ + "Hosts" + ], + "summary": "Find host by CSEntry ID", + "operationId": "findCSentryHostById", + "parameters": [ + { + "name": "hostCSEntryId", + "in": "query", + "description": "Unique CSEntry ID of the host", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Found Host", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TargetHost" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + } + } + }, + "/api/v1/gitHelper/userInfo": { + "get": { + "tags": [ + "Git" + ], + "summary": "Current-user information", + "operationId": "userInfo", + "responses": { + "200": { + "description": "Information about the current user", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserInfoResponse" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Git exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/gitHelper/userInfo/{userName}": { + "get": { + "tags": [ + "Git" + ], + "summary": "User information", + "operationId": "infoFromUserName", + "parameters": [ + { + "name": "userName", + "in": "path", + "description": "The username to retrieve info from", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Information about the current user", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UserInfoResponse" + } + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Git exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/gitHelper/tagsAndCommitIds": { + "get": { + "tags": [ + "Git" + ], + "summary": "List Tags and CommitIds", + "operationId": "listTagsAndCommitIds", + "parameters": [ + { + "name": "projectId", + "in": "query", + "description": "Git repo project ID", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "reference", + "in": "query", + "description": "Git reference", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "List of Tags and CommitIds for a specific GitLab repo", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GitReference" + } + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Git exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + } + } + }, + "/api/v1/gitHelper/projects": { + "get": { + "tags": [ + "Git" + ], + "summary": "List IOC projects", + "operationId": "listProjects", + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "List of Gitlab projects of allowed groups", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GitProject" + } + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Git exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/deployments": { + "get": { + "tags": [ + "Deployments" + ], + "summary": "List deployments", + "operationId": "listDeployments", + "parameters": [ + { + "name": "iocId", + "in": "query", + "description": "IOC ID", + "required": false, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "hostCSEntryId", + "in": "query", + "description": "Host CSEntry ID", + "required": false, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "status", + "in": "query", + "description": "Status", + "required": false, + "schema": { + "type": "string", + "enum": [ + "QUEUED", + "PENDING", + "RUNNING", + "SUCCESSFUL", + "FAILED", + "FINISHED" + ] + } + }, + { + "name": "user", + "in": "query", + "description": "User name", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "query", + "in": "query", + "description": "Search text (Search for IOC name, IOC version, Host name, Description, Created by)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "orderBy", + "in": "query", + "description": "Order by", + "required": false, + "schema": { + "type": "string", + "enum": [ + "ID", + "CREATED_BY", + "CREATED_AT", + "START_TIME", + "FINISHED_AT", + "IOC_NAME", + "GIT_REFERENCE" + ] + } + }, + { + "name": "isAsc", + "in": "query", + "description": "Order Ascending", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "page", + "in": "query", + "description": "Page offset", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "limit", + "in": "query", + "description": "Page size", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "A paged array of deployments", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedDeploymentResponse" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + } + } + }, + "/api/v1/deployments/{deploymentId}": { + "get": { + "tags": [ + "Deployments" + ], + "summary": "Get deployment details", + "operationId": "fetchDeployment", + "parameters": [ + { + "name": "deploymentId", + "in": "path", + "description": "Unique ID of deployment", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "A deployment with details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeploymentInfoDetails" + } + } + } + }, + "404": { + "description": "Deployment not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + } + } + }, + "/api/v1/deployments/jobs/{awxJobId}": { + "get": { + "tags": [ + "Deployments" + ], + "summary": "Get deployment job details", + "operationId": "fetchDeploymentJobDetails", + "parameters": [ + { + "name": "awxJobId", + "in": "path", + "description": "Unique ID of AWX job", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Remote service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "Deployment not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "AWX job details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AwxJobDetails" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/deployments/commands": { + "get": { + "tags": [ + "Deployments" + ], + "summary": "Get ad-hoc command list for a specific deployment", + "operationId": "listCommandsByDeployment", + "parameters": [ + { + "name": "iocId", + "in": "query", + "description": "Unique ID of IOC", + "required": false, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "deploymentId", + "in": "query", + "description": "Unique ID of deployment", + "required": false, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "page", + "in": "query", + "description": "Page offset", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "limit", + "in": "query", + "description": "Page size", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Remote service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "Deployment not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "A command with details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedCommandResponse" + } + } + } + } + } + } + }, + "/api/v1/deployments/commands/{commandId}": { + "get": { + "tags": [ + "Deployments" + ], + "summary": "Get ad-hoc command", + "operationId": "fetchCommand", + "parameters": [ + { + "name": "commandId", + "in": "path", + "description": "Unique ID of command", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "Ad-hoc command not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "A command descriptor", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdHocCommand" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + } + } + }, + "/api/v1/deployments/commands/ongoing/{iocId}": { + "get": { + "tags": [ + "Deployments" + ], + "summary": "Get list of ongoing ad-hoc commands for a specific IOC", + "operationId": "ongoingCommandByIoc", + "parameters": [ + { + "name": "iocId", + "in": "path", + "description": "Unique ID of the IOC", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Remote service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "IOC not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Ongoing command (if exists)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdHocCommand" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/deployments/commands/jobs/{awxCommandId}": { + "get": { + "tags": [ + "Deployments" + ], + "summary": "Get ad-hoc command details", + "operationId": "fetchCommandDetails", + "parameters": [ + { + "name": "awxCommandId", + "in": "path", + "description": "Unique ID of AWX command", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "AWX command not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Remote service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "A command with details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AwxCommandDetails" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/broadcasts/announcements": { + "get": { + "tags": [ + "Broadcasts" + ], + "summary": "Get announcements", + "operationId": "fetchAnnouncements", + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Announcements text", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Announcements" + } + } + } + } + } + } + }, + "/api/v1/authentication/users": { + "get": { + "tags": [ + "Authentication" + ], + "summary": "Get Deployment Tool users", + "description": "Get Deployment Tool users from authorization service", + "operationId": "getToolUsers", + "parameters": [ + { + "name": "query", + "in": "query", + "description": "User name query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "404": { + "description": "Role not found by authorization service", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Login server unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/authentication/roles": { + "get": { + "tags": [ + "Authentication" + ], + "summary": "Get user roles", + "description": "Get user roles from authorization service", + "operationId": "getUserRoles", + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Login server unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/authentication/logout": { + "delete": { + "tags": [ + "Authentication" + ], + "summary": "Logout", + "description": "Logging out the user", + "operationId": "logout", + "responses": { + "403": { + "description": "Logout error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "503": { + "description": "Login server unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + }, + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "500": { + "description": "Service exception", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneralException" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + } + }, + "components": { + "schemas": { + "IOCUpdateRequest": { + "type": "object", + "properties": { + "owner": { + "type": "string" + }, + "externalNameId": { + "type": "integer", + "format": "int64" + }, + "gitProjectId": { + "type": "integer", + "format": "int64" + } + } + }, + "GeneralException": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "Alert": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "ERROR", + "WARNING" + ] + }, + "message": { + "type": "string" + } + } + }, + "Deployment": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "createdBy": { + "type": "string" + }, + "startDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "enum": [ + "QUEUED", + "PENDING", + "RUNNING", + "SUCCESSFUL", + "FAILED", + "FINISHED" + ] + }, + "undeployment": { + "type": "boolean" + }, + "namingName": { + "type": "string" + }, + "gitProjectId": { + "type": "integer", + "format": "int64" + }, + "sourceVersion": { + "type": "string" + }, + "endDate": { + "type": "string", + "format": "date-time" + }, + "comment": { + "type": "string" + }, + "iocName": { + "type": "string" + }, + "host": { + "$ref": "#/components/schemas/Host" + }, + "awxJobId": { + "type": "integer", + "format": "int64" + }, + "sourceUrl": { + "type": "string" + }, + "sourceVersionShort": { + "type": "string" + } + } + }, + "Host": { + "type": "object", + "properties": { + "csEntryId": { + "type": "integer", + "format": "int64" + }, + "fqdn": { + "type": "string" + }, + "hostName": { + "type": "string" + }, + "network": { + "type": "string" + } + } + }, + "Ioc": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "description": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "createdBy": { + "type": "string" + }, + "namingName": { + "type": "string" + }, + "externalNameId": { + "type": "integer", + "format": "int64" + }, + "gitProjectId": { + "type": "integer", + "format": "int64" + }, + "sourceUrl": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "sourceVersionShort": { + "type": "string" + }, + "activeDeployment": { + "$ref": "#/components/schemas/Deployment" + }, + "alerts": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Alert" + } + }, + "active": { + "type": "boolean" + } + } + }, + "AwxJobMeta": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "started": { + "type": "string", + "format": "date-time" + }, + "finished": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "waiting", + "running", + "failed", + "canceled", + "successful" + ] + } + }, + "description": "Command to update" + }, + "CreateIoc": { + "type": "object", + "properties": { + "externalNameId": { + "type": "integer", + "format": "int64" + }, + "gitProjectId": { + "type": "integer", + "format": "int64" + } + }, + "description": "IOC to create" + }, + "UpdateAndDeployIoc": { + "type": "object", + "properties": { + "comment": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "hostCSEntryId": { + "type": "integer", + "format": "int64" + } + }, + "description": "IOC to update" + }, + "AdHocCommand": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "type": { + "type": "string", + "enum": [ + "START", + "STOP" + ] + }, + "createdBy": { + "type": "string" + }, + "startDate": { + "type": "string", + "format": "date-time" + }, + "endDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "enum": [ + "PENDING", + "RUNNING", + "SUCCESSFUL", + "FAILED" + ] + }, + "iocName": { + "type": "string" + }, + "awxCommandId": { + "type": "integer", + "format": "int64" + } + } + }, + "Undeployment": { + "type": "object", + "properties": { + "comment": { + "type": "string" + } + }, + "description": "Undeployment parameters" + }, + "LoginResponse": { + "type": "object", + "properties": { + "token": { + "type": "string" + } + } + }, + "Login": { + "type": "object", + "properties": { + "userName": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "PersonalStatisticsResponse": { + "type": "object", + "properties": { + "numberOfActiveIocs": { + "type": "integer", + "format": "int64" + } + } + }, + "GeneralStatisticsResponse": { + "type": "object", + "properties": { + "numberOfActiveHosts": { + "type": "integer", + "format": "int64" + }, + "numberOfCreatedIocs": { + "type": "integer", + "format": "int64" + }, + "numberOfActiveIocs": { + "type": "integer", + "format": "int64" + }, + "numberOfDeployments": { + "type": "integer", + "format": "int64" + }, + "numberOfActiveDeployments": { + "type": "integer", + "format": "int64" + } + } + }, + "DeploymentCount": { + "type": "object", + "properties": { + "iocCount": { + "type": "integer", + "format": "int64" + }, + "countingDate": { + "type": "string", + "format": "date-time" + } + } + }, + "DeploymentOnHost": { + "type": "object", + "properties": { + "network": { + "type": "string" + }, + "deployments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DeploymentCount" + } + } + } + }, + "DeploymentStatisticsResponse": { + "type": "object", + "properties": { + "deploymentDate": { + "type": "string", + "format": "date-time" + }, + "numberOfDeployments": { + "type": "integer", + "format": "int64" + }, + "successfulDeployments": { + "type": "integer", + "format": "int64" + } + } + }, + "ActiveIOCSForHistoryResponse": { + "type": "object", + "properties": { + "iocCount": { + "type": "integer", + "format": "int64" + }, + "historyDate": { + "type": "string", + "format": "date-time" + } + } + }, + "ActiveIocStatisticsResponse": { + "type": "object", + "properties": { + "network": { + "type": "string" + }, + "iocCount": { + "type": "integer", + "format": "int64" + } + } + }, + "NameResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "LokiMessage": { + "type": "object", + "properties": { + "logDate": { + "type": "string", + "format": "date-time" + }, + "logMessage": { + "type": "string" + } + } + }, + "LokiResponse": { + "type": "object", + "properties": { + "lines": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LokiMessage" + } + } + } + }, + "IocStatusResponse": { + "type": "object", + "properties": { + "iocId": { + "type": "integer", + "format": "int64" + }, + "alertSeverity": { + "type": "string", + "enum": [ + "ERROR", + "WARNING" + ] + }, + "alerts": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Alert" + } + }, + "active": { + "type": "boolean" + } + } + }, + "ActiveDeployment": { + "type": "object", + "properties": { + "host": { + "$ref": "#/components/schemas/DeploymentHostInfo" + } + } + }, + "DeploymentHostInfo": { + "type": "object", + "properties": { + "externalHostId": { + "type": "integer", + "format": "int64" + } + } + }, + "IocInfo": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "namingName": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "gitProjectId": { + "type": "integer", + "format": "int64" + }, + "sourceVersion": { + "type": "string" + }, + "activeDeployment": { + "$ref": "#/components/schemas/ActiveDeployment" + } + } + }, + "PagedIocResponse": { + "type": "object", + "properties": { + "totalCount": { + "type": "integer", + "format": "int64" + }, + "listSize": { + "type": "integer", + "format": "int32" + }, + "pageNumber": { + "type": "integer", + "format": "int32" + }, + "limit": { + "type": "integer", + "format": "int32" + }, + "iocList": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IocInfo" + } + } + } + }, + "IocDetails": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "description": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "createdBy": { + "type": "string" + }, + "namingName": { + "type": "string" + }, + "externalNameId": { + "type": "integer", + "format": "int64" + }, + "gitProjectId": { + "type": "integer", + "format": "int64" + }, + "sourceUrl": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "sourceVersionShort": { + "type": "string" + }, + "activeDeployment": { + "$ref": "#/components/schemas/Deployment" + }, + "alerts": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Alert" + } + }, + "operationInProgress": { + "type": "boolean" + }, + "active": { + "type": "boolean" + } + } + }, + "IocWithAlarm": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "description": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "createdBy": { + "type": "string" + }, + "namingName": { + "type": "string" + }, + "externalNameId": { + "type": "integer", + "format": "int64" + }, + "gitProjectId": { + "type": "integer", + "format": "int64" + }, + "sourceUrl": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "sourceVersionShort": { + "type": "string" + }, + "activeDeployment": { + "$ref": "#/components/schemas/Deployment" + }, + "alerts": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Alert" + } + }, + "alert": { + "type": "string", + "enum": [ + "ERROR", + "WARNING" + ] + }, + "active": { + "type": "boolean" + } + } + }, + "HostInfo": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "fqdn": { + "type": "string" + }, + "name": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "description": { + "type": "string" + }, + "network": { + "type": "string" + }, + "deviceType": { + "type": "string" + } + } + }, + "HostInfoWithStatus": { + "type": "object", + "properties": { + "csEntryHost": { + "$ref": "#/components/schemas/HostInfo" + }, + "status": { + "type": "string", + "enum": [ + "AVAILABLE", + "UNREACHABLE" + ] + }, + "alert": { + "type": "string", + "enum": [ + "ERROR", + "WARNING" + ] + } + } + }, + "PagedCSEntryHostResponse": { + "type": "object", + "properties": { + "totalCount": { + "type": "integer", + "format": "int64" + }, + "listSize": { + "type": "integer", + "format": "int32" + }, + "pageNumber": { + "type": "integer", + "format": "int32" + }, + "limit": { + "type": "integer", + "format": "int32" + }, + "csEntryHosts": { + "type": "array", + "items": { + "$ref": "#/components/schemas/HostInfoWithStatus" + } + } + } + }, + "PagedAssociatedIocs": { + "type": "object", + "properties": { + "totalCount": { + "type": "integer", + "format": "int64" + }, + "listSize": { + "type": "integer", + "format": "int32" + }, + "pageNumber": { + "type": "integer", + "format": "int32" + }, + "limit": { + "type": "integer", + "format": "int32" + }, + "deployedIocs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IocInfo" + } + } + } + }, + "CSEntryHost": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "fqdn": { + "type": "string" + }, + "name": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "description": { + "type": "string" + }, + "user": { + "type": "string" + }, + "interfaces": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CSEntryInterface" + } + }, + "is_ioc": { + "type": "boolean" + }, + "device_type": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "ansible_vars": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "ansible_groups": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "CSEntryHostWithStatus": { + "type": "object", + "properties": { + "csEntryHost": { + "$ref": "#/components/schemas/CSEntryHost" + }, + "assigned": { + "type": "boolean" + }, + "status": { + "type": "string", + "enum": [ + "AVAILABLE", + "UNREACHABLE" + ] + }, + "alerts": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Alert" + } + } + } + }, + "CSEntryInterface": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "network": { + "type": "string" + }, + "domain": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "mac": { + "type": "string" + }, + "host": { + "type": "string" + }, + "cnames": { + "type": "array", + "items": { + "type": "string" + } + }, + "is_main": { + "type": "boolean" + } + } + }, + "TargetHost": { + "type": "object", + "properties": { + "externalId": { + "type": "integer", + "format": "int64" + }, + "fqdn": { + "type": "string" + }, + "hostName": { + "type": "string" + }, + "network": { + "type": "string" + } + } + }, + "UserInfoResponse": { + "type": "object", + "properties": { + "fullName": { + "type": "string" + }, + "loginName": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "email": { + "type": "string" + } + } + }, + "GitReference": { + "type": "object", + "properties": { + "reference": { + "type": "string" + }, + "shortReference": { + "type": "string" + } + } + }, + "GitProject": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "url": { + "type": "string" + } + } + }, + "DeploymentInfo": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "createdBy": { + "type": "string" + }, + "startDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "enum": [ + "QUEUED", + "PENDING", + "RUNNING", + "SUCCESSFUL", + "FAILED", + "FINISHED" + ] + }, + "undeployment": { + "type": "boolean" + }, + "namingName": { + "type": "string" + }, + "gitProjectId": { + "type": "integer", + "format": "int64" + }, + "sourceVersion": { + "type": "string" + }, + "host": { + "$ref": "#/components/schemas/DeploymentHostInfo" + } + } + }, + "PagedDeploymentResponse": { + "type": "object", + "properties": { + "totalCount": { + "type": "integer", + "format": "int64" + }, + "listSize": { + "type": "integer", + "format": "int32" + }, + "pageNumber": { + "type": "integer", + "format": "int32" + }, + "limit": { + "type": "integer", + "format": "int32" + }, + "deployments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DeploymentInfo" + } + } + } + }, + "DeploymentInfoDetails": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "createdBy": { + "type": "string" + }, + "startDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "enum": [ + "QUEUED", + "PENDING", + "RUNNING", + "SUCCESSFUL", + "FAILED", + "FINISHED" + ] + }, + "undeployment": { + "type": "boolean" + }, + "namingName": { + "type": "string" + }, + "gitProjectId": { + "type": "integer", + "format": "int64" + }, + "sourceVersion": { + "type": "string" + }, + "endDate": { + "type": "string", + "format": "date-time" + }, + "comment": { + "type": "string" + }, + "host": { + "$ref": "#/components/schemas/HostWithFqdn" + }, + "awxJobId": { + "type": "integer", + "format": "int64" + }, + "iocId": { + "type": "integer", + "format": "int64" + }, + "awxJobUrl": { + "type": "string" + }, + "sourceUrl": { + "type": "string" + }, + "sourceVersionShort": { + "type": "string" + } + } + }, + "HostWithFqdn": { + "type": "object", + "properties": { + "csEntryId": { + "type": "integer", + "format": "int64" + }, + "fqdn": { + "type": "string" + } + } + }, + "AwxJobDetails": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "waiting", + "running", + "failed", + "canceled", + "successful" + ] + }, + "started": { + "type": "string", + "format": "date-time" + }, + "finished": { + "type": "string", + "format": "date-time" + }, + "elapsed": { + "type": "number", + "format": "double" + }, + "stdout": { + "type": "string" + } + } + }, + "PagedCommandResponse": { + "type": "object", + "properties": { + "totalCount": { + "type": "integer", + "format": "int64" + }, + "listSize": { + "type": "integer", + "format": "int32" + }, + "pageNumber": { + "type": "integer", + "format": "int32" + }, + "limit": { + "type": "integer", + "format": "int32" + }, + "commands": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AdHocCommand" + } + } + } + }, + "AwxCommandDetails": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "waiting", + "running", + "failed", + "canceled", + "successful" + ] + }, + "started": { + "type": "string", + "format": "date-time" + }, + "finished": { + "type": "string", + "format": "date-time" + } + } + }, + "Announcements": { + "type": "object", + "properties": { + "announcementsText": { + "type": "string" + } + } + } + }, + "securitySchemes": { + "apiKey": { + "type": "apiKey", + "name": "CCCE-TOKEN", + "in": "header" + }, + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT" + } + } + } +} \ No newline at end of file diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js index d9b510d377b7d982320a8e0a426d81b7003ab3b7..3aa1a8534f5a0c65906ac5b43471041262af5e59 100644 --- a/cypress/plugins/index.js +++ b/cypress/plugins/index.js @@ -11,7 +11,19 @@ // This function is called when a project is opened or re-opened (e.g. due to // the project's config changing) +// See https://docs.cypress.io/guides/component-testing/framework-configuration#React-Create-React-App +const injectDevServer = require('@cypress/react/plugins/react-scripts') const cucumber = require('cypress-cucumber-preprocessor').default module.exports = (on, config) => { on('file:preprocessor', cucumber()) + + // Configure the dev-server to use the same + // Webpack configuration used by Create React App + injectDevServer(on, config) + + if (config.testingType === 'component') { + require('@cypress/react/plugins/react-scripts')(on, config) + } + + return config } \ No newline at end of file diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 119ab03f7cda1f826e896639924a8612d075d3fc..970728c4ada4ea4641f29458bd0d303f2fa2ec69 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -23,3 +23,4 @@ // // -- This will overwrite an existing command -- // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) +Cypress.Commands.add("login", () => {cy.setCookie("ccce-auth", "DEADBEEF")}); \ No newline at end of file diff --git a/cypress/support/index.js b/cypress/support/index.js index d68db96df2697e0835f5c490db0c2cc81673f407..b7f929c0ec8c042ad84be2df868647c2869d30b6 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -15,6 +15,22 @@ // Import commands.js using ES2015 syntax: import './commands' +import { mockAPI } from './mockAPI' // Alternatively you can use CommonJS syntax: // require('./commands') + +// load the configuration variables into the window +// these are copied from public/config.js +// but copying is bad +// maybe we need to rethink how we do config +window.SERVER_ADDRESS='' +window.API_BASE_ENDPOINT='/ccce-api' +window.TOKEN_RENEW_INTERVAL=180000 +window.CCDB_ADDRESS='https://ccdb.esss.lu.se' +window.ENVIRONMENT_TITLE = '' + +// intercept the request and return the API specification fixture +beforeEach(() => { + mockAPI(); +}); diff --git a/cypress/support/mockAPI.js b/cypress/support/mockAPI.js new file mode 100644 index 0000000000000000000000000000000000000000..26831e044658f0d58933805bacf96ab46a8eb4f5 --- /dev/null +++ b/cypress/support/mockAPI.js @@ -0,0 +1,57 @@ +function login(req) { + const token = "DEADBEEF"; + req.reply({ + headers: { + "Set-Cookie": `ccce-auth=${token};` + }, + body: { + token, + }, + }) +} + +function isLoggedIn(req) { + return (req.headers?.cookie ?? "").includes("ccce-auth="); +} + +function auth(routeHandler) { + return function(req) { + if (isLoggedIn(req)) { + routeHandler(req); + } + else { + req.reply({ + statusCode: 401 + }) + } + } +} + +function roles(req) { + req.reply(["Cowboy", "Gunslinger"]); +} + +function userInfo(req) { + req.reply({ + "fullName": "John Wayne", + "loginName": "johnwayne", + "avatar": "https://gitlab.esss.lu.se/uploads/-/system/user/avatar/92/avatar.png", + "email": null + }); +} + +function renew(req) { + req.reply({token: "FADEDEAD"}); +} + +// Mock the CCCE API +export function mockAPI() { + cy.intercept("**/ccce-api", { fixture: "ccce-api.json" }).as("ccceAPI"); + + cy.intercept("**/authentication/login", login).as("login"); + cy.intercept("**/authentication/roles", auth(roles)).as("userRoles"); + cy.intercept("**/authentication/renew", auth(renew)).as("renew") + + cy.intercept("**/gitHelper/userInfo", auth(userInfo)).as("userInfo"); + +} \ No newline at end of file diff --git a/package.json b/package.json index a75837f6398615e9d16539ca645b8c03639f186f..6d57dfd890e1257e395f6b6efb59b708014166fe 100644 --- a/package.json +++ b/package.json @@ -41,10 +41,12 @@ "eslint-plugin-react": "7.28.0" }, "scripts": { - "preinstall": "npx npm-force-resolutions", + "preinstall": "npx npm-force-resolutions", + "postinstall": "npx patch-package", "start": "react-scripts start", "build": "react-scripts build", - "test": "set DEBUG_PRINT_LIMIT=20000&& react-scripts test", + "test": "DEBUG_PRINT_LIMIT=20000 react-scripts test", + "testComponents": "npx cypress run-ct", "coverage": "DEBUG_PRINT_LIMIT=20000 react-scripts test --env=jsdom --watchAll=false --coverage --passWithNoTests", "eject": "react-scripts eject", "lint": "eslint src --ext .js -o eslint/report.html -f html", @@ -55,7 +57,8 @@ "react-app", "react-app/jest", "eslint:recommended", - "plugin:react/recommended" + "plugin:react/recommended", + "plugin:cypress/recommended" ], "rules": { "react/prop-types": "off", @@ -76,21 +79,28 @@ ] }, "devDependencies": { + "@cypress/react": "^5.12.4", + "@cypress/webpack-dev-server": "^1.8.3", + "cypress": "^9.5.2", "cypress-cucumber-preprocessor": "^4.1.0", "eslint": "^7.27.0", "eslint-formatter-gitlab": "^2.2.0", + "eslint-plugin-cypress": "^2.12.1", + "eslint-plugin-react": "7.28.0", + "html-webpack-plugin": "^4.5.2", "jest-cucumber": "^3.0.1", + "patch-package": "^6.4.7", + "react-error-overlay": "6.0.9", "react-scripts": "^4.0.3", "serve": "^11.3.2", - "react-error-overlay": "6.0.9", - "eslint-plugin-react": "7.28.0" + "webpack-dev-server": "^4.7.4" }, "cypress-cucumber-preprocessor": { "nonGlobalStepDefinitions": true }, "jest": { "collectCoverageFrom": [ - "src/**/*.js", + "**.test.js", "!src/index.js" ], "coverageReporters": [ @@ -100,6 +110,9 @@ ], "moduleNameMapper": { "react-markdown": "<rootDir>/node_modules/react-markdown/react-markdown.min.js" - } + }, + "testMatch": [ + "**.test.js" + ] } } diff --git a/patches/react-scripts+4.0.3.patch b/patches/react-scripts+4.0.3.patch new file mode 100644 index 0000000000000000000000000000000000000000..f78081946780803fa9d019daf1c4fda5e4cf65c8 --- /dev/null +++ b/patches/react-scripts+4.0.3.patch @@ -0,0 +1,21 @@ +diff --git a/node_modules/react-scripts/config/webpack.config.js b/node_modules/react-scripts/config/webpack.config.js +index 26c2a65..b26b392 100644 +--- a/node_modules/react-scripts/config/webpack.config.js ++++ b/node_modules/react-scripts/config/webpack.config.js +@@ -794,5 +794,16 @@ module.exports = function (webpackEnv) { + // Turn off performance processing because we utilize + // our own hints via the FileSizeReporter + performance: false, ++ // PATCH: ++ // Disable the error overlay for warnings ++ // The error-overlay breaks cypress tests ++ devServer: { ++ client: { ++ overlay: { ++ errors: true, ++ warnings: false, ++ } ++ } ++ }, + }; + }; diff --git a/src/CypressComponentTestExample.spec.js b/src/CypressComponentTestExample.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..e9ef42294ab99e67414a28a753575da07e68ce3e --- /dev/null +++ b/src/CypressComponentTestExample.spec.js @@ -0,0 +1,8 @@ +import React from 'react' +import { mount } from '@cypress/react' +import { Button } from '@material-ui/core' + +it('Button', () => { + mount(<Button>Test button</Button>) + cy.get('button').contains('Test button') +}) \ No newline at end of file diff --git a/src/api/APIProvider.spec.js b/src/api/APIProvider.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..938e452ebed281e7dcbbfdacb2e753a08634015b --- /dev/null +++ b/src/api/APIProvider.spec.js @@ -0,0 +1,54 @@ +import React, { useContext } from 'react' +import { mount } from '@cypress/react' +import { apiContext, APIProvider, useAPI } from './SwaggerApi'; +import { SnackbarProvider } from 'notistack'; + +function AppHarness({ children }) { + return ( + <SnackbarProvider preventDuplicate maxSnack="5"> + {children} + </SnackbarProvider> + ) +} + +function mountIntoHarness(children) { + mount(<AppHarness>{children}</AppHarness>); +} + +function checkAPIDisplayed() { + cy.get('#api').contains('CCCE API'); +} + +function DisplayAPISpecification({api}) { + return <pre id="api">{JSON.stringify(api, null, 2)}</pre>; +} + +context("API", () => { + + describe("useAPI", () => { + it('creates the SwaggerClient', () => { + const Glue = () => { + const [api] = useAPI(); + return <DisplayAPISpecification api={api} />; + } + mountIntoHarness(<Glue />) + checkAPIDisplayed(); + }) + }) + + describe("APIProvider", () => { + it('makes the API available via context', () => { + const Glue = () => { + const api = useContext(apiContext); + return <DisplayAPISpecification api={api} /> + } + mountIntoHarness( + <APIProvider> + <Glue /> + </APIProvider> + ); + checkAPIDisplayed(); + }) + }) + +}); \ No newline at end of file diff --git a/src/api/UserProvider.spec.js b/src/api/UserProvider.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..e857fed51b0550c7d3a30b7bf2178dfecc7e4644 --- /dev/null +++ b/src/api/UserProvider.spec.js @@ -0,0 +1,52 @@ +import React, { useContext } from 'react' +import { mount } from '@cypress/react' +import { APIProvider, userContext, UserProvider } from './SwaggerApi'; +import { SnackbarProvider } from 'notistack'; + +function AppHarness({ children }) { + return ( + <SnackbarProvider preventDuplicate maxSnack="5"> + <APIProvider> + {children} + </APIProvider> + </SnackbarProvider> + ) +} + +function mountIntoHarness(children) { + mount(<AppHarness>{children}</AppHarness>) +} + +function DisplayUserContextValue() { + const contextValue = useContext(userContext); + + return ( + <pre id="display"> + {JSON.stringify(contextValue, null, 2)} + </pre> + ) +} + +describe("UserProvider", () => { + context("when logged in", () => { + + beforeEach(() => { + cy.login(); + }) + + it("provides the user", () => { + mountIntoHarness( + <UserProvider> + <DisplayUserContextValue /> + </UserProvider> + ); + + cy.get("#display") + .contains("John Wayne") + .contains("Gunslinger"); + + cy.get("@userInfo.all").should('have.length', 1); + }) + + }) +}) \ No newline at end of file diff --git a/src/components/auth/TokenRenew.spec.js b/src/components/auth/TokenRenew.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..d6156e21d1d1ef8abbe246efd5d3036edf24046d --- /dev/null +++ b/src/components/auth/TokenRenew.spec.js @@ -0,0 +1,36 @@ +import { mount } from "@cypress/react"; +import { SnackbarProvider } from "notistack"; +import React from "react"; +import { APIProvider, UserProvider } from "../../api/SwaggerApi"; +import TokenRenew from "./TokenRenew"; + +function AppHarness({ children }) { + return ( + <SnackbarProvider preventDuplicate maxSnack="5"> + <APIProvider> + <UserProvider> + {children} + </UserProvider> + </APIProvider> + </SnackbarProvider> + ) +} + +function mountIntoHarness(children) { + mount(<AppHarness>{children}</AppHarness>) +} + +describe("TokenRenew", () => { + context("when logged in", () => { + beforeEach(() => { + cy.login(); + }) + + it("renews the token periodically", () => { + cy.clock(); + mountIntoHarness(<TokenRenew />); + cy.tick(window.TOKEN_RENEW_INTERVAL); + cy.wait("@renew", {responseTimeout: 1000}) + }) + }) +}) \ No newline at end of file diff --git a/src/components/common/notification/Notifications.spec.js b/src/components/common/notification/Notifications.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..11c84d8e50767bbd19e60f465b10472dabc5d9f9 --- /dev/null +++ b/src/components/common/notification/Notifications.spec.js @@ -0,0 +1,67 @@ +import { mount } from "@cypress/react"; +import { SnackbarProvider } from "notistack"; +import React, { useContext } from "react"; +import { APIProvider, userContext, UserProvider } from "../../../api/SwaggerApi"; +import { useEffectOnMount } from "../../../hooks/MountEffects"; +import { notificationContext, NotificationProvider } from "./Notifications"; + +function AppHarness({ children }) { + return ( + <SnackbarProvider preventDuplicate maxSnack="5"> + <APIProvider> + <UserProvider> + {children} + </UserProvider> + </APIProvider> + </SnackbarProvider> + ) +} + +function mountIntoHarness(children) { + mount(<AppHarness>{children}</AppHarness>) +} + +function DisplayContextValue({context}) { + const {login} = useContext(userContext); + const contextValue = useContext(context); + console.log(contextValue); + useEffectOnMount(() => login()); + + return ( + <pre id="display"> + {JSON.stringify(contextValue, (key, val) => (typeof(val) === "function") ? val.toString() : val, 2)} + </pre> + ) +} + +describe("Notifications", () => { + context("when logged in", () => { + beforeEach(() => { + cy.intercept("**/authentication/login", { token: "DEADBEEF" }) + cy.intercept("**/authentication/roles", ["Cowboy", "Gunslinger"]).as("userRoles"); + cy.intercept("**/gitHelper/userInfo", { + "fullName": "John Wayne", + "loginName": "johnwayne", + "avatar": "https://gitlab.esss.lu.se/uploads/-/system/user/avatar/92/avatar.png", + "email": null + }).as("userInfo"); + }) + + it("Provides the notification context", () => { + mountIntoHarness( + <NotificationProvider> + <DisplayContextValue context={notificationContext} /> + </NotificationProvider> + ) + + cy.wait("@userInfo"); + cy.wait("@userRoles") + + cy.get("#display") + .contains("notifications") + .contains("watchDeployment") + .contains("watchCommand") + .contains("clearNotifications") + }) + }) +}) \ No newline at end of file